I have two jar files from a client, one of which is used for a testing and another for final versions. Currently I put them in different folders and modify the library path when deploying our code, but it would be nice to be able to load both jar files and switch between them dynamically at runtime.
Is this possible?
You can always write your own ClassLoader and chain it with the standard ClassLoader.
http://download.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html
I used this method 10 years ago to load classes that were recieved via sockets and specified in an XML file (via sockets as well). My java program didn't know that the classes even existed before it got the XML file and the classes.
Using OSGi bundles you can do that. Take a look at http://blog.springsource.com/2008/02/18/creating-osgi-bundles/. Search for "multiple versions".
justinjh,
chrisparker2000's suggestion looks to be the most feasible - You have to write a custom classloader, the only change that I can think of is something along the following lines:
1. For the client deliverable jars - say client.dev.jar and client.prod.jar, rename to a different extension and place these in the classpath. Rename to a different extension to prevent the container from loading the contents of the jar.
Using the custom classloader, load the contents on demand, based on the solution offered by chrisparker2000, by placing a small facade on top of the client classes, say ClientClassFactory which based on the environment(dev/prod/anything else) would use the custom classloader to load from either client.dev.otherext or client.prod.otherext .
If you use a build-tool like maven, you can define different jar files (dependencies) for different scopes (test vs production).
You may also use maven profiles to define different set of jar files/versions.
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
What is need for an application or system to go for Custom class loader?
What are the limitations of the current set of classloaders that generally web application use ( Server + JVM )
Regards
Illustrate one of the usage of Custom class loader, I will take an example of my implementation,
Couple of months ago I have built a custom class loader to load a class created at run time from xml data source. The requirement was to create a java source files from a XML files, compile, jar it.
In order to load/execute these classes which resides in a DB I had to write custom class loader (extending URLClassLoader) since all the operation happening at run time, default/system classloader is not aware of the new classes(jar) , or not in the claspath.
We have used custom classloaders in our application to build a plugin framework. This classloader allowed us to embed jar files inside the plugin file (Which is also a jar file).
You can create your own ClassLoader to
1. Create new classes (for example, when custom proxying needed)
2. Redefine existing classes, when on some reason java.lang.instrumentation (javaagent) is not available.
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.
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.
Suppose you have several applications which share the same code and most of the other resources, but have a somewhat different look and feel, some labels change, etc. (think branding). If each web app is to go in its own WAR file, where do you put the shared resources?
I already use the classpath to share classes and property files. But what about javascript and css files? Is the best way to create and deploy one extra WAR file that will serve these shared files to whatever other application requires them?
I also thought of a build script that does some magic and from a common source spews out the (slightly) different WARs, but I don't like it because it just complicates stuff unnecessarily when you need to build/test/run a single application.
Any other tips and tricks would be appreciated.
You can deploy both WARs in the same EAR and put common resources in the EAR. Then put the appropriate dependencies in the manifest of the web apps to link to the jar files in the ear.
A strategy that I have seen used for such product-line like configurations is using WAR overlays when building with maven. You define a common WAR that contains the common stuff and overlay it with those other WARs that contain the specific stuff to generate different WARs for every application. This method is probably most useful if you deploy the WAR-variants on different machines. But I'm not sure whether I can actually recommend this.
Remember to specify the overlays configuration if you actually override stuff, since otherwise the overriding order is not deterministic. It might even change with a maven-war-plugin upgrade. (It did in our case.)
If you don't want to go the EAR route, using tomcat, etc; there are a few other ways to achieve the consistency you want.
If you want to share just js and css, look into pack:tag. You could host the .js and css from an apache server, set up your httpd.conf so your webapps can call it, then use pack:tag from your application wars - DRY and compression in one step.
Thanks for the replies so far, but I'm afraid I forgot to mention that the WARs will be deployed in different environments that are completely isolated from each other.
So maybe having a common WAR deployed next to the actual application is the only option. I think I'll go with the following:
WAR1, WAR2 containing app-specific stuff
CommonWAR containg common stuff (no kidding)
EAR1: WAR1 + CommonWAR, to be deployed in env1
EAR2: WAR2 + CommonWAR, to be deployed in env2
Update
Yes, me again. I have actually changed my mind (again :) ). I am currently trying (being more prudent here):
(Common)WAR: containing the application, common (most part) + some specific stuff
EAR1: CommonWAR + specific configuration file for env1
EAR2: CommonWAR + specific configuration file for env2
The configuration file is picked up by the WAR. It is on the the EAR classpath and only contains one property 'application' with a value. The single WAR will then use this information where appropriate to distinguish between the two apps (config, style sheets, ...).
With my solution of EAR1 = CommonWAR + WAR1, EAR2 = CommonWAR + WAR2, it was too difficult or impossible to lookup static resources in the CommonWAR without using a web url (e.g. images in PDF documents generated with iText).
How about putting your css and js in the classpath and serve them with a servlet? Then you can build the common resources as a jar and that jar can even contain the servlet (resource dispatcher if you like) and the war files can contain the jar file in the WEB-INF/lib folder.