java: use two version of the same lib in one webapp - java

I'm facing the following problem: I have one module in my webapp that needs jaxb 1.x and the other module needs jaxb 2.x. The first module doesn't work with the new version of jaxb, and the opposite. How can I use these two jars in one project?
Thanks.

For a regular application, usually very different versions use different package names. If this is the case, you can use them both at once without problem. However if they are the same, you can use jarjar to rename the package.
However since you are using a web container each application should use the version you deploy and not the other version. i.e. the web container works it out for you.
OSGi is another container which manages the versions much more explicitly and give you more control over these issues (however I believe you need it just for this)

You have got a jar-hell issue. Generally speaking in normal java environment it's impossible to solve this problem. You have to force modularization into your project by using OSGI. Starting point: http://www.osgi.org/About/HowOSGi

If you are using the JAXB reference implementation, then you can use your JAXB 1 models with the JAXB 2 runtime by including the jaxb1-impl.jar.
http://jaxb.java.net/faq/index.html#running1Apps

As Shaman said is imposible to resolve this issue.
Let's see: the servlet container JRE has only one classloader, and this classloader can load and use one class from jaxb or the other, but not both that will give you a classdefnotfound exception or something similar.
You can not solve this directly:
you can get the code (is opensource) and change the package of one to another name so the classloader can use both. I do not recommend you this solution, is a bad one.
Better is that you migrate the code to use the most modern API (jaxb 2)

Related

How to use two different version of same package of a dependency?

I am working with a usecase where my application is using okhttp [version:3.14.9] which is written in java, I am also importing some external dependency which uses okhttp [version:4.10.0] which is written in Kotlin, the external dependency is using a method of okhttp (4.10.0) which is not in okhttp (3.14.9). since my application during runtime calls the method with the (3.14.9) dependency hence unable to find the method and throws java.lang.NoSuchMethodError.
I know this is such a common problem which every other java application faced at some point of time. So I want to know the right approach which is used widely by most java applications.
There is no proper solution for this problem. You can choose only one version.
Way around this is to use OSGi, but it will require major redesign of your application.
This is a classic problem where you have more than 1 version of the dependency available during the runtime. I would suggest you uplift the version of okHttp in your application to match that of the external dependency. This would remove many headaches like "class path resolution" issues.
If you're using a build tool like Maven, there may be another solution where you use define 3.14.9v as the compile time dependency and include 4.10.0v as the runtime dependency so only 4.10.0v is packaged-in and available during the runtime.
I would try to upgrade my code from 3.14 to 4.10 to match the external dependency. Generally it is good to upgrade packages whenever you can, so this should be a good thing (unless there is a strong reason why you can't). The worse case is when an external dependency forces you to downgrade, which is frustrating but sometimes unavoidable.

It is possible to specify the classes/module where a dependency can be used?

Situation: I'm facing a component with old code that still uses Spring 1.2.6. The component is critical and cannot be altered. This old Spring version contains a query() method that is edited in Spring 2.x.x. In the rest of my code I want to use a more recent version of Spring (e.g. Spring 2.5.6).
Goal: I would like to specify that I this old query() method can only be used in the component, so the other code isn't bothered by this. It is possible to specifiy the classes/module where a dependency can be used?
Thanks in advance!
As far as I'm aware it's not possible, since you're loading the Libraries into the same Class-Loader, so all Objects will be overloaded when the Object already exist.
For example you (or the Component) load Spring 1.2.6 so the Component can use it, and you actually try to load 2.5.6 it'd overload it, which means that the 1.2.6 get's unloaded and the 2.5.6 instead.
A way to solve this is to either load the Spring-Versions in two Class-Loaders (See this Question) or when Spring does support it (which is unusual and prob. not the case - Sorry, not familiar with Spring), is it to have different Packages for both Versions. Because they are in different Packages, the Class-Loader won't overload the Objects since they have a different "Path" (Full-Package + Object-Name + .java).

How to solve external library causing JAR conflicts?

I'm using Lucene 5.0 in my application and I'm also using the DISCO java library, which does, in turn, use Lucene 3.5. When I import the DISCO jar I can't run the program anymore, because I get runtime errors regarding Lucene classes that are conflicting between the two versions of the library.
Is there a way to solve this?
Yes. There are three solutions:
You can downgrade your code to use Lucene 3.5
You can upgrade DISCO to use Lucene 5.0 (either yourself or by getting the DISCO team to do it)
You can use several ClassLoaders to isolate the code.
The last point works since two classes in Java are the same if the fully qualified name is the same and when they were loaded by the same ClassLoader.
The OSGi framework can do tricks like this. Eclipse uses Equinox, which is an implementation of OSGi.
One option is to bundle everything and set up OSGi to load things correctly.
To solve your problem yourself, you can create two ClassLoaders. One loads your application and Lucene 5.0. The other loads DISCO and and Lucene 3.5.
The ugly part is that you can now get ClassCastException for classes in Lucene. They will have the same name but they won't be the same as far as Java is concerned (different classloaders). To be able to pass data between the two classloaders, you need a parent ClassLoader which has POJOs in which you can put all the data which you want to share. java.lang.String will also be in this ClassLoader (otherwise, things would be very, very complicated).
Shared dependencies can also go in the parent ClassLoader.
You then need a thin adapter layer on top of the DISCO/Lucene code which allows you to do the operations you want without using any of the classes which this classloader doesn't like.

How can I tell my application that it shouldn't use an external Service Provider from a jar?

For some reason we need the xdb.jar from the Oracle XDK. This jar depends on the xmlparserv2.jar. The actual problem is that the xmlparserv2.jar comes with some provider-configuration files (META-INF/services). The jar has Services Providers for javax.xml.transform.TransformerFactory, javax.xml.parsers.SAXParserFactory and javax.xml.parsers.DocumentBuilderFactory. The implementations (from oracle.*) don't work together with some other parts of our software (they need the standard factories).
You could use separate classloaders to isolate the jars from the rest of the application. This question about covers it.
You could just remove these classes from the xmlparserv2.jar
See here - "JNLP classpath precedence 1.5 vs 1.6"
I solved the problem by removing the services (META-INF/services). We ran our unit tests and everything worked. Even the parts of our software which are depending on xmlparserv2.jar/xdb.jar.

Runnable JAR with duplicate dependencies

I am trying to create a runnable JAR using Eclipse, but an running into problems. The Eclipse workspace contains two separate projects which depend on the same library. I can create the runnable JAR, but the problem is when I run it I receive a java.lang.NoSuchMethodError exception.
I believe I'm receiving the java.lang.NoSuchMethodError exception because the libraries are different versions. Is there a common solution to fix this problem? If not, what would you recommend I do?
If the major version number changes it means that backwards compatibility may have changed.
You could try with the latest version and hope that they just did add methods and that the old way of working, but even if NoSuchMethod exception is not thrown there is no guarantee (maybe with the new API you should call differente methods to get the same results).
I would contact the provider of the library and ask them if compatibility is broken. If they do not answer or it is broken, and you have the source code, the only possibility would be refactoring one of the libraries (probably 1.0); v.g. putting all of it in new packet v1. Then you would have to change the project that depends of it.
If none of the above works, then the solution would be an OSGi container or to setup project A and project B as two different executables and setup project B as a server that answer project A messages. Messy
The fix is to only include one version of the library which can satisfy both of the libraries that use it. If that's not possible, you'll have to find a different way of going about things such that you can eliminate the conflict. Options include:
Remove one or more of the uses from your code that are causing the NoSuchMethodError.
Modify the source of one or more of the libraries so they can happily coexist.
Use an OSGi container, which would allow you to have two versions of the same library in the same application.
As SJuan stated, you could use OSGI to set it up correctly. http://en.wikipedia.org/wiki/Java_Classloader#JAR_hell

Categories

Resources