package my.home.agent;
import java.util.logging.Logger;
import my.home.agent.ctx.CommandProcessor;
import my.home.agent.ctx.ExecutionContext;
import my.home.agent.ctx.TraceSnapshot;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* Advice all methods of the intercepted application. This method is called by
* AspectJ Load Time Weaver. See aop.xml in META-INF. This class defines what
* methods and classes to trace, the maximum scope. The tracing scope can be
* externally restricted in aop.xml.
* <p>
* AspectJ JVM agent initialise {@link TraceAllAgent} before any code from
* intercepted application is called, that is very early on.
* <p>
* This class in turn serves as a bootstrap class for trace agent - an
* application on its own; as bootstrap object for {@link ExecutionContext}, the
* whole agent, socket server start and other stuff.
* <p>
* This implementation of trace agent is based on Socket Server Listener. It
* listens on certain port, 4444 is default. It listens for commands like
* "start", "stop", "print". See {@link CommandProcessor} for full list. This
* enables to record method calls only in specific time, only for a specific
* functionality.
* <p>
* The application is suspended by default on the first traced method, on the
* first cross cut. This is important for tracing even very short lived
* application. User needs to issue "resume" command to start intercepted
* application. Tracing is also switched off by default. User has to issue
* "start" command.
* <p>
* Command can be issued by any socket send/receive capable application. On UNIX
* you can use basic shell command "echo" available in every distribution.
* Example: <code>echo "start" > /dev/tcp/127.0.0.1/4444</code>.
* <p>
* This trace agent can be used to trace any JVM based application, not just
* Java, but also Groovy or Scala.
*
* @author Jan Uhlir
*/
@Aspect
public class TraceAllAgent implements TraceSnapshotProvider {
private final TraceSnapshot traceSnapshot;
private final ExecutionContext ctx;
private final Logger logger = Logger.getLogger("TraceAgent");
/**
* Main constructor. Called by AspectJ LTW just in time, before any cross
* cut is reached. Bootstrap whole trace agent execution environment - the
* {@link ExecutionContext}.
*/
public TraceAllAgent() {
ctx = ExecutionContext.BOOTSTRAP.get(this);
traceSnapshot = new TraceSnapshot(ctx.getTraceWriter());
}
/**
* Define AOP point cut. Intercept all public methods on all classes,
* including library ones. Define only one essential exception, to avoid
* circular reference to itself, exclude whole 'my.home.agent' package
*/
@Pointcut("execution(public * *(..)) && !within(my.home.agent..*)")
public void anyMethod() {}
/**
* Define AOP point cut. Intercept all public methods on all classes,
* including library ones. Define only one essential exception, to avoid
* circular reference to itself, exclude whole 'my.home.agent' package
*/
@Pointcut("execution(public * *.main(..)) && !within(my.home.agent..*)")
public void mainMethod() {}
@Before("anyMethod()")
public void beforeTraceMethods(JoinPoint joinPoint) {
ctx.waitIfAppExecutionSuspended();
if (ctx.isTracingActive()) {
String className = joinPoint.getStaticPart().getSignature().getDeclaringTypeName();
String methodName = joinPoint.getStaticPart().getSignature().getName();
traceSnapshot.addClass(className);
traceSnapshot.addClassAndMethod(className + "#" + methodName + "(..)");
}
}
@After("mainMethod()")
public void afterMainMethod(JoinPoint joinPoint) {
// Register actions needed to be done when application ends.
// For multi-threaded applications it cannot be done in after main() advice,
// since threads can outlive then main() method.
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
logger.fine(">>> application ends");
ctx.waitIfAppExecutionSuspended();
// kill socket listener
ctx.stop();
logger.fine(">>> application ended");
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void flush() {
traceSnapshot.flush();
}
}