|
View:
New views
19 Messages
—
Rating Filter:
Alert me
|
|
|
Advice on Constructors With SubclassesI am attempting to write some advice that will run only after the construction has completely finished, i.e. after the an object has been completely initialized.
An object can be created in any of the types in an inheritance tree, and thus, I would like to advise all of the constructors, but only run the advice once, after the object has been constructed. This is probably easier to describe via an example: public class A { public A() { System.out.println("init A"); } } public class B extends A { public B() { super(); System.out.println("init B"); } } public class C extends B { public C() { super(); System.out.println("init C"); } } after(A item) : execution(A+.new()) && target(item) { System.out.println(thisJoinPoint.getSourceLocation()); } public static void main(String[] args) { C c = new C(); B b = new B(); } The above code has the following output: init A A.java:3 init B B.java:3 init C C.java:3 init A A.java:3 init B B.java:3 What I am attempting to create is advice that will produce the following: init A init B init C C.java:3 init A init B B.java:3 i.e. Only run the advice after the object is completely created I have tried a few approaches, including !within(A+.new()) and !cflow(execution(A+.new()), but due to the way the compiler inlines the super() calls, these approaches produce the same results as above. Does anybody have any experience with this problem, and/or suggestions on how to solve it? |
|
|
Re: Advice on Constructors With SubclassesTry the following:
after() returning(A created): call(A+.new(..)) { } Eric 2008/9/2 BiggusJimmus <jvelliott@...>: > > I am attempting to write some advice that will run only after the > construction has completely finished, i.e. after the an object has been > completely initialized. > > An object can be created in any of the types in an inheritance tree, and > thus, I would like to advise all of the constructors, but only run the > advice once, after the object has been constructed. > > This is probably easier to describe via an example: > > public class A { > public A() { > System.out.println("init A"); > } > } > > public class B extends A { > public B() { > super(); > System.out.println("init B"); > } > } > > public class C extends B { > public C() { > super(); > System.out.println("init C"); > } > } > > after(A item) : execution(A+.new()) && target(item) { > System.out.println(thisJoinPoint.getSourceLocation()); > } > > public static void main(String[] args) { > C c = new C(); > B b = new B(); > } > > The above code has the following output: > init A > A.java:3 > init B > B.java:3 > init C > C.java:3 > > init A > A.java:3 > init B > B.java:3 > > What I am attempting to create is advice that will produce the following: > init A > init B > init C > C.java:3 > > init A > init B > B.java:3 > > i.e. Only run the advice after the object is completely created > > I have tried a few approaches, including !within(A+.new()) and > !cflow(execution(A+.new()), but due to the way the compiler inlines the > super() calls, these approaches produce the same results as above. > > Does anybody have any experience with this problem, and/or suggestions on > how to solve it? > -- > View this message in context: http://www.nabble.com/Advice-on-Constructors-With-Subclasses-tp19280739p19280739.html > Sent from the AspectJ - users mailing list archive at Nabble.com. > > _______________________________________________ > aspectj-users mailing list > aspectj-users@... > https://dev.eclipse.org/mailman/listinfo/aspectj-users > -- Eric Bodden Sable Research Group McGill University, Montréal, Canada _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With SubclassesThanks a lot. Guess I never tried the "returning" keyword.
Much appreciated. _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With SubclassesActually, it is the "call", not the "returning" that made it work.
Should probably work with or without the "returning" clause. In AspectJ, super calls are not considered "calls" and so are not matched by the call designator, but the method bodies are executed, hence they *are* matched by the execution designator. On Tue, Sep 2, 2008 at 8:25 PM, James Elliott <jvelliott@...> wrote: > Thanks a lot. Guess I never tried the "returning" keyword. > > Much appreciated. > _______________________________________________ > aspectj-users mailing list > aspectj-users@... > https://dev.eclipse.org/mailman/listinfo/aspectj-users > _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With SubclassesI could have *sworn* I tried doing this with the call before, but it
looks like you're right. I think I might have tried doing it while improperly using target/this instead of returning, which probably broke the pointcut, so I just skipped right past it. Amazing how you can spend so much time dancing right around the answer. Thanks again. _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With SubclassesHi all,
I also stumbled upon this problem. In fact, I think AspectJ is missing something here. There should be a way to say after "the object has been completely initialized". That is because I might want to advice the constructor of an abstract class and call abstract methods, which will probably fail if the advice is executed after the abstract class constructor instead of after the concrete class constructor. Using the call instead of execution is a solution, but limited to the situation where the aspects are consistently applied, which could not be the case if I'm programming my own jar using AOP but then using it in a non-aspectj system. In fact, using "execution", AspectJ will add the code to my own classes, while using call requires the client code of my library to be weaved. Simone Andrew Eisenberg wrote: > Actually, it is the "call", not the "returning" that made it work. > Should probably work with or without the "returning" clause. > > In AspectJ, super calls are not considered "calls" and so are not > matched by the call designator, but the method bodies are executed, > hence they *are* matched by the execution designator. > > On Tue, Sep 2, 2008 at 8:25 PM, James Elliott <jvelliott@...> wrote: > >> Thanks a lot. Guess I never tried the "returning" keyword. >> >> Much appreciated. >> _______________________________________________ >> aspectj-users mailing list >> aspectj-users@... >> https://dev.eclipse.org/mailman/listinfo/aspectj-users >> >> > _______________________________________________ > aspectj-users mailing list > aspectj-users@... > https://dev.eclipse.org/mailman/listinfo/aspectj-users > -- Simone Gianni http://www.simonegianni.it/ CEO Semeru s.r.l. Apache Committer _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With SubclassesOn Thu, Sep 4, 2008 at 8:37 AM, Simone Gianni <simoneg@...> wrote:
> Hi all, > I also stumbled upon this problem. In fact, I think AspectJ is missing > something here. There should be a way to say after "the object has been > completely initialized". This is what after() : (Object+.new()) does. > That is because I might want to advice the > constructor of an abstract class and call abstract methods, which will > probably fail if the advice is executed after the abstract class > constructor instead of after the concrete class constructor. Using the > call instead of execution is a solution, Yes. > but limited to the situation > where the aspects are consistently applied, which could not be the case > if I'm programming my own jar using AOP but then using it in a > non-aspectj system. In fact, using "execution", AspectJ will add the > code to my own classes, while using call requires the client code of my > library to be weaved. Another possibility might be initialization joinpoints. It's not exactly the same, but might be what you want in this situation. http://www.eclipse.org/aspectj/doc/released/faq.html#q:initializationjoinpoints > > Simone > > Andrew Eisenberg wrote: >> Actually, it is the "call", not the "returning" that made it work. >> Should probably work with or without the "returning" clause. >> >> In AspectJ, super calls are not considered "calls" and so are not >> matched by the call designator, but the method bodies are executed, >> hence they *are* matched by the execution designator. >> >> On Tue, Sep 2, 2008 at 8:25 PM, James Elliott <jvelliott@...> wrote: >> >>> Thanks a lot. Guess I never tried the "returning" keyword. >>> >>> Much appreciated. >>> _______________________________________________ >>> aspectj-users mailing list >>> aspectj-users@... >>> https://dev.eclipse.org/mailman/listinfo/aspectj-users >>> >>> >> _______________________________________________ >> aspectj-users mailing list >> aspectj-users@... >> https://dev.eclipse.org/mailman/listinfo/aspectj-users >> > > > -- > Simone Gianni > http://www.simonegianni.it/ > CEO Semeru s.r.l. > Apache Committer > > _______________________________________________ > aspectj-users mailing list > aspectj-users@... > https://dev.eclipse.org/mailman/listinfo/aspectj-users > aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With SubclassesHi Andrew,
yes, initialization joinpoints seems to be what is needed in this case. Thanks for your pointer! Simone Andrew Eisenberg wrote: > On Thu, Sep 4, 2008 at 8:37 AM, Simone Gianni <simoneg@...> wrote: > >> Hi all, >> I also stumbled upon this problem. In fact, I think AspectJ is missing >> something here. There should be a way to say after "the object has been >> completely initialized". >> > > This is what after() : (Object+.new()) does. > > >> That is because I might want to advice the >> constructor of an abstract class and call abstract methods, which will >> probably fail if the advice is executed after the abstract class >> constructor instead of after the concrete class constructor. Using the >> call instead of execution is a solution, >> > > Yes. > > >> but limited to the situation >> where the aspects are consistently applied, which could not be the case >> if I'm programming my own jar using AOP but then using it in a >> non-aspectj system. In fact, using "execution", AspectJ will add the >> code to my own classes, while using call requires the client code of my >> library to be weaved. >> > > Another possibility might be initialization joinpoints. It's not > exactly the same, but might be what you want in this situation. > > http://www.eclipse.org/aspectj/doc/released/faq.html#q:initializationjoinpoints > > > >> Simone >> >> Andrew Eisenberg wrote: >> >>> Actually, it is the "call", not the "returning" that made it work. >>> Should probably work with or without the "returning" clause. >>> >>> In AspectJ, super calls are not considered "calls" and so are not >>> matched by the call designator, but the method bodies are executed, >>> hence they *are* matched by the execution designator. >>> >>> On Tue, Sep 2, 2008 at 8:25 PM, James Elliott <jvelliott@...> wrote: >>> >>> >>>> Thanks a lot. Guess I never tried the "returning" keyword. >>>> >>>> Much appreciated. >>>> _______________________________________________ >>>> aspectj-users mailing list >>>> aspectj-users@... >>>> https://dev.eclipse.org/mailman/listinfo/aspectj-users >>>> >>>> >>>> >>> _______________________________________________ >>> aspectj-users mailing list >>> aspectj-users@... >>> https://dev.eclipse.org/mailman/listinfo/aspectj-users >>> >>> >> -- >> Simone Gianni >> http://www.simonegianni.it/ >> CEO Semeru s.r.l. >> Apache Committer >> >> _______________________________________________ >> aspectj-users mailing list >> aspectj-users@... >> https://dev.eclipse.org/mailman/listinfo/aspectj-users >> >> > _______________________________________________ > aspectj-users mailing list > aspectj-users@... > https://dev.eclipse.org/mailman/listinfo/aspectj-users > -- Simone Gianni http://www.simonegianni.it/ CEO Semeru s.r.l. Apache Committer _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With SubclassesIn trying to achieve the same results after an object has been
constructed using reflection, I have run into a wall. I can't use the call joinpoints for this case, since the pointcut obviously won't match on the newInstance() call, not to mention the client code weaving that Simone was talking about. I have tried using initialization joinpoints as Andrew suggested, but I keep running into the same problems that I had in my original posting, namely, the advice is being run after every super call. My first thought was to advise calls to Class.newInstance() as well, but this is problematic because some of the calls are being made from an external library that I am using, and I can't advise calls within that library. Similarly, I can't advise the execution of newInstance() because that would be advising the standard library. My current pointcut looks like this: after(A a) returning: initialization(A+.new(..)) && this(a) { System.out.println(thisJoinPoint.getSourceLocation()); } And the output is back to the same as it was in my original question, for both normal construction, and reflection-style construction. Any suggestions? _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With SubclassesHi James,
In my humble opinion, AspectJ should offer a way to execute something after the complete initialization cycle (which includes all calls to super() or other this()), while still using an "execution like" pointcut, and weaving the target classes and not the client classes. I'll try to write what I understand about it, Andy or Andrew will then correct my errors. A naive approach could be to make an around advice on constructors, build a stack of calls, and then execute the advice code only when the stack is empty (aka, we are exiting from the last constructor). Unfortunately this can not work, because the super() call (or the call to another this() constructor) in java must be the first instruction of a constructor, so declaring a before advice on a constructor pointcut will execute before the constructor code, but after the call to super(), so our stack would always contain only one element, and always execute the code. For the same reason, using cflow or cflowbelow does not work : cflow uses a counter to determine if it has or has not yet executed a certain method, but since the super() call must be executed before this counter is set, it will not see the flow of calls. Anyway, Java provides a way to inspect the current thread stack trace. Having it it's possible to understand if we are in the "after" advice of a constructor called by another constructor, and in that case avoid executing potentially offending code. This is how I solved my problem, a while ago, in first instance. Then I refactored the code and removed the need for such an after advice. Anyway, it would be great to have AspectJ implement such a check, and provide a way to say "I want this to execute AFTER the object has been completely initialized (all the constructors chain has finished)", and make this while advicing and weaving target classes and not client classes. For example, if I have the following classes : public class A { public A() { System.out.println("I'm in A"); } } public class B extends A { public B() { super(); // It's implicit anyway System.out.println("I'm in B"); } } public class C extends B { public C() { super(); // It's implicit anyway System.out.println("I'm in C"); } } Writing an advice like : after() : hasBeenCompletelyInitialized(A+.new()) { System.out.println("Ok, I can run"); } Should produce : new A(); I'm in A Ok, I can run new B(); I'm in A I'm in B Ok, I can run new C(); I'm in A I'm in B I'm in C Ok, I can run The simple way to implement it (both the AspectJ new pointcut, if there are good usecases for it, or your temporary solution) is to inspect the stack trace before executing the advice to see if this is the last call to the initialization chain. Unfortunately simply checking for "the number of <init> methods in the stack trace" if not enought, cause a "new A()" could be called from inside the constructor of another class Z, so inspection of the real A-B-C hierarchy is needed. Anyway, if this was an AspectJ keyword, such a check could be optimized performing part of the analysis at compile time. To make things clear, the following aspect works, but it still misses a special case where a new A() (or new B() or new C()) is called from inside the constructor of A (or B or C) deliberately by the code. This could be anyway prevented using common cflow pointcuts or adding a before advice to manually implement a check for this case. WARNING : ugly code follows, used only to demonstrate a proof of concept, handle it with care! package it.semeru.tests.initialization; public aspect InitializationAspect { pointcut initializing() : execution(A+.new()); after() : initializing() { if (isLastConstructorInChain(A.class)) System.out.println("Done"); } 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) return true; if (!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(); Class<?> forname = null; try { forname = Class.forName(callingclass); } catch (ClassNotFoundException e) { e.printStackTrace(); return false; } if (clazz.isAssignableFrom(forname)) { return false; } return true; } } Called with the following main : public static void main(String[] args) { new A(); new B(); new C(); } Outputs : I'm in A Done I'm in A I'm in B Done I'm in A I'm in B I'm in C Done Hope this helps, Simone James Elliott wrote: > In trying to achieve the same results after an object has been > constructed using reflection, I have run into a wall. > > I can't use the call joinpoints for this case, since the pointcut > obviously won't match on the newInstance() call, not to mention the > client code weaving that Simone was talking about. > > I have tried using initialization joinpoints as Andrew suggested, but > I keep running into the same problems that I had in my original > posting, namely, the advice is being run after every super call. > > My first thought was to advise calls to Class.newInstance() as well, > but this is problematic because some of the calls are being made from > an external library that I am using, and I can't advise calls within > that library. Similarly, I can't advise the execution of > newInstance() because that would be advising the standard library. > > My current pointcut looks like this: > after(A a) returning: initialization(A+.new(..)) && this(a) { > System.out.println(thisJoinPoint.getSourceLocation()); > } > > And the output is back to the same as it was in my original question, > for both normal construction, and reflection-style construction. > > Any suggestions? > _______________________________________________ > aspectj-users mailing list > aspectj-users@... > https://dev.eclipse.org/mailman/listinfo/aspectj-users > -- Simone Gianni http://www.simonegianni.it/ CEO Semeru s.r.l. Apache Committer _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With Subclasses> I also stumbled upon this problem. In fact, I think AspectJ is missing
> something here. There should be a way to say after "the object has been > completely initialized". My initial reaction without thinking through all details is that I agree there is something missing. Feel free to raise an enhancement request to
discuss it. cheers, Andy. 2008/9/4 Simone Gianni <simoneg@...> Hi all, _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
|
|
|
Re: Advice on Constructors With SubclassesI 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()); } } _______________________________________________ aspectj-users mailing list aspectj-users@... https://dev.eclipse.org/mailman/listinfo/aspectj-users |
|
|
Re: Advice on Constructors With SubclassesHi James,
nice to hear my code was somewhat useful. Have you seen the comments on your feature request? Simone James Elliott wrote: > 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.o |