package com.subhajit.metering;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.ReentrantLock;
import com.subhajit.common.jmx.util.AggregateData;
import com.subhajit.common.jmx.util.Counter;
import com.subhajit.common.jmx.util.Timer;
import com.subhajit.common.util.IConstants;
/**
* Invocation handler that counts, times and measures CPU utilization for method
* calls.
*
* <p>
* Used by {@link MeteringProxy}.
* </p>
*
* @author sdasgupta
*
*/
public class MeteringInvocationHandler implements InvocationHandler {
/**
* The target object being proxied.
*/
private final Object target;
/**
* {@link Map} in which each entry's {@link Entry#getKey()} returns a String
* representing a {@link Method} of a proxied interface, and
* {@link Entry#getValue()} returns a {@link CpuMeter} metering invocations
* of that method.
*/
private final Map<String, CpuMeter> cpuMeterMap;
/**
* {@link Map} in which each entry's {@link Entry#getKey()} returns a String
* representing a {@link Method} of a proxied interface, and
* {@link Entry#getValue()} returns a {@link Timer} metering invocation
* times for that method.
*/
private final Map<String, Timer> executionTimers;
/**
* {@link Map} in which each entry's {@link Entry#getKey()} returns a String
* representing a {@link Method} of a proxied interface, and
* {@link Entry#getValue()} returns a {@link Counter} measuring invocation
* counts of that method.
*/
private final Map<String, Counter> executionCounters;
/**
* The interfaces being proxied.
*
* @see {@link MeteringProxy}.
*/
private final Class<?>[] interfaces;
private final ReentrantLock lock;
// private final Map<Method, MethodBean> methodBeans;
/**
* Package protected constructor for use by {@link MeteringProxy}.
*
* @param target
* The target object being metered.
* @param interfaces
* Proxied interfaces implemented by the target object.
*/
MeteringInvocationHandler(Object target, Class<?>... interfaces) {
super();
executionTimers = new HashMap<String, Timer>();
executionCounters = new HashMap<String, Counter>();
this.target = target;
cpuMeterMap = new HashMap<String, CpuMeter>();
this.interfaces = new Class<?>[interfaces.length];
System.arraycopy(interfaces, 0, this.interfaces, 0, interfaces.length);
// methodBeans = new HashMap<Method, MethodBean>();
for (Class<?> klass : interfaces) {
for (Method method : klass.getMethods()) {
// MethodBean methodBean = new MethodBean(method);
// methodBeans.put(method, methodBean);
String methodStr = toString(method);
cpuMeterMap.put(methodStr, new CpuMeter());
executionTimers.put(methodStr, new Timer());
executionCounters.put(methodStr, new Counter());
}
}
lock = new ReentrantLock();
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
lock.lock();
try {
final String methodStr = toString(method);
CpuMeter cpuMeter = cpuMeterMap.get(methodStr);
AggregateData<Long> executionTimer = executionTimers.get(methodStr);
Counter executionCounter = executionCounters.get(methodStr);
executionCounter.add(1);
long t0 = System.nanoTime();
try {
cpuMeter.beginRequest();
try {
return method.invoke(target, args);
} finally {
cpuMeter.endRequest();
}
} finally {
t0 = System.nanoTime() - t0;
executionTimer.addData(t0);
}
} finally {
lock.unlock();
}
}
/**
* Gets a copy of {@link #cpuMeterMap}.
*
* @return
*/
public Map<String, CpuMeter> getCpuMeters() {
Map<String, CpuMeter> ret = new HashMap<String, CpuMeter>();
ret.putAll(cpuMeterMap);
return ret;
}
/**
* Gets a copy of {@link #executionTimers}.
*
* @return
*/
public Map<String, Timer> getExecutionTimes() {
Map<String, Timer> ret = new HashMap<String, Timer>();
ret.putAll(executionTimers);
return ret;
}
/**
* Gets a copy of {@link #executionCounters}.
*
* @return
*/
public Map<String, Counter> getExecutionCounters() {
Map<String, Counter> ret = new HashMap<String, Counter>();
ret.putAll(executionCounters);
return ret;
}
public void reset() {
lock.lock();
try {
for (Map.Entry<String, CpuMeter> entry : cpuMeterMap.entrySet()) {
entry.getValue().reset();
}
for (Map.Entry<String, Counter> entry : executionCounters
.entrySet()) {
entry.getValue().reset();
}
for (Map.Entry<String, Timer> entry : executionTimers.entrySet()) {
entry.getValue().reset();
}
} finally {
lock.unlock();
}
}
private static final ThreadLocal<Map<Method, String>> methodsMap = new ThreadLocal<Map<Method, String>>();
private static String toString(Method method) {
Map<Method, String> methods = methodsMap.get();
if (methods == null) {
methods = new HashMap<Method, String>();
methodsMap.set(methods);
}
if (methods.containsKey(method)) {
return methods.get(method);
} else {
StringBuilder methodStr = new StringBuilder().append(
method.getName()).append("(");
boolean firstArg = true;
for (Class<?> klass : method.getParameterTypes()) {
if (!firstArg) {
methodStr.append(IConstants.COMMA);
firstArg = false;
}
methodStr.append(klass.getCanonicalName());
}
methodStr.append(")");
String methodStrStr = methodStr.toString();
methods.put(method, methodStrStr);
return methodStrStr;
}
}
}