How to make maven place all jars common to wars inside the same EAR to EAR root? - maven-2

We have a solution with numerous wars. Wars are similar in the sense they all use hibernate and spring. This means that we have a number of same jars inside each war. This is becoming a problem, because the size of the ear is starting to grow out of proportion.
I would like to use Maven to calculate dependencies and to place all jars common to multiple wars to the root of the EAR.
I tried organizing my project using j2ee archetype (maven-archetype-j2ee-simple), but all wars are still packaged with dependencies inside the WEB-INF/lib.
Is there a way to make Maven calculate common dependencies and place them to EAR, just as he is able to calculate all transitional dependencies when constructing a war or a jar?

As you've mentioned in a comment, it's maven's task to calculate every dependency. When you're creating an artifact, with every common dependency, then you'll also have to guess, which dependencies belong there.
It could also be possible, that you have to deploy one war, with it's dependencies on another machine without an ear, an when you set every war dependency to provided, then you're stuck again.
The only right way, to get skinny wars is from the examples:
http://maven.apache.org/plugins/maven-war-plugin/examples/skinny-wars.html
But, and now comes the interesting part, there is one big! shortcut (which completly takes away the mentioned pain), to tell maven, which dependencies your WARs have.
Go inside your EAR-Module an declare a second dependency on the WAR with type pom for every WAR dependency.
<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>
<parent>
<groupId>com.foo</groupId>
<artifactId>skinny</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>ear</artifactId>
<packaging>ear</packaging>
<dependencies>
<dependency>
<groupId>com.foo</groupId>
<artifactId>war</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>war</type>
</dependency>
<dependency>
<groupId>com.foo</groupId>
<artifactId>war</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>pom</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>2.8</version>
<configuration>
<skinnyWars>true</skinnyWars>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<modules>
<webModule>
<groupId>com.foo</groupId>
<artifactId>war</artifactId>
</webModule>
</modules>
</configuration>
</plugin>
</plugins>
</build>
Now, every WAR will be packaged independently with it's own dependencies and the EAR will be packaged with skinny WARs and every dependency inside the lib folder
Update:
Keep in mind, that the ear/lib folder can't be used for every dependency jar in a strict Container like JBoss EAP 6. JSF Component libraries like tomahawk, primefaces, etc. have to reside in WEB-INF/lib folder.
A handy way to achieve this with the above described solution is to make an exclusion for the component library in the EARs pom.xml like this:
...
<dependencies>
<dependency>
<groupId>com.foo</groupId>
<artifactId>war</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>war</type>
</dependency>
<dependency>
<groupId>com.foo</groupId>
<artifactId>war</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<exclusion>
</exclusions>
</dependency>
</dependencies>
...
Now every dependency of the WAR will be placed in ear/lib except the component library which will be placed in WEB-INF/lib inside the WAR

Create a new artifact named commons-jars and package it as pom. It should depend on all the common jars you are using - Spring, Hibernate, Log4j, etc.
Then, in each on your wars add it as dependency with scope "provided" (and don't forget to set the type as pom). You will be able to see it in your classpath but they won't be packaged into the war. This way you can also have war specific dependencies packaged into it, which the solution from skinny wars does not provide.

You can set the dependancies scope to "provided". This means they will be provided by some other module and will not be included in the final jar or war.
Perhaps the assembly plugin can help you when packaging up the final EAR and place common jars there.

http://maven.apache.org/plugins/maven-war-plugin/examples/skinny-wars.html

Related

Why does Maven use already non-existent classes and dependencies from history in build

I am using maven 3.2.1. for my project.
Maven builds artifacts and puts classes and dependencies into them which do not exist. For example, I used omnifaces as dependency in pom-file. I removed omnifaces from pom-file weeks ago but maven still builds it into the artifact. Another issue is, that maven builds an old and the new structure of the project. I removed a package and put all the classes into another package but maven still builds the old package next to the new one. That causes a ClassCastException at some points.
I'd like to know if anyone knows about that problem. Is there a way to configure maven to use only the current dependecies and project structure to build artifacts?
<?xml version="1.0" encoding="UTF-8"?>
http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
<parent>
<groupId>myProject</groupId>
<artifactId>webApplication</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>webApplication-war</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>myProject</groupId>
<artifactId>model-jar</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>myProject</groupId>
<artifactId>ordering-ejb</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.faces</groupId>
<artifactId>javax.faces-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>5.0</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
<name>${project.artifactId}-1.0-SNAPSHOT</name>
<!--<url>http://localhost</url> -->
<!--<build>-->
<!--<finalName>${project.artifactId}-1.0-SNAPSHOT</finalName>-->
<!--<pluginManagement>-->
<!--<plugins>-->
<!--<plugin>-->
<!--<groupId>org.apache.maven.plugins</groupId>-->
<!--<artifactId>maven-war-plugin</artifactId>-->
<!--<version>2.4</version>-->
<!--<configuration>-->
<!--<packagingExcludes>WEB-INF/lib/model-1.0-SNAPSHOT.jar</packagingExcludes>-->
<!--<webappDirectory>src/main/webapp</webappDirectory>-->
<!--<webXml>src/main/webapp/WEB-INF/web.xml</webXml>-->
<!--<outputDirectory>${env.M3_REPO}/ordentity/webApplication/1.0-SNAPSHOT</outputDirectory>-->
<!--</configuration>-->
<!--</plugin>-->
<!--</plugins>-->
<!--</pluginManagement>-->
<!--<plugins>-->
<!--<plugin>-->
<!--<groupId>org.apache.maven.plugins</groupId>-->
<!--<artifactId>maven-dependency-plugin</artifactId>-->
<!--<version>2.8</version>-->
<!--<executions>-->
<!--<execution>-->
<!--<phase>install</phase>-->
<!--<goals>-->
<!--<goal>copy</goal>-->
<!--</goals>-->
<!--<configuration>-->
<!--<overWriteIfNewer>true</overWriteIfNewer>-->
<!--<artifactItems>-->
<!--<artifactItem>-->
<!--<groupId>${project.groupId}</groupId>-->
<!--<artifactId>${project.artifactId}</artifactId>-->
<!--<version>${project.version}</version>-->
<!--<type>${project.packaging}</type>-->
<!--</artifactItem>-->
<!--</artifactItems>-->
<!--<outputDirectory>${env.JBOSS_HOME}/standalone/deployments</outputDirectory>-->
<!--</configuration>-->
<!--</execution>-->
<!--</executions>-->
<!--</plugin>-->
<!--</plugins>-->
<!--</build>-->
Maven puts classes and directories into that war file which do not exist in project structure. I had a package called bean, but deleted it weeks ago. It still appears in the war file. I am really disapointed...
SOLVED: I found a helpful thread (link below) where a comment gave me the solution.
I found a classes directory in my project under:
webApplication/war/src/main/webapp/WEB-INF
It contained all the old classes which were build into the artifact as well. Therefore maven also built all the dependencies into the artifact. After I deleted the classes directory all the ugly sideeffects disappeared.
Why might Maven ignore updated classes during install?

How to make one module depend on another module artifact?

I have maven multiple-module project.
A: parent.
B: child1.
C: child2.
B will be packaged to get jar file and then c will use this jar file to compile the code.
In B, if I run mvn package, it will create b.jar (stays in B/target/jars not in B/target -for another purpose).
In C, I need to use that b.jar to compile the code.
Now, from A, when I run: mvn package. First, I am successful to create b.jar file for B.
But when it come to C's compilation phase, it looks like C doesn't recognize b.jar in the classpath (the compilation gets errors because C's code can not import the class file from B).
My question is: How can I solve this problem?
---------- Below are the pom files
A: pom.xml
<groupId>AAA</groupId>
<artifactId>A</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>C</module>
<module>B</module>
</modules>
B: pom.xml
<groupId>AAA</groupId>
<artifactId>B</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<parent>
<artifactId>A</artifactId>
<groupId>AAA</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
C: pom.xml
<parent>
<artifactId>A</artifactId>
<groupId>AAA</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>AAA</groupId>
<artifactId>C</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>AAA</groupId>
<artifactId>B</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
....
Try ${project.version}
e.g.
<dependency>
<groupId>AAA</groupId>
<artifactId>B</artifactId>
<version>${project.version}</version>
</dependency>
Looks like it should work to me. But you might try mvn install instead of mvn package.
My question is how I can solve this problem?
Dependency resolution is done through the local repository so the canonical way to "solve" the problem is to run install from A so that modules will get installed in the local repository.
Now, regarding the following comment
But if I go with install then c war file will also be installed. That one is not accepted in my current project".
Sure, I'm not on your project, I don't know all constraints and rules. But if you decide to use Maven, this is a totally ridiculous policy (seriously, WTF?) and using a system scoped dependency is certainly not a good solution (more troubles later guaranteed). If this policy is real, better not use Maven in that case.
i have a solution: using the dependency with the scope=system
in C pom.xml
<dependency>
<groupId>AAA</groupId>
<artifactId>B</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>system</scope>
<systemPath>${basedir}\..\B\target\jars\b.jar</systemPath>
</dependency>
and in A pom.xml, put module B on the top like this
<modules>
<module>B</module>
<module>C</module>
</modules>
Doing mvn install only places the artifact into the local .m2 repository of the machine you're running the command on. How can that not be acceptable? I agree with Pascal. If you building A, there should be no reason that a the war is placed there.
On the other hand, if you're using Maven 2.2.x, take a look at the maven reactor plugin? This should help the crazy unacceptable cannot install C.war into your local .m2 repository policy for the current project.
If you have moduleA on your machine say at D:\moduleA and inside moduleA you have created another module say moduleB at D:\moduleA\moduleB , for you to use moduleB inside moduleA you create a dependency in the pom.xml file of moduleA like so:
<project xmlns="http://maven.apache.org/POM/4.0.0" ...>
...
<groupId>net.passioncloud</groupId>
<artifactId>moduleA</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>net.passioncloud</groupId>
<artifactId>moduleB</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependencies>
...
To compile the project so you can use it, from the module folder (moduleB) do:
.\mvnw clean install
Here are what I did to solve it:
From intelij , create new module from existing source.
Change the version of dependency B in A.pom same as version of B in B.pom

Maven2: dependency set to compile not added to war

I have a multi module project consisting of several jar modules and a war module. When I do mvn package, the war is created but one dependency (javax.mail) is not included in the lib folder of the war.
The dependency is set to compile is the main pom. The war is not dependent from the mail.jar but a module.jar is.
When I do mvn dependency:tree, the three looks fine to me. Running in debug does not show me anything wrong either.
Anyone has an idea?
BB
Peter
Edit: in the master POM I have
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.1</version>
<scope>compile</scope>
</dependency>
as a managed dependency. The module jar has the dependency as follows:
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</dependency>
The war module has no dependency to javax.mail.
Edit2:
I do override the war plugin in the master pom like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1</version>
<configuration>
<warName>${war.name}</warName>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<Implementation-Build>${buildNumber}</Implementation-Build>
</manifestEntries>
</archive>
</configuration>
</plugin>
Does your pom or master pom override the maven-war-plugin? It's possible to explicitly exclude artifacts from being put in the war:
http://maven.apache.org/plugins/maven-war-plugin/war-mojo.html#packagingExcludes
Also,
http://maven.apache.org/plugins/maven-war-plugin/war-mojo.html#warSourceExcludes
...I can't remember exactly the difference between the two excludes, and it doesn't really matter if you're not overriding the war plugin anyway.
If the javax.mail dependency is a dependency of one of your module, it should be included. However, if it is defined as an optional dependency, it will break the transitive dependency mechanism.
In others words, if in your module, you have that definition:
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.1</version>
<optional>true</optional>
</dependency>
If this is the case, simply remove this <optional>true</optional> statement.

Add jar (dependency with scope system) in an Ear build

I work with Maven and I want to do a build with packaging ear, i want to add a dependency with scope system and also with specifing the systemPath of the jar like follow:
<dependency>
<groupId>group1</groupId>
<artifactId>group1</artifactId>
<version>1</version>
<scope>system</scope>
<systemPath>D:\Buildear\Jars\file.jar</systemPath>
</dependency>
But I don't found the jar in my generater ear!!!
Help please.
I work with Maven and I want to do a build with packaging ear, I want to add a dependency with scope system (...). But I don't found the jar in my generater ear!!!
Yes, that's just what you get when (ab)using a system scoped dependency which is supposed to be always available by definition. I wrote many times about this, for example in this previous answer that I'm quoting below:
I already wrote many, many,
really many times about this
here on SO and in 99% of the cases,
system scoped dependencies should be
avoided. And I'll repeat what the
Dependency Scopes mini guide says
one more time:
system: This dependency is required in some phase of your
project's lifecycle, but is
system-specific. Use of this scope
is discouraged: This is considered an
"advanced" kind of feature and should
only be used when you truly understand
all the ramifications of its use,
which can be extremely hard if not
actually impossible to quantify.
This scope by definition renders your
build non-portable. It may be
necessary in certain edge cases. The
system scope includes the
<systemPath> element which points to
the physical location of this
dependency on the local machine. It is
thus used to refer to some artifact
expected to be present on the given
local machine an not in a repository;
and whose path may vary
machine-to-machine. The systemPath
element can refer to environment
variables in its path: ${JAVA_HOME}
for instance.
So, instead of using the system
scope, either:
Add your libraries to your local repository via install:install-file.
This is a quick and dirty way to get
things working, it might be an option
if you're alone but it makes your
build non portable.
Install and run an "enterprise repository" like Nexus, Archiva, or
Artifactory and add your libraries via
deploy:deploy-file. This is the
ideal scenario.
Setup a file based repository as described in this previous answer
and put your libraries in there. This
is the best compromise if you
don't have a corporate repository but
need to work as a team and don't want
to sacrifice portability.
Please, stop using the system scope.
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>aaa</artifactId>
<groupId>aaa</groupId>
<version>1.0</version>
</parent>
<groupId>aaa</groupId>
<artifactId>aaa</artifactId>
<version></version>
<packaging>ear</packaging>
<name>aaa - Ear</name>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>aaa-ejb</artifactId>
<version>${project.version}</version>
<type>ejb</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>aaa-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jboss</groupId>
<artifactId>jboss-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jboss</groupId>
<artifactId>jbosssx</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${aaa.name}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<generateApplicationXml>false</generateApplicationXml>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<modules>
<ejbModule>
<groupId>${project.groupId}</groupId>
<artifactId>aaa-ejb</artifactId>
</ejbModule>
<jarModule>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<excluded>true</excluded>
</jarModule>
</modules>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<aaa.name>aaa-batch</aaa.name>
</properties>
This creates an ear and copies the libraries into the lib folder in the ear.

Maven reuse in poms

In our Maven project, we are trying the following directory structure (with about 80 projects total, only a few are shown so that you get the idea):
myappli (pom)
-- module1 (pom)
--|-- utils (pom)
--|-- ejb (pom)
--|--|-- myappli-module1-a-ejb (jar)
--|--|-- myappli-module1-b-ejb (jar)
--|-- war (pom)
--|-- applet (pom)
...
-- module6 (pom)
--|-- utils (pom)
--|-- ejb (pom)
--|--|-- myappli-module6-c-ejb (jar)
--|-- war (pom)
--|-- applet (pom)
Note: This is a flat structure for Maven, as all non-leaf projects have a packaging value of "pom". (cf BetterBuildsWithMaven book).
We define the dependency versions in "dependencyManagement", in the "myappli" pom. This works fine.
Our problem is with the reuse of the dependencies themselves.
For example, the ejb dependencies are common to all ejb projects (by design).
We don't want to cut'n-paste, and maintain all that with each change!
We were thinking to use some "import notion" for the ejb dependencies, and define our ejb dependencies once at the application level. Our unsuccessful attempts were:
The Maven "parent pom" notion would be fine, but it is already used by the modules, so it is not available for our requirement.
No import facility found in Maven (except for dependencyManagement)
XML entity definition is not recognized. We tried a pom like the following, and got the error
"Reason: Parse error reading POM. Reason: could not resolve entity named 'ejbDependencies'":
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project [
<!ENTITY ejbDependencies SYSTEM "./ejbDependencies.txt">
]>
<project ...
...
&ejbDependencies;
...
Edited : I am trying the solution suggested by Robert, but something is wrong.
When I compile my ejb project, it doesn't find the dependencies themselves. I get an error when compiling (mvn compile), saying the javax.ejb package is missing.
Note: I did run "mvn install" on the dependencies project before.
This is my configuration :
<project ...>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.company</groupId>
<artifactId>myproj-maven</artifactId>
<version>3.1-SNAPSHOT</version>
</parent>
<groupId>com.company</groupId>
<artifactId>myproj-maven-ejb</artifactId>
<version>${myproj-version}</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>ejb</artifactId>
</dependency>
<dependency>
<groupId>ojdbc</groupId>
<artifactId>ojdbc</artifactId>
</dependency>
</dependencies>
</project>
---------------------------------
<project ...>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.company</groupId>
<artifactId>myproj-identite-ejb</artifactId>
<version>3.1-SNAPSHOT</version>
</parent>
<groupId>com.company</groupId>
<artifactId>myproj-identite-metier</artifactId>
<name>SNR IDENTITE METIER</name>
<version>2.0.1</version>
<packaging>ejb</packaging>
<dependencies>
<dependency>
<groupId>com.company</groupId>
<artifactId>myproj-maven-ejb</artifactId>
<version>${myproj-version}</version>
<type>pom</type>
</dependency>
</dependencies>
</project>
I don't know if it changes something, but we have a hierarchy that relates the two poms.
We have a strict Maven structure, where each directory declares all subdirectories as maven modules, and each subdirectory declares the parent as a maven parent.
And the common parent directory is part of this structure.
+---maven
| \---ejb
+---identite
| +---ejb
| | \---SNR_IDENTITE_METIER
Edited:
The answer given by reef seem correct. It is impossible to do with Maven, because our dependency are provided, and therefore not transitive :-(
We really have many problems with setup up Maven. So many little things just don't work. Today I found out that the site target cannot handle properties, that we are using for version numbers!
You can use pom dependencies to import dependencies into arbitrary projects.
A pom project can look similar to:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>persistence-deps</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>${hibernateVersion}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>${hibernateAnnotationsVersion}</version>
</dependency>
</dependencies>
</project>
And is imported as:
<dependency>
<groupId>com.example</groupId>
<artifactId>persistence-deps</artifactId>
<version>1.0</version>
<type>pom</type>
</dependency>
See Maven, the definitive guide - Grouping Dependencies for details.
Do your imported dependencies have a provided scope?
Indeed this scope is not transitive (see Maven Dependency Scopes).
This could be the reason of the non-replacement.