35

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!

mkobit
  • 43,979
  • 12
  • 156
  • 150
roesslerj
  • 2,611
  • 5
  • 30
  • 44
  • Related to http://stackoverflow.com/questions/6366288/how-to-change-default-class-loader-in-java, http://stackoverflow.com/questions/5380275/replacement-system-classloader-for-classes-in-jars-containing-jars/19128964#19128964 and http://stackoverflow.com/questions/5380275/replacement-system-classloader-for-classes-in-jars-containing-jars. – roesslerj Aug 03 '15 at 19:55

1 Answers1

25

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...

Martin Serrano
  • 3,727
  • 1
  • 35
  • 48
pgras
  • 12,614
  • 4
  • 38
  • 46
  • The ContextClassLoader is a good idea, will try it. Thanks a lot! As for the parent-last classloader: I don't think this will make a difference. I actually do not want to define the class that I am loading, I only want my ClassLoader to be the current ClassLoader, so all subsequent calls to `findClass` are channeled through it... – roesslerj Nov 04 '10 at 12:13
  • How do you write a parent-last class loader? isn't there an existing implementation? if not, then either it's not a commom problem or there is another, better way to solve it. – Eran Medan Mar 27 '11 at 01:40