I'm trying to extend webapp functionality without redeploying webapp archive. App runs under Glassfish 3.
Basically, what I did is the following:
webapp.war contains some part.jar which is a part of webapplication. It contains some class SomeClass. Webapp allows some custom configuration where descendant of SomeClass may be loaded dynamically using Class.forName.
I derived ExtensionClass from SomeClass (ExtensionClass extends SomeClass), compiled it using part.jar and got some extension.jar.
I tried to put extension.jar into domain/lib and domain/lib/ext. But then app when loading ExtensionClass says java.lang.NoClassDefFoundError: SomeClass (note that it says about parent class). It seems that classloader which loads library cannot find the base class, which is contained in the webapp.
Goal: I'd like to extend app using dependencies from it but without rebuilding.
Question: what can be done in this case?
Edit
If I just put my extension.jar into applications/mywebapp/WEB-INF/libs it obviously works, as if I'd put it into webapp.war itself. But it is very dirty, I want to solve it without doing this dirtiness and without touching webapp.war.
Related
I am running into an issue deploying a Quarkus App that uses an SPI implementation injected by our deployment system.
In our pom, we specify the SPI interface (which calls to ServiceLoader.load(class) in it's static initializer). When we deploy the Quarkus app, we decompose the QuarkusRunner jar, extract the Main-Class from the MANIFEST and construct a command line similar to "java -cp ... io.quarkus.bootstrap.runner.QuarkusEntryPoint". The class path includes everything in quarkus-app/app, lib/boot and lib/main plus the SPI implementation we intend to use.
When we run the app, and try to use code that invokes our SPI ServiceLoader code, we get the following error:
java.util.ServiceConfigurationError: : not a subtype.
I read this as the ClassLoader used by Quarkus (which contains the SPI-interface) and the ClassLoader that loads the SPI-Implementation, are somehow not connected (i.e., isolated from one another).
Things of interest:
We are using Quarkus 1.13.2-Final
I have tried to make our SPI Interface a parentFirstArtifact (it has no dependencies), with no luck.
Looking at the code for QuarkusEntryPoint, it looks like it loads all the classes placed into quarkus/quarkus-application.dat, which is created during the maven build, into the Quarkus RunnerClassLoader, whose parent is the System ClassLoader. My assumption was items on the classpath were added to the System ClassLoader.
Question:
At this point, I am completely lost as to what is actually happening. How do I get my SPI-Implementation to work with Quarkus?
When using Quarkus's fast-jar, almost everything is loaded into the JVM via the RunnerClassLoader (the exceptions are the classloader itself, and a tiny number of supporting classes and utility libraries).
What you would consider the classpath (that is User code, code generated or transformed by Quarkus and dependencies) are indexed in the quarkus-application.dat file which is built at build time and cannot be modified.
I have Java web application running on a web application server single node setup, in which I am using a liberary the I included in my Web-Inf and using in my code.
The issue is that I have another application that added its liberaries to the WebSphere parent lib folder, one of which are the same liberary I am using but with an older version, creating conflict and jamming my code.
The server class loader is configured Parent first unfourtunatly and I cannot change that fact. My question is, how can I make my app use my liberary, ignoring the one used by the class loader?
The solution is to move the conflicting package to a shared library, configure the library to use an isolated class loader, and associate that library with your application or module. The "isolated class loader" setting creates a separate parent-last class loader for the shared library, so you get that behavior targeted to only the artifacts that need it rather than having to apply it to the entire application or module.
https://www.ibm.com/support/knowledgecenter/en/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/tcws_sharedlib.html
I'm specifically referencing the "Use an isolated class loader for this shared library" setting.
If you can't change your application server setup there are basically three things you can do:
Downgrade your application dependency to lower version used by WebSphere server and keep it in sync. This is preferable as it's least hassle.
Shade the dependency during build to your own package to prevent package clash. This can be done with Maven Shade Plugin, see Relocating Classes usage example.
Write a new custom classloader to work around the problem.
I'd try them in 1 -> 2 -> 3 order. Option 3 is possible but is an error-prone nightmare. I'd rather deploy to another server than do it.
I am deploying two war say A,B in Java EE Server say Jboss.
Deployment order will be
WAR A
WAR B
I need to call a method once war B is deployed. That method will create an instance of a class which will be loaded only from the class path mentioned in the manifest file of the war.
I used a ServletContextListener, but this failed.
Note : I tested ServletContextListener - by specifying a sample content in the static block of the class that has to be instantiated and tried to create an instance from the ServletContextListener. But an error is thrown stating "RunTime Exception unable to load the class", and the sample content is also not displayed. More over the class is loaded in the server which is confirmed via -verbose:class option in the JAVA_OPTS.
But it works if I load the class from war A. It even works fine. It's a big process to explain why there is a need for creating such instances. I don't want to change the way in which the instance of the class is created.
Is there any other way to call the method after the war and all its dependencies are completely loaded?
I have two war file app1.war and app2.war deployed in a single JBoss instance. Package names for java classes for both war files starts with com.myapp
To add further, there are some Classes that are common between the two apps while there are some that have same fully qualified class names but are different (Source Code has changed).
I want to know, if this could pose threat of any kind to the deployment scenario?
You could get class loading problems if your applications are not isolated, i.e. have their own class loading repository and class loaders. If you configure JBoss to isolate the applications from each other you should be fine (I don't know what is the default for your version but 4.2.3 that we use does not isolate apps by default).
To clarify that a bit:
If you have two classes with different implementations but the same FQCN you could get the wrong class from the class loader for the application that is loaded second. Even if the implementation was the same you could get class cast exceptions or other strange behavior if one app gets the class from the other app.
I had a similar situation with multiple apps.Look at my solution here
Best way is to isolate class loading for your application archives.
For JBoss 5.1.0 GA following worked for me.
Create jboss-classloading.xml file in WEB-INF folder.
Added following lines to this file
Here,
export-all="NON_EMPTY" => Makes sure the classes loaded for this app is not exported
import-all="true" => Imports and uses all of the class definition available.
parent-first="false" => If more than one class with same name is found, one defined under the application will be used first.
FYI. This also helped me embedding the log configuration of log4j in the application war file. Will need to place log4j.xml in WEB-INF/classes and have a log4j.jar in WEB-INF/lib folder.
There will be one class loader instance for each application or standalone module. In other words, classes in app1.war will be loaded in different class loader than the classes in app2.war. This is the default behavior of any Java EE server; So it really doesn't matter about having classes with the same package/names and/or different content. This is the default behavior of any Java EE server.
Having said that, if you tweak the class loader policy of the server or try to load classes (reflect) using anything other than Thread.currentThread().getContextClassLoader(), you could be asking for trouble.
If I have classes that need to be shared between my webapp and Tomcat (e.g. a custom realm and principal), where should the .jar file containing those classes go?
Currently I'm putting the .jar in ${CATALINA_HOME}/lib. This result is a ClassCastException when assigning references from classes of the same type. Here's an example:
MyCustomPrincipal principal = (MyCustomPrincipal)FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
The method above throws a ClassCastException. The method returns an actual MyCustomPrincipal type (since that's what my custom realm gave Tomcat when it performed authentication) that, apparently, was created by a different classloader. How do I fix this so both Tomcat and my webapp can use MyCustomPrincipal?
http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html
Any help is appreciated.
Andrew
It looks like you have 2 copies loaded, once in tomcat and once in your WEB-INF/lib jars or other classpath of your deployed application.
The reason you get classpath exception lies in the way a WAR looks for classes. Contrary to the normal Java rules, a war first looks inside the war for a class and only then passes the request to teh parent classloader.
A class's identity is dependent of the classloader and the same class loaded in 1 classloader will generate a classcast exception when it is casted in the other classloader.
The solution is to make sure that the war does not contain the classes which should be provided by the container. If you use maven you can mark these dependencies as 'provided', if you use ant, you have to split your classpath list in 2 and compile against both, but use only the ones you need for constructing the war.