|
@@ -0,0 +1,226 @@
|
|
|
+/**
|
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one
|
|
|
+ * or more contributor license agreements. See the NOTICE file
|
|
|
+ * distributed with this work for additional information
|
|
|
+ * regarding copyright ownership. The ASF licenses this file
|
|
|
+ * to you 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.
|
|
|
+ */
|
|
|
+package org.apache.hadoop.metrics.util;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+import javax.management.Attribute;
|
|
|
+import javax.management.AttributeList;
|
|
|
+import javax.management.AttributeNotFoundException;
|
|
|
+import javax.management.DynamicMBean;
|
|
|
+import javax.management.InvalidAttributeValueException;
|
|
|
+import javax.management.MBeanAttributeInfo;
|
|
|
+import javax.management.MBeanException;
|
|
|
+import javax.management.MBeanInfo;
|
|
|
+import javax.management.MBeanOperationInfo;
|
|
|
+import javax.management.ReflectionException;
|
|
|
+
|
|
|
+import org.apache.hadoop.metrics.MetricsUtil;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * This abstract base class facilitates creating dynamic mbeans automatically from
|
|
|
+ * metrics.
|
|
|
+ * The metrics constructors registers metrics in a registry.
|
|
|
+ * Different categories of metrics should be in differnt classes with their own
|
|
|
+ * registry (as in NameNodeMetrics and DataNodeMetrics).
|
|
|
+ * Then the MBean can be created passing the registry to the constructor.
|
|
|
+ * The MBean should be then registered using a mbean name (example):
|
|
|
+ * MetricsHolder myMetrics = new MetricsHolder(); // has metrics and registry
|
|
|
+ * MetricsTestMBean theMBean = new MetricsTestMBean(myMetrics.mregistry);
|
|
|
+ * ObjectName mbeanName = MBeanUtil.registerMBean("ServiceFoo",
|
|
|
+ * "TestStatistics", theMBean);
|
|
|
+ *
|
|
|
+ *
|
|
|
+ */
|
|
|
+public abstract class MetricsDynamicMBeanBase implements DynamicMBean {
|
|
|
+ private final static String AVG_TIME = "AvgTime";
|
|
|
+ private final static String MIN_TIME = "MinTime";
|
|
|
+ private final static String MAX_TIME = "MaxTime";
|
|
|
+ private final static String NUM_OPS = "NumOps";
|
|
|
+ private final static String RESET_ALL_MIN_MAX_OP = "resetAllMinMax";
|
|
|
+ private MetricsRegistry metricsRegistry;
|
|
|
+ private MBeanInfo mbeanInfo;
|
|
|
+ private Map<String, MetricsBase> metricsRateAttributeMod;
|
|
|
+ private int numEntriesInRegistry = 0;
|
|
|
+ private String mbeanDescription;
|
|
|
+
|
|
|
+ protected MetricsDynamicMBeanBase(final MetricsRegistry mr, final String aMBeanDescription) {
|
|
|
+ metricsRegistry = mr;
|
|
|
+ mbeanDescription = aMBeanDescription;
|
|
|
+ createMBeanInfo();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateMbeanInfoIfMetricsListChanged() {
|
|
|
+ if (numEntriesInRegistry != metricsRegistry.size())
|
|
|
+ createMBeanInfo();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void createMBeanInfo() {
|
|
|
+ metricsRateAttributeMod = new HashMap<String, MetricsBase>();
|
|
|
+ boolean needsMinMaxResetOperation = false;
|
|
|
+ List<MBeanAttributeInfo> attributesInfo = new ArrayList<MBeanAttributeInfo>();
|
|
|
+ MBeanOperationInfo[] operationsInfo = null;
|
|
|
+ numEntriesInRegistry = metricsRegistry.size();
|
|
|
+
|
|
|
+ for (MetricsBase o : metricsRegistry.getMetricsList()) {
|
|
|
+
|
|
|
+ if (MetricsTimeVaryingRate.class.isInstance(o)) {
|
|
|
+ // For each of the metrics there are 3 different attributes
|
|
|
+ attributesInfo.add(new MBeanAttributeInfo(o.getName() + NUM_OPS, "java.lang.Integer",
|
|
|
+ o.getDescription(), true, false, false));
|
|
|
+ attributesInfo.add(new MBeanAttributeInfo(o.getName() + AVG_TIME, "java.lang.Long",
|
|
|
+ o.getDescription(), true, false, false));
|
|
|
+ attributesInfo.add(new MBeanAttributeInfo(o.getName() + MIN_TIME, "java.lang.Long",
|
|
|
+ o.getDescription(), true, false, false));
|
|
|
+ attributesInfo.add(new MBeanAttributeInfo(o.getName() + MAX_TIME, "java.lang.Long",
|
|
|
+ o.getDescription(), true, false, false));
|
|
|
+ needsMinMaxResetOperation = true; // the min and max can be reset.
|
|
|
+
|
|
|
+ // Note the special attributes (AVG_TIME, MIN_TIME, ..) are derived from metrics
|
|
|
+ // Rather than check for the suffix we store them in a map.
|
|
|
+ metricsRateAttributeMod.put(o.getName() + NUM_OPS, o);
|
|
|
+ metricsRateAttributeMod.put(o.getName() + AVG_TIME, o);
|
|
|
+ metricsRateAttributeMod.put(o.getName() + MIN_TIME, o);
|
|
|
+ metricsRateAttributeMod.put(o.getName() + MAX_TIME, o);
|
|
|
+
|
|
|
+ } else if ( MetricsIntValue.class.isInstance(o) || MetricsTimeVaryingInt.class.isInstance(o) ) {
|
|
|
+ attributesInfo.add(new MBeanAttributeInfo(o.getName(), "java.lang.Integer",
|
|
|
+ o.getDescription(), true, false, false));
|
|
|
+ } else if ( MetricsLongValue.class.isInstance(o) || MetricsTimeVaryingLong.class.isInstance(o) ) {
|
|
|
+ attributesInfo.add(new MBeanAttributeInfo(o.getName(), "java.lang.Long",
|
|
|
+ o.getDescription(), true, false, false));
|
|
|
+ } else {
|
|
|
+ MetricsUtil.LOG.error("unknown metrics type: " + o.getClass().getName());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (needsMinMaxResetOperation) {
|
|
|
+ operationsInfo = new MBeanOperationInfo[] {
|
|
|
+ new MBeanOperationInfo(RESET_ALL_MIN_MAX_OP, "Reset (zero) All Min Max",
|
|
|
+ null, "void", MBeanOperationInfo.ACTION) };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ MBeanAttributeInfo[] attrArray = new MBeanAttributeInfo[attributesInfo.size()];
|
|
|
+ mbeanInfo = new MBeanInfo(this.getClass().getName(), mbeanDescription,
|
|
|
+ attributesInfo.toArray(attrArray), null, operationsInfo, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object getAttribute(String attributeName) throws AttributeNotFoundException,
|
|
|
+ MBeanException, ReflectionException {
|
|
|
+ if (attributeName == null || attributeName.equals(""))
|
|
|
+ throw new IllegalArgumentException();
|
|
|
+
|
|
|
+ updateMbeanInfoIfMetricsListChanged();
|
|
|
+
|
|
|
+ Object o = metricsRateAttributeMod.get(attributeName);
|
|
|
+ if (o == null) {
|
|
|
+ o = metricsRegistry.get(attributeName);
|
|
|
+ }
|
|
|
+ if (o == null)
|
|
|
+ throw new AttributeNotFoundException();
|
|
|
+
|
|
|
+ if (o instanceof MetricsIntValue)
|
|
|
+ return ((MetricsIntValue) o).get();
|
|
|
+ else if (o instanceof MetricsLongValue)
|
|
|
+ return ((MetricsLongValue) o).get();
|
|
|
+ else if (o instanceof MetricsTimeVaryingInt)
|
|
|
+ return ((MetricsTimeVaryingInt) o).getPreviousIntervalValue();
|
|
|
+ else if (o instanceof MetricsTimeVaryingLong)
|
|
|
+ return ((MetricsTimeVaryingLong) o).getPreviousIntervalValue();
|
|
|
+ else if (o instanceof MetricsTimeVaryingRate) {
|
|
|
+ MetricsTimeVaryingRate or = (MetricsTimeVaryingRate) o;
|
|
|
+ if (attributeName.endsWith(NUM_OPS))
|
|
|
+ return or.getPreviousIntervalNumOps();
|
|
|
+ else if (attributeName.endsWith(AVG_TIME))
|
|
|
+ return or.getPreviousIntervalAverageTime();
|
|
|
+ else if (attributeName.endsWith(MIN_TIME))
|
|
|
+ return or.getMinTime();
|
|
|
+ else if (attributeName.endsWith(MAX_TIME))
|
|
|
+ return or.getMaxTime();
|
|
|
+ else {
|
|
|
+ MetricsUtil.LOG.error("Unexpected attrubute suffix");
|
|
|
+ throw new AttributeNotFoundException();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ MetricsUtil.LOG.error("unknown metrics type: " + o.getClass().getName());
|
|
|
+ throw new AttributeNotFoundException();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public AttributeList getAttributes(String[] attributeNames) {
|
|
|
+ if (attributeNames == null || attributeNames.length == 0)
|
|
|
+ throw new IllegalArgumentException();
|
|
|
+
|
|
|
+ updateMbeanInfoIfMetricsListChanged();
|
|
|
+
|
|
|
+ AttributeList result = new AttributeList(attributeNames.length);
|
|
|
+ for (String iAttributeName : attributeNames) {
|
|
|
+ try {
|
|
|
+ Object value = getAttribute(iAttributeName);
|
|
|
+ result.add(new Attribute(iAttributeName, value));
|
|
|
+ } catch (Exception e) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MBeanInfo getMBeanInfo() {
|
|
|
+ return mbeanInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object invoke(String actionName, Object[] parms, String[] signature)
|
|
|
+ throws MBeanException, ReflectionException {
|
|
|
+
|
|
|
+ if (actionName == null || actionName.equals(""))
|
|
|
+ throw new IllegalArgumentException();
|
|
|
+
|
|
|
+
|
|
|
+ // Right now we support only one fixed operation (if it applies)
|
|
|
+ if (!(actionName.equals(RESET_ALL_MIN_MAX_OP)) ||
|
|
|
+ mbeanInfo.getOperations().length != 1) {
|
|
|
+ throw new ReflectionException(new NoSuchMethodException(actionName));
|
|
|
+ }
|
|
|
+ for (MetricsBase m : metricsRegistry.getMetricsList()) {
|
|
|
+ if ( MetricsTimeVaryingRate.class.isInstance(m) ) {
|
|
|
+ MetricsTimeVaryingRate.class.cast(m).resetMinMax();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void setAttribute(Attribute attribute)
|
|
|
+ throws AttributeNotFoundException, InvalidAttributeValueException,
|
|
|
+ MBeanException, ReflectionException {
|
|
|
+ throw new ReflectionException(new NoSuchMethodException("set" + attribute));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public AttributeList setAttributes(AttributeList attributes) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|