4

I have two java projects Test1 and Test2. Test1 has a library lib added to it in its eclipse Java Build Path. Test2 has a jar added in its build path.

Test1      Test2
/src        /src
lib         Referenced Libraries
 x-1.1.jar   a.jar
 y.jar        x-1.2
              z

The code in Test1 calls functions/classes of y.jar which has dependency on x-1.1.jar. I don't know what functions/classes of x-1.1.jar are used by y.jar as I don't have source for the jars. Similarly Test2 calls functions/classes of z package of a.jar which have dependency on x-1.2 verion of jar.

Now I have a Test project where I need both the projects Test1 and Test2

Test
/src
 Test1 code
 Test2 code
 some other code which uses Test1 as well as Test2 libraries
lib
 x-1.1.jar
 y.jar
Referenced Libraries
 a.jar
  x-1.2
  z 

Now when I run the Test project I get into jar-hell situation. Two approaches used here by my research are:

  1. Classpath: Problem with this approach is that since both the library/jar for Test1, Test2 are added to the eclipse Java Build Path, only the first loaded version of the x.jar is accessed and Test2 code breaks even on using classloader.
  2. osgi: Problem with approach is that I am able to export only packages in the src folder of Test1 and Test2 from the osgi bundle, not the one's which are referenced by the project. But the Test project has code which uses Test1 and Test2 libraries.

Hope I am clear enough. Any help is greatly appreciated. Thanks in advance.

Adding more information to the question: I have to use two different java sdks in my project which have bundled different jar files. Conflicting ones are:

jar file                     Test1 ver  Test2 ver
org.apache.commons.codecs    1.3        1.6
org.apache.commons.logging   1.1.1      1.1.1
org.apache.log4j             1.2.7      1.2.15
httpclient                   4.1.1      4.0.3   
httpcore                     4.1        4.1.4

What can be possible ways to do this?

  • 2
    OSGi: You can simple add the foreign packages to your export: http://stackoverflow.com/a/1235145/1165132 – Adrian Apr 12 '13 at 13:42
  • 1
    Do you know *for certain* that `x-1.2.jar` is *not* backward-compatible with `x-1-1.jar`? Also, you can use a tool like JDepend to learn what functions one JAR consumes from another. – kdgregory Apr 12 '13 at 13:46

2 Answers2

1

OSGI actually most likely IS a solution and you have two ways to do it:

  1. Convert dependent jars to osgi bundles, define which version of packages they do export (simply use real version numbers) reference (OSGI way) them from your test projects, when importing packages specify exact required versions. This way you would create osgi bundle for each library jar and for each of your projects.
  2. Create two osgi bundles: test1 and test2. Embed their required version of libraries (as embedded jars or inline) and do not export dependant packages. You can freely reference test1 and test2 from test bundle. They will not clash, but will reside in isolated classloaders (bundle classloader space). Information on how to embed dependencies using maven bundle plugin: http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html#ApacheFelixMavenBundlePlugin%28BND%29-EmbedDependencyandExportPackage

Emphasis on this configuration:

<Embed-Dependency>
    *;scope=compile|runtime;inline=false
</Embed-Dependency>

How to wrap jars to osgi using BND tool: http://java.dzone.com/articles/how-creategenerate-osgi

By the way most (if not all) of your mentioned libs should be already osgi-ready so you will not even need to convert them to osgi bundles. The only problem could arise here is that some jars are not compatible with osgi (for example the ones making asumptions about container's class loader architecture), but it's quiet rare case.

user177889
  • 11
  • 1
0

You could try to emulate classpath separation like the one OSGi does by implementing a custom class loading mechanism with two URLClassLoaders, one for each of the x-1.*.jars.

This is the general idea:

File x1jar = new File("path/to/x-1.1.jar");
URLClassLoader x1loader = new URLClassLoader (x1ar.toURL(), this.getClass().getClassLoader());

File x2jar = new File("path/to/x-1.2.jar");
URLClassLoader x2loader = new URLClassLoader (x2ar.toURL(), this.getClass().getClassLoader());

// Test1 should look into x-1.1.jar ..
Class test1class = Class.forName("Test1", true, x1loader);

// .. Test2 should look into x-1.2.jar ..
Class test2class = Class.forName("Test2", true, x2loader);

// .. but both should see y.jar and z.jar via system class locader.

// Invoke whatever methods in testXclasses.

IMPORTANT: x-1.1.jar and x-1.2.jar must not be in the system classpath, i.e. you should place them in a separate lib folder, i.e. your class deployment should look like this:

+ myapp
  + lib
    myapp.jar
    y.jar
    z.jar
  + sandbox
    x-1.1.jar
    x-1.2.jar

And you must not refer to Test1 / Test2 class directly (with Test1 or Test1.class), only with class names (in a string "Test1").

Look at this answer and this one.

Community
  • 1
  • 1
Cebence
  • 2,406
  • 2
  • 19
  • 20