[2.3.5-scala] Problem integrating aspectJ load time weaving with third party classes

470 views
Skip to first unread message

Bhashit Parikh

unread,
Nov 2, 2014, 10:15:02 PM11/2/14
to play-fr...@googlegroups.com
Hello,

I have been trying to use AspectJ to modify the behavior of a third-party Java library (Hibernate) that I am including as an SBT dependency. I specify the javaagent as an environment variable using something like 
export ACTIVATOR_OPTS="-javaagent:/home/brp/.ivy2/cache/org.aspectj/aspectjweaver/jars/aspectjweaver-1.8.2.jar"

And the weaver is picking up my from "conf/META-INF/aop.xml". I know that because the debug messages show me that my aspect has been registered, plus if I specify an incorrect format for the xml, I get an exception originating from some aspectj class. 

Also, AspectJ is atleast picking up by Aspect class and reading it as well. If I use incorrect syntax for my pointcut, I get a parse exception from aspectj. 

If the advised class is one of the classes in my source code, the advice seems to be getting applied. However, if I specify a third party package like 
<include within="org.hibernate.cfg.*" />

The aspect is never applied. I know this bacause my debug statements in the aspect never get printed. Also, in the AspectJ debug log, I get the following output

 [ [36mdebug [0m] o.a.w.t.WeavingAdaptor - RETURN false
[ [36mdebug [0m] o.a.weaver.Dump - ENTRY null
[ [36mdebug [0m] o.a.weaver.Dump - ENTRY org.aspectj.weaver.tools.WeavingAdaptor$WeavingAdaptorMessageHolder@3bba665
[ [36mdebug [0m] o.a.weaver.Dump - ENTRY null
[ [36mdebug [0m] o.a.weaver.Dump - ENTRY org.aspectj.weaver.tools.WeavingAdaptor$WeavingAdaptorMessageHolder@3bba665
[ [36mdebug [0m] o.a.weaver.Dump - EVENT
[ [36mdebug [0m] o.a.weaver.Dump - EVENT
[ [36mdebug [0m] o.a.weaver.Dump - RETURN false
[ [36mdebug [0m] o.a.weaver.Dump - RETURN Excluded
[ [36mdebug [0m] o.a.w.l.Aj - RETURN byte[5851]

As the debug log says, my intended class is getting "Excluded".

My pointcut is getting applied to a static, private method. And the pointcut definition is correct. I have tried with very generic, all encompassing pointcuts and even those are not getting applied.  My aspect class is:
@Aspect
public class ComponentAspect {
  @Around("execution (private static * buildBaseMapping())")
  public Object setupTuplizer(ProceedingJoinPoint pjp) throws Throwable  {
    System.out.println("\n\n\n\nDoitdoit \n\n\n\n\n");
    return pjp.proceed();
  }
}


I am not sure if this has something to do with play framework or not. For ex. I have ran this SBT project activator-akka-aspectj and it is working correctly, applying aspects around the akka actor classes. The akka classes are a third party dependency in that project. I have been looking around and I have tried out several things. The sbt-aspectj plugin is not an answer here since it is working fine in that project.

I am going to keep looking around, but if anybody has any idea why this could be happening, that would be helpful.

Bhashit Parikh

unread,
Nov 2, 2014, 11:36:06 PM11/2/14
to play-fr...@googlegroups.com
This is looking more and more like a class-loader related issue. I am not sure yet.

Bhashit Parikh

unread,
Nov 2, 2014, 11:50:48 PM11/2/14
to play-fr...@googlegroups.com
Yeah, it is definitely a class-loader related issue. Play has a dynamic class-loading mechanism that loads dependency classes differently then it does the classes in your source code. Take a look at https://github.com/playframework/playframework/issues/2847

James Roper

unread,
Nov 3, 2014, 12:59:12 AM11/3/14
to play-framework
Is this in dev mode?  ie, is it when you run activator run?

If it is, note that your configuration file is not on the classpath until after third party jars such as hibernate are loaded, when Play is first started, only the dependencies of your application, including Play itself, are on the classpath.  When it receives the first request, it compiles the application if need be, and then creates a new classloader, and this one will contain the contents of the conf directory (it's actually copied to a classes directory in target), and then aspectj must be dynamically finding that.  But by that stage, the hibernate jars have probably already been loaded, so aspectj wouldn't be able to weave them if it didn't have that configuration around at the time it loaded them.

I don't know how to solve this, it probably depends on what you're trying to achieve, and probably also requires the classes that your pointcuts point to be in the same or a parent classloader of the application classloader, which means you'll have to create an external library to put them into.

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
James Roper
Software Engineer

Typesafe – Build reactive apps!
Twitter: @jroper

Bhashit Parikh

unread,
Nov 3, 2014, 1:18:22 AM11/3/14
to play-fr...@googlegroups.com
It is in dev mode.

Hibernate is specified as a dependency and aspectj is being used with the "-javaagent" parameter.

The configuration is definitely being read before my targeted class is being loaded (I have been debugging through the aspectj load-time-weaver code). And the debug log does show a LOT of classes (probably several thousand), including the hibernate ones passing through aspectj.

However, I noticed that the weaving code is initialized for multiple class-loaders. So, I guess the problem could be there. The class-loader that loads my config file and my source-classes could be a different one from the one that loads my dependency classes. Thanks for that pointer.

I'm going to continue trying.

Thanks.

Peter Vlugter

unread,
Nov 3, 2014, 1:38:59 AM11/3/14
to play-framework
Hi,

Note that the akka aspectj sample you've linked to modifies the run options with aspectj, not activator itself. It does this by forking the run so that the java agent can be added.

You should be able to also fork Play run to do the same, but you'll lose the dev mode reloading in that case.

For weaving Play's dev run for Typesafe Console and Activator Inspect we used a WeavingURLClassLoader rather than a java agent (and Play run was set up to allow the class loader to be configured). You can see how Activator Inspect does that here:

https://github.com/typesafehub/sbt-echo/blob/master/play/src/main/scala/com/typesafe/sbt/echo/EchoPlayRun.scala

Peter

Bhashit Parikh

unread,
Nov 3, 2014, 2:15:27 AM11/3/14
to play-fr...@googlegroups.com
I did try the fork-in-run and the "javaOptions in run" part. Didn't work. I don't mind that the load-time weaving will be disabled for the time-being. I guess play wouldn't automatically disable reloading feature.

Then, after your suggestion, I thought about trying to use the "runMain" task. And voila! it works... So, it was a class-loading issue. Weird thing is that some of the classes that are needed by my application (like hibernate's SessionFactory) did get initialized by the same class-loader as the one that loads my source classes. However, that same thing didn't get applied to a few other classes like ServiceRegitryBuilder, which is also being used (for building the SessionFactory).

Thanks for your help. I did take a look at your WeavingClassLoader. I guess I'll need to take a look at it again when I need to tidy up things. 

Thanks.

Bhashit

Bhashit Parikh

unread,
Nov 3, 2014, 2:20:00 AM11/3/14
to play-fr...@googlegroups.com
BTW, I gave up on the forking the JVM after reading this issue: https://github.com/playframework/playframework/issues/1372

Bhashit Parikh

unread,
Nov 3, 2014, 4:26:02 AM11/3/14
to play-fr...@googlegroups.com
Even though the weaving is working with the "runMain" task, I am getting an aspectj internal exception. It complai about not being able to find the advised class. That is: it modifies the bytecode as I expected it to. It creates some internal class (like HibernateClassName$AjcClosure1). And then it complains about not being able to find that class HibernateClassName$AjcClosure1. 

This, again, suspiciously looks like something related to class-loading. I may be wrong though. I guess I should start looking at the custom class-loading code now.

Bhashit

Bhashit Parikh

unread,
Nov 3, 2014, 5:01:20 AM11/3/14
to play-fr...@googlegroups.com
@James Roper: The best way to tackle the problem is to indeed create a separate project/jar and include it as a dependency. I just did that and all the problems are gone. Things started making a but more sense after  leaning a bit more about play's class-loading in dev mode. 

All the aspects and the aop.xml are now in a separate jar.

This is super cool. Thanks. 

Bhashit Parikh

unread,
Nov 3, 2014, 7:37:01 AM11/3/14
to play-fr...@googlegroups.com
Just when I thought the problem was solved. I didn't think that the my aspect code will access all the other classes in my whole project, namely, all the entities. Since the classloader for the dependencies is the parent for the reloading class-loader, My entities won't be available to the aspect code. Argh... Back to square one.
Message has been deleted

Bhashit Parikh

unread,
Nov 3, 2014, 9:08:06 AM11/3/14
to play-fr...@googlegroups.com
If somebody is reading this, sorry for posting so often.

Solved the problem by adding a static configuration field to give it the Play Application object. Since the aspect code will only access my entity classes after/while building the session-factory, I can inject the play application object to it (from my Global.scala) that it can use to get the appropriate class objects.

Thanks for the help. It seems to be working perfectly now.
Reply all
Reply to author
Forward
0 new messages