I have an ear file that will have root deployments (deploy configuration), lib deployments (earlibs configuration) and an additional custom utilJars configuration that I want to place in a utilJars folder at the root of the ear. I am aware that the first two configurations are automatically handled by the Ear task.
How can I add an additional CopySpec to the Ear task (or any AbstractCopyTask for that matter) to handle the third configuration?
My understanding of copy specs was erroneous. Instead of copyspecs existing side by side, they exist in a hierachy, as explained in section 16.6.3 of the gradle user guide.
thus additional copy specs may be 'nested' within the root specification of the task. These nested specs inherit the parent specs unless otherwise specified:
e.g. in the following spec, the root spec contains an into, exclude and from spec. in the from spec, there is a nested include spec. This spec does not overwrite anything from the root spec, and the root spec does not see it. The into spec does however overwrite the from copyspec and thus will copy everything from the configuration 'runtime' into the folder libs but not anything from the src/dist folder.
task nestedSpecs(type: Copy) {
into 'build/explodedWar'
exclude '**/*staging*'
from('src/dist') {
include '**/*.html'
}
into('libs') {
from configurations.runtime
}
}
I hope this helps someone else :)
Related
Background:
I have the following problem: I have several WAR files I need to have deployed on same Websphere server. The WAR files use libraries that depend on having a specific version of XMLSec regisered as the XML Signature Provider (with the Java Security class). Currently I bundle this library with each WAR file (since the WAR files also need to work standalone and on Tomcat's without any special shared library configuration etc.). Each WAR files registers the provider with Security.addProvider() in a ServerContextListener. But this causes problems in the multi-WAR setup, because if one WAR file does the registration with Security.addProvider) and another WAR files tries to fetch it using the XMLSignatureFactory class (which is actually a javax.* class contained inside the XMLSec JAR itself, but which ultimately calls back to the global provider list configured with Security.addProvider), then it causes a ClassCastException inside XMLSignatureFactory, because this class does a cast of what it gets from Security into to its own version of the provider classes, which doesn't work. The exact stack trace is as follows:
Caused by: java.lang.ClassCastException:
org.apache.jcp.xml.dsig.internal.dom.DOMXMLSignatureFactory
incompatible with javax.xml.crypto.dsig.XMLSignatureFactory at
javax.xml.crypto.dsig.XMLSignatureFactory.findInstance(XMLSignatureFactory.java:202)
at
javax.xml.crypto.dsig.XMLSignatureFactory.getInstance(XMLSignatureFactory.java:292)
By the way this is not a case of conflict with different versions of XMLSec being in play or conflicts with Websphere's own version. There is only one version albeit it is loaded from different WAR's.
Of course the solution is to have the xmlsec library loaded with a common classloader so that there is only one version of the classes loaded that all WAR files see, which would avoid ClassCastExceptions etc.. But here is the rub: I also need to have each application loaded with the "parent last" policy - or rather, I need the JAR files inside each application to take precedence over Websphere's built-in version of the libraries (for instance Axis2 that I also include in the WAR filesetc.). Furter, I would prefer that I can keep the xmlsec library in each WAR files' WEB-INF/lib folder, so that the WAR files can still work stand-alone (i.e. in other environments which might not have the shared library configured etc.).
So basically I want to have a common class loader loading the XMLSec library, say, somewhere from disk. Let's denote that [SHARED XMLSEC]. Then I want each application classpath to ultimately appear like this:
App1: [SHARED XMLSEC][App1 WEB-inf/lib][Websphere libraries][JDK libraries]
App2: [SHARED XMLSEC][App2 WEB-inf/lib][Websphere libraries][JDK libraries]
etc.
In such a configuration it doesn't matter if App1+App2 themselves contain the XMLSec library since the shared one will take precedence so they will use the common one. At the same time, App1+App2 are still free to override other built-in Websphere libraries (Axis2).
Is it possible to realize this configuration and what options do I need to set? Do you see alternative ways to achieve the same objective?
Since you have a conflict between classes here, I would suggest going for isolated class loaders for each application. On the server side, setting the class loader policy to 'Multiple' should provide isolation between applications.
Once you have this set, configure class loading at the application level to the 'Parent last' configuration for both the applications.
The following Knowledge Center link has the relevant instructions [Steps 2,3 & 4 under the 'Procedure' section] :
http://www.ibm.com/support/knowledgecenter/en/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/trun_classload.html
[Note: The version of WAS in use is not specified in the question. The Knowledge Center link refers to version 8.5.5.]
I use package-info.java to specify #XmlAccessorType(XmlAccessType.NONE) and some xml java adapters using #XmlJavaTypeAdapters. Model objects (with JAXB annotations) are placed in separate maven module shared by other modules. The configuration in package-info.java is not discovered if model objects are in separate maven module. If I move for testing purposes model objects to same maven module everything is OK. I think separate maven module can be considered equivalent to 3rd party lib from JAXBContext point of view. I use JDK1.7 JAXB reference implementation. Any ideas how configuration may differ?
I also encounter this problem, in my case qualified/unqualified property from package-info.java was ignored. I managed to find two way to workaround this:
like Pavla wrote, copy all JAXB classes with package-info.java locally
include module as a dependency with compile scope (which gives similar result that classes are in module. In my case I created separate jar lib with JAXB classes)
I also spotted that it do not work only in case of creating WebServices (creating object and sending to WS works fine in different modules).
I am using Jbossas7.1.1 and cxf 2.4.6. In the time of registering service Jboss created wsdl from JAXB (in my case path /opt/jboss/jboss-as-7.1.1.Final/standalone/data/wsdl/module.war/SubmitMessage.wsdl). In local setting file is generated properly.
Any ideas why creating WS behaves like this?
I hit this issue recently and the actual problem (with Java 8, i.e. no Java modules involved) was that I had on the classpath two *.jar files which both contained the same package - in one JAR, there was package-info.class with JAXB annotations and in the other one, there wasn't.
In that case, I guess that if package-info.class file is discovered depends on the classpath ordering (which is very brittle and only semi-deterministic).
I have a WAR application that includes a JAR library. The JAR library contains the Batch Job and the Batch Artifacts (META-INF/batch-jobs/...). The WAR app includes this jar as a library and defines a JAX-RS Service that allows to clients to invoke the batch job calling the JobOperator Interface...
When i run this deployment, the JSR 352 implementation (JBeret) keeps complaining that the Job cannot be found anyware when the JobOperator Interface is called... However, if the Batch Job and the Batch Artifacts are included as classes of the WAR deployment, everything runs smoothly...
So, what is the problem?
After a "little" research, i found the answer (dispersed) in the following links:
Wildfly Issues
Mailing list
Briefly, In order to put this kind of deployment to work, you have to modify the deployment that calls the Job Operator interface to invoke the requested Job (in my case, it was the WAR File)... These are the modifications:
Include an "empty" batch-jobs folder under the META-INF folder. (I guess the empty is optional, because i have to put a README file under that folder to prevent GIT from removing such folder)
Define a ServiceLoader (file) under META-INF/services folder. This ServiceLoader (file) must be called: org.jberet.spi.JobXmlResolver and should contain the following implementation as content: org.jberet.tools.MetaInfBatchJobsJobXmlResolver
That's all.
The WildFly issue (https://issues.jboss.org/browse/WFLY-7000, similar to the one mentioned above, but is a different one) has been fixed, and should address your point 1 (having to use empty batch-jobs/ directory).
In my build.gradle file I need to add the line:
shadowJar {
mergeServiceFiles()
}
Otherwise the jar does not run properly. I wonder what this line does exactly?
I use the Gradle plugin in Eclipse Luna. I create the jar on one Java project which depends on another one.
mergeServiceFiles is declared exactly here and its implementation is as follows:
/**
* Syntactic sugar for merging service files in JARs
* #return
*/
public ShadowJar mergeServiceFiles() {
try {
transform(ServiceFileTransformer.class);
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
}
return this;
}
As you can see it uses ServiceFileTransfomer which is defined here. From its docs:
Modified from org.apache.maven.plugins.shade.resource.ServiceResourceTransformer.java
Resources transformer that appends entries in META-INF/services resources into a single resource. For example, if there are several META-INF/services/org.apache.maven.project.ProjectBuilder resources
spread across many JARs the individual entries will all be
concatenated into a single
META-INF/services/org.apache.maven.project.ProjectBuilder resource
packaged into the resultant JAR produced by the shading process.
TL;DR - It merges the service files in the META-INF/services folder.
Long Answer
Some libraries (e.g. Micronaut) create a few service files in the META-INF/services folder. These files can contain any information useful at runtime. In case of Micronaut framework, it creates a file that lists the Bean References (or beans that are instantiated) in a file called io.micronaut.inject.BeanDefinitionReference under META-INF/services.
Usually, if you just have one application, it works fine even without mergeServiceFiles(). But if there are two micronaut projects that you are bundling into a single jar (e.g. a micronaut lib project with some utils and a micronaut app project with core business logic), you will have two io.micronaut.inject.BeanDefinitionReference. Each one will contain the beans of it's own project.
If you don't use mergeServiceFiles(), one of the BeanDefinitionReference files will be overwritten by the other. In that case, you will get a runtime exception saying BeanNotInstantiated or something of that sort.
Using mergeServiceFiles() merges (or concatenates in this case) the BeanDefinitionReference files of both the projects so that at runtime, you get all the beans defined.
More details can be found in the gradle forum topic here.
I'm running tomcat 6.0.23, and according to the classloader documentation webapps should look for classes in this order:
Bootstrap
System
WEB-INF/classes
WEB-INF/lib
Common
I have a webapp that uses hibernate and the hibernate jars are in it's WEB-INF/lib directory. When running on it's own, all works fine.
I also have a jar file that needs to sit in the tomcat/lib directory as it contains some classes that need to be loaded on startup (an object factory and the objects it creates). These classes use toplink for their JPA implementation and here is where I get a problem.
I need to put the toplink jars where they can be accessed on tomcat startup, so I've put them in the tomcat/lib directory. According to the order of classloading listed above, when the webapp that uses hibernate wants the hibernate implementation classes, it should find them in it's WEB-INF/lib directory, but what actually happens is it finds the toplink implementation classes from the tomcat/lib directory, and I get a class cast exception.
Can anyone please explain why my webapp class loaders aren't finding what they need in their WEB-INF/lib directory, or suggest a way to debug the classpath at runtime?
Thanks.
Be sure to read and understand the paragraph preceding the list you quoted:
"When a request to load a class from the web application's WebappX class loader is processed, this class loader will look in the local repositories first, instead of delegating before looking. There are exceptions. Classes which are part of the JRE base classes cannot be overriden. For some classes (such as the XML parser components in J2SE 1.4+), the J2SE 1.4 endorsed feature can be used."
What are these "hibernate implementation classes" that you're having problems with? (Hibernate's "implementation" classes would be completely different from Toplink's.) Are they javax.persistence classes? These may (or may not) fall under the category of "JRE base classes", which behave differently.
Edit: Based on your comment, this is just a typical cross-class-loader loading problem. Tomcat's class loading is working exactly as you expect it to. If you look into the JPA classes where this initialization is going on, you'll find a line like this:
Enumeration<URL> resources =
cl.getResources("META-INF/services/" + PersistenceProvider.class.getName());
That loads all PersistenceProviders from all ClassLoaders, including your Toplink one in the lib directory. It then immediately does this:
for ( PersistenceProvider provider : providers ) { ...
That's on line 77 of javax.persistence.Persistence, where your exception is coming from. That's because the PersistenceProvider class referred to on that line is from your webapp class loader, but the collection contains two instances: your Hibernate implementation from the same class loader and the Toplink one from a different class loader.
This global, static initialization is one major thing that's kept me from shifting to JPA. I still just use straight Hibernate because of problems like this.