// Copyright 2005 The Apache Software Foundation
//
// 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.
package org.apache.hivemind.management.mbeans;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.AttributeNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.ReflectionException;
import org.apache.hivemind.management.impl.PerformanceCollector;
import org.apache.hivemind.management.impl.PerformanceMonitorFactory;
import org.apache.hivemind.service.MethodSignature;
import mx4j.AbstractDynamicMBean;
/**
* MBean that holds and calculates the performance data for service method calls intercepted by the
* {@link PerformanceMonitorFactory performanceMonitor}interceptor. Creates for each intercepted
* method 5 MBean attributes: Number of Calls, Minimum, maximum, average and last execution time
*
* @author Achim Huegen
* @since 1.1
*/
public class PerformanceMonitorMBean extends AbstractDynamicMBean implements PerformanceCollector
{
protected static final String DATA_TYPE_MAXIMUM_TIME = "Maximum time";
protected static final String DATA_TYPE_MINIMUM_TIME = "Minimum time";
protected static final String DATA_TYPE_LAST_TIME = "Last time";
protected static final String DATA_TYPE_AVERAGE_TIME = "Average time";
protected static final String DATA_TYPE_COUNT = "Count";
private static final String DATA_TYPE_SEPARATOR = " : ";
private Set _methods;
private Map _countersByMethodSignature = new HashMap();
private Map _countersByMethodId = new HashMap();
private MBeanAttributeInfo[] mBeanAttributeInfos;
/**
* Creates a new instance
*
* @param methods
* Set with instances of {@link org.apache.hivemind.service.MethodSignature}Contains
* the methods for that calls can be counted by this MBean
*/
public PerformanceMonitorMBean(Set methods)
{
_methods = methods;
initCounters();
initAttributes();
}
/**
* Builds two maps for accessing the counters by method signature and method id
*/
protected void initCounters()
{
for (Iterator methodIterator = _methods.iterator(); methodIterator.hasNext();)
{
MethodSignature method = (MethodSignature) methodIterator.next();
Counter counter = new Counter();
_countersByMethodSignature.put(method, counter);
_countersByMethodId.put(method.getUniqueId(), counter);
}
}
/**
* Creates for each intercepted method 5 MBean attributes: Number of Calls, Minimum, maximum,
* average and last execution time
*/
protected void initAttributes()
{
List mBeanAttributeInfoList = new ArrayList();
for (Iterator methodIterator = _methods.iterator(); methodIterator.hasNext();)
{
MethodSignature method = (MethodSignature) methodIterator.next();
addAttribute(
mBeanAttributeInfoList,
method,
Long.class,
DATA_TYPE_COUNT,
"Number of method calls for method " + method);
addAttribute(
mBeanAttributeInfoList,
method,
Long.class,
DATA_TYPE_AVERAGE_TIME,
"Average execution time in ms of method " + method);
addAttribute(
mBeanAttributeInfoList,
method,
Long.class,
DATA_TYPE_LAST_TIME,
"Last execution time in ms of method " + method);
addAttribute(
mBeanAttributeInfoList,
method,
Long.class,
DATA_TYPE_MINIMUM_TIME,
"Minimum execution time in ms of method " + method);
addAttribute(
mBeanAttributeInfoList,
method,
Long.class,
DATA_TYPE_MAXIMUM_TIME,
"Maximum execution time in ms of method " + method);
}
mBeanAttributeInfos = (MBeanAttributeInfo[]) mBeanAttributeInfoList
.toArray(new MBeanAttributeInfo[mBeanAttributeInfoList.size()]);
}
/**
* Creates a new MBean Attribute for a performance counter
*/
private void addAttribute(List mBeanAttributeInfoList, MethodSignature method,
Class attributeType, String performanceDataType, String description)
{
String attributeName = buildAttributeName(method, performanceDataType);
MBeanAttributeInfo infoCount = new MBeanAttributeInfo(attributeName, attributeType
.getName(), description, true, false, false);
mBeanAttributeInfoList.add(infoCount);
}
/**
* Builds the attribute name that holds the measurement data of type
* <code>performanceDataType</code> for the method
*/
protected String buildAttributeName(MethodSignature method, String performanceDataType)
{
String attributeName = method.getUniqueId() + DATA_TYPE_SEPARATOR + performanceDataType;
return attributeName;
}
/**
* @see PerformanceCollector#addMeasurement(MethodSignature, long)
*/
public void addMeasurement(MethodSignature method, long executionTime)
{
Counter counter = (Counter) _countersByMethodSignature.get(method);
counter.addMeasurement(executionTime);
}
protected MBeanAttributeInfo[] createMBeanAttributeInfo()
{
return mBeanAttributeInfos;
}
/**
* @see AbstractDynamicMBean#getAttribute(java.lang.String)
*/
public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException,
ReflectionException
{
// Split the attribute to get method id and performance data type separately
int posSep = attribute.indexOf(DATA_TYPE_SEPARATOR);
String methodId = attribute.substring(0, posSep);
String type = attribute
.substring(posSep + DATA_TYPE_SEPARATOR.length(), attribute.length());
Counter counter = (Counter) _countersByMethodId.get(methodId);
if (type.equals(DATA_TYPE_COUNT))
return new Long(counter.count);
else if (type.equals(DATA_TYPE_AVERAGE_TIME))
return new Long(counter.average);
else if (type.equals(DATA_TYPE_LAST_TIME))
return new Long(counter.last);
else if (type.equals(DATA_TYPE_MINIMUM_TIME))
return new Long(counter.min);
else if (type.equals(DATA_TYPE_MAXIMUM_TIME))
return new Long(counter.max);
else
throw new IllegalArgumentException("Unknown performance data type");
}
}
/**
* Class that holds and calculates the performance data for a single method
*/
class Counter
{
long count = 0;
long last = 0;
long average = 0;
long max = 0;
long min = 0;
public String toString()
{
return "" + count;
}
/**
* Should be synchronized, but this could slow things really down
*
* @param executionTime
*/
public void addMeasurement(long executionTime)
{
count++;
last = executionTime;
// not an exact value without a complete history and stored as long
average = (average * (count - 1) + executionTime) / count;
if (executionTime < min || min == 0)
min = executionTime;
if (executionTime > max || max == 0)
max = executionTime;
}
}