/*******************************************************************************
* Copyright (c) 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* Contributors:
* Matthew Webster - initial implementation
* Sian January
*******************************************************************************/
package org.aspectj.lib.tracing;
import org.aspectj.lang.*;
/**
* This root abstract aspect determines the basic tracing behaviour
* i.e. entry/exit/exception using the method/constructor execution() pointcut
* and before/after returning/after throwing advice. Determining what
* methods and constructors belonging to which classes is delegated to a
* user-supplied concrete aspect using an abstract pointcut. When tracing
* occurs and what is done with the captured data is delegated to an abstract,
* infrastructure-specific sub-aspect through template methods.
*/
public abstract aspect Tracing {
/**
* Sub-aspects must implement this pointcut to determine what and when to
* trace
*/
protected abstract pointcut shouldTrace ();
private pointcut staticContext () : !this(Object);
private pointcut nonStaticContext (Object obj) : this(obj);
private pointcut voidMethod () : execution(void *(..));
public final static pointcut methodExecution () : execution(* *(..));
public final static pointcut constructorExecution () : execution(new(..));
public final static pointcut objectMethod () : execution(* Object.*(..));
/**
* Sub-aspects may override this point to determine which methods if any
* are traced. By default include only public methods and those not inherited
* from java.lang.Object e.g. toString().
*/
protected pointcut includedMethod () :
execution(public * *(..))
&& !objectMethod();
/**
* Sub-aspects may override this point to determine which constructors if any
* are traced. By default include only public constructors.
*/
protected pointcut includedConstructor () :
execution(public new(..));
/*
* Exclude methods and constructors in Tracing and sub-aspects as well as
* those in the control flow of Tracing advice or constructors to avoid recursion.
*/
private pointcut excluded () :
within(Tracing+)
// || cflow((adviceexecution() || execution(new(..))) && within(Tracing+))
|| cflow((adviceexecution() && within(Tracing+)))
;
/*
* Trace only method execution included by the user but excluded by the aspect e.g. itself
*/
private pointcut tracedMethod () :
methodExecution()
&& includedMethod()
&& !excluded()
;
/*
* Trace only constructor execution included by the user but excluded by the aspect e.g. itself
*/
private pointcut tracedConstructor (Object obj) :
constructorExecution()
&& includedConstructor()
&& !excluded()
&& this(obj)
;
/*
* Trace entry to instance methods
*
* Tracing pattern 1: Only use thisJoinPoint in before()
*/
before (Object obj) : tracedMethod() && nonStaticContext(obj) && shouldTrace() {
enter(thisJoinPoint,obj);
}
/*
* Trace entry to static methods
*
* Tracing pattern 1: Only use thisJoinPoint in before()
*/
before () : tracedMethod() && staticContext() && shouldTrace() {
enter(thisJoinPoint);
}
/*
* Trace exit from void methods
*
* Tracing pattern 1: Use thisJoinPointStaticPart in after()
*/
after() returning() : tracedMethod() && voidMethod() && shouldTrace() {
exit(thisJoinPointStaticPart);
}
/*
* Trace exit from non-void methods including return value
*
* Tracing pattern 1: Use thisJoinPointStaticPart in after()
*/
after() returning(Object ret) : tracedMethod() && !voidMethod() && shouldTrace() {
exit(thisJoinPointStaticPart,ret);
}
/*
* Trace exceptions thrown from methods and constructors
*
* Tracing pattern 1: Use thisJoinPointStaticPart in after()
*/
after() throwing(Throwable th) : (tracedMethod() || tracedConstructor(Object)) && shouldTrace() {
if (shouldTrace(th)) exception(thisJoinPointStaticPart,th);
}
/*
* Trace entry to constructors
*
* Tracing pattern 1: Only use thisJoinPoint in before()
*/
before () : tracedConstructor(Object) && shouldTrace() {
enter(thisJoinPoint);
}
/*
* Trace exit from constructors including new object
*
* Tracing pattern 1: Only use thisJoinPoint in before()
*/
after (Object obj) : tracedConstructor(obj) && shouldTrace() {
exit(thisJoinPointStaticPart,obj);
}
/*
* Template methods to log data implemented by infrastructure-specific sub-aspects
* e.g. java.util.logging.Logger
*/
protected abstract void enter (JoinPoint jp, Object obj);
protected abstract void enter (JoinPoint jp);
protected abstract void exit (JoinPoint.StaticPart sjp);
protected abstract void exit (JoinPoint.StaticPart sjp, Object ret);
protected abstract void exception (JoinPoint.StaticPart sjp, Throwable th);
/**
* Format arguments into a comma separated list
*
* @param names array of argument names
* @param args array of arguments
* @return the formated list
*/
protected String formatArgs (String[] names, Object[] args) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < args.length; i++) {
sb.append(formatParam(names[i],args[i]));
if (i < args.length-1) sb.append(", ");
}
return sb.toString();
}
/**
* Format objects safely avoiding toString which can cause recursion,
* NullPointerExceptions or highly verbose results.
*
* @param obj parameter to be formatted
* @return the formated parameter
*/
protected Object formatObj (Object obj) {
if (obj == null
|| obj instanceof String
|| obj instanceof Number
|| obj instanceof Boolean
|| obj instanceof Character
|| obj instanceof Class
|| obj instanceof StringBuffer
) return obj;
else try {
return obj.getClass().getName() + "@" + Integer.toString(obj.hashCode(),16);
} catch (Exception ex) {
return obj.getClass().getName();
}
}
/**
* Format parameter into name=value pair
*
* @param name parameter name
* @param arg parameted to be formatted
* @return the formated parameter
*/
protected String formatParam (String name, Object arg) {
return name + "=" + formatObj(arg);
}
/**
* By default we do not trace errors e.g. OutOfMemoryError because the
* system my be in an inconsistent state. However users may override this
*
* @param th excpeption or error to be traced
* @return whether it should be traced
*/
protected boolean shouldTrace (Throwable th) {
return !(th instanceof Error);
}
}