JBoss standalone AOP

This document give a try to JBoss standalone AOP solution, which has been released as a DR 1 (developper release) in may 2003.

If more and more people are asking for a cross platform AOP solution, and even if JBoss tried to address this issue while adding AOP to their core product, this document will show you thru concrete sample that their approach is wrong.

CAUTION: JBoss standalone AOP is not suitable for other stuff than JBoss app server !

Just ok for hello world demo...


If you don't know about AOP or JBoss AOP, give a try in google , AOSD.net or in JBoss site (follow Project / AOP).

If you are really looking for a true wide AOP solution, check for AspectWerkz and stay tune.

  1. Case study
  2. Observation
  3. Conclusion
  4. Download

Case study

The experiment is done thru a very simple J2SE application. The application makes use of major concepts which are at the heart of any J2EE architecture: ClassLoader, URLClassLoader and hierarchy.

ClassLoader hierarchy

Java class loading mechanism follow a hierarchy of class loader. Class are loaded thru the class path of each ClassLoader (java.lang.ClassLoader and subclasses). Class are loaded (when available) by the highest ClassLoader in the hierarchy.

Core classes (java.lang.String ...) are loaded by the bootstrap ClassLoader . This is the "Parent" with a big "P".

Standard extensions put by default in jre/lib/ext are loaded by the extension ClassLoader .

System ClassLoader is in charge of loading the "main" class and related. It looks for classes in the regular classpath. This is the major component for J2SE application but is not really at the heart of J2EE application servers.

The application loaded by the System ClassLoader can define custom ClassLoaders thru java.lang.ClassLoader inheritance, or usually thru java.lang.URLClassLoader. In term of ClassLoader hierarchy a custom ClassLoader can be the child of nobody meaning Bootstrap classloader, or a child any other ClassLoader.

Thus you have something like


		Boot CL
		  |
		Extension CL
		  |
		System CL (main)

	and Custom CL can be plugged at any level and between each other

Usually in J2EE environment, if you deploy an EAR you have something like:
  • An URLClassLoader for your EAR, child of system ClassLoader
  • An URLClassLoader associated to the EJB deployed in this EAR, child of EAR ClassLoader
  • An URLClassLoader associated to the WAR deployed in this EAR, child of EJB ClassLoader, allowing a servlet to call a local EJB without client jar issue
As a consequence if you decide to package a jar in the WAR//WEB-INF/lib directory, this won't be visible to your EJBs.

	Boot CL
	  |
	Extension CL
	  |
	System CL (main - app server classes)
	 /	|
	|       |
	|      URLClassLoader_my_war (deployed standalone war)
	|
  URLClassLoader_my_ear (deployed ear, jar put in the ear except ejb-jars)
        |
  URLClassLoader_my_ejb-jar (ejb-jar in my_ear. Use Manifest entries to point the ear wide jars in the parent CL)
	|
  URLCLassLoader_my_war (war in my_ear, servlet classes of WEB-INF/classes and jar of WEB-INF/lib)

Sample application

In order to easily test this fundamental behavior of any J2EE app server, the following sample application has been developped :


	Boot CL
	/  |
       /  Extension CL
      /	      |
     |	     System CL (classpath contains JBoss AOP jars, POJO.class and META-INF/jboss-aop.xml for POJO)
     |		|    |
     |		|   URLClassLoader_1 (search path contains POJO2.class and NO META-INF/jboss-aop.xml)
     |		|
     |	       URLClassLoader_2 (search path conteains POJO2.class and META-INF/jboss-aop.xml for POJO2)
     |
    URLClassLoader_2 (search path contains JBoss AOP jars, POJO.class, POJO2.class and META-INF/jboss-aop.xml for POJO*)

The jboss-aop.xml is taken from the example 1 of the JBoss standalone AOP Oreilly article , but modified to instrument POJO or POJO2 or both POJO*


deployed in System CL

	<?xml version="1.0" encoding="UTF-8"?>
	<aop>
	   <interceptor-pointcut class="POJO">
	     <interceptors>
	       <interceptor class="TracingInterceptor"/>
	     </interceptors>
	   </interceptor-pointcut>
	</aop>

deployed in URLClassLoader_2

	<?xml version="1.0" encoding="UTF-8"?>
	<aop>
	   <interceptor-pointcut class="POJO2">
	     <interceptors>
	       <interceptor class="TracingInterceptor"/>
	     </interceptors>
	   </interceptor-pointcut>
	</aop>

deployed in URLClassLoader_3

	<?xml version="1.0" encoding="UTF-8"?>
	<aop>
	   <interceptor-pointcut class="POJO*">
	     <interceptors>
	       <interceptor class="TracingInterceptor"/>
	     </interceptors>
	   </interceptor-pointcut>
	</aop>


Expected behavior

Based on what we can read in the doc, ant our knowledge on ClassLoader hierarchy we expect the following:

  • POJO loaded by System CL should be instrumented
  • POJO called thru URLClassLoader_1 should be the same and instrumented
  • POJO2 loaded by URLClassLoader_1 should NOT be instrumented
  • POJO2 loaded by URLClassLoader_2 should be instrumented according to local jboss-aop.xml
  • POJO and POJO2 loaded by URLClassLoader_3 should be instrumented
This behavior should really be in place if I want to use JBoss standalone AOP in my favorite J2EE app server (which is not JBoss by the way). Indeed, my deployed EJBs are like POJO2 in URLClassLoader_1 in which I don't want AOP. Other deployed EJBs are also like POJO2 in URLClassLoader_2 in which I want AOP.

Note the URLClassLoader_3 is more for demonstration purpose.

Off course, the sample application should run correctly without AOP before going further.

Results

Below is the stdout and stderr of the simple application running twice: without AOP and with AOP.

You should notice :

  • The class.hashcode information wich demonstrates class POJO and POJO2 are loaded as explained
  • A stack trace appears on stderr - it does not comes from the sample app - bad idea
  • Instrumentation fails but class is still loaded correctly uninstrumented
  • A sample line of code was added to JBoss AOP code for tracing jboss-aop.xml deployment.


You can run this by your own - read here


**************************************
       BUILDING (JAVA_HOME is C:\java\j2sdk1.4.0_02)
**************************************


**************************************
       TEST WITHOUT AOP
**************************************

** test POJO loaded by SystemCL
class.hashcode = 5383406
POJO say Hello World!

** test POJO loaded by SystemCL thru URLClassLoader_1 with no AOP xml child of SystemCL
class.hashcode = 5383406
POJO say Hello World!

** test POJO2 loaded by URLClassLoader_1 with no AOP xml child of SystemCL
class.hashcode = 12338265
POJO2 say hello !

** test POJO loaded by SystemCL thru URLClassLoader_2 with AOP xml child of SystemCL
class.hashcode = 5383406
POJO say Hello World!

** test POJO2 loaded by URLClassLoader_2 with AOP xml child of SystemCL
class.hashcode = 8947545
POJO2 say hello !

** test POJO loaded by URLClassLoader_3 with AOP xml child of BootstrapCL
class.hashcode = 16130931
POJO say Hello World!

** test POJO2 loaded by URLClassLoader_3 with AOP xml child of BootstrapCL
class.hashcode = 15939189
POJO2 say hello !



**************************************
       TEST WITH AOP (requires JAVA 1.4)
**************************************
xml deploy
AOP = /C:/cvs_02/jboss-aop/docs/oreilly-aop/example1/aop_system/META-INF/jboss-aop.xml

** test POJO loaded by SystemCL
class.hashcode = 15700311
Entering method: helloWorld
POJO say Hello World!
Leaving method: helloWorld

** test POJO loaded by SystemCL thru URLClassLoader_1 with no AOP xml child of SystemCL
class.hashcode = 15700311
Entering method: helloWorld
POJO say Hello World!
Leaving method: helloWorld

** test POJO2 loaded by URLClassLoader_1 with no AOP xml child of SystemCL
javassist.NotFoundException: POJO2
        at javassist.ClassPoolTail.openClassfile(ClassPoolTail.java:294)
        at javassist.ClassPoolTail.checkClassName(ClassPoolTail.java:177)
        at javassist.ClassPool.checkClassName(ClassPool.java:679)
        at javassist.ClassPool.checkClassName(ClassPool.java:679)
        at javassist.ClassPool.get0(ClassPool.java:542)
        at org.jboss.aop.AOPClassPool.getLocally(AOPClassPool.java:73)
        at org.jboss.aop.AspectManager.translate(AspectManager.java:348)
        at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:324)
        at org.jboss.aop.standalone.SystemClassLoader.loadClass(SystemClassLoader.java:173)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:299)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:262)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:322)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:207)
        at StandaloneTest.testInheritWithoutAOP(StandaloneTest.java:63)
        at StandaloneTest.main(StandaloneTest.java:104)
class.hashcode = 6616781
POJO2 say hello !

** test POJO loaded by SystemCL thru URLClassLoader_2 with AOP xml child of SystemCL
class.hashcode = 15700311
Entering method: helloWorld
POJO say Hello World!
Leaving method: helloWorld

** test POJO2 loaded by URLClassLoader_2 with AOP xml child of SystemCL
javassist.NotFoundException: POJO2
        at javassist.ClassPoolTail.openClassfile(ClassPoolTail.java:294)
        at javassist.ClassPoolTail.checkClassName(ClassPoolTail.java:177)
        at javassist.ClassPool.checkClassName(ClassPool.java:679)
        at javassist.ClassPool.checkClassName(ClassPool.java:679)
        at javassist.ClassPool.get0(ClassPool.java:542)
        at org.jboss.aop.AOPClassPool.getLocally(AOPClassPool.java:73)
        at org.jboss.aop.AspectManager.translate(AspectManager.java:348)
        at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:324)
        at org.jboss.aop.standalone.SystemClassLoader.loadClass(SystemClassLoader.java:173)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:299)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:262)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:322)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:207)
        at StandaloneTest.testInheritWithAOP(StandaloneTest.java:73)
        at StandaloneTest.main(StandaloneTest.java:110)
class.hashcode = 8215850
POJO2 say hello !

** test POJO loaded by URLClassLoader_3 with AOP xml child of BootstrapCL
class.hashcode = 9558209
POJO say Hello World!

** test POJO2 loaded by URLClassLoader_3 with AOP xml child of BootstrapCL
class.hashcode = 6183504
POJO2 say hello !

Conclusion

Everithing is OK for the SystemCL: POJO is instrumented.

As we can see, POJO2 is correctly loaded by URLClassLoader_1 for which we don't want instrumentation but a Javasssit stack trace strangely get out of there .

The same behavior occurs for URLClassLoader_2 for which we do want instrumentation. As we can see, the jboss-aop.xml available in the search path is not taken into account .

No instrumentation at all occurs in URLCLassLoader_3. At least there is no stack trace problem here, just nothing...

URLClassLoader not supported

JBoss standalone AOP does not bring AOP in java.lang.URLClassLoader.

It is obvious it won't work for other app server than JBoss.

JBoss standalone AOP even print stack traces saying the class is not found by its instrumentation layer, even if the class is found by the application itself.

ClassLoader hierarchy not supported

As soon as you try to use JBoss AOP in a java.lang.ClassLoader hierarchy which goes beyond than the hello world, it won't work. No way.

Final word

If you really need a global cross application and cross application server AOP solution, forget about JBoss AOP . The JBoss standalone AOP is just good to build light demo to help user get in the JBoss stuff.

Stay tune on beSee, and begin to learn AspectWerkz . You won't loose your time.

It's my personnal choice.