Tomcat custom classloader failed to load some jars - java

I have a web application that have an upload functionnality which consist of uploading a package containing a java application (it may contains multiple dependencies)
For that , for every uploaded application, i'm creating a custom classloader to dynamically load the application classpath.
The solution is working fine until I get this error when uploading a new application:
javax.xml.stream.FactoryConfigurationError: Error creating stream factory: java.lang.ClassNotFoundException: de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl
I have verified that my uploaded package contains staxon and also verifyed that my custom classloader can load that class:
Object a=Class.forName("de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl",true , classLoader).newInstance();
So, why this exception and specially for this jar?

When you call Class.forName(className, initialize, classLoader) you are asking the JVM to load a class using that custom class loader. If you don't specify a ClassLoader -- for example just by calling Class.forName(className), then the JVM will use the ClassLoader known as the "context ClassLoader" for the thread. Each thread has one of these, and your code can change the context classloader somewhat easily.
If you have some code that causes a class to be loaded -- something like this:
MyClass foo = new MyClass();
The MyClass class will be loaded by the ClassLoader if it's not already loaded. It's not possible to call a constructor and supply a ClassLoader to load the class in case it's not already loaded. In this case, the thread's context ClassLoader is user.
Furthermore, if you call some code that you don't control, there are many ways in which that code could cause other classes to be loaded, and since you have no control over that code, the thread's context ClassLoader will also be used.
So, how do you set the thread's context ClassLoader? Easy:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
ClassLoader myClassLoader = ...; // You figure this out
try
{
Thread.currentThread().setContextClassLoader(myClassLoader);
// Do work that requires your ClassLoader to work
}
finally
{
// Always restore the previous CCL after work is done
Thread.currentThread().setContextClassLoader(cl);
}
Another thing you'll want to do it make sure that your custom ClassLoader delegates any requests for class-loading to a parent ClassLoader. That parent ClassLoader should probably be the ClassLoader that would naturally be used if you weren't trying to use your own: the thread's context ClassLoader.
So, you probably want something like the following:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
ClassLoader myClassLoader = new MyClassLoader(cl); // Try 'cl' before your custom class loading
try
{
Thread.currentThread().setContextClassLoader(myClassLoader);
// Do work that requires your ClassLoader to work
}
finally
{
// Always restore the previous CCL after work is done
Thread.currentThread().setContextClassLoader(cl);
}
You can find more information about ClassLoaders, and, specifically the TCCL, in these few references:
http://docs.oracle.com/javase/jndi/tutorial/beyond/misc/classloader.html
Difference between thread's context class loader and normal classloader
http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html (also referenced from the previous reference)
http://njbartlett.name/2012/10/23/dreaded-thread-context-classloader.html

Related

Need to replace the existing class in runtime by a dynamically generated and compiled java .classfile at runtime

My purpose is to dynamically inject new attributes + getter setter methods to a class definition at runtime. Currently I have a method to regenerate the code with newly added attributes which then will compile the generated code.
Initially I'll have a template for each class at compile time. Upon running the project, the template class is loaded to runtime. I have written some code to dynamically generate java code and compile it. When I load the newly created class using the below code, I am not able to access injected methods. I think I am not able to overwrite existing runtime definition. I went through a lot of blogs but still couldn't understand why. Please help.
I am accessing the newly added methods in DROOLS and its not referenced in any other class which can raise issues during compilation. Rules of the rule engine with new attributes are updated at runtime and so I need to adapt my code accordingly. Below is the ClassLoader code. This code doesn't throw any exception but fails to solve my purpose. Not sure if coding is right.
public static boolean loadClass2RunTime() {
try {
File folder = new File("target");
File dir = new File(folder, "com/itap/template");
File[] classFiles = dir.listFiles();
URL[] url = new URL[] { folder.toURI().toURL() };
int i = 0;
for (File classFile : classFiles) {
if (classFile.getName().matches(".*\\.class")) {
System.out.println(classFile.getName().substring(0,
classFile.getName().lastIndexOf(".")));
ClassLoader loader = URLClassLoader
.newInstance(new URL[] { folder.toURI().toURL() });
Class cls = loader.loadClass("com.itap.template."
+ classFile.getName().substring(0,
classFile.getName().lastIndexOf(".")));
ClassLoader temp = cls.getClassLoader();
}
}
return true;
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
URLClassLoader can be useful when you need to load newly generated classes at runtime. Unfortunately, you will not be able to reload the definition of a already loaded class (i.e. reload a class) using URLCLassLoader.
When I load the newly created class using the below code, I am not
able to access injected methods. I think I am not able to overwrite
existing runtime definition.
URLClassLoader will check if the specified class has been already loaded. If found it simply returns the instance pointing to loaded class. Thus you will not be able to overwrite existing runtime definition.
Solution:
A standard approach to support dynamic class reloading in Java, one should read the byte information of the class (from its .class file) and write a custom class loader to load a class using this byte information.
Points to remember while using custom ClassLoader:
To reload a class, existing instance of the ClassLoader (which loaded the class) and all the loaded instances of the class should be garbage collected.
Custom ClassLoader must have load only specific classes (which needs to be loaded / reloaded). Request to load all the other classes (in-build Java classes or classes from other packages) must be delegated to its parent class loader. Trying to load in-built system classes might result in privilege exception.
The steps followed by the class loader when loading classes are:
Check if the class was already loaded.
If not loaded, ask parent class loader to load the class.
If parent class loader cannot load class, attempt to load it in this class loader.
When you implement a class loader that is capable of reloading classes you will need to deviate a bit from this sequence. The class reloading request should not be delegated to the parent class loader.
Here are sample link which can help you develop your custom class loader in Java.
http://www.javablogging.com/java-classloader-2-write-your-own-classloader/
http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html
Edited after your comment:
I think the statement InputStream stream =
getClass().getClassLoader().getResourceAsStream(name); of
loadClassData function in the example is not picking up the latest
version of the class.
You are right, it is not picking up the latest version of the class simply because it is using existing ClassLoader to get the stream (which points to the old version of your class).
You can rather use,
FileInputStream stream = new FileInputStream("Path to your class file");
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = stream.read();
while(data != -1){
buffer.write(data);
data = stream.read();
}
stream.close();
byte[] classData = buffer.toByteArray();
to read the byte information for the class and try to load it using your version of ClassLoader (custom ClassLoader).

Javaassists and Java Web Start: class sign doesn't match the others classes sign is the same package

i'm using javaassists to modify the behaviour of a class in runtime. When i run the app in my computer all works fine.
But this app is launched by the users with Java Web Start, so that the .jar of the app must be signed. When the class is modified in runtime time by javaassists and loaded with the
CtClass.toClass()
method a SecurityException is launched:
java.lang.SecurityException: class "com.sch.coberturas.db.CobDao"'s signer information does not match signer information of other classes in the same package
I can avoid it by isolating the modified class in a single class package, but this is a weird solution. Is there another workarround?
Like OP requested I'm creating an answer regarding my comment.
When you are using a SecurityManager you must always provide the protected domain in use. With this information javassist will be able to be generate classes with the same signing information.
This means that instead of using CtClass.toClass() you should use ClassPool.toClass(CtClass ct, java.lang.ClassLoader loader, java.security.ProtectionDomain domain).
More information about this method in javassist javadoc
I'd just like to add, that if the class you're changing has more classes on the package it belongs it may throw a signer's information exception also.
I fixed it by loading them and adding them in the same spot I changed my class.
Example:
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath( new ClassClassPath( this.getClass() ));
ClassLoader loader = this.getClass().getClassLoader();
ProtectionDomain domain = this.getClass().getProtectionDomain();
CtClass class = pool.get("package1.myChangingClass");
// Here goes the code to change the class;
pool.toClass( class, loader, domain );
//
// Now insert code to pre-load other classes with same domain
CtClass otherClass1 = pool.get("package1.someOtherClass1"); // Gets
pool.toClass( otherClass1, loader, domain ); // Preloads
...
CtClass otherClassN = pool.get("package1.someOtherClassN");
pool.toClass( otherClassN, loader, domain );
//
// To be done for all other classes of the same package that throws
// the signer's information exception, after package1.myChangingClass
// is modified and built.
Kind regards.

Leaving Classloader open after first use

I am creating a URLClassloader to load some jars. Each jar gets loaded correctly from a different classloader and each jar contains a class with a method run(). Now the body of this run() can create an anonymous inner class in it. However, because i created my URLClassloader in a try-with-resources block it gets autoclosed and at run time when it tries to load the anonymous inner class it throws a NoClassDefFoundError because the classloader is already closed.
Now my question is, what is the normal practice for these situations? is it ok to leave the classloader open so that when later it needs to load something else, it can? is there a way to reopen a closed classloader?
If I leave the classloader open, the compiler gives me warnings about potential resource leaks so I have a feeling this is like streams where you are not supposed to leave them open indefinitely. However because of the nature of classloaders, if it's not the same classloader that loads the anonymous class, it cannot be used in the outer class
here is the code where the classloader is created
public Player(File codePath) throws PlayerException {
try (URLClassLoader loader = new URLClassLoader(new URL[] { codePath.toURI().toURL() })) {
//load class from Jar where run() method creates anonymous class that comes in the jar too
} catch (ClassCastException | IOException | ClassNotFoundException | InstantiationException
| IllegalAccessException | IllegalArgumentException | InvocationTargetException
| SecurityException exc) {
throw new PlayerException("Error loading player's code", exc);
}
The life time of a class loader should be at least the life time of the instances of the classes loaded with it. As long as they and their classes are not eligible for garbage collection neither is their class loader. And should they need to load additional code or resources you need the class loader open.
So when you're done with a player, that's the time when you should close the class loader.
Rather than creating a new classloader for each player, you could use a Factory Pattern (or something similar):
URLClassLoader loader = PlayerClassLoaderFactory.getInstance().getClassLoader(codePath.toURI())
where the factory maintains the references to the classloaders (so the classes are not orphaned). Then you have the ability to "shutdown" the factory if needed to close the classloaders.

How do I "close" a ClassLoader?

I have a case where I need to create a lot of class loaders in my application to temporarily make some code visible while user supplied scripts are running. I'm using an URLClassLoader for this and it works pretty well.
When the script terminates, I want to "unload" or "close" the class loader to free the resources.
Is it enough to set the reference to the class loader to null? I'm especially wondering if I'll eventually run out of file handles because the extra classes are in JAR files.
PS: Must work with Java 5 and up. Yeah, I know...
A little late, but hopefully this'll be helpful for those who come to this Question later (like me).
With Java 7, a close() method has been added to URLClassLoader, which is exactly what OP was asking for.
EDIT (thanks to #Hot Licks): OK, so it isn't exactly what the OP has asked for. It doesn't free up all the resources, or make the resources and the loader collectible. It simply prevents the loading of more resources using the class loader. It does, however, close the jar file that was loaded with the URLClassLoader.
If you can't use Java7 and it's close() method, use reflection to close all open JAR archives of a classloader, like so:
public void close() {
try {
Class clazz = java.net.URLClassLoader.class;
java.lang.reflect.Field ucp = clazz.getDeclaredField("ucp");
ucp.setAccessible(true);
Object sun_misc_URLClassPath = ucp.get(this);
java.lang.reflect.Field loaders =
sun_misc_URLClassPath.getClass().getDeclaredField("loaders");
loaders.setAccessible(true);
Object java_util_Collection = loaders.get(sun_misc_URLClassPath);
for (Object sun_misc_URLClassPath_JarLoader :
((java.util.Collection) java_util_Collection).toArray()) {
try {
java.lang.reflect.Field loader =
sun_misc_URLClassPath_JarLoader.getClass().getDeclaredField("jar");
loader.setAccessible(true);
Object java_util_jar_JarFile =
loader.get(sun_misc_URLClassPath_JarLoader);
((java.util.jar.JarFile) java_util_jar_JarFile).close();
} catch (Throwable t) {
// if we got this far, this is probably not a JAR loader so skip it
}
}
} catch (Throwable t) {
// probably not a SUN VM
}
return;
}
When all classes that the class loader loaded no longer have any references, and all references to the class loader itself have been erased, the class loader and the classes it loaded will be garbage collected as a group.
Note that this is dependent on having the JVM attribute set that causes unreferenced classes to be unloaded. It's set by default in most environments, but may not be in some embedded cases.
[Note that it's a non-trivial matter to remove references to a class. Any other class that references it by name will of course prevent removal. So the class must be loaded using ClassLoader.findClass or something similar.]
If you do not longer have classes (and object) loaded from that classloader, and if you do not keep any reference to that classloader, it will automatically handled by the garbage collector.
There are no close() methods in URL class loader or any of its parent classes, so you're out of luck.
Shouldn't GC handle this?
I extended URLClassLoader and made a close method based on Java 7s. I wanted to develop my IRC bot on my iPad 2, so I did what was needed. Now my plugin system is stable on Java 6 and 7, hurray.

Is it possible to have the System ClassLoader load .class files specified at run time?

I am writing a static analysis tool for an assignment, it analyses Java bytecode using the ASM library. One of the parts of ASM that we use requires (or at least, appears to require) that the class be loaded from the ClassLoader.
We were hoping the tool would be able to analyse .class files without requiring them on the classpath. We already load the .classes from a specified directory at run time and read them in using an InputStream. This is acceptable for ASM in most cases. There are some classes, such as SimpleVerifier, which attempt to load the classes though.
Is it possible, under this scenario, to register the .class files to be loaded so that calls to Class.forName() will load them? Or is there an easy way to extend the ClassLoader to allow this?
Edit: the information on URLClassLoader was useful. Unfortunately, using Thread.currentThread().setContextClassLoader() to an instance of that didn't work in this scenario. The library code I'm calling into uses a loader it retrieves on instance initialisation using getClass().getClassLoader().
By the time I set the URLClassLoader the class hasn't been initialised so I guess the contextClassLoader does not load that class.
Have I understand the responses correctly? Would using the URLClassLoader to load the 3rd party class be a possibility?
Almost.
If you have classes compiled somewhere, you can load them with a URLClassLoader. You can then set this ClassLoader to be the ClassLoader for the current Thread: Thread.setContextClassLoader(ClassLoader)
Users can that get the current threads context class loader and use that to access the class definition.
First of all, ASM can be used in a such way that it won't use ClassLoader to obtain information about classes.
There are several places in ASM framework where it loads classes by default but all those places can be overridden in your own subclasses. Out of the top of my head:
ClassWriter.getCommonSuperClass() method is called only when ClassWriter.COMPUTE_FRAMES flag is used and can be overwriten to not use ClassLoader to get inforamtion about classes. You can find an example of that in ClassWriterComputeFramesTest that introduces a ClassInfo abstraction
Similarly SimpleVerifier.getClass() method is used by SimpleVerifier.isAssignableFrom() and you can overwrite the latter and use the ClassInfo abstraction to find the common super type. If I am not mistaken, AspectWerkz project had implemented similar thing in its type pattern matching code. Also note that there is SimpleVerifier.setClassLoader() method, which you can use if you still want to load your own classes.
On a side note, on a Sun's JVMs, loaded classes gets to PermGen area and can't be unloaded, so it is not a good idea to load classes only for static code analysis purposes if you can avoid that, especially if tool would be integrated into a long-live process, such as IDE.
You can't, as far as I know, extend the System class loader at runtime, but you can dynamically load classes from an arbitrary location (jar or directory) using URLClassLoader.
You could try to setup a "launcher" in the startup of your application that creates an URLClassLoader passing it the locations on the classpath and your own .class locations and start the application from that classloader.
When the SimpleVerifier is loaded by the URLClassLoader it will also be able to load the classes from the extra locations.
Yes, you can use URLClassLoader
I have a test where I do load the class at runtime. This class is not in the classpath (nor even exist when the test is run for that matter ), later is it loaded and works great.
Here's the code.
void testHello() throws MalformedURLException, ClassNotFoundException {
URL[] url = {
new URL("file:/home/oreyes/testwork/")
};
try {
new URLClassLoader(url).loadClass("Hello");
throw new AssertionError("Should've thrown ClassNotFoundException");
} catch ( ClassNotFoundException cnfe ){}
c.process();// create the .class file
new URLClassLoader(url).loadClass("Hello");
// it works!!
}
Taken from this question.
I created my own ClassLoader its quite simple.
/**
* Used to hold the bytecode for the class to be loaded.
*/
private final static ThreadLocal<byte[]> BYTE_CODE = new ThreadLocal<byte[]>();
#Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
final byte[] bytes = BYTE_CODE.get();
if (null == bytes) {
throw new ClassNotFoundException(name);
}
return this.defineClass(null, bytes, 0, bytes.length);
}

Categories

Resources