com.zavakid.mushroom.impl.MetricsSourceAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.zavakid.mushroom.impl.MetricsSourceAdapter.java

Source

/**
   Copyright [2013] [Mushroom]
    
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
    
   http://www.apache.org/licenses/LICENSE-2.0
    
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
/**
 Notice : this source is extracted from Hadoop metric2 package
 and some source code may changed by zavakid
 */
package com.zavakid.mushroom.impl;

import java.util.HashMap;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.ObjectName;
import javax.management.ReflectionException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.zavakid.mushroom.Metric;
import com.zavakid.mushroom.MetricsFilter;
import com.zavakid.mushroom.MetricsSource;
import com.zavakid.mushroom.MetricsTag;
import com.zavakid.mushroom.util.Contracts;
import com.zavakid.mushroom.util.MBeans;

/**
 * An adapter class for metrics source and associated filter and jmx impl
 * 
 * @author Hadoop metric2 package's authors
 * @since 0.1
 */
public class MetricsSourceAdapter implements DynamicMBean {

    private static final Log LOG = LogFactory.getLog(MetricsSourceAdapter.class);

    private final String prefix, name;
    private final MetricsSource source;
    private final MetricsFilter recordFilter, metricFilter;
    private final HashMap<String, Attribute> attrCache;
    private final MBeanInfoBuilder infoBuilder;
    private final Iterable<MetricsTag> injectedTags;

    private Iterable<MetricsRecordImpl> lastRecs;
    private long jmxCacheTS;
    private int jmxCacheTTL;
    private MBeanInfo infoCache;
    private ObjectName mbeanName;

    MetricsSourceAdapter(String prefix, String name, String description, MetricsSource source,
            Iterable<MetricsTag> injectedTags, MetricsFilter recordFilter, MetricsFilter metricFilter,
            int jmxCacheTTL) {
        this.prefix = Contracts.checkNotNull(prefix, "prefix");
        this.name = Contracts.checkNotNull(name, "name");
        this.source = Contracts.checkNotNull(source, "source");
        attrCache = new HashMap<String, Attribute>();
        infoBuilder = new MBeanInfoBuilder(name, description);
        this.injectedTags = injectedTags;
        this.recordFilter = recordFilter;
        this.metricFilter = metricFilter;
        this.jmxCacheTTL = Contracts.checkArg(jmxCacheTTL, jmxCacheTTL > 0, "jmxCacheTTL");
    }

    MetricsSourceAdapter(String prefix, String name, String description, MetricsSource source,
            Iterable<MetricsTag> injectedTags, int period, MetricsConfig conf) {
        this(prefix, name, description, source, injectedTags, conf.getFilter(MetricsConfig.RECORD_FILTER_KEY),
                conf.getFilter(MetricsConfig.METRIC_FILTER_KEY), period);
    }

    void start() {
        if (mbeanName != null) {
            LOG.warn("MBean Source " + name + " already initialized!");
        }
        mbeanName = MBeans.register(prefix, name, this);
        LOG.info("MBean for source " + name + " registered.");
        LOG.debug("Stacktrace: " + new Throwable());
    }

    @Override
    public Object getAttribute(String attribute)
            throws AttributeNotFoundException, MBeanException, ReflectionException {
        updateJmxCache();
        synchronized (this) {
            Attribute a = attrCache.get(attribute);
            if (a == null) {
                throw new AttributeNotFoundException(attribute + " not found");
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug(attribute + ": " + a.getName() + "=" + a.getValue());
            }
            return a.getValue();
        }
    }

    @Override
    public void setAttribute(Attribute attribute)
            throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
        throw new UnsupportedOperationException("Metrics are read-only.");
    }

    @Override
    public AttributeList getAttributes(String[] attributes) {
        updateJmxCache();
        synchronized (this) {
            AttributeList ret = new AttributeList();
            for (String key : attributes) {
                Attribute attr = attrCache.get(key);
                if (LOG.isDebugEnabled()) {
                    LOG.debug(key + ": " + attr.getName() + "=" + attr.getValue());
                }
                ret.add(attr);
            }
            return ret;
        }
    }

    @Override
    public AttributeList setAttributes(AttributeList attributes) {
        throw new UnsupportedOperationException("Metrics are read-only.");
    }

    @Override
    public Object invoke(String actionName, Object[] params, String[] signature)
            throws MBeanException, ReflectionException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public MBeanInfo getMBeanInfo() {
        updateJmxCache();
        return infoCache;
    }

    private void updateJmxCache() {
        boolean getAllMetrics = false;
        synchronized (this) {
            if (System.currentTimeMillis() - jmxCacheTS >= jmxCacheTTL) {
                // temporarilly advance the expired while updating the cache
                jmxCacheTS = System.currentTimeMillis() + jmxCacheTTL;
                if (lastRecs == null) {
                    getAllMetrics = true;
                }
            } else {
                return;
            }
        }

        if (getAllMetrics) {
            MetricsBuilderImpl builder = new MetricsBuilderImpl();
            getMetrics(builder, true);
        }

        synchronized (this) {
            int cacheSize = attrCache.size(); // because updateAttrCache changes it!
            int numMetrics = updateAttrCache();
            if (cacheSize < numMetrics) {
                updateInfoCache();
            }
            jmxCacheTS = System.currentTimeMillis();
            lastRecs = null;
        }
    }

    Iterable<MetricsRecordImpl> getMetrics(MetricsBuilderImpl builder, boolean all) {
        builder.setRecordFilter(recordFilter).setMetricFilter(metricFilter);
        synchronized (this) {
            if (lastRecs == null) {
                all = true; // Get all the metrics to populate the sink caches
            }
        }
        source.getMetrics(builder, all);
        for (MetricsRecordBuilderImpl rb : builder) {
            for (MetricsTag t : injectedTags) {
                rb.add(t);
            }
        }
        synchronized (this) {
            lastRecs = builder.getRecords();
            return lastRecs;
        }
    }

    synchronized void stop() {
        MBeans.unregister(mbeanName);
        mbeanName = null;
    }

    synchronized void refreshMBean() {
        MBeans.unregister(mbeanName);
        mbeanName = MBeans.register(prefix, name, this);
    }

    private void updateInfoCache() {
        LOG.debug("Updating info cache...");
        infoCache = infoBuilder.reset(lastRecs).get();
        LOG.debug("Done");
    }

    private int updateAttrCache() {
        LOG.debug("Updating attr cache...");
        int recNo = 0;
        int numMetrics = 0;
        for (MetricsRecordImpl record : lastRecs) {
            for (MetricsTag t : record.tags()) {
                setAttrCacheTag(t, recNo);
                ++numMetrics;
            }
            for (Metric m : record.metrics()) {
                setAttrCacheMetric(m, recNo);
                ++numMetrics;
            }
            ++recNo;
        }
        LOG.debug("Done. numMetrics=" + numMetrics);
        return numMetrics;
    }

    private static String tagName(String name, int recNo) {
        StringBuilder sb = new StringBuilder(name.length() + 16);
        sb.append("tag.").append(name);
        if (recNo > 0) {
            sb.append('.').append(recNo);
        }
        return sb.toString();
    }

    private void setAttrCacheTag(MetricsTag tag, int recNo) {
        String key = tagName(tag.name(), recNo);
        attrCache.put(key, new Attribute(key, tag.value()));
    }

    private static String metricName(String name, int recNo) {
        if (recNo == 0) {
            return name;
        }
        StringBuilder sb = new StringBuilder(name.length() + 12);
        sb.append(name);
        if (recNo > 0) {
            sb.append('.').append(recNo);
        }
        return sb.toString();
    }

    private void setAttrCacheMetric(Metric metric, int recNo) {
        String key = metricName(metric.name(), recNo);
        attrCache.put(key, new Attribute(key, metric.value()));
    }

    String name() {
        return name;
    }

    MetricsSource source() {
        return source;
    }

}