[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-users] Advice on Constructors With Subclasses

I played with Simone's code a bit, and it does seem to work as described.

Here's the solution I came up with for when the constructor call is
made within a constructor call.

This solution works assuming that the only places where A or it's
subclasses are instantiated within the constructor of A or it's
subclasses are in code that can be woven, which, for my project, is a
safe assumption.

I made the class abstract so that it can be easily reused whenever
this pattern is appropriate.

I don't like having the getMyClass() method, but I don't know of a
good way to get the generic type without it.

I am also caching the return value from isAssignableFrom, since this
call appears to be relatively expensive in some versions of java.

It's still a bit of a hack, but it seems to fulfill my needs.

public abstract aspect InitializationAspect<T> {
	/* Cache to avoid reflective calls */
	private Map<String, Boolean> isAssignableMap = new HashMap<String, Boolean>();
	
	/**
	 * Returns the class highest in the hierarchy.  Should be T.
	 */
	protected abstract Class<T> getMyClass();
	
	/**
	 * The Advice to perform after an object has been instantiated.
	 * @param t The object that was created.
	 */
	protected abstract void myAdvice(T t);
	
	/**
	 * Checks to make sure that the constructor that is executing
	 * is the type of the object to be created (i.e. lowest on the stack)
	 * and calls myAdvice() if it is. Does nothing if not.
	 * @param t The object that was created
	 */
	after(T t) : execution(T+.new(..)) && this(t)  {
		if (isLastConstructorInChain(getMyClass())) {
			myAdvice(t);
		}
	}
	
	/**
	 * Captures any calls to create a new object of type T or it's subtypes
	 * that happen within type T or it's subtypes, and calls myAdvice().
	 */
	after() returning(T t): call(T+.new()) && withincode(T+.new(..)) {
		myAdvice(t);
	}
	
	/**
	 * Returns true if a class' constructor the lowest method call in a
	 * chain of constructor calls.  If it isn't, return true if the next lowest
	 * call is not type T or a subclass of type T, false otherwise.
	 * @param clazz The class to look for.
	 */
	public boolean isLastConstructorInChain(Class<?> clazz) {
		StackTraceElement[] stackTrace =
			Thread.currentThread().getStackTrace();
		
		// find the first constructor, cause there are a few AspectJ
internal calls before it
		int acpos = 0;
		while (acpos < stackTrace.length &&
				!stackTrace[acpos].getMethodName().equals("<init>")) {
			acpos++;
		}
		// Check if we run out, should never happen
		if (acpos >= stackTrace.length) {
			return false;
		}
		// Now we have the last call to <init>
		// if the following one is not a constructor (or is not present),
		// we can skip all the checks and return true
		if (acpos + 1 == stackTrace.length ||
				!stackTrace[acpos + 1].getMethodName().equals("<init>")) {
			return true;	
		}
		// Otherwise we have to check it the next constructor in the
		// chain is a proper subclass of the current class
		String callingClass = stackTrace[acpos + 1].getClassName();
		
		Boolean isAssignable = isAssignableMap.get(callingClass);
		
		if(isAssignable == null) {
			Class<?> forname = null;
			try {
				forname = Class.forName(callingClass);
			}
			catch (ClassNotFoundException e) {
				e.printStackTrace();
				isAssignable = false;
			}
			isAssignable = !clazz.isAssignableFrom(forname);
			
			isAssignableMap.put(callingClass, isAssignable);
		}
		return isAssignable;
	}
}

public aspect AAspect extends InitializationAspect<A> {
	protected Class<A> getMyClass() {
		return A.class;
	}
	
	protected void myAdvice(A a) {
		System.out.println("Done:" + a.getClass());
	}
}