Dear Javassist Users and Developers
Thank you for an excellent tool
I have tried using Javassist to make a tool for Tomcat and it seems to me that the ClassPool as a sideeffect og its purpose of keeping the classes together, really is a ClassLoader that caches loaded (and modified) classes.
Tomcat (and other J2EE servers) uses a hierarki of ClassLoaders and you should be able to plug the ClassPool directly in to this hierarki
If ClassPool extended the WebappClassLoader from Tomcat it would fit directly in (but it would probably be getting to close to Tomcat).
Extending URLClassLoader should be close enough to only force minor rewrites in the Tomcat classes to get the full functionality
Sounds like a good idea
I ended up making a custom classloader inheriting ..catalina..WebappClassLoader only overriding a method "findResourceInternal" in WebappClassLoader, and then exchanging the loaded bytecode with bytecode loaded by ClassPool (and then modfied).
The WebappLoader (container) makes a cast somewhere so even if the documentation states that you can define your own classloader, for now the only way to do it is to inherit WebappClassLoader.
To make this work I had to give ClassPool the path from the whole classloader Hierarky, potentially mixing different sources for the code loaded by the original hierarky and ClassPool.
(And loading the class twice not very efficient, I think that I can find a way around this)
What would make this work was that I could tell Classpool to use my new class as ClassLoader, it would then be up to me to (re)place the modfied class in the inherited ClassLoaders internal repository.
By using this structure you would not need to make ClassPool aware of the hierarky. And it would be up to my class to choose between delegating "upwards" or loading fom my own classpath.
I think a general solution could be letting the ClassPool implement some sort of listener listening for "ClassLoadEvent"s
The ClassLoader should then be able to accept listeners and make synchronous calls to the listeners when loading a class. The registering could be with some sort of Regex String for filtering classes.
A ClassLoadEvent could have the the byte in the "ClassLoadEvent" (maybe alert a Translator) modify the classes and return the modfied bytecode in the event.
The ClassLoader would then call defineClass() and resolve() on the modied class and cache it internally.
To make this work in a concrete J2EE server the person implementing this would have some skeleton code to subclass the J2EE servers ClassLoader with
This needs to be integrating with something like the JDK 1.5 java.lang.instrument.ClassFileTransformer, not by requiring subclasses of the app server class loader.
byte transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte classfileBuffer) throws IllegalClassFormatException;
The app server class loaders should be able to evolve independent of the aop layer. There has to be a non-intrusive api for integrating the aop class generation provided by the app server.
But above the JDK in the API means letting the ClassLoader implement something like the java.lang.instrument.Instrumentation interface
By having the standard classloaders used by the server in normal mode as default, and then suclassing them with an implementation of the interface, it could be a configuration option if an application should instantiate with the subclass
The filtering and the potentially "slow" syncronous call to java.lang.instrument.ClassFileTransformer (ClassPool) would only be active if the subclass is used
In this way the AOP would only be able to modify classes in their own context and the ClassLoader hierarky would be intact
No, the class loader repository would have to provide support for this, and we already have an interface for this associated with the org.jboss.util.loading.Translator which is associated with the loader repository and optionally used by the class loader if present. AOP is not the only framework that may want to do byte code manipulation, so there has to be a general mechanism not tied to AOP.
Further, subclassing the class loaders is very problematic due to the deadlock problems caused by the private synchronized Class loadClassInternal(String name) method in ClassLoader. Look at the org.jboss.mx.loading classes for the hoops that have to be jumped through to implement the default flat class loading model for example.
Just being a pest ..
Ok looking at org.jboss.mx.loading I can see that a lot of work has gone into making classloading work in jboss.
Javassist beeing a subproject of jboss should of course respect the jboss strategies.
But still it seems to me that it would make sense to have a general way to make ClassPool plug in to an existing classloader hierarky
In Tomcat they have chosen to make a reimplementation of the functionalities of URLClassLoader (maybe to avoid some internal problems in URLClassLoader/ClassLoader) and as it is a userdefined classloader has to inherit WebappClassLoader (therefore you cannont give it javaassist.Loader as a userdefined classloader). In some other J2EE servers there might be other specialities.
The basic problem beeing that the only way to get the classes you want to manipulate into ClassPool is by setting the classPath, any structure that makes ClassPoll use an existing classloader (for finding/loading classes, and for compiling and later defining classes) would be a step towards a generic solution.
Ok, I understand your suggestion is that we should improve
the perforamance of ClassPool.makeClass(InputStream)
and javassist.ByteArrayClassPath. These are for feeding a
byte array to ClassPool. It should be a general framework
for integrating your own ClassLoader and Javassist.
Of course, these have not been tuned very well compared
to the standard ClassPath stuff, but I think it is possible to
make them faster.
Sorry for responding so late I thought the issue was dead
I am not really proud of my coding skills and i am working my way throug commenting the code by you can find my classloader at:
As catspeedCL.jar (in catspeed.tar.gz) which includes source code
I have tried solving the plugin problem by recursing through the classloading hierarky by building a classpath for classpool that should behave somewhat like being "plugged in" (that means taking the classes in the "reverse order" nearest the bottom of the hierarky except for system classes)
this reverse ordering being a servletcontainer feature et seems