Loading two classes in different JARs - java

I got two classes with the same package in different JARs. Until the previous version, both classes were identical, so i had no issues in loading them. Now, one of them has a new method added and if I want to access it, not only should I import the class with that package, i also need to make sure the jar with the correct class comes first in the classpath.
i.e. javac -classpath "%classpath%;a.jar;b.jar" MyClasses..
where a.jar has the class with my new method.
Now, how do i ensure this when my app goes to production, where it's deployed as an EAR file, with all the libraries under WEB-INF/lib?
How do I know which jar gets the preference over the other? Is it the alphabetical order like a.jar is given the first preference over b.jar?
I've read this safe-class-imports-from-jar-files thread and got to know about writing a custom classloader, but is there a better simpler solution that? Cos I'm just going to access this method in that whole JAR in this current project and writing a classloader seems a bit overkill.
And please don't ask me "Why the hell same class with same package in different JARs?" It's absolutely out of my control and it'll take some time to get this corrected.
Environment details: IBM WAS 6.1 on their 1.5 Java.
Please ask me more questions, if I don't make much sense. Thanks in advance!

You can try to change the startup script of your server and specify the jar with the correct class in the bootclasspath by using java -Xbootclasspath .... Otherwise there is no guarantee which one of the 2 jars will load up first.

As far as I know, the order of jars being loaded from WEB-INF/lib is arbitrary - I asked a similar question about JBOSS and got the reply ( from RedHat ) that it depends on the order that java.io.File.listFiles() returns them in ( and that is not a guaranteed order ).
A custom classloader would be an option, but have you considered repackaging the jars - removing the duplicated classes?

Websphere allows you to specify the order in which classloaders of a particular application are inquired when searching for a class (the classloaders are hierarchically structured, from the topmost that loads JRE classes, down to classloader loading classes in your WAR).
During deployment of an app, you can specify if the order of inquiring the classloaders when searching for a class. There are two modes - Parent first (i.e. query the topmost classloader first) and parent last (query the app classloader first). This can be specified on both EAR and WAR level.
Packaging the duplicated jars to different locations in the app (e.g. one to EAR's classpath, the other to WAR's WEB-INF/lib) and setting the classloader orderING apropriately may solve your problem. However, if both your JARs have to be on the same level (e.g. WEB-INF/lib), then there's no way to specify which one will be used when loading the duplicated class.

The order of the JARs in one application is likely to be alphabetical but the order of applications might not. Additionally, it depends on how the server handles classloading, i.e. whether it replaces existing classes or skips the new ones.
Although you already stated that, I'd still like to give that advice: Having the same class in multiple JARs deployed in one application (which could happen with versioned jars, for example) is always a bad idea. Your better off to invest the time to fix that instead of trying to mess with class loading.

This might come out to be pretty vague but I do remember resolving this issue a long time back by messing around with the WAS admin console for that given application and rearranging the relevant JAR files using their web UI. Not sure if this is an acceptable step in your case but worth a try in case everything else fails.

assuming you have some control over the deployment, fix the classloading yourself. combine the problematic jars yourself by unzipping them in reverse loading order into the same directory and then re-zipping into a new jar. then deploy the app with the new combo jar. no duplicate classes, problem solved.
or, just delete the dupe classes from the jars before deploying.

Related

How to better handle having the same classes in two JARs

I wrote two jars. Each one of them is responsible for sending different http/https request.
Each one of them uses, naturally, certain same classes. Like the ones that builds the requests or send them. The process might be a bit different, but still the general structure and classes names are the same.
Building different jars per request is a requirement from my managers! So using 1 jar for all my http requests is not acceptable.
Now, in my client program I need to send a request one time for JarA and one time from JarB. But compilation fails because, naturally, I am using very similar namings for the classes and methods.
For example, I have a UserData class in both jars. So when I try to use it in my client program, the compiler yells: "reference to SystemData is ambiguous".
I can start improvising specific classes names for each jar, but it is ugly...
How would you suggest to solve this problem?
If the classes are identical, pull them out into a third JAR and then have the client program reference the common JAR plus JarA or JarB.
If the classes are similar but not identical, then put them into different packages. You can have classes with the same names if they're in different packages.
Put common classes in a third jar and either bundle it in the two http jars or add it to the classpath at runtime (which is the best choice will depend on how you're deploying, etc.).
Firstly you have to decide which kind of architecture you are working with.
If managers asking you to have different jar's for sake of modularization - sure it's worth to make common jar which will contain all common classes.
I suppose you should have your project built with Maven, Gradle or another build system which will help you managing dependencies.
Another issue could be if you are supposed to do 'Microservices' architecture. Then code duplication is inevitable.
To overcome same class names when you have duplication - I would recommend to have for every module different package names then.
Use a build system like maven where one can have library dependencies, to a common third jar. It maintains a repository of versioned jars.
One solution is that - if you see a same class with same package in two different jars and both jars are required in your project,
Solution
you can download the source code of that duplicate class and creat keep the same in your project with package structure. So this way JVM loads your project classes first and give first preference to invoke your project class rather then other jar's class

What I have to do to guarantee that ccc.jar is loaded before aaa.jar?

Does somebody know in which order of precedence are the jars loaded?
Having several jars in WEB-INF/lib (in a war inside an ear in WAS 6.1).
Let's say aaa.jar, bbb.jar, ccc.jar. I have two different versions of a class paq1.paq2.paq3.MyClass inside aaa.jar and ccc.jar
The one inside ccc.jar is the correct one; the one in the aaa.jar is the wrong.
It seems that the wrong version is localized before the correct one.
Does somebody know what I have to do to guarantee that ccc.jar is loaded before aaa.jar?
(This is a simplification of a more real/complex problem, so renaming jars doesn't apply in this case).
Thank you in advance
I don't believe the servlet specification guarantees any specific Classpath ordering for the jars in the lib-folder. Hence you are looking at vendor specific functionality or - even worse - vendor specific accidental behavior, which tends to be very brittle.
If the bad class is in your deployment, the cure is removing the bad class definitions from your deployment. I would suggest that your build procedure is responsible for this.
If the bad class is in WAS itself you can usually override it by putting jars in an extension folder in the server itself, not as part of your deployment.
JVM takes class from the first jar in class path, where appropriate class exists. All others are discarded. You can remove this jars from WEB-INF and put them directly to webserver's class path. But this will be ugly solution.
WebSphere Application Server makes no guarantees about the ordering of JARs within WEB-INF/lib, and there is no option to force a particular ordering. I recommend removing duplicate classes and resources.

How can I include a jar file in a distinct package when deploying

I have an ant script that I use to build my J2EE application and create jar files. The problem is the following: Two jar files are necessary for the application to run.
commons-math-2.0.jar
commons-math-1.0.jar
However, I want to only use the 2.0 for a particular package inside the application with the rest of the application using 1.0. How can I build the application to only use the 2.0 version for example with a package name such as com.naurus.eventhandler.risk? Again, I'm using an Ant script, but if there's an easier way to do this sort of thing I'm willing to experiment. Thanks!
If the two jars contain different classes/packages there should be no problem to have all of them in the application classpath. It is then a matter of discipline not to use the classes from the one jar in the other package.
However I guess these two jars contain mostly the same classes/methods? There are many ways of using different versions of the same classes:
Using different ClassLoader instances. I would not qualify it as "easy", far from it means opening the door to a bunch of nasty bugs. (can be helped using a tool like OSGi)
Splitting the application in two processes, these process being launched in the same Ant target and using any mean (CORBA, RMI, REST, etc.) to communicate.
I would not advise using any of these methods though. It would probably be simpler to make all your packages use the same version. Is there any specific difficulty in doing so?
That will be problematic since both JAR files will end up in the same classpath when you deploy your J2EE application. You could achieve what you are trying to attempt with OSGI bundles, which allow each package to have separate dependencies. However, that is a relatively large refactoring of your application.
IMO, it would be best to either:
a) Duplicate the features you need from 2.0 (if the number is small and the license allows it, e.g., package individual classes).
or
b) Spend the time to upgrade the entire application to 2.0
You could use the manisfest in your jar to define the classpath.
http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html
Although honestly it seems a bit convoluted, but it is your requirement.

select different jar at runtime - toggling

I was wondering if there was a way to select different versions of the same jar at runtime. I am hoping to avoid using maven profiles since this would be a build-time selection.
Lets say there are two versions of the same jar:
greatFeature_version_A.jar
greatFeature_version_B.jar
My initial idea was to use JMX to create a basic runtime toggle selecting between those two jars which are both included in the classpath. Also, the contents of the two jars are the same (no versioning or different naming of the internal classes).
So when a class is requested, lets say from version_A, the class loader will always select the first one it finds, regardless of which jar it belongs to.
My thinking is that no effective runtime toggling can be achieved this way. I was wondering if anyone might have a experienced something similar and have a recommended workaround. I am also hoping to avoid writing a custom class loader if possible.
Many thanks!
P.S. Other related questions on SO indicate custom class loaders, like this one.
You can use jarjar to "rename" the packages so greatFeature_version_A.jar's packages start ajar.* and the other start bjar.* This way you can use the code from both jars in the same application, potentially at the same time.

Will using multiple version of a jar in an application cause problems?

I came across an application in which multiple versions of jar files are included. For instance commons-fileupload-1.8.jar and commons-fileupload-1.6.jar.
Would this cause any issues?
Thanks,
Raghuram
Yes, that's a bad idea. What will probably happen if you're lucky is that whichever of the two versions that comes first in the classpath will satisfy all the references. If that happens, then the other versions of the .jar file won't matter at all. However, old code that relies on an old version of the library might incorrectly pick up new versions of some classes, and so all sorts of weird bad things can happen.
Now, in an application with many separate class loaders, such a thing might work out, as long as the separate subsystems with separate class loaders keep the different versions separated. If you're talking about multiple references to a .jar in the system classpath, however, then it's not a case of multiple class loaders.
In my experience, yes it will. The jar that gets used will be the one that is loaded first and that is based on the class loader and not, I think, in a guaranteed order. So that means that some code might be depending on a feature in version 1.8 and then 1.6 gets loaded and throws an exception when you try to use it.
There will only be issues if both versions are actually loaded through the same class loader, e.g. by both appearing on the regular classpath.
It can be made to work if you load the different versions through separate class loaders.
Presumably the application you looked at is doing this. Or they just upgraded the JAR and forgot to delete the old version.
Definitely and it might give you different results sometimes depending on the app server and sometimes depending on the packaging.
If your application uses a class say X which is in both jars, the X.class one of them will be loaded by the classloader, and lets say that needs a class Y which is in both jars again one of them will be loaded (usually the first) but there is no guarantee that they will be from same jar.
So if there are two versions of same jar you need to inspect why this is happening and try and remove one of them. (If you are using maven there are different ways of achieving this)
yes it causes problems because only one of them will actually be used depending on which one gets loaded by the class loader(s) and what order they are loaded.

Categories