📄 exceptionhandling.aj
字号:
package exception;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Stack;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;
/**
* Normalizes Exception Handling within the application, based
* on the Fault/Contingency/Barrier model. The aspect addresses
* these concerns:
* <ul>
* <li>Capturing diagnostic information for fault conditions,
* including a trace of method calls leading to the fault with
* arguments and their values.
* <li>Ensuring that fault exceptions reach the handler in the
* Fault Barrier, regardless of the actions of intervening
* methods.
* <li>Translating uncaught Runtime execeptions to
* FaultExceptions, ensuring that they are logged as faults and
* ensuring that the Fault Barrier receives them.
* <li>Compile-time detection of departures from the model such
* as throwing a checked exception that is not a subclass of
* ContingencyException.
* </ul>
*
* @author Barry Ruzek
*/
public abstract aspect ExceptionHandling {
/*
* Compile-time exception policy enforcement is implemented
* here. A method or constructor may not declare that it
* throws a checked exception other than
* ContingencyException or one of its subclasses. This
* restriction forces methods and constructors to catch and
* handle checked exceptions from APIs that they use such as
* SQLException or IOException. This aspect does not specify
* what those methods do with those checked exceptions, only
* that they may never allowed to propagate past the method
* boundary.
*/
pointcut methodViolation():
execution(* *(..) throws (Exception+
&& !ContingencyException+
&& !RuntimeException+));
declare error:
methodViolation():
"Method throws a checked exception that is not a ContingencyException";
pointcut constructorViolation():
execution(*.new(..) throws (Exception+
&& !ContingencyException+
&& !RuntimeException+));
declare error:
constructorViolation():
"Constructor throws a checked exception that is not a ContingencyException";
/**
* Pointcut that determines where Exception advice is
* applied. Advice is applied to all methods in the
* application except when those methods are invoked during
* the execution of this advice. Methods in the application
* may be invoked during advice processing. The pointcut
* includes these join points:
* <ul>
* <li>All Method executions
* <li>All Constructor executions
* <li>All Object initializations
* <li>All Object pre-initializations
* <li>All Class initializations
* </ul>
* The pointcut excludes these join points:
* <ul>
* <li>Any join point within the lexical scope of this
* aspect
* <li>Any join point within the lexical scope of
* subaspects
* <li>Any join point in the control flow of advice
* execution
* </ul>
*/
pointcut exceptionAdvicePoints():
(execution (* *.*(..))
|| execution (*.new(..))
|| initialization(*.new(..))
|| preinitialization(*.new(..))
|| staticinitialization(*))
&& !within(ExceptionHandling+)
&& !cflow(adviceexecution());
/**
* Advice for detecting uncaught Throwables and translating
* them to FaultExceptions. This produces uniform treatment
* of fault conditions, making the job of the fault barrier
* easier. Any uncaught Throwable that is defined in the our
* exception model is considered to be a fault, and gets
* wrapped inside a new FaultException. Catching uncaught
* RuntimeExceptions is not enough because the JVM will
* throw Error types during static initialization if it
* finds a problem. Since our pointcut includes static
* initialization the advice here needs to handle Error
* types, too. That is, any Throwable.
*/
after() throwing(Throwable throwable):exceptionAdvicePoints(){
if (!(throwable instanceof FaultException || throwable instanceof ContingencyException)) {
throw new FaultException("Unhandled exception: ",
throwable);
}
}
/**
* Pointcut that picks out all exception handlers in the
* application, regardless of the Throwable class they are
* catching. The pointcut exposes the object that the
* handler resides in and the Throwable object that is being
* caught.
*/
pointcut allHandlers(Object handlerType, Throwable throwable):
handler(Throwable+) && args(throwable) && this(handlerType);
/**
* Advice that guarantees that FaultExceptions are caught by
* methods within classes that are designated Fault
* Barrriers. If an undesignated method inadvertently
* catches a FaultException (by catching Exception, for
* example) the FaultException will be re-thrown until it
* reaches a handler in a class designated to contain a
* FaultBarrier.
*/
before(Object handler, Throwable throwable):
allHandlers(handler, throwable) {
if (throwable instanceof FaultException) {
if (!(isFaultBarrier(handler))) {
FaultException fault = (FaultException) throwable;
throw (fault);
}
}
}
/**
* Integrates this aspect with a particular application by
* allowing the application to judge whether an object
* catching a FaultException is a designated Fault Barrier.
* Subaspects implement this method to integrate operation
* with a particular application.
*
* @param exceptionHandler
* The object about to catch a FaultException.
* @return True if the object is a designated Fault Barrier
* for the application, otherwise false.
*/
abstract boolean isFaultBarrier(Object exceptionHandler);
/**
* Introduces a flag into the FaultException class that
* allows the advice in this aspect to detect when it has
* already created the log entry for a FaultException.
* AfterThrowing advice runs for every method on the call
* stack as the FaultException propagates until it is caught
* by the Fault Barrier.
*/
private boolean FaultException.logged = false;
/**
* Determines if a FaultException has already been logged.
*
* @return True If the aspect has already logged the Fault,
* otherwise false.
*/
private boolean FaultException.isLogged() {
return this.logged;
}
/**
* Marks the FaultException as logged.
*/
private void FaultException.setLogged() {
this.logged = true;
}
/**
* Advice for detecting FaultExceptions and logging the
* diagnostic information they contain. This method uses the
* flag in the FaultException class introduced by this
* aspect to avoid logging the same fault exception multiple
* times as it is thrown outward to calling methods.
*/
after() throwing(FaultException fault): exceptionAdvicePoints(){
if (!fault.isLogged()) {
logFault(fault);
fault.setLogged();
}
}
/**
* This aspect maintains a trace stack of JoinPoint objects,
* local to each thread that traverses the application. This
* stack is maintained by advice that executes before and
* after method executions. The trace of JoinPoints enables
* method calls, parameter names, types, and values to be
* added to the diagnostic information this aspect logs when
* a fault condition is detected.
*/
private static ThreadLocal<Stack<JoinPoint>> traceStack = new ThreadLocal<Stack<JoinPoint>>() {
protected Stack<JoinPoint> initialValue() {
return new Stack<JoinPoint>();
}
};
/**
* Pushes a JoinPoint onto the trace stack.
*
* @param joinPoint
* The JoinPoint to add.
*/
private static void pushJoinPoint(JoinPoint joinPoint) {
traceStack.get().push(joinPoint);
}
/**
* Pops a JoinPoint from the trace stack.
*/
private static JoinPoint popJoinPoint() {
Stack<JoinPoint> stack = traceStack.get();
if (stack.empty()) {
return null;
} else {
JoinPoint joinPoint = stack.pop();
return joinPoint;
}
}
/**
* Obtains a snapshot of the current contents of the trace
* stack.
*/
private static JoinPoint[] getJoinPointTrace() {
Stack<JoinPoint> stack = traceStack.get();
return stack.toArray(new JoinPoint[stack.size()]);
}
/**
* Advice that pushes the class, method, and argument values
* onto the trace stack before a working method runs.
*/
before(): exceptionAdvicePoints(){
pushJoinPoint(thisJoinPoint);
}
/**
* Advice that pops the class, method, and argument values
* from the trace stack after a working method runs.
*/
after(): exceptionAdvicePoints(){
popJoinPoint();
}
/**
* Records the diagnostic information contained in a
* FaultException and the thread local trace stack
* maintained by this aspect.
*
* @param fault
* The fault exception to log.
*/
private void logFault(FaultException fault) {
ByteArrayOutputStream traceInfo = new ByteArrayOutputStream();
PrintStream traceStream = new PrintStream(traceInfo);
fault.printStackTrace(traceStream);
StringBuffer applicationTrace = new StringBuffer();
JoinPoint[] joinPoints = getJoinPointTrace();
for (int i = joinPoints.length - 1; i >= 0; i--) {
applicationTrace.append("\n\t"
+ formatJoinPoint(joinPoints[i]));
}
recordFaultDiagnostics("Application Fault Detected"
+ "\n" + traceInfo.toString()
+ "\nApplication Trace:"
+ applicationTrace.toString());
}
/**
* The aspect invokes this abstract method to record the
* fault diagnostics that it generates upon fault detection.
* Subaspects supply concrete behavior for this recording,
* allowing applications to decide how the recording
* happens.
*
* @param diagnostics
* The diagnotic string, generated by this aspect
* that must be recorded.
*/
abstract void recordFaultDiagnostics(String diagnostics);
/**
* Produces a string that contains the executing class,
* method, and argument names, types, and values that are
* present in a JointPoint object. The format is:
* class.method(arg:type=value, ..., arg:type=value)
*
* @param joinPoint
* The joint point.
* @return The formatted join point information.
*/
private String formatJoinPoint(JoinPoint joinPoint) {
CodeSignature signature = (CodeSignature) joinPoint.getSignature();
String[] names = signature.getParameterNames();
Class[] types = signature.getParameterTypes();
Object[] args = joinPoint.getArgs();
StringBuffer argumentList = new StringBuffer();
for (int i = 0; i < args.length; i++) {
if (argumentList.length() != 0) {
argumentList.append(", ");
}
argumentList.append(names[i]);
argumentList.append(":");
argumentList.append(types[i].getName());
argumentList.append("=");
argumentList.append(args[i]);
}
StringBuffer format = new StringBuffer();
format.append(joinPoint.getKind());
format.append(": ");
format.append(signature.getDeclaringTypeName());
format.append(".");
format.append(signature.getName());
format.append("(");
format.append(argumentList);
format.append(")");
return format.toString();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -