Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
AspectJ generics use cases (was Re: [aspectj-users] Dynamic type casting)

This thread raises interesting questions about how AspectJ will support generics. I would definitely like to see generic advice, e.g., replacing Wes's example code with

    <T> T around() : loaders() {
        return <T>make(thisJoinPoint.getArgs());
    }

I believe generic pointcuts should be supported in generics aspects, e.g.,:

aspect Persistence <T> 
   pointcut loaders () : execution(T load(..));
   T around() : loaders() {
        return <T>make(thisJoinPoint.getArgs());
   }
...
}

And they should also be able to be declared directly:

aspect Persistence {
   <T> pointcut loaders () : execution(T load(..));
   // I haven't done the analysis of whether type inference is feasible here...
   <T> T around() : loaders<T>() {
   //<T> T around() : loaders() {
        return <T>make(thisJoinPoint.getArgs());
    }
...
}

Another common example for generics support is static initialization of per-class loggers:

public aspect Logging <T extends Loggable> {
    // extending a familiar idiom: using a marker interface to tag classes that a 
    // generic aspect will affect
    // note generics can be used with marker interfaces but not metadata tags...
    declare parents: com.example..* implements Loggable;
    private static Logger T.logger;
    public Logger T.getLogger() {
        return T.logger;
    }

    pointcut initializationInPackage() : staticintialization(T);
    after() returning : initializationInPackage() {
        T.logger = new Logger(T.class);
    }
}

public interface Loggable {
    Logger getLogger();
}

Ron

Ron Bodkin
Chief Technology Officer
New Aspects of Security
m: (415) 509-2895

> ------------Original Message-------------
> From: Wes Isberg <wes@xxxxxxxxxxxxxx>
> To: aspectj-users@xxxxxxxxxxx
> Date: Wed, Sep-3-2003 11:48 AM
> Subject: Re: [aspectj-users] Dynamic type casting
> 
> Hi Venkat, Ramnivas -
> 
> Venkat brings up two issues:
> 
> -- runtime class of "this" in method execution join points
> 
> V. suggests the type of "this" for method execution is Object
> when it should be A or B.  That would be a bug, but I am
> unable to reproduce it.  Using a variant of the code below,
> I get "A" and "B", not "java.lang.Object", as the className.
> If you can write a test case that causes this, please submit
> it as a bug.
> 
> -- implicit dynamic casts of around advice return values
> 
> I think Venkat is concerned about getting ClassCastException
> from returning the wrong type at a particular join point.
> Ram is right that the sample code looks correct, but it's
> worth an explanation.
> 
>  From the AspectJ programming guide:
> 
>   If the return value of around advice is typed to Object,
>   then the result of proceed is converted to an object
>   representation, even if it is originally a primitive
>   value. And when the advice returns an Object value,
>   that value is converted back to whatever representation
>   it was originally.
> 
> It is this "converting back" that might fail.
> 
> This danger lurks whenever one uses around advice declared
> to return a supertype of the type the join point returns.
> AspectJ's implicit casts will cause runtime ClassCastException
> if the advice body returns the wrong subtype for a given
> join point.  There is no good way AspectJ can prevent this
> at compile time, so the question is how to detect/test it.
> 
> When around advice changes the return value, I prefer to
> specify the type returned to enjoy the benefits of compile-time
> type safety.  When I specify the subtype, the compiler will
> signal an error if someone changes the pointcut to include
> join points returning a different type.
> 
> Doing this might mean breaking out different pieces of around
> advice which delegate to a common implementation method,
> as well as separating out the pointcuts to enforce the subtypes.
> The pointcuts could be refactored into type-specific
> variants of a common pointcut.[1]
> 
> E.g., the pointcuts
> 
>    pointcut loaders () : execution(* load(..));
>    pointcut loadersOfB () : loaders() && execution(B *(..));
>    pointcut loadersOfA()  : loaders() && execution(A *(..));
> 
> are used by the different advice, delegating to the factory:
> 
>    A around() : loadersOfA() {
>       return (A) makeThings(A.class, thisJoinPoint.getArgs());
>    }
>    B around() : loadersOfB() {
>       return (B) makeThings(B.class, thisJoinPoint.getArgs());
>    }
> 
> Separating out the common functionality into a method (here
> "makeThings(...)" can make it easier to test this method to
> ensure that it returns the correct subtypes.[2]
> 
> The problem is still there, but the solution is more testable
> and less prone to accidentally upgrading the pointcut without
> upgrading the advice functionality.
> 
> Wes
> 
> [1] Yes, to avoid respecifying the kinded pointcut execution(..),
> it might be nice to have a staticly-determinable PCD
> 
>      returns(Type)
> 
> Perhaps this could even be used to identify subtypes, to
> make it possible to write declare errors for pointcuts
> that were modified to pick out join points returning new
> subtypes:
> 
>     // join points declared to return a subtype of Type
>     returns(Type+) && !returns(Type)
> 
> Then you could declare an error whenever someone updated
> a pointcut to include a join point declared to return a subtype
> of a given type that wasn't included in a list of approved
> (i.e., tested) subtypes.  This would be better than [2].
> 
> [2] You still have to manually track what return types need
> to be covered by the method to keep the advice and method in
> sync, so you'll have to have comments or docs that when there
> are new advice clients of the delegate method, the test should
> be updated to cover the new types.
> 
> If you really wanted to fix that problem, delegate not to
> the generic factory but to type-specific clients of the
> factory and declare a warning for any clients that are
> not known and tested.  This might add too much complexity
> for some, but might work well when the advice and factory
> method are in separate components.  E.g.,:
> 
> aspect Factory {
> 
>      // type-specific around advice
>      Point around() : call(Point.new(..)) {
>          return makePoint(thisJoinPoint.getArgs());
>      }
>      Line around() : call(Line.new(..)) {
>          return makeLine(thisJoinPoint.getArgs());
>      }
> 
>      // type-specific factory for declare error below to track
>      Line makeLine(Object[] args) {
>          return (Line) makeThings(Line.class, args);
>      }
>      Point makePoint(Object[] args) {
>          return (Point) makeThings(Point.class, args);
>      }
> 
>      // the factory itself
>      Object makeThings(Class clazz, Object [] args) {
>          ...
>      }
> 
>      // track factory clients to avoid untested new clients
>      declare error : call(Object makeThings(Class, Object[]))
>          && !withincode(Line Factory.makeLine(Object[] args))
>          && !withincode(Point Factory.makePoint(Object[] args)) :
>          && !within(FactoryTest) :
>          "Untested factory client!  "
>          + "Update "
>          + FactoryTest.class.getName()
>          + ", and update declare error pointcut in "
>          + Factory.class.getName();
> 
>      // could also put pointcut in test, but this creates a
>      // dependency of production code on test code:
>      // declare error : call(Object makeThings(Class, Object[]))
>      //    && !FactoryTest.testedFactoryClients() : "Untested ...
> }
> 
> 
> Ramnivas Laddad wrote:
> > Venkat,
> > 
> > The question is not completely clear to me. But from what
> > I gather, your aspects and classes as shown here should
> > work just fine. When you return type "Object" from around
> > advice, AspectJ compiler will perform the necessary casting
> > (as well as wrapping and unwrapping, in case of primitive
> > types).
> > 
> > This information is a part of a free sample chapter of 
> > "AspectJ in Action" (chapter 3, section 3.2.7):
> > http://www.manning.com/laddad/
> > 
> > Try it and let us know if it works.
> > 
> > -Ramnivas
> > 
> > 
> > --- Venkat Dosapati <venkat@xxxxxxxxxxxxxxxxx> wrote:
> > 
> >>Sorry, my previous mail was missing the question. Here are the full
> >>details with the question:
> >>
> >>Please see the question in the end.
> >>
> >>==============MyAspect.java====================
> >>public aspect MyAspect {
> >>
> >> Object around() : execution(* *.load(..)) {
> >>  Object receiver = thisJoinPoint.getThis();
> >>  String className = receiver.getClass().getName();
> >>  Object[] args = getArguments(thisJoinPoint);
> >>  System.out.println("Class:" + className + "::ARGS:" + args);
> >>
> >>  Object returnValue = myapp.PersistenceHandler.load(className,
> >>args); /*
> >>Here I get common Object, but the Object is 'className' type. */
> >>  return returnValue;
> >> }
> >>
> >> private Object[] getArguments(JoinPoint jp)
> >> {
> >>  Object[] argumentValues = jp.getArgs();
> >>  return argumentValues;
> >> }
> >>}
> >>===============================================
> >>
> >>
> >>================Class A=========================
> >>public class A {
> >>
> >>    public A load(int id) {
> >>        // no implementation
> >>        return null;
> >>    }
> >>}
> >>================================================
> >>
> >>
> >>================Class B=========================
> >>public class B {
> >>
> >>    public B load(int id) {
> >>        // no implementation
> >>        return null;
> >>    }
> >>}
> >>===============================================
> >>
> >>
> >>
> >>When I replace the load() method in 2 classes A & B with my aspect,
> >>the
> >>advice is returning the common Object. But the classes A & B should
> >>return
> >>their respective type object.
> >>
> >>how can I type caste the Object to its original class in my aspect
> >>like
> >>this:
> >>
> >>Object returnValue = myapp.PersistenceHandler.load(className, args);
> >>return (className) returnValue;  /* Here className is the variable
> >>for the
> >>class name */
> >>
> >>[I think it is not possible with JAVA. Is it possible with AspectJ?]
> >>
> >>
> >>Thanks in advance!
> >>Venkat
> >>
> >>
> >>
> > 
> > 
> > 
> > __________________________________
> > Do you Yahoo!?
> > Yahoo! SiteBuilder - Free, easy-to-use web site design software
> > http://sitebuilder.yahoo.com
> > _______________________________________________
> > aspectj-users mailing list
> > aspectj-users@xxxxxxxxxxx
> > http://dev.eclipse.org/mailman/listinfo/aspectj-users
> > 
> 
> _______________________________________________
> aspectj-users mailing list
> aspectj-users@xxxxxxxxxxx
> http://dev.eclipse.org/mailman/listinfo/aspectj-users
> 


Back to the top