Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
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




Back to the top