I've got an EAR which contains a WAR and an EJB (jar). The EJB and the WAR share some libraries, so I wanted to produce a skinnyWar using maven-ear-plugin.
I have my ear pom configured with the shared libs as dependencies, and have the <skinnyWars> option set to true.
I see my EAR is properly built; the common libs are extracted from my WAR and placed in the EAR.
My problem, when I launch my EAR on Websphere 8.5, is that the WAR is unable to find classes which remain in the WAR/WEB-INF/lib folder.
I have checked the MANIFEST.MF in the war/META-INF/ and see that all the libs are listed (both those in the war/web-inf/lib folder as well as the common libs), however I keep getting ClassNotFoundException whenever I try to access classes from within the WEB-INF/lib/xxx.jar.
(snippets from the pom.xml)
<dependencies>
<dependency>
<groupId>com.webinfo</groupId>
<artifactId>WebInfoWebApp</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>com.webinfo</groupId>
<artifactId>WebInfoEJB</artifactId>
<version>${project.version}</version>
<type>ejb</type>
</dependency>
<!-- In order to make skinnyWars, need to list all the common dependencies of the EAR here -->
<!-- Use the ejb pom to list all the ejb dependencies here automatically -->
<dependency>
<groupId>com.webinfo</groupId>
<artifactId>WebInfoEJB</artifactId>
<version>${project.version}</version>
<type>pom</type>
</dependency>
</dependencies>
<build>
<plugins>
<!-- EAR plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>2.10</version>
<configuration>
<defaultLibBundleDir>/</defaultLibBundleDir>
<skinnyWars>true</skinnyWars>
<archive>
<manifestFile>META-INF/MANIFEST.MF</manifestFile>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
<modules>
<webModule>
<groupId>com.webinfo</groupId>
<artifactId>WebInfoWebApp</artifactId>
<bundleFileName>WebInfoWeb.war</bundleFileName>
</webModule>
<ejbModule>
<groupId>com.webinfo</groupId>
<artifactId>WebInfoEJB</artifactId>
<bundleFileName>WebInfoEJB.jar</bundleFileName>
</ejbModule>
</modules>
</configuration>
</plugin>
</plugins>
</build>
EAR manifest.mf:
Class-Path: WebInfoWeb.war WebInfoEJB.jar core_business-5.1.0.jar core
_common-5.1.0.jar core-5.1.0.jar ICS_SSEClient-3.5.15.jar com.ibm.uti
l.ini-1.0.jar ICS_SecuredUserContext-3.5.15.jar ICS_SecuredUserContex
tClient-3.5.15.jar ICS_ManageClient-3.5.15.jar ICS_ConfigurationClien
t-3.5.15.jar ICS_Common-3.5.15.jar
WAR manifest.mf:
Class-Path: WebInfoJava-3.9.0-SNAPSHOT.jar com.ibm.util.ini-1.0.jar ow
asp-esapi-full-java-1.4.jar regexp-1.2.jar com.ibm.regex.REUtil-1.3.0
.jar ojdbc6-11.2.0.3.0.jar AdobeFDF-1.0.jar core_business-5.1.0.jar c
ore_common-5.1.0.jar core-5.1.0.jar ICS_SSEClient-3.5.15.jar ICS_Secu
redUserContext-3.5.15.jar ICS_SecuredUserContextClient-3.5.15.jar ICS
_ManageClient-3.5.15.jar ICS_ConfigurationClient-3.5.15.jar ICS_Commo
n-3.5.15.jar
Exception:
java.lang.ClassNotFoundException: com.calculator.sendmail.SendMailCommunicationBusinessFactory
But SendMailCommunicationBusinessFactory is located in WebInfoJava-3.9.0-SNAPSHOT.jar which is in the war/WEB-INF/lib folder.
Am I doing something incorrect in the way the classpath is setup/configured?
Related
I am working on converting a J2EE application to Maven where the EAR project will contain a WAR module. I have followed the below URL to convert the project and it does work with some minor changes:
https://www.ibm.com/docs/en/wasdtfe?topic=projects-converting-existing-maven
In the current project, there are some libraries under the EAR folder which I cannot move to the local maven repository. The reason is old legacy code which expects these library names to be intact (myCommon.jar and no version to be added like myCommon-1.0.jar).
As a workaround, I placed these libs under EAR->src->main->application->lib folder. There is no build failure observed but the major problem is with the ClassPath for these EAR lib files as shown below:
[err] java.lang.ClassNotFoundException: com.myClass.classFromWAR
[err] at java.lang.Class.forNameImpl(Native Method)
[err] at java.lang.Class.forName(Class.java:332)
E.g. myCommon.jar contains code like the below:
public void EARLibFunc( string classNameFromWAR){
.........
//E.g. classNameFromWAR = "com.myClass.classFromWAR";
final Class warClass = Class.forName( classNameFromWAR );
.........
}
Calling above function from the java files inside WAR module reports ClassNotFoundException: EARLibFunc("com.myClass.classFromWAR");
The directory structure looks like the below:
WARProject
-src
----com
--------myClass
------------classFromWAR.java
EARProject
-src
----main
--------application
------------lib
----------------myCommon.jar
The jar files from EAR/src/main/application don't seem to be part of the ClassPath.
Can you please suggest the best practice to handle such an issue? What should be the correct layout of the EAR libraries to make it part of the ClassPath? Please be informed that the code from the EAR libraries cannot be changed (legacy code dependency issue).
For reference here are my pom settings:
WARProject pom.xml:
.......
.......
<groupId>MyApp</groupId>
<artifactId>MyApp</artifactId>
<version>3.5</version>
<packaging>war</packaging>
<description>MyApp Maven</description>
........
<build>
<resources>
<resource>
<directory>Java Source</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<warSourceDirectory>Web Content</warSourceDirectory>
</configuration>
</plugin>
</plugins>
</build>
......
EAR Project pom.xml (contains WAR module as dependency):
.....
<groupId>EARProject_EAR</groupId>
<artifactId>EARProject_EAR</artifactId>
<version>3.5</version>
<packaging>ear</packaging>
<description>My Project EAR</description>
<build>
<plugins>
<plugin>
<artifactId>maven-ear-plugin</artifactId>
<version>2.10</version>
<configuration>
<version>7</version>
<skinnyWars>true</skinnyWars>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>MyApp</groupId>
<artifactId>MyApp</artifactId>
<version>3.5</version>
<type>war</type>
</dependency>
</dependencies>
......
SOLUTION:
The crash reported for the CLASSPATH is resolved. Since I am moving an old legacy application to Maven, there were some old references to be cleaned-up. Below changes were required:
There were duplicate classpath references in the eclipse Project
(Project -> Properties -> Java Build Path). Even though I had
dependencies mentioned in the pom.xml of the WAR file, the project
properties were also having its references. This may or may not be
the real reason.
Reverted earlier workaround solution. Removed libraries from
EAR->src-main->application->lib and added those as dependency in the
WAR pom.xml reference. Though it has re-created other legacy issue
but I believe this will adhere to the best practices.
I think, it should be possible this way:
Install the jar in your local maven repository.
Configure the maven-ear-plugin to include third party libraries as shown here.
Add <bundleFileName>myCommon.jar</bundleFileName> to jarModule in order to give your JAR file the desired name within the EAR.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>3.2.0</version>
<configuration>
[...]
<modules>
<jarModule>
<groupId>artifactGroupId</groupId>
<artifactId>artifactId</artifactId>
<includeInApplicationXml>true</includeInApplicationXml>
<bundleFileName>myCommon.jar</bundleFileName>
</jarModule>
</modules>
</configuration>
</plugin>
</plugins>
</build>
More information can be found at the usage page of the plugin.
I'm currently working on a modular JavaFX application and I'm using maven to compile/deploy it.
One of the dependencies of my application is JBibTex which doesn't have a module-info file so it's used as an automodule. In order for my javafx:jlink to work (because javafx:jlink cannot work on automodules), I had to convert JBibTex to an explicit module. I used the maven's ModiTect plugin for that.
I used ModiTect to generate the module-info file for the JBibTex library and I added it to the JBibTex's jar file (I used moditect:add-module-infos for that).
Now the problem is that javafx:jlink uses the JBibTex.jar file that is located in my ".m2" folder (where maven automatically downloads all your projects' dependencies). The JBibTex.jar file located in my .m2 folder is not the file to which I added the module-info file.
So I still get the error:
Error: automatic module cannot be used with jlink: jbibtex
The explicit JBibtex module that I have generated through the use of ModiTect is automatically put in target/modules by my ModiTect plugin configuration.
How can I get javafx:jlink to use the target/modules/JBibTex.jar file that contains the module-info.class file and not the JBibTex.jar of my .m2 folder?
Here is my pom.xml just in case:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>xxx</groupId>
<artifactId>BRM</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>BRM</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
<mainClass>xxx.App</mainClass>
<moduleName>xxx.brm</moduleName>
<javafx.version>17.0.1</javafx.version>
<javafx.maven.plugin.version>0.0.8</javafx.maven.plugin.version>
</properties>
<dependencies>
<!-- javafx dependencies -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jbibtex/jbibtex -->
<dependency>
<groupId>org.jbibtex</groupId>
<artifactId>jbibtex</artifactId>
<version>1.0.17</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>configs</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>15</source>
<target>15</target>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>${javafx.maven.plugin.version}</version>
<configuration>
<mainClass>${mainClass}</mainClass>
<launcher>launcher</launcher>
<jlinkImageName>BRM</jlinkImageName>
</configuration>
</plugin>
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
<version>1.0.0.RC2</version>
<executions>
<execution>
<id>add-module-infos</id>
<phase>generate-resources</phase>
<goals>
<goal>add-module-info</goal>
</goals>
<configuration>
<overwriteExistingFiles>true</overwriteExistingFiles>
<outputDirectory>${project.build.directory}/modules</outputDirectory>
<modules>
<module>
<artifact>
<groupId>org.jbibtex</groupId>
<artifactId>jbibtex</artifactId>
<version>1.0.17</version>
</artifact>
<moduleInfoSource>
module jbibtex
{
exports org.jbibtex;
}
</moduleInfoSource>
</module>
</modules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Specifically to answer your question:
How to specify the module-path used?
You could run jlink from the command-line instead of via Maven.
That way you can specify any module path you wish.
From the man page for jlink:
jlink [options] --module-path modulepath --add-modules module [, module...]
modulepath
The path where the jlink tool discovers observable modules. These modules can be modular JAR files, JMOD files, or exploded modules.
module
The names of the modules to add to the runtime image. The jlink tool adds these modules and their transitive dependencies.
If you wish to continue using the openjfx maven plugin jlink command, you can do that.
The code which configures the module path passed to jlink by the maven plugin is in git.
if (modulepathElements != null && !modulepathElements.isEmpty()) {
commandArguments.add(" --module-path");
String modulePath = StringUtils.join(modulepathElements.iterator(), File.pathSeparator);
if (jmodsPath != null && ! jmodsPath.isEmpty()) {
getLog().debug("Including jmods from local path: " + jmodsPath);
modulePath = jmodsPath + File.pathSeparator + modulePath;
}
commandArguments.add(modulePath);
commandArguments.add(" --add-modules");
if (moduleDescriptor != null) {
commandArguments.add(" " + moduleDescriptor.name());
} else {
throw new MojoExecutionException("jlink requires a module descriptor");
}
}
This is based on the javafx:jlink options:
jmodsPath: When using a local JavaFX SDK, sets the path to the local JavaFX jmods
So if you put your module on the path specified by that option, it should be found.
Here are some other options.
Package your app as a non-modular app using jpackage
I think mipa's suggestion of using JPackageScriptFX is probably the best approach:
github.com/dlemmermann/JPackageScriptFX
Create a new jar from binaries
Unjar the JBibTex.jar.
Add in the compiled module-info to unjared directory.
Create a new jar, e.g. JBibTexModule.jar.
Install that to your maven repository via a mvn install command.
Add a maven dependency on JBibTexModule instead of JBibTex.
Add any needed statements to your main app module-info file to use the library.
Then, I believe you are done, and it would build, link, and run ok.
Create a new jar from source
The jbibtex library is also in github, so you could create an issue or pull-request on the project to ask it to be modularized.
Also, you could:
Clone the repository.
Add the module-info.java.
Build the jar file.
Install it locally in your local maven repository.
Then depend on the local version you have created.
I am currently building an EAR file in Maven. When I build it, I get the following in my target folder:
-target/
-MyProject.ear
-MyProject/
-MainProject.jar
-Dependency.jar
If I open up MyProject.ear in 7Zip, I see the same file structure that exists in MyProject/
I have been asked to change this so that all dependencies are in a subfolder, like this:
-target/
-MyProject.ear
-MyProject/
-MainProject.jar
-lib/
-Dependency.jar
Now I can make this work for the one dependency by making the following change to my pom.xml file:
<build>
[...]
<plugins>
<plugin>
<artifactId>maven-ear-plugin</artifactId>
<version>2.9</version>
<configuration>
<modules>
<jarModule>
<groupId>Dependency</groupId>
<artifactId>Dependency</artifactId>
<bundleDir>lib</bundleDir>
</jarModule>
</modules>
</configuration>
</plugin>
</plugins>
</build>
However in the real project, I have about 30 dependencies. I could manually add a <jarModule> tag for each and every dependency, but ideally I would like for it to automatically move all of the dependencies to a subdirectory (making an exclusion for MainProject.jar).
I had hoped that I could declare <jarModule> once and use wild card characters for the groupId and artifactId, like Maven allows with exclusions:
<!-- Moving all for the time being, add the exclusion if it works -->
<jarModule>
<groupId>*</groupId>
<artifactId>*</artifactId>
<bundleDir>lib</bundleDir>
</jarModule>
I would expect the above to take all artifacts and put them into a lib folder. Unfortunatley, this doesn't seem to work in Maven 3.2.1, providing an error saying that Artifact[jar:*:*] is not a dependency of the project
So how can I move all of my dependencies to a subfolder when building an EAR in Maven?
You should move the bundle directory restriction under your maven-ear-plugin build configuration as below:
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>${maven-ear-plugin.version}</version>
<configuration>
<modules>
<jarModule>
<groupId>${group_id}</groupId>
<artifactId>${artifact_id}</artifactId>
<bundleDir>lib</bundleDir>
</jarModule>
<webModule>
...
</webModule>
</modules>
<defaultLibBundleDir>/lib</defaultLibBundleDir>
</configuration>
</plugin>
</plugins>
Hope it helps.
Thanks to a happy accident of failing to fully clean up my pom file after a test, I discovered how to do this.
In the <configuration> tag, add <defaultLibBundleDir>/lib</defaultLibBundleDir>. This will put all modules by default in a lib folder in a root of a directory. No wildcards are needed; in fact, Maven doesn't support wild cards in this part, probably because there are no clear bounds (it could be interpreted as "move all artifacts ever"). Wild cards only work for exclusions because there are clear bounds; it's a subset of a clearly defined set of artifacts.
If you'd like to make an exception, then you specify the module and type in <bundleDir>/</bundleDir>, which will place it back in the root. The full XML looks like this:
<build>
[...]
<plugins>
<plugin>
<artifactId>maven-ear-plugin</artifactId>
<version>2.9</version>
<!-- All dependencies not otherwise specified are going in /lib -->
<defaultLibBundleDir>/lib</defaultLibBundleDir>
<modules>
<jarModule>
<!-- An exception to keep the main project in the root -->
<groupId>MainProject</groupId>
<artifactId>MainProject</artifactId>
<bundleDir>/</bundleDir>
</jarModule>
</modules>
</configuration>
</plugin>
</plugins>
This results in the original goal of putting all dependencies in a lib folder except for MainProject.jar:
-target/
-MyProject.ear
-MyProject/
-MainProject.jar
-lib/
-Dependency.jar
I have a maven ear module with the following configuration for my ear file:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>2.6</version>
<configuration>
<displayName>app</displayName>
<description>app</description>
<modules>
...
<ejbModule>
<groupId>com.example</groupId>
<artifactId>example-client</artifactId>
<bundleFileName>example-client.jar</bundleFileName>
</ejbModule>
..
</modules>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<version>5</version>
</configuration>
</plugin>
When I use Intellij (ultimate 12.1.4) to deploy my ear to a jboss as, the bundleFileName is ignored.
It creates a jar file with version number, but in the application.xml the jar file is defined without version.
Is there a way to fix this? I deploy using the "exploded" ear.
Thanks!
The issue is fixed in Intelij 13 (currently not yet release): see http://youtrack.jetbrains.com/issue/IDEA-114355
Im facing an issue how to correctly package my enterprise (EAR) application with simple WAR and EJB3 module for JBoss7 application server. The thing is, that EJB module is using XML-RPC library (from Apache) and Im still getting NoDefClassFound (classes from this xmlrpc lib) during deployment of EAR.
The thing is, that maven-ejb-plugin does not package dependencies within final EJB jar but maven-ear-plugin does package it at the root of EAR directory.
When EAR gets deployed, INSTALL is invoked on inner EJB module but it does not find xmlrpc lib class (it is not packaged with EJB jar but EAR and it does not have any entry in manifest).
EJB pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cz.ctu.fee.voxport.app_logic</groupId>
<artifactId>core</artifactId>
<version>1.0</version>
<packaging>ejb</packaging>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlrpc</groupId>
<artifactId>xmlrpc-common</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.xmlrpc</groupId>
<artifactId>xmlrpc-client</artifactId>
<version>3.1.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<version>2.3</version>
<configuration>
<ejbVersion>3.1</ejbVersion>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
Is there any way how to cleanly solve this using maven?
I managed to solve the problem. It seems that these libraries has to be packaged within /lib directory and not in root of EAR. Adding defaultLibBundleDir element solved the problem.
E.g.:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>2.6</version>
<configuration>
<defaultLibBundleDir>lib</defaultLibBundleDir>
...
Did you leave the <addClasspath>true</addClasspath> on the EJB config?
Well, you can leave it like this, but you'll get a heap of log entries (WARN) on server start complaining about the classpath entries.
I prefer to set it to false. <addClasspath>false</addClasspath>