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