Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-users] newbie AspectJ questions

To try and explain some of what's going on for you here...

The closure classes are generated by the AspectJ compiler when around advice cannot be inlined (the default). In this case, the call to "proceed()" in the around advice is escaping the advice body (by being passed to the security interceptor inside the callback object) and so AspectJ must generate a closure class (think of it like a continuation implementation).

The reason you have so many of them.... is that the pointcut you have written matches a very broad set of join points. In particular, you have used "target(AclSecurable)" (and I'm presuming AclSecurable is an interface). "target" matches based on the runtime type of the target at a join point. Since you are using execution I'll use that for the example.

Give some class A

public class A {

  public void foo() { ... }

}

When AspectJ weaves this class, it is possible that the "target" when foo executes *is* an AclSecurable, even though A isn't. The way this could happen is if someone defines a subclass B:

public class B extends A implements AclSecurable {

}

Now given b of type B, a call to b.foo() results in an execution of foo() that should have security interception applied to it. Given the requirements to support separate and incremental compilation,  AspectJ will apply a runtime test before the execution of A.foo to see if the target object really does implement AclSecurable, and the around advice will execute if so. Given the potential for this to happen at almost any method execution join point, you will have a lot of closure classes!

You can narrow this down using one or both of the following techniques:

* constrain the join point matching to only those types within your domain model. For example, you might add a pointcut:

pointcut inDomainModel() : within( edu.uscs.whisper.domain..*); 

(or whatever package prefix or prefixes you are using for the domain types).

and then refine the domainObjectInstanceExecution pointcut as follows:

pointcut domainObjectInstanceExecution() :
    target(AclSecurable) &&
    execution(public * *(..)) &&
    inDomainModel();

(assuming the aspect itself is not in the same package as the domain model types, the additional !within will no longer be required).

* The second narrowing technique is to switch from using target to using static type matching. This will work well if you use the AclSecurable interface at the root of any domain type hierarchy (eg., in the example above, "A" implements AclSecurable, and not "B" only). The only drawback to this solution is that if you have a domain type hierarchy such as A and B above, and only B implements the AclSecurable interface, then methods on A that are not overriden by B will NOT go through the SecurityInterceptor. Use the interface at the root of the domain hierarchy you want to secure.

The pointcut will now look like this:

pointcut domainObjectInstanceExecution() :
    execution(public * AclSecurable+.*(..)) &&
    inDomainModel();

(the execution _expression_ reads: "the execution of any public method defined in AclSecurable or any subtype of AclSecurable").

Now to address the other issues:

* lazyTjp is a compiler optimisation that applies when using thisJoinPoint. Prior to AspectJ 5, this optimisation had to be explicitly turned on, but from AspectJ 5 onwards, the optimisation is enabled by default. You don't say what version of AspectJ you are using, but we shouldn't be warning for this under AspectJ 5 - we will fix that as a bug if so. If you are using e.g. 1.2.1 and have enabled the -XlazyTjp flag then this is why the warning is coming out. The warning is a simple advisory that an optimisation was not applied, and your program will behave perfectly correctly.

*  the needsSerialVersionUIDField warnings are coming out because the AspectJ weaving process has had to generate synthetic fields in your domain types. These can affect serialization (in particular if you haven't explicitly specified a serialVersionUID, then the generated alternative will be different after weaving). This can be an issue if you want to serialize and deserialize woven and unwoven versions of the type. Defining a serialVersionUID is recommended best practice in any case, and will ensure you never encounter any issues of this type. I notice from that message that even the execution of hashCode and equals methods are being advice by your pointcut. You probably don't want to apply security to these methods (if you do, fine). You can exclude methods from Object from having security interception applied by refining the pointcut _expression_ once more:

pointcut javaLangObjectMethodExecution() :
   execution(* Object.*(..));

pointcut domainObjectInstanceExecution() :
    execution(public * AclSecurable+.*(..)) &&
    !javaLangObjectMethodExecution() &&
    inDomainModel();

Sorry for the long reply - your post touched on a number of interesting issues!

On 31/01/06, Mark Slater < lists@xxxxxxxxxxxxxxxxxx> wrote:
I'm new to AspectJ, and direct use of aspects in general. I've been using them indirectly with the Spring Framework and the Acegi security system that complements spring. But now I need to implement my own aspect to provide security on my domain objects.

Based on the Acegi documentation, I've got the following aspect:

package edu.ucsc.whisper.security.aspectj;

import org.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor ;
import org.acegisecurity.intercept.method.aspectj.AspectJCallback;

import org.springframework.beans.factory.InitializingBean;

import edu.ucsc.whisper.core.AclSecurable;

public aspect DomainObjectInstanceSecurityAspect
    implements InitializingBean
{
    private AspectJSecurityInterceptor securityInterceptor;
    
    pointcut domainObjectInstanceExecution(): target( AclSecurable )
        && execution( public * *(..) ) && !within( DomainObjectInstanceSecurityAspect );
    
    Object around(): domainObjectInstanceExecution()
    {
        if( this.securityInterceptor != null )
        {
            AspectJCallback callback = new AspectJCallback()
                {
                    public Object proceedWithObject()
                    {
                        return( proceed() );
                    }
                };
            
            return( this.securityInterceptor.invoke( thisJoinPoint, callback ) );
        }
        else
        {
            return( proceed() );
        }
    }
    
    public AspectJSecurityInterceptor getSecurityInterceptor()
    {
        return( securityInterceptor );
    }
    
    public void setSecurityInterceptor( AspectJSecurityInterceptor newInterceptor )
    {
        securityInterceptor = newInterceptor;
    }
    
    public void afterPropertiesSet()
        throws Exception
    {
        if( securityInterceptor == null )
        {
            throw( new IllegalArgumentException( "SecurityInterceptor required." ) );
        }
    }
}


As you can see, I only want this aspect applied to objects that implement the AclSecurable interface. However, when I look at the output classes (I'm using maven to build), I've got one or more ...$AjcClosure#.class files for each class in the project, and my JUnit test cases. I'm not sure why they would be needed for classes that aren't even indirectly related to the AclSecurable class/interface hierarchy. And I really don't see why the JUnit test cases (which are defined in a separate source tree and compiled to a separate class tree) should be getting this treatment.

But worse (in my mind) is that when I build, I'm getting the following output for every method in my project: 

{package}/{class}.java:55 [warning] can not implement lazyTjp on this joinpoint method-execution(void {package}.{class}.{method}( {parameters} )) because around advice is used [Xlint:canNotImplementLazyTjp]
public void setEditUserAccountInfoValidator( EditUserAccountInfoValidator newValidator )
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        see also: /Users/mark/Documents/school/whisper/ftr116_mslater/src/aspectj/edu/ucsc/whisper/security/aspectj/DomainObjectInstanceSecurityAspect.java:18::0

I've done some searching and I can't even figure out what it means that it can't implement lazyTjp, so I don't know if this can be safely ignored, or if it means I've got a bug someplace.

I'm also getting a few messages that look like this:

edu/ucsc/whisper/security/aspectj/DomainObjectInstanceSecurityAspect.java:18 [warning] serialVersionUID of type edu.ucsc.whisper.core.DefaultAuthority needs to be set because of added non-private method hashCode_aroundBody10 [Xlint:needsSerialVersionUIDField]

And once again, I'm not sure what this is talking about... is it suggesting that I need to add new fields to objects that (in this case) I don't even want the aspect applied to?

I'm finding this more confusing than I'd expected it to be. Any help?

Thanks,
Mark


_______________________________________________
aspectj-users mailing list
aspectj-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/aspectj-users





--
-- Adrian
adrian.colyer@xxxxxxxxx

Back to the top