blob: 8174326a8fedff1cc9bfac5d5cee67bc3858c4ab [file] [log] [blame]
package org.apache.helix.monitoring.mbeans.dynamicMBeans;
/*
* 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.
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
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.JMException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import org.apache.helix.monitoring.SensorNameProvider;
import org.apache.helix.monitoring.mbeans.MBeanRegistrar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Dynamic MBean provider that reporting DynamicMetric attributes
*/
public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNameProvider {
protected final Logger _logger = LoggerFactory.getLogger(getClass());
protected static final long DEFAULT_RESET_INTERVAL_MS = 60 * 60 * 1000; // Reset time every hour
private static final String HELIX_MONITOR_TIME_WINDOW_LENGTH_MS =
"helix.monitor.slidingTimeWindow.ms";
private static String SENSOR_NAME_TAG = "SensorName";
private static String DEFAULT_DESCRIPTION =
"Information on the management interface of the MBean";
// Attribute name to the DynamicMetric object mapping
private final Map<String, DynamicMetric> _attributeMap = new HashMap<>();
private ObjectName _objectName = null;
private MBeanInfo _mBeanInfo;
/**
* Instantiates a new Dynamic MBean provider.
* @param dynamicMetrics Dynamic Metrics that are exposed by this provider
* @param description the MBean description
* @param domain the MBean domain name
* @param keyValuePairs the MBean object name components
*/
protected synchronized boolean doRegister(Collection<DynamicMetric<?, ?>> dynamicMetrics,
String description, String domain, String... keyValuePairs) throws JMException {
return doRegister(dynamicMetrics, description,
MBeanRegistrar.buildObjectName(domain, keyValuePairs));
}
/**
* Instantiates a new Dynamic MBean provider.
* @param dynamicMetrics Dynamic Metrics that are exposed by this provider
* @param description the MBean description
* @param objectName the proposed MBean ObjectName
*/
protected synchronized boolean doRegister(Collection<DynamicMetric<?, ?>> dynamicMetrics,
String description, ObjectName objectName) throws JMException {
if (_objectName != null) {
_logger.debug("Mbean {} has already been registered. Ignore register request.",
objectName.getCanonicalName());
return false;
}
updateAttributtInfos(dynamicMetrics, description);
_objectName = MBeanRegistrar.register(this, objectName);
return true;
}
protected synchronized boolean doRegister(Collection<DynamicMetric<?, ?>> dynamicMetrics,
ObjectName objectName) throws JMException {
return doRegister(dynamicMetrics, null, objectName);
}
/**
* Update the Dynamic MBean provider with new metric list.
* @param description description of the MBean
* @param dynamicMetrics the DynamicMetrics
*/
private void updateAttributtInfos(Collection<DynamicMetric<?, ?>> dynamicMetrics,
String description) {
_attributeMap.clear();
// get all attributes that can be emit by the dynamicMetrics.
List<MBeanAttributeInfo> attributeInfoList = new ArrayList<>();
if (dynamicMetrics != null) {
for (DynamicMetric dynamicMetric : dynamicMetrics) {
Iterator<MBeanAttributeInfo> iter = dynamicMetric.getAttributeInfos().iterator();
while (iter.hasNext()) {
MBeanAttributeInfo attributeInfo = iter.next();
// Info list to create MBean info
attributeInfoList.add(attributeInfo);
// Attribute mapping for getting attribute value when getAttribute() is called
_attributeMap.put(attributeInfo.getName(), dynamicMetric);
}
}
}
// SensorName
attributeInfoList.add(new MBeanAttributeInfo(SENSOR_NAME_TAG, String.class.getName(),
"The name of the metric sensor", true, false, false));
MBeanConstructorInfo constructorInfo = new MBeanConstructorInfo(
String.format("Default %s Constructor", getClass().getSimpleName()),
getClass().getConstructors()[0]);
MBeanAttributeInfo[] attributeInfos = new MBeanAttributeInfo[attributeInfoList.size()];
attributeInfos = attributeInfoList.toArray(attributeInfos);
if (description == null) {
description = DEFAULT_DESCRIPTION;
}
_mBeanInfo = new MBeanInfo(getClass().getName(), description, attributeInfos,
new MBeanConstructorInfo[]{constructorInfo}, new MBeanOperationInfo[0],
new MBeanNotificationInfo[0]);
}
/**
* Call doRegister() to finish registration MBean and the attributes.
*/
public abstract DynamicMBeanProvider register() throws JMException;
/**
* Unregister the MBean and clean up object name record.
* Note that all the metric data is kept even after unregister.
*/
public synchronized void unregister() {
MBeanRegistrar.unregister(_objectName);
_objectName = null;
}
@Override
public Object getAttribute(String attribute)
throws AttributeNotFoundException, MBeanException, ReflectionException {
if (SENSOR_NAME_TAG.equals(attribute)) {
return getSensorName();
}
if (!_attributeMap.containsKey(attribute)) {
return null;
}
return _attributeMap.get(attribute).getAttributeValue(attribute);
}
@Override
public AttributeList getAttributes(String[] attributes) {
AttributeList attributeList = new AttributeList();
for (String attributeName : attributes) {
try {
Object value = getAttribute(attributeName);
attributeList.add(new Attribute(attributeName, value));
} catch (AttributeNotFoundException | MBeanException | ReflectionException ex) {
_logger.error("Failed to get attribute: " + attributeName, ex);
}
}
return attributeList;
}
@Override
public MBeanInfo getMBeanInfo() {
return _mBeanInfo;
}
@Override
public void setAttribute(Attribute attribute)
throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException,
ReflectionException {
// All MBeans are readonly
return;
}
@Override
public AttributeList setAttributes(AttributeList attributes) {
// All MBeans are readonly
return null;
}
@Override
public Object invoke(String actionName, Object[] params, String[] signature)
throws MBeanException, ReflectionException {
// No operation supported
return null;
}
/**
* NOTE: This method is not thread-safe nor atomic.
* Increment the value of a given SimpleDynamicMetric by 1.
*/
protected void incrementSimpleDynamicMetric(SimpleDynamicMetric<Long> metric) {
incrementSimpleDynamicMetric(metric, 1);
}
/**
* NOTE: This method is not thread-safe nor atomic.
* Increment the value of a given SimpleDynamicMetric with input value.
*/
protected void incrementSimpleDynamicMetric(SimpleDynamicMetric<Long> metric, long value) {
metric.updateValue(metric.getValue() + value);
}
/**
* Return the interval length for the underlying reservoir used by the MBean metric configured
* in the system env variables. If not found, use default value.
*/
protected Long getResetIntervalInMs() {
return getSystemPropertyAsLong(HELIX_MONITOR_TIME_WINDOW_LENGTH_MS, DEFAULT_RESET_INTERVAL_MS);
}
/**
* Get the value of system property
* @param propertyKey
* @param propertyDefaultValue
* @return
*/
private long getSystemPropertyAsLong(String propertyKey, long propertyDefaultValue) {
String valueString = System.getProperty(propertyKey, "" + propertyDefaultValue);
try {
long value = Long.parseLong(valueString);
if (value > 0) {
return value;
}
} catch (NumberFormatException e) {
_logger.warn("Exception while parsing property: " + propertyKey + ", string: " + valueString
+ ", using default value: " + propertyDefaultValue);
}
return propertyDefaultValue;
}
}