package de.is24.util.monitoring.wrapper;
import de.is24.util.monitoring.InApplicationMonitor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* This class provides the ability to wrap arbitrary objects and monitor the
* execution times of methods invoked on the object. The wrapped object must be
* described by an interface so that the wrapper can generate a proxy object
* during runtime.
*
* <p>
* The wrapper can be configured with a {@link TimingReporter} that is used to
* report the timinig measurement of a single method invocation. A default
* implementation based on the {@link de.is24.util.monitoring.InApplicationMonitor} is provided.
* </p>
*
* <p>
* Use one of the static {@link #wrapObject(Class, Object)} or
* {@link #wrapObject(Class, Object, TimingReporter)} factory methods to wrap a
* given object. Here is a small code snipped demonstrating the use:
*
* <pre>
* final Connection connection = dataSource.getConnection();
* final wrappedConnection = GenericMonitoringWrapper.wrapObject(Connection.class, connection);
* </pre>
*
* <p>Now, if you make method calls on the <code>wrappedConnection</code> object,
* each method invocation will be reported to the {@link de.is24.util.monitoring.InApplicationMonitor},
* as the {@link InApplicationMonitorTimingReporter} is the default reporter to
* use in case none is given.
* <p>
*
* @see de.is24.util.monitoring.InApplicationMonitor
* @see java.lang.reflect.Proxy
*
* @author Alexander Metzner
*
* @param <E>
* the interface of the object being wrapped.
*/
public class GenericMonitoringWrapper<E> implements InvocationHandler {
/** Stores the interface the wrapped object want's to expose. */
private final Class<E> targetClass;
/** The target object being wrapped. */
private final Object target;
/** The reported used to report method invocation timings. */
private final TimingReporter reporter;
/**
* Wraps the given object and returns the reporting proxy. Uses the
* {@link InApplicationMonitorTimingReporter} to report the timings.
*
* @param <E>
* the type of the public interface of the wrapped object
* @param clazz
* the class object to the interface
* @param target
* the object to wrap
* @return the monitoring wrapper
*/
public static <E> E wrapObject(final Class<E> clazz, final Object target) {
return wrapObject(clazz, target, new InApplicationMonitorTimingReporter());
}
/**
* Wraps the given object and returns the reporting proxy. Uses the given
* timing reporter to report timings.
*
* @param <E>
* the type of the public interface of the wrapped object
* @param clazz
* the class object to the interface
* @param target
* the object to wrap
* @param timingReporter
* the reporter to report timing information to
* @return the monitoring wrapper
*/
@SuppressWarnings("unchecked")
public static <E> E wrapObject(final Class<E> clazz, final Object target, final TimingReporter timingReporter) {
return (E) Proxy.newProxyInstance(GenericMonitoringWrapper.class.getClassLoader(),
new Class[] { clazz },
new GenericMonitoringWrapper<E>(clazz, target, timingReporter));
}
/**
* Constructs a new wrapper object. Use internally by the static
* {@link #wrapObject(Class, Object)} factory methods.
*
* @param targetClass
* the class object of the public interface to wrap
* @param target
* the target object to wrap
* @param timingReporter
* the timing reporter
*/
protected GenericMonitoringWrapper(final Class<E> targetClass, final Object target,
final TimingReporter timingReporter) {
this.targetClass = targetClass;
this.target = target;
this.reporter = timingReporter;
}
/**
* Handles method invocations on the generated proxy. Measures the time needed
* to execute the given method on the wrapped object.
*
* @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) /* CSOFF: IllegalThrows */
throws Throwable /* CSON: IllegalThrows */ {
final long startTime = System.currentTimeMillis();
Object result = null;
try {
result = method.invoke(target, args);
if ((result != null) && !method.getReturnType().equals(Void.TYPE) && method.getReturnType().isInterface()) {
result = wrapObject(method.getReturnType(), result, reporter);
}
} catch (InvocationTargetException t) {
if (t.getCause() != null) {
throw t.getCause();
}
} finally {
final long endTime = System.currentTimeMillis();
reporter.reportTimedOperation(targetClass, method, startTime, endTime);
}
return result;
}
/**
* Interface for objects that receive and handle timinig information for a
* given method invocation.
*
* @author Alexander Metzner
*
*/
public static interface TimingReporter {
void reportTimedOperation(Class<?> targetClass, Method targetMethod, long startTime, long endTime);
}
/**
* Default implementation of the {@link TimingReporter} interface that uses
* the {@link de.is24.util.monitoring.InApplicationMonitor} as it's backend.
*
* @author Alexander Metzner
*
*/
public static class InApplicationMonitorTimingReporter implements TimingReporter {
@Override
public void reportTimedOperation(Class<?> targetClass, Method targetMethod, long startTime, long endTime) {
InApplicationMonitor.getInstance()
.addTimerMeasurement(targetClass.getName() + "." + targetMethod.getName(),
startTime, endTime);
}
}
}