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.
Related
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
I recently started using Eclipse IDE and have read at a number of places that one shouldn't use the default(src) package and create new packages.
I just wanted to know the reason behind this.
Using the default package may create namespace collisions. Imagine you're creating a library which contains a MyClass class. Someone uses your library in his project and also has a MyClass class in his default package. What should the compiler do? Package in Java is actually a namespace which fully identifies your project. So it's important to not use the default package in the real world projects.
Originally, it was intended as a means to ensure there were no clashes between different pieces of Java code.
Because Java was meant to be run anywhere, and over the net (meaning it might pick up bits from Sun, IBM or even Joe Bloggs and the Dodgy Software Company Pty Ltd), the fact that I owned paxdiablo.com (I don't actually but let's pretend I do for the sake of this answer) meant that it would be safe to call all my code com.paxdiablo.blah.blah.blah and that wouldn't interfere with anyone else, unless they were mentally deficient in some way and used my namespace :-)
From chapter 7, "Packages", of the Java Language Spec:
Programs are organized as sets of packages. Each package has its own set of names for types, which helps to prevent name conflicts.
I actually usually start by using the default package and only move it into a real package (something fairly easy to do with the Eclipse IDE) if it survives long enough to be released to the wild.
Java uses the package as a way to differentiate between classes. By using packages, you can have an org.example.Something class and an org.example.extended.Something class and be able to differentiate between them even though they are both named Something. Since their packages are different, you can use them both in the same project.
By declaring a package you define your own namespace (for classes). This way if you have two identical classes using a different package name (namespace) will differentiate between which one you want to use.
The main reasons I can think of are:
It keeps things organised, which will help you (and others!) know where to look for classes/functionality.
You can define classes with the same name if they are in different packages.
Classes/etc in the default package cannot be imported into named packages. This means that in order to use your classes, other people will have to put all their classes in the default package too. This exacerbates the problems which reasons 1 & 2 solve.
From a java point of view, there are two general dev/deploy lifecycles you can folllow, either using ant to build and deploy, or the maven lifecycle. Both of these lifecycles look for source code and resources in local directories, and in the case of maven, in defined repositories, either locally or on the net.
The point is, when you set up a project, for development and eventually deployment, you want to build a project structure that is portable, and not dependent on the IDE, ie. your project can be built and deployed using either of your build environments. If you use a heavy dependence on the Eclipse framework for providing class variables, compile paths, etc.. you may run into the problem that your project will only build and deploy using that configurationj, and it may not be portable to another developers environment, so to speak.
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.
It is time to sub-divide a platform I'm developing and I'm looking for advice on how to handle cross-component dependencies. I spose there a many cases, so I'll give an example.
I have an Address class that I want to make visible to developers. It is also referenced by classes in my.Contacts, my.Appointments, and my.Location packages - each of which I want to be separately compiled, jar-d, and delivered. Of course I want Address to be a single class - an Address works across these platform components transparently.
How should Address be packaged, built, and delivered?
Thanks!
Two thoughts:
Address sounds like a common component that can be used in different deliverables and so should be available in some common or core library
It may make sense for your components to talk to an Address interface, and the implementation can be provided separately (e.g. provide an Address interface and an AddressImpl implementation). This will reduce the amount of binding between the core library and the library your developers will develop.
In this case Address is a part of a library which deserves its own jar. If you create a class named Address in my.Contacts, my.Appointments, and my.Location and you want to use all theses jar in a same application, you'll have a conflict for your Address class.
I suggest you don't "Deliver" these jars separately. Java has very subtle versioning issues that you don't want to run into. Build everything together and package it into one or two jars and always deliver both jars, or build them together and deliver a subset of jars (but never combine new and old jars--don't just try to send a single jar as an update).
If you must build them separately be very aware that final constants are compiled in and not referenced--so if you change one and deliver a new jar, any references from an older jar will not be updated.
Also method signatures that change will have strange, unpredictable results.
It sounds like you want a developer interface as well--that may be a set of interfaces and classes that reside in a separate jar. If you make that one jar well enough that you never have to rev it (and, of course, with no references to external constants) you can probably get away with not updating it which will keep your customer's extensions from getting crusty.
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.