How to load 2 versions of one Jar in 1 Java class

This is a tricky question, but I'll do my best to describe my problem.

I need to download 2 versions of the same JAR in the top level class (v1.jar and v2.jar), so I have access to both jar versions. The reason for this is I want to check if any function has changed in v2.jar from v1.jar

In my top level class, I want to call the v1.jar and v2.jar methods and then check the output from v1 to output v2. This way, I can be sure that nothing worked.

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 cannot import v1 and v2 bans as maven dependencies, as this will create a version conflict in maven and maven will use the newest jar by default. So I thought about creating a common interface and having two different classes to implement that interface. Then in the top tier I can use classloaders to load v1 and v2 boxes, etc. But this way does not work as I would have to change production v1.jar to implement a common interface.

Any help or understanding would be greatly appreciated. I'd really like to see some samples if possible. And please don't link to other topics.

+3


source to share


2 answers


Your test class can customize ClassLoader

for each .jar file. The easiest way to do this 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 the .jar file is on the classpath of the test code, the code cannot declare any type variables from the .jar files. All access from the test code must be done using reflection.




UPDATE

You may also need to change the context class loader when making 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

      

+1


source


I found this from another Stackoverflow question where I needed to load a jar at 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

-1


source







All Articles