/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the QPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.joinpoint;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.io.ObjectInputStream;
import org.codehaus.aspectwerkz.exception.DefinitionException;
import org.codehaus.aspectwerkz.pointcut.ThrowsPointcut;
import org.codehaus.aspectwerkz.metadata.MethodMetaData;
import org.codehaus.aspectwerkz.metadata.ReflectionMetaDataMaker;
import org.codehaus.aspectwerkz.metadata.ClassMetaData;
import org.codehaus.aspectwerkz.IndexTuple;
import org.codehaus.aspectwerkz.SystemLoader;
import org.codehaus.aspectwerkz.System;
import org.codehaus.aspectwerkz.xmldef.XmlDefSystem;
import org.codehaus.aspectwerkz.attribdef.AttribDefSystem;
import org.codehaus.aspectwerkz.util.Util;
/**
* Matches well defined point of execution in the program where an exception is
* thrown out of a method.<br/>Stores meta data from the join point.
* I.e a reference to original object an method, the original exception etc.<br/>
* Handles the invocation of the advices added to the join point.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r</a>
*/
public class ThrowsJoinPoint extends AbstractJoinPoint {
/**
* The AspectWerkz system for this join point.
*/
protected transient System m_system;
/**
* The serial version uid for the class.
* @todo recalculate
*/
private static final long serialVersionUID = 6363637170952486892L;
/**
* The method join point for this join point.
*/
protected MethodJoinPoint m_methodJoinPoint;
/**
* The exception for this join point.
*/
protected Throwable m_exception;
/**
* The advice indexes.
*/
protected IndexTuple[] m_adviceIndexes = new IndexTuple[0];
/**
* The index of the current advice.
*/
protected int m_currentAdviceIndex = -1;
/**
* Meta-data for the class.
*/
protected ClassMetaData m_classMetaData;
/**
* Meta-data for the method.
*/
protected MethodMetaData m_methodMetaData;
/**
* The UUID for the AspectWerkz system to use.
*/
protected String m_uuid;
/**
* Creates a new throws join point.
*
* @param uuid the UUID for the AspectWerkz system to use
* @param methodJoinPoint the method join point
* @param exception the exception
*/
public ThrowsJoinPoint(final String uuid,
final MethodJoinPoint methodJoinPoint,
final Throwable exception) {
if (methodJoinPoint == null) throw new IllegalArgumentException("method join point can not be null");
if (exception == null) throw new IllegalArgumentException("exception exception can not be null");
m_system = SystemLoader.getSystem(uuid);
m_system.initialize();
m_uuid = uuid;
m_methodJoinPoint = methodJoinPoint;
m_exception = exception;
createMetaData();
Util.fakeStackTrace(m_exception, getTargetClass().getName());
loadAdvices();
}
/**
* To be called instead of proceed() when a new thread is spawned.
* Otherwise the result is unpredicable.
*
* @return the result from the next invocation
* @throws Throwable
*/
public Object proceedInNewThread() throws Throwable {
return deepCopy().proceed();
}
/**
* Invokes the next advice in the chain until it reaches the end.
*
* @return the result from a throws pointcut
* @throws Throwable
*/
public Object proceed() throws Throwable {
Object result = null;
m_currentAdviceIndex++;
if (m_currentAdviceIndex != m_adviceIndexes.length) {
try {
// TODO: handle a list of systems that nees to be matched to the advice lists (loadAdvices etc.)
if (m_system.isAttribDef()) {
AttribDefSystem system = (AttribDefSystem)m_system;
IndexTuple index = m_adviceIndexes[m_currentAdviceIndex];
int aspectIndex = index.getAspectIndex();
int methodIndex = index.getMethodIndex();
result = system.getAspect(aspectIndex).___AW_invokeAdvice(methodIndex, this);
}
else {
XmlDefSystem system = (XmlDefSystem)m_system;
result = system.getAdvice(m_adviceIndexes[m_currentAdviceIndex]).doExecute(this);
}
}
catch (ArrayIndexOutOfBoundsException ex) {
StringBuffer cause = new StringBuffer();
cause.append("advices for ");
cause.append(getTargetClass().getName());
cause.append("#");
cause.append(getMethodName());
cause.append("#");
cause.append(getExceptionName());
cause.append(" are not correctly mapped");
throw new DefinitionException(cause.toString());
}
}
return result;
}
/**
* Returns the exception.
*
* @return the exception
*/
public Throwable getException() {
return m_exception;
}
/**
* Returns the exception class.
*
* @return the exception class
*/
public Class getExceptionClass() {
return m_exception.getClass();
}
/**
* Returns the exception class name.
*
* @return the exception class name
*/
public String getExceptionName() {
return m_exception.getClass().getName();
}
/**
* Returns the message.
*
* @return the message
*/
public String getMessage() {
return m_exception.getMessage();
}
/**
* Returns the localized message.
*
* @return the localized message
*/
public String getLocalizedMessage() {
return m_exception.getLocalizedMessage();
}
/**
* Returns the line number in the file where the exception was thrown.
*
* @return the line number
*/
// Works for JDK 1.4.x only
// public int getLineNumberForThrow() {
// return m_exception.getStackTrace()[0].getLineNumber();
// }
/**
* Returns the method name where the exception was thrown.
*
* @return the method name
*/
// Works for JDK 1.4.x only
// public String getMethodNameForThrow() {
// return m_exception.getStackTrace()[0].getMethodName();
// }
/**
* Returns the file name where the exception was thrown.
*
* @return the file name
*/
// Works for JDK 1.4.x only
// public String getFileNameForThrow() {
// return m_exception.getStackTrace()[0].getFileName();
// }
/**
* Returns the class name where the exception was thrown.
*
* @return the class name
*/
// Works for JDK 1.4.x only
// public String getClassNameForThrow() {
// return m_exception.getStackTrace()[0].getClassName();
// }
/**
* Returns the target instance.
*
* @return the target instance
*/
public Object getTargetInstance() {
return m_methodJoinPoint.getTargetInstance();
}
/**
* Returns the target class.
*
* @return the target class
*/
public Class getTargetClass() {
return m_methodJoinPoint.getTargetClass();
}
/**
* Returns the target method.
*
* @return the target method
*/
public java.lang.reflect.Method getMethod() {
return m_methodJoinPoint.getMethod();
}
/**
* Returns the method name of the original invocation.
*
* @return the method name
*/
public String getMethodName() {
return m_methodJoinPoint.getMethodName();
}
/**
* Returns the parameters from the original invocation.
*
* @return the parameters
*/
public Object[] getMethodParameters() {
return m_methodJoinPoint.getParameters();
}
/**
* Returns the parameter types from the original invocation.
*
* @return the parameter types
*/
public Class[] getMethodParameterTypes() {
return m_methodJoinPoint.getParameterTypes();
}
/**
* Returns the return type from the original invocation.
*
* @return the return type
*/
public Class getMethodReturnType() {
return m_methodJoinPoint.getReturnType();
}
/**
* Loads the advices for this pointcut.
*/
protected void loadAdvices() {
synchronized (m_adviceIndexes) {
List adviceIndexes = new ArrayList();
// get all the throws pointcuts for this class
List pointcuts = m_system.getThrowsPointcuts(m_classMetaData, m_methodMetaData);
for (Iterator it = pointcuts.iterator(); it.hasNext();) {
ThrowsPointcut throwsPointcut = (ThrowsPointcut)it.next();
IndexTuple[] indexes = throwsPointcut.getAdviceIndexes();
for (int j = 0; j < indexes.length; j++) {
adviceIndexes.add(indexes[j]);
}
}
m_adviceIndexes = new IndexTuple[adviceIndexes.size()];
int i = 0;
for (Iterator it = adviceIndexes.iterator(); it.hasNext(); i++) {
m_adviceIndexes[i] = (IndexTuple)it.next();
}
}
}
/**
* Creates meta-data for the join point.
*/
protected void createMetaData() {
m_classMetaData = ReflectionMetaDataMaker.createClassMetaData(getTargetClass());
m_methodMetaData = ReflectionMetaDataMaker.createMethodMetaData(
getMethodName(),
getMethodParameterTypes(),
getMethodReturnType());
}
/**
* Makes a deep copy of the join point.
*
* @return the clone of the join point
*/
protected ThrowsJoinPoint deepCopy() {
final ThrowsJoinPoint clone = new ThrowsJoinPoint(m_uuid, m_methodJoinPoint, m_exception);
clone.m_currentAdviceIndex = m_currentAdviceIndex;
clone.m_adviceIndexes = new IndexTuple[m_adviceIndexes.length];
java.lang.System.arraycopy(m_adviceIndexes, 0, clone.m_adviceIndexes, 0, m_adviceIndexes.length);
return clone;
}
/**
* Provides custom deserialization.
*
* @param stream the object input stream containing the serialized object
* @throws java.lang.Exception in case of failure
*/
private void readObject(final ObjectInputStream stream) throws Exception {
ObjectInputStream.GetField fields = stream.readFields();
m_uuid = (String)fields.get("m_uuid", null);
m_currentAdviceIndex = fields.get("m_currentAdviceIndex", -1);
m_classMetaData = (ClassMetaData)fields.get("m_classMetaData", null);
m_methodMetaData = (MethodMetaData)fields.get("m_fieldMetaData", null);
m_methodJoinPoint = (MethodJoinPoint)fields.get("m_methodJoinPoint", null);
m_exception = (Throwable)fields.get("m_exception", null);
m_adviceIndexes = (IndexTuple[])fields.get("m_adviceIndexes", null);
m_system = SystemLoader.getSystem(m_uuid);
m_system.initialize();
}
}