Bug 158957 - NPE in LTW with RMI dynamic proxies w/ pointcut reuse
Summary: NPE in LTW with RMI dynamic proxies w/ pointcut reuse
Status: REOPENED
Alias: None
Product: AspectJ
Classification: Tools
Component: LTWeaving (show other bugs)
Version: DEVELOPMENT   Edit
Hardware: PC Windows XP
: P5 normal (vote)
Target Milestone: 1.5.3   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-09-27 08:18 EDT by Christian Schneider CLA
Modified: 2009-08-30 02:48 EDT (History)
0 users

See Also:


Attachments
verbose debug logs (32.33 KB, text/plain)
2006-09-28 07:21 EDT, Christian Schneider CLA
no flags Details
Highlights (3.80 KB, text/plain)
2006-09-28 11:46 EDT, Matthew Webster CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Christian Schneider CLA 2006-09-27 08:18:00 EDT
Hello AspectJ team,


I'm using AspectJ with some kind of pointcut reuse (achieved by placing certain public pointcuts into an otherwise empty Aspect and referencing these from other aspects). As this feature works in most situations I suppose it is a supported feature of AspectJ. If such a style of pointcut reuse is not intended in AspectJ just ignore this bug report.

When trying to apply an advice using LTW for a RMI application that uses dynamic proxies the class-loading fails with a NullPointerException. But interestingly that only happens when the pointcut used in the Aspect is itself referring to a reusable pointcut (which could be the case for complex pointcuts that share common parts). When I inline the reusable pointcut into the pointcut that's used by the advice everything runs fine. The pointcut definition in question could be even some completely unrelated pointcut and LTW's class-loading fails too (which I assume is the case since AspectJ has to intercept the class-loading anyway).

I've tested it with the development build: aspectj-DEVELOPMENT-20060925164058


Here's the reusable (simplified for testcase purposes :) pointcut definition:

	public final aspect DemoPointcuts {
	    public static pointcut justATestReferenced() :
	    	call(* System.getenv()); // content of this pointcut seems to be unrelated to problem reported. Could be anything you like.
	}




Here's the aspect in question:

	public final aspect TestcaseToReproducePotentialBug {
    
	  private pointcut justATest() :
	  	DemoPointcuts.justATestReferenced() 
		// NOTE that replacing the previous line with the inline call works fine:   call(* System.getenv()) 
	  ;
  
	  after() returning() : justATest() {
	  	System.out.println("TEST");
	  }
  
	}    




And that's the stack-trace for the class-loading process, having RMI dynamic proxies involed:

27.09.2006 11:24:47 org.aspectj.weaver.tools.Jdk14Trace error
FATAL: preProcess
java.lang.NullPointerException
	at org.aspectj.weaver.bcel.BcelWeaver.weaveParentTypeMungers(BcelWeaver.java:1404)
	at org.aspectj.weaver.bcel.BcelWeaver.weaveParentsFor(BcelWeaver.java:1274)
	at org.aspectj.weaver.bcel.BcelWeaver.weave(BcelWeaver.java:1103)
	at org.aspectj.weaver.tools.WeavingAdaptor.getWovenBytes(WeavingAdaptor.java:337)
	at org.aspectj.weaver.tools.WeavingAdaptor.weaveClass(WeavingAdaptor.java:243)
	at org.aspectj.weaver.loadtime.Aj.preProcess(Aj.java:76)
	at org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter.transform(ClassPreProcessorAgentAdapter.java:55)
	at sun.instrument.TransformerManager.transform(TransformerManager.java:122)
	at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:155)
	at java.lang.reflect.Proxy.defineClass0(Native Method)
	at java.lang.reflect.Proxy.getProxyClass(Proxy.java:504)
	at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:641)
	at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:588)
	at java.rmi.server.RMIClassLoader$2.loadProxyClass(RMIClassLoader.java:628)
	at java.rmi.server.RMIClassLoader.loadProxyClass(RMIClassLoader.java:294)
	at sun.rmi.server.MarshalInputStream.resolveProxyClass(MarshalInputStream.java:238)
	at java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1500)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1463)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1699)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1305)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
	at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:290)
	at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:139)
	at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:179)
	at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:132)
	at $Proxy4.prepareLogin(Unknown Source)
	at demo.Demo.main(Demo.java:73)



Best Regards & keep up the good work,
Christian
Comment 1 Matthew Webster CLA 2006-09-27 09:29:21 EDT
This has the same symptoms, but perhaps different cause, to Bug 151182 "NPE in BcelWeaver using LTW". In this case we are generating a proxy class rather than reflection delegate using Unsafe. Please can you turn on trace, that will go to stderr, so we can find out a bit more about what's going on by specifying the following system properties:

            -Dorg.aspectj.tracing.enabled=true
            -Dorg.aspectj.tracing.factory=default
            -Dorg.aspectj.tracing.messages=true
Comment 2 Christian Schneider CLA 2006-09-28 07:21:46 EDT
Created attachment 51078 [details]
verbose debug logs
Comment 3 Matthew Webster CLA 2006-09-28 11:46:24 EDT
Created attachment 51094 [details]
Highlights

Christian,

Thanks for the trace which I think tells me what is going wrong although not why. Attached is an interesting part. Here is a commentary
1.	We create a weaving adaptor for the proxy loader
2.	We _start_ to initialize it and register the two aspects: RuntimeExceptionCheck and DemoAspect.
3.	We seem to create several ReflectionWorld instances while parsing the pointcuts for DemoAspect. This needs investigation and is probably the cause of the bug. This class was written for non-LTW pointcut parsing using reflection and probably needs some adjustment to work properly inside LTWWorld.
4.	I believe an Error is thrown and control is returned to JMVTI without completing the adaptor initialization: prepareForWeave() has not been called, there is no exit trace and addLibraryAspect() is not the the stack trace for the NPE. We only handle Exceptions today, like the NPE in this report, on the understanding that Errors tend to be fatal. However I think we need to catch Errors and disable the adaptor.
5.	We attempt to weave class $Proxy5 using the uninitialized weaver.

You appear to have a good workaround by not using a pointcut library. You could exclude proxies from weaving by adding <exclude within="$Proxy*"/> to the <weaver> section of aop.xml but that _won’t_ because the problem happens during adaptor initialization. Another approach would be to exclude weaving for proxy loaders inside Aj as we do for reflection delegate loaders on the grounds that these are system classes. 

I will add more trace and exception handling to ensure we capture the Error and weaver is disabled. I will also investigate the use of ReflectionWorld. I may then ask you to reproduce the problem once more (if I still can’t) with an updated build. In the meantime could you post your aspects so that I can add a testcase to the suite?
Comment 4 Christian Schneider CLA 2006-09-29 04:43:14 EDT
Matthew,

thanks for you in-depth analysis.

I've further tested the whole stuff and continously stripped the code in question down. To my own surprise I've eventually found the solution:

When stripping down the pointcut library further and further I've noticed that the pointcut library contains unused (i.e. unreferenced) pointcut definitions with execution pointcuts that reference a class from javax.servlet. As I wanted to reuse my pointcut library for the RMI based application I'm working on I simply copied the pointcut library into the app's classpath. Since the RMI based app has nothing to do with web-development its runtime classpath didn't contian the servlet-api which holds the unused javax.servlet class. As soon as I commented-out the unused servlet-related pointcut definition (which works well in a servlet-based environment) the whole stuff ran as expected. On the other hand, when keeping the unused pointcut definition within the pointcut library *and* adding the otherwise not required servlet-api to the runtime classpath of the RMI app the whole stuff ran fine too.

So it all boils down to having all referenced compile-time required libraries (i.e. servlet-api) also within the runtime-classpath when the application has LTW aspects that (even when unused at run-time) reference these otherwise compile-time dependencies. As the whole RMI app does not have any dependency (either compile-time or run-time) on servlet-api I didn't add that to the classpath. But as (though unused) parts of the pointcut library I wrote have compile-time dependencies on servlet-api and (in case the pointcuts will be referenced in aspects) also run-time dependencies on the servlet-api I simply have to resolve these dependencies by adding the missing jar to the runtime classpath. 

The interesting point is the fact that the pointcut definition in question which was referencing the servlet api was not used in any aspect. Neither in an aspect of the RMI app nor within the pointcut library. So I thought that adding servlet api to the classpath is not required. But when LTW comes into play certain things that are otherwise static become dynamic (by design, as it is LTW) so I had to rethink about the classpath.  :-)

I hope that this information is helpful to you. Maybe the exception message could be clearer for other users. And maybe I've even (by chance) pointed you to some code blocks that should be inspected anyway... 

So just keep up the good work & Best Regards,
Christian


BTW: If you need any further stuff from me or would like me to test a newer dev build against my app without having servlet-api in the classpath, just let me know... 

Comment 5 Matthew Webster CLA 2006-09-29 10:59:27 EDT
While the declaration of an aspect with missing dependencies should fail AspectJ LTW should cope more gracefully and issue a message that helps users to correct their configuration. Could you possibly post the offending aspects to help me reproduce the problem? Thanks again for your co-operation and diagnosis of the problem.
Comment 6 Matthew Webster CLA 2006-10-04 05:54:20 EDT
In the absence of a testcase to reproduce the problem I suggest prevention of secondary failure. The adaptor should be disabled by default so that if initialization fails for some reason we won't be able to use it. Also I will restore the catch (Throwable) in Aj to report any Errors which we have now discovered are swallowed by JVMTI.
Comment 7 Christian Schneider CLA 2006-10-05 12:17:16 EDT
sorry for the rather late reply (I was a few days offline while on travel)...

The following is the pointcut library that I reuse from different aspects:


public aspect Pointcuts {

    public pointcut anyMethodCall() :
        call(* *(..));

    public pointcut anyMethodExecution() :
        execution(* *(..));

    public pointcut anyPublicMethodExecution() :
        execution(public * *(..));

    public pointcut anyNonPrivateMethodExecution() :
        execution(!private * *(..));

    public pointcut anyConstructorCall() :
        call(new(..));

    public pointcut anyPublicConstructorCall() :
        call(public new(..));

    public pointcut anyNonPrivateConstructorCall() :
        call(!private new(..));


    public pointcut servletGetRequest(
javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) :
        execution(void javax.servlet.http.HttpServlet.doGet(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse))
        && args(request, response);


}

When I try to use any advice from some other aspect which references one of the non-web-relevant pointcuts defined in the pointcut library AspectJ's LTW tries to load the "Pointcuts" pointcut library. During that initialization process even the javax.servlet classes must be resolveable to AspectJ's LTW runtime classpath though the last pointcut in the above library (the only one that's referencing the javax.servlet classes) is not used. Neither within the pointcuts library or within some advice referencing it. So the dependency is only a compile-time dependency in "normal" non-AOP programming. But in a world of LTW this otherwise compile-time dependency becomes a runtime-dependency.
Comment 8 Ron Bodkin CLA 2006-10-05 12:31:00 EDT
As an aside and suggestion, on the Glassbox project we have to work around these kinds of issues, e.g., by using

    public pointcut servletRequestExec() :
        within(HttpServlet+) && !within(HttpJspPage+) && (execution(* HttpServlet.do*(..)) || execution(* HttpServlet.service(..)));

    before(Object servlet, Object request) : servletRequestExec() && this(servlet) && args(request, *) {
...

I am testing a converted version of our servlet monitor that uses reflection to allow a shared aspect that handles servlets even though the containing classloader doesn't have that API visible.

When weaving of javax..* is allowed with AspectJ, we can instead use this technique to handle the situation:

  declare parents: javax.servlet.http.HttpServlet implements IHttpServlet;
  private interface IHttpServlet { /* extract needed methods from HttpServlet */ }

Then in advice refer to IHttpServlet instead.
Comment 9 Matthew Webster CLA 2006-10-13 04:24:07 EDT
I still cannot reproduce this problem even with provided testcase. I might need the aop.xml file being used and the other aspects. I suspect the failure occurs if type resolution for the Servlet classes happens during adaptor initialization when registering the Pointcuts library aspect. However by default we do no resolution of pointcut types until weaving. However we do if completeBinaryTypes=true which is now disabled by default. But even with this re-enabled I can't see a failure.

The next thing to try will be a class loader hierarchy using TestServer because the problem may lie in completeBinaryTypes/completeNonLocalType().
Comment 10 Matthew Webster CLA 2006-11-01 05:18:04 EST
I am closing this because I don't believe the problem still exists given the number of changes. If a similar problem does occur the weaver will remain disabled and we will get more diagnostic information.
Comment 11 Eclipse Webmaster CLA 2009-08-30 02:48:54 EDT
LATER/REMIND bugs are being automatically reopened as P5 because the LATER and REMIND resolutions are deprecated.