Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to "reset" a class loader?

I have to dynamically load a class with the same name from some JAR, but different implementation, multiple times.

I'm creating an evaluator backend and I have to dynamically load classes and test them.

The tests are JUnit classes that instantiate the classes which should be tested, this is a simple example:

package evaluator.tests;

import static org.junit.Assert.*;

import org.junit.*;

import evaluator.tested.*;

public class KTest {
    private K tested;

    @Before
    public void setup() {
        tested = new K();
    }

    @Test
    public void returnsTrueTest() {
        assertTrue(tested.returnsTrue());
    }

}

The rest of my application would need to receive JAR files from users which would contain implementations of the K class which is being tested above. Then, KTest would have to run on their K classes, not the ones in the application.

I know how to dynamically load a class, but I don't know how to make a test work with it, and not the one which I made.

One of the solutions I came up with was to isolate testing in a completely new class, e.g. Evaluation, create a new class loader in that class, and make it load all referenced classes. After creating the class loader, it would load the K class from the JAR file.

This would mean that each time a user submits his JAR, a separate Evaluation would be instantiated, it would create its own class loader and start the JUnit test. When that happens, the test would use the user's implementation of K, and not the default one.

Is this possible, and how can it be done?

I read that class loaders always ask their parent whether a class is already loaded. This would mean that I would have to somehow "flush" all the classes that I loaded dynamically from the JAR file in Evaluation, so that they would be unloaded and then loaded again in another Evaluation.

Load class K, test class K, unload class K, repeat with different K.

like image 820
corazza Avatar asked Dec 24 '13 12:12

corazza


1 Answers

Yes you can do this.

If you for example use java.net.URLClassLoader with null as parent: new URLClassLoader( urlArray , null ). Then the bootstrap ClassLoader will be used as parent for your ClassLoader.

Here is an example Class which simply uses a new classloader two reload a class.

package com.anarsoft.agent.regression;

import java.net.URL;
import java.net.URLClassLoader;

public class TestClassLoading {


public static boolean field = false;


public static void main(String[] args)  throws Exception
{


    URL[] urlArray = new URL[] { TestClassLoading.class.getProtectionDomain().getCodeSource().getLocation().toURI().toURL() };

    URLClassLoader firstClassloader = new URLClassLoader( urlArray   , null );

    Class firstClass = firstClassloader.loadClass("com.anarsoft.agent.regression.TestClassLoading");

    firstClass.getField("field").setBoolean(null,true);

    System.out.println(firstClass.getField("field").getBoolean(null));  // true


   URLClassLoader secondClassloader = new URLClassLoader( urlArray   , null );

    Class secondClass = secondClassloader.loadClass("com.anarsoft.agent.regression.TestClassLoading");



    System.out.println(secondClass.getField("field").getBoolean(null));  // false
      // the static field is false since its a new loaded class

}

}

like image 79
Thomas Krieger Avatar answered Sep 28 '22 09:09

Thomas Krieger