[
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
>