package org.perf4j.aop;
import org.perf4j.LoggingStopWatch;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import java.lang.reflect.Method;
/**
* This is the base class for TimingAspects that use the EJB interceptor framework.
* Subclasses just need to implement the {@link #newStopWatch} method to use their logging framework of choice
* (e.g. log4j or java.logging) to persist the StopWatch log message.
*
* @author Alex Devine
*/
public abstract class AbstractEjbTimingAspect extends AgnosticTimingAspect {
/**
* This is the interceptor that runs the target method, surrounding it with stop watch start and stop calls.
*
* @param ctx The InvocationContext will be passed in by the Java EE server.
* @return The return value from the executed method.
* @throws Exception Any exceptions thrown by the executed method will bubble up.
*/
@AroundInvoke
public Object doPerfLogging(final InvocationContext ctx) throws Exception {
final Method executingMethod = ctx.getMethod();
//need to get the Profiled annotation off the method, otherwise use a default
Profiled profiled = (executingMethod == null) ?
DefaultProfiled.INSTANCE :
ctx.getMethod().getAnnotation(Profiled.class);
if (profiled == null) {
profiled = DefaultProfiled.INSTANCE;
}
//note - the EJB 3.0 Interceptor spec requires that we only throw Exception, NOT throwable, but
//runProfiledMethod throws Throwable.
try {
return runProfiledMethod(
new AbstractJoinPoint() {
public Object proceed() throws Throwable { return ctx.proceed(); }
public Object getExecutingObject() { return ctx.getTarget(); }
public Object[] getParameters() { return ctx.getParameters(); }
public String getMethodName() {
return (executingMethod == null) ? "null" : executingMethod.getName();
}
public Class<?> getDeclaringClass() { return (executingMethod == null) ? null : executingMethod.getDeclaringClass() ; }
},
profiled,
newStopWatch(profiled.logger(), profiled.level())
);
} catch (Exception e) {
throw e;
} catch (Error e) {
throw e;
} catch (Throwable t) {
//in practice this should rarely happen, as in standard usage Throwables fall under Exception or Error.
//However, by the Java spec, ONLY RuntimeExceptions and Errors are unchecked, so the compiler prevents us
//from executing "throw t" at this point. This seems like a poor design decision of the EJB spec, as all
//interceptors that wish to call a method that throws Throwable need to do something special like this.
//The best we can do here is wrap the Throwable as a RuntimeException, even though this could potentially
//change the semantics from the callers point of view.
throw new RuntimeException(t);
}
}
/**
* Subclasses should implement this method to return a LoggingStopWatch that should be used to time the wrapped
* code block.
*
* @param loggerName The name of the logger to use for persisting StopWatch messages.
* @param levelName The level at which the message should be logged.
* @return The new LoggingStopWatch.
*/
protected abstract LoggingStopWatch newStopWatch(String loggerName, String levelName);
}