Bug 168479 - pertypewithin can't advice package private class
Summary: pertypewithin can't advice package private class
Status: NEW
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-12-18 18:42 EST by deng kun CLA
Modified: 2013-06-24 11:05 EDT (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description deng kun CLA 2006-12-18 18:42:01 EST
Hi, I have the following code from http://www.aspectprogrammer.org/blogs/adrian/2005/03/ramnivas_on_ann.html

public aspect SingletonManager pertypewithin(@Singleton *) {
    Object _instance = null;

    /** use a static inner aspect as we need to advise call jps
     *  that happen outside of the pertypewithin type...
     *  (and the implicit && within(T) would exclude them)
     */
    static aspect SingletonManagerHelper {
        
        pointcut singletonCreation() : call((@Singleton *).new(..));
        
        Object around() : singletonCreation() {
            Class clazz = 
                  thisJoinPoint.getSignature().getDeclaringType();
            SingletonManager mgr = SingletonManager.aspectOf(clazz);
            if (mgr._instance == null) {
                mgr._instance = proceed();
            }
            return mgr._instance;
        }
        
    }
    
    // Eager instantiation support
    after(Singleton singleton) returning :  
         staticinitialization(@Singleton *) 
         && @annotation(singleton) 
    {
        if (singleton.value() == SingletonKind.Eager) {
            try {
                _instance = thisJoinPoint
                   .getSignature().getDeclaringType().newInstance();
            } catch (IllegalAccessException illEx) {
                ; // nothing we can do - default cons not visible
            } catch (InstantiationException instEx) {
                ; // nothing we can do
            }
        }
    }
    
@Retention(RetentionPolicy.RUNTIME)
public @interface Singleton {
    public SingletonKind value() default SingletonKind.Lazy;
}
public enum SingletonKind {
    Lazy,   // create the singleton instance upon first use
    Eager;  // create the singleton instance as soon as possible
}


and two testing classes

@Singleton(SingletonKind.Eager)
public class EagerSingleton {
   static public boolean instanceCreated = false;
   public EagerSingleton() {
       instanceCreated = true;
   }
}

@Singleton(SingletonKind.Lazy)
public class LazySingleton {
}

everything works fine when the testing classes are public. But when they are package private,  the above code breaks. I tried putting previledged before each aspect, and it didn't work. 

The main effect is that SingletonManager.aspectOf(clazz); throws noaspectbound exception.

Any idea how to fix it?
Comment 1 Matt Chapman CLA 2006-12-19 05:54:35 EST
passing over to compiler
Comment 2 Andrew Clement CLA 2007-02-19 03:40:38 EST
You haven't specified which version of AspectJ or AJDT you are having trouble with.  I typed in your code and added this test class:

public class Test {
  public static void main(String []argv) {
    EagerSingleton es = new EagerSingleton();
    LazySingleton ls  = new LazySingleton();
    System.err.println("aspect for eagersingleton = "+SingletonManager.aspectOf(EagerSingleton.class));
    System.err.println("aspect for lazysingleton = "+SingletonManager.aspectOf(LazySingleton.class));
  }
}  

If I compile your code as is and run Test, i get:
aspect for eagersingleton = SingletonManager@11b9fb1
aspect for lazysingleton = SingletonManager@913fe2

If I change your EagerSingleton and LazySingleton to default (package) visibility (which I presume is what you mean by 'package private'):

@Singleton(SingletonKind.Eager)
class EagerSingleton {
   static public boolean instanceCreated = false;
   public EagerSingleton() {
       instanceCreated = true;
   }
}

@Singleton(SingletonKind.Lazy)
class LazySingleton {
}

and then I recompile and rerun, again it works for me:

aspect for eagersingleton = SingletonManager@11b9fb1
aspect for lazysingleton = SingletonManager@913fe2

Have you tried this on an up to date AspectJ/AJDT?  If it still doesn't work, please can you attach the actual definitions of Eager and Lazy singleton that fail for you?
Comment 3 deng kun CLA 2007-02-19 13:21:52 EST
I used the latest aspectj compiler. By package private, I mean the two advised classes are defined in a DIFFERENT package and with a DEFAULT visibility  as the aspect.



(In reply to comment #2)
> You haven't specified which version of AspectJ or AJDT you are having trouble
> with.  I typed in your code and added this test class:
> 
> public class Test {
>   public static void main(String []argv) {
>     EagerSingleton es = new EagerSingleton();
>     LazySingleton ls  = new LazySingleton();
>     System.err.println("aspect for eagersingleton =
> "+SingletonManager.aspectOf(EagerSingleton.class));
>     System.err.println("aspect for lazysingleton =
> "+SingletonManager.aspectOf(LazySingleton.class));
>   }
> }  
> 
> If I compile your code as is and run Test, i get:
> aspect for eagersingleton = SingletonManager@11b9fb1
> aspect for lazysingleton = SingletonManager@913fe2
> 
> If I change your EagerSingleton and LazySingleton to default (package)
> visibility (which I presume is what you mean by 'package private'):
> 
> @Singleton(SingletonKind.Eager)
> class EagerSingleton {
>    static public boolean instanceCreated = false;
>    public EagerSingleton() {
>        instanceCreated = true;
>    }
> }
> 
> @Singleton(SingletonKind.Lazy)
> class LazySingleton {
> }
> 
> and then I recompile and rerun, again it works for me:
> 
> aspect for eagersingleton = SingletonManager@11b9fb1
> aspect for lazysingleton = SingletonManager@913fe2
> 
> Have you tried this on an up to date AspectJ/AJDT?  If it still doesn't work,
> please can you attach the actual definitions of Eager and Lazy singleton that
> fail for you?
> 
Comment 4 Andrew Clement CLA 2007-02-20 05:38:48 EST
I have now recreated this - you could have included the package statements in your test program to demonstrate what you meant.

For efficient implementation of pertypewithin, the aspect instance is held as a static member in the type affected by the aspect (the type that matches the pertypewithin).  However, due to Java visibility rules, this cannot be accessed if the type isnt visible, just like you can't compile these two classes:

--- A.java ---
package a;
class A {
  public static int i;
}
--- B.java ---
public class B {
  public static void main(String []argv) {
    a.A.i = 5;
  }
}

javac A.java B.java
B.java:3: a.A is not public in a; cannot be accessed from outside package
    a.A.i = 5;
     ^
1 error

AspectJ currently generates code that breaks this rule - and so it fails at runtime.  The reflection code that actually breaks swallows the exception and treats it as meaning 'there is no aspect instance', the actual swallowed exception is something like:
java.lang.IllegalAccessException: Class c.SingletonManager can not access a member of class a.EagerSingleton with modifiers "public static"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
        at java.lang.reflect.Method.invoke(Method.java:578)
        at c.SingletonManager.ajc$getInstance(SingletonManager.java:39)
        at c.SingletonManager.aspectOf(SingletonManager.java:22)
        at a.Test.main(Test.java:8)

The short term solution is to consider this a compiler limitation and check it up front at compile time - if the visibility of the types involved prevents the optimized implementation of pertypewithin then put out an error message (marked compiler limitation).

The longer term solution would be to consider how the aspect instances can be managed for these non-visible types.  

Privileged has no effect - that is intended for exposing hidden (package visibility, private) members for use in advice.
Comment 5 Andrew Clement CLA 2013-06-24 11:05:33 EDT
unsetting the target field which is currently set for something already released