简体   繁体   中英

How to load 2 versions of the same Jar in 1 class Java

This is a complicated question but I will do my best to describe my problem.

I need to load 2 versions of the same JAR in a top level class (v1.jar and v2.jar) so I have access to both versions of the jar. The reason for this is because I want to test if any feature in v2.jar has regressed from v1.jar

In my top level class, I want to call methods of v1.jar and v2.jar and then validate the output from v1 against v2 output. This way I can be certain nothing got screwed up.

class Common {

    // Names of the classes would be the same so not sure how I would invoke the classes from the 2 different jars? 
   String resultv1 = EngineV1.run("a","b","c");
   String resultv2 = EngineV2.run("a","b","c");

   Assert.equals(resultv1, resultv2, "Regression has been introduced...");


}

I can't import v1 and v2 jars as maven dependencies since this will create a version conflict in maven and by default maven will use the newest jar. So I thought about creating a common interface and having 2 different implementation classes of that interface. Then in the toplevel I can use class loaders to load v1 and v2 jars, etc. But this way not work since I would have to change production v1.jar to implement the common interface.

Any help or insight will be much appreciated. I'd very much like to see samples if possible. And please don't refer me to other threads

Your test class can set up a ClassLoader for each .jar file. The easiest way to do that, is to use URLClassLoader .

Example:

File jar1 = new File("/path/to/v1.jar");
File jar2 = new File("/path/to/v2.jar");

URLClassLoader v1Loader = URLClassLoader.newInstance(new URL[] { jar1.toURI().toURL() });
URLClassLoader v2Loader = URLClassLoader.newInstance(new URL[] { jar2.toURI().toURL() });

Class<?> engineClass1 = v1Loader.loadClass("org.example.Engine");
Class<?> engineClass2 = v2Loader.loadClass("org.example.Engine");

Method runMethod1 = engineClass1.getMethod("run");
Method runMethod2 = engineClass2.getMethod("run");

Object engine1 = engineClass1.newInstance();
Object engine2 = engineClass2.newInstance();

String result1 = (String) runMethod1.invoke(engine1);
String result2 = (String) runMethod2.invoke(engine2);

Note that since neither .jar file is on the classpath of the test code, the code cannot declare any variables of types from the .jar files. All access from test code must be done using reflection.


UPDATE

You might also need to change the context class loader when making the calls:

String result1, result2;
Thread thread = Thread.currentThread();
ClassLoader myLoader = thread.getContextClassLoader();
try {
    thread.setContextClassLoader(v1Loader);
    result1 = (String) runMethod1.invoke(engine1);
    thread.setContextClassLoader(v2Loader);
    result2 = (String) runMethod2.invoke(engine2);
} finally {
    thread.setContextClassLoader(myLoader);
}
// Compare result1 and result2

I found this from a different Stackoverflow question where I needed to load a jar during runtime

    /*
    * Adds the supplied Java Archive library to java.class.path. This is benign
    * if the library is already loaded.
    */
   public static synchronized void loadLibrary(java.io.File jar) throws Exception {
       try {
           /*We are using reflection here to circumvent encapsulation; addURL is not public*/
           java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
           java.net.URL url = jar.toURI().toURL();

           /*Disallow if already loaded*/
           for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
               if (it.equals(url)){
                   return;
               }
           }
           java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
           method.setAccessible(true); /*promote the method to public access*/
           method.invoke(loader, new Object[]{url});
       } catch (final java.lang.NoSuchMethodException | 
           java.lang.IllegalAccessException | 
           java.net.MalformedURLException | 
           java.lang.reflect.InvocationTargetException e){
           throw new Exception(e);
       }
   }

Works for my purposes

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM