26

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?

Dan
  • 11,077
  • 20
  • 84
  • 119

4 Answers4

69

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

Beryllium
  • 12,808
  • 10
  • 56
  • 86
Turbokiwi
  • 1,288
  • 10
  • 15
  • 4
    This ***really*** needs **TONS** more upvotes !! Awesome ! :D – Andrea Ligios Oct 07 '13 at 12:38
  • 1
    NICE! :) this has really helped me! – Christophe Jan 10 '14 at 12:48
  • Does this solution also work with deeper dependencies? Or do I need to declare the artifacts in the WAR explicitly so I can exclude them in the EAR? – Ivo Limmen Apr 02 '14 at 07:03
  • Haven't tested it. But from my experience, you're not forced to declare a dependency on a lib which you want to exclude. Have you tried it and did it work? – Turbokiwi May 20 '14 at 12:39
  • 1
    This is an exceptionally neat trick—it makes managing dependencies far easier in an enterprise app which is bundled in an EAR. It bundles all transitive dependencies into the /lib directory whilst bundling just skinny WAR files at the root. Kudos! – Matthew Cachia Apr 23 '15 at 13:44
  • 1
    This actually works and should be the best answer. It creates some nice isolation. The WAR's pom.xml does not have to be touched and stays decoupled from the EAR project. – timh Mar 16 '16 at 13:01
  • I'm trying to use this setup but facing some strange behavior. If I have an EAR with just 1 WAR as a dependency, why do I get libraries outside the WAR and into EAR/lib directory or at the root of the EAR? – Gabriel Pereira Borges Jan 27 '18 at 21:53
9

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.

David Rabinowitz
  • 29,904
  • 14
  • 93
  • 125
4

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.

Mike Pone
  • 18,705
  • 13
  • 53
  • 68
4

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

  • 4
    from suggested URL: "Now the painful part. Your EAR project's pom.xml needs to list every dependency that the WAR has. This is because Maven assumes fat WARs and does not include transitive dependencies of WARs within the EAR." I asked: I would like to use Maven to calculate dependencies... without using Maven to calculate dependencies, it is a no-go... – Dan Jan 02 '10 at 23:15
  • 2
    @Dan, read Turbokiwi answer to know how to avoid the painful part in a superb way :) – Andrea Ligios Oct 07 '13 at 12:40