Loading different version of jar lib from servlet
I have several third party JARs which will be used by my webapps. These JAR files are actually available in different versions.
E.g.
Version 1.0 JARs
/opt/lib/third-party-jars/1.0/3rdparty1.jar
/opt/lib/third-party-jars/1.0/3rdparty2.jar
Version 2.0 JARs
/opt/lib/third-party-jars/2.0/3rdparty1.jar
/opt/lib/third-party-jars/2.0/3rdparty2.jar
Is it possible to load a different version of library dynamically before the servlet get loaded? Is any framework able to do this without changing my existing code?
public class Servlet1 extends HttpServlet {
protected void doPost(....) {
MyBusinessLogic businessLogic = new MyBusinessLogic();
businessLogic.run() // My business 开发者_如何学Clogic spawns here which will start involving third-party classes
}
}
Or is it possible to load version 1.0 or 2.0 library dynamically in the code before my business logic get invoked? One approach I can come up is using custom classloader and "setContextClassLoader"
but this requires to refactor all my business logic using reflection. This will be a huge effort. I am trying not to change my existing business logic code.
Having multiple versions of the same jar is not something supported by Java. Two options:
- extend tomcat's classloader to provide your custom loading logic
- use OSGi. Virgo is an OSGi-based servlet container
However, both of these will complicate things unnecessarily. I'd suggest introducing some sort of SPI which is implemented by multiple providers. So:
public interface BusinessLogic { .. }
And then two jars, with two implementations - com.foo.FooBL
and com.bar.BarBL
. Then you configure which one you need, and instantiate it with Class.forName(..).newInstance(..)
In general what you want is non-trivially difficult to do: servlet containers already contain complex classloader hierarchies, and trying to mix yet another one in place usually causes unnecessary pain and should be avoided. However, take a look at JCL: it may do what you want.
As far as I know you have no control over which classloader is used to load servlets defined in web.xml.
In JavaEE 6, you might be able to use ServletContext.addServlet() to add a servlet whose class you obtain from your own classloader. So you could have a dummy 'bootstrap' servlet (or listener or filter - as long as it is loaded on startup) that:
- creates your custom classloader using appropriate version of JARs
- create your real servlet class from your custom classloader (this servlet class should not be specified in web.xml)
- registers this class using ServletContext.addServlet()
I'm going purely off the spec here, I haven't tested it and don't know whether it will actually work.
As several others have already mentioned, doing what you propose is far from trivial. It might help for you to further explain why you need to load different versions of the JARs, and what the conditions are that trigger this decision. Understanding this might help others to come up with a different solution to your problem.
For example, is it possible to create multiple WAR or EAR assemblies for each version that bundles the corresponding third party JARs in the WAR/EAR? Bundling the third party JARs in an independent WAR/EAR would ensure the class loader visibility you are looking for. This shouldn't require much/any new coding, just some reassembly. Or, if bundling the third party JARs is not an option and you are using an app server such as WAS, you could possibly take advantage of shared libraries so that one of your WARs references the 1.0 version of the JARs, and another references the 2.0 versions.
精彩评论