Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to put custom ClassLoader to use?

Hello all and thanks for the attention! I have a problem that must both be easy and obvious, yet I am stuck.

I want to deliver dynamically created Java classes to be used by a 3rd party library via a custom ClassLoader.

Now my problem is: How do I set my custom ClassLoader to be used to load this classes when I do not load them directly myself?

I thought when I used my ClassLoader to load a certain class, it became this class's ClassLoader, and all classes loaded from that class would be channeled through my ClassLoader.

I created a custom ClassLoader, following this official tutorial: http://java.sun.com/developer/onlineTraining/Security/Fundamentals/magercises/ClassLoader/help.html.

public class DynamicClassloader extends ClassLoader {      private Map<String, Class<?>> classesMap = new HashMap<String, Class<?>>();      public DynamicClassloader(ClassLoader parent) {         // Also tried super(parent);         super(sun.misc.Launcher.getLauncher().getClassLoader());     }      // Adding dynamically created classes     public void defineClass(String name, Class<?> clazz) {         classesMap.put(name, clazz);     }      @Override     protected Class<?> findClass(String name) throws ClassNotFoundException {         // load from parent         Class<?> result = findLoadedClass(name);         if (result != null) {             return result;         }         try {             result = findSystemClass(name);         } catch (Exception e) {             // Ignore these         }         if (result != null) {             return result;         }         result = classesMap.get(name);         if (result == null) {             throw new ClassNotFoundException(name);         }         return result;     } } 

I wanted to use this somewhere else in the code like that:

ClassLoader thisClassLoader = this.getClass().getClassLoader(); ((DynamicClassloader) thisClassLoader).defineClass(className, dynClass); 

Now my problem is that when I call findSystemClass(name) of the 3rd party library class, the parent ClassLoader finds this class (because it is on the classpath) and becomes its ClassLoader. And since the parent ClassLoader doesn't know about my custom ClassLoader, it is effectively been put out of use and this.getClass().getClassLoader() cannot be cast to DynamicClassLoader.

Another approach would be to set my ClassLoader to be the system ClassLoader via JVM argument -Djava.system.class.loader=my.DynamicClassloader. But that gives me a StackOverflowError:

    ... at de.unisaarland.cs.st.DynamicClassloader.findClass(DynamicClassloader.java:39) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at java.lang.ClassLoader.loadClass(ClassLoader.java:248) at java.lang.ClassLoader.findSystemClass(ClassLoader.java:916) at de.unisaarland.cs.st.DynamicClassloader.findClass(DynamicClassloader.java:39)     ... 

This must be really easy to do, yet I am now out of ideas... any help is greatly appreciated!

like image 827
roesslerj Avatar asked Nov 04 '10 11:11

roesslerj


People also ask

When would you use a custom ClassLoader?

Custom class loaders You might want to write your own class loader so that you can load classes from an alternate repository, partition user code, or unload classes. There are three main reasons why you might want to write your own class loader. To allow class loading from alternative repositories.

Can we create custom ClassLoader in Java?

We will create our own ClassLoader by extending the ClassLoader class and overriding the loadClass(String name) method. If the class name will start from com. journaldev then we will load it using our custom class loader or else we will invoke the parent ClassLoader loadClass() method to load the class.

Can you create a custom class loader?

All classes from java system runtime classes to extension classes to application classes gets loaded by these class loaders and can be loaded from other class loaders as well. However, you can build your own custom class loader in java.


1 Answers

Not sure I understand the question, you have a 3rd party lib and you want it to use your classloader to load classes.

If you're lucky the third party lib uses the threads context classloader which you can set using Thread.currentThread().setContextClassLoader(myClassLoader), in the same thread you can access this classloader with Thread.currentThread().getContextClassLoader()...

Another point, but not sure it is important in your context, is that you can also write a parent-last classloader that will try to load the class before delegating to its parent (instead of trying to delegate first)

Edited after your comment:

parent_last classloader will make a difference if your library doesn't rely on the thread context classloader, then you have to load the library with your parent-last classloader thus setting your classloader as the classloader for the library instead of its parent loader (the parent of your classloader)...

You may also make a classloader with a parent-first behavior but for your 3rd party library...

And a good link about classloaders...

like image 163
pgras Avatar answered Oct 04 '22 11:10

pgras