Enterprise Java

Java EE7 and Maven project for newbies – part 4 – defining the ear module

Resuming from the previous parts

Part #1
Part #2
Part #3

We are resuming for the 4th part, our simple project currently has

  • a web maven module (a war)
  • an ejb module (ejb)  holding our stateless session beans (EJB 3.1)
  • and a second (ejb) module holding our entity beans (JPA2)

but we are still missing the one to package them all, archive, which will be of ‘ear’ type (aka Enterprise Archive).

 Defining our ear maven module

As you can see in the image below, we create emtpy folder called sample-ear under the sample-parent. This folder needs to have a pom.xml file. Our new module needs to be correctly referenced in the  ‘modules‘  section of the sample-parent\pom.xml.

CapturFiles_3

The main purpose of our ear maven module is to ‘configure’ the famous maven-ear-plugin, which is going to be invoked by maven and is going to produce our final deployable application.

There 2 simple things we need to do, add configuration for the maven-ear-plugin, and add our ‘internal‘ application dependencies on the ear module, so that it ‘knows’ which modules should look up. Let’s have a look:

Inside the ear pom.xml

    <build>
       <finalName>sampleapp</finalName>
        <plugins>
           <!--Ear plugin -creating the ear - watch out skinny WARS!-->
            <plugin>
               <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-ear-plugin</artifactId>
                <configuration>
                  <finalName>sampleapp</finalName>
                    <defaultJavaBundleDir>lib/</defaultJavaBundleDir>
                    <skinnyWars>true</skinnyWars>
                     <modules>
                          <webModule>
                             <groupId>gr.javapapo</groupId>
                             <artifactId>sample-web</artifactId>
                           </webModule>
                            <ejbModule>
                               <groupId>gr.javapapo</groupId>
                               <artifactId>sample-services</artifactId>
                           </ejbModule>
                     </modules>
                </configuration>
             </plugin>
         </plugins>
    </build>

This is the build, section make note on the following things:

  • Remember as we did other modules, we have defined some basic common configuration for our plugin, in the ‘parent‘ pom. Go back and have a look what is already there for you.
  • Watch out the ‘defaultJavaBundleDir‘ this where we define where all the libraries (apart from the top-level modules that will reside in our ear, usually is  a sub-folder in the ear called ‘lib’.
  • What is a top level module? It is actually, the  jar(s), and wars that are going to be packaged in the ear, and are considered first level citizens,as you can see we define 2, the sample-web and the sample-services.
  • Watch out the ‘skinnyWars‘ property. With this switch enabled, we enforce a certain pattern on packaging our third party libs, referenced from our war project. Simply put, our war archives are NOT going to include any external libraries we might define as dependencies under their WEB-INF\lib folder, instead all those libs,they are going to be packaged in the ‘defaultJavaBundleDir‘ path on the ear level.

The above configuration is not going to work, if we dont add the ‘dependencies’ section of our ear-pom.

<!-- our in app dependencies-->
    <dependencies>
        <dependency>
            <groupId>gr.javapapo</groupId>
            <artifactId>sample-web</artifactId>
            <version>${project.version}</version>
            <type>war</type>
        </dependency>
 
        <dependency>
            <groupId>gr.javapapo</groupId>
            <artifactId>sample-services</artifactId>
            <version>${project.version}</version>
            <type>ejb</type>
        </dependency>
    </dependencies>

Make note of the following:

  • the dependency element in this pom, needs the ‘type’ attribute.

One good question you may have is, where the sample-domain (jar) module?

Well this module, is not promoted as a top level element in our ear, because we are going to add it as a dependency on the sample-services module. So our services will hold a dependency on the module of the entity beans. (Sounds fair). So we need to update the pom.xml of our sample-services module.

      <artifactId>sample-services</artifactId>
      <name>sample-services</name>   
       <description>EJB service layer</description>
            <packaging>ejb</packaging>
            <dependencies>
                <dependency>
                    <groupId>javax</groupId>
                    <artifactId>javaee-api</artifactId>
               </dependency>
               <dependency>
                 <groupId>gr.javapapo</groupId>
                 <artifactId>sample-domain</artifactId>
                 <version>${project.version}</version>
              </dependency>
            </dependencies>
    </project>

By doing that, the sample-services.jar is going to ‘fetch’ along the sample-domain.jar. By default (remember Maven is all about conventions), when we define a top level module to an ear,l ike the sample-services, it’s dependencies are bundled automatically under the defaultJavaBundleDir lib of the ear! So when we package our ear, we will be expecting to see the sample-domain jar packaged.

One more missing dependency

After our first ‘ in app dependency between the services module and the entities module, we need another one. Our war module, (web layer) is going to use some of our services, but in order to being able to do it needs to have a dependency on the ‘services’ module. So we need to the pom.xml on the sample-web project, accordingly.

    <packaging>war</packaging>
            <build>
             <finalName>${project.artifactId}</finalName>
            </build>
              <dependencies>
                 <dependency>
                       <groupId>javax</groupId>
                        <artifactId>javaee-api</artifactId>
                         <scope>provided</scope>
                 </dependency>
                <dependency>
                  <groupId>gr.javapapo</groupId>
                  <artifactId>sample-services</artifactId>
                <version>0.0.1-SNAPSHOT</version>
               </dependency>
            </dependencies>

Let’s package our war.

We are ready for now, our basic dependencies are set, our ear is configured, we just need to package. Under the sample-parent folder level on command line we just need to type:

mvn clean package

We are done, let’s check under the ‘target’ folder of the sample-ear module. Our final ear is ready, maven also creates the ‘exploded‘ version of the ear, (it is, expanded in the image below). Notice our 2 top level ear elements, and how the sample-domain.jar is under the ‘lib’ folder of our ear. Also notice that some basic libraries like the javaee-api.jar are not included in the lib folder. Since we have added the provided in the pom. (see the final version of the xml).

CapturFiles_5

One last thing…skinny war(s) and MANIFEST.MF files

Eventually, we could stop here, our final ear is ok and is going to work, but with all the above configuration, especially with our preference to create, skinny wars , we need to pay attention to a small detail. MANIFEST files are special descriptors within jars and wars, that are used by application servers on locating and class loading ‘dependent’ jars in the class path, within the ear.

Our small problem resides in the MANIFEST.MF file of the sample-web.war. If we unpack the generated war file and we open with a text editor the MANIFEST.MF we will see is something like that.

    Manifest-Version: 1.0
    Built-By: papo
    Build-Jdk: 1.7.0_45
    Class-Path: lib/sample-services-0.0.1-SNAPSHOT.jar lib/sample-services-0.0
     .1-SNAPSHOT.jar lib/sample-domain-0.0.1-SNAPSHOT.jar
    Created-By: Apache Maven 3.2.1
    Archiver-Version: Plexus Archiver

Can you spot the mistake? By default the MANIFEST.MF generated, is indicating a wrong path for one of our top-level ejb jars(sample-services). Our sample-services.jar is not place under the \lib within the ear, but is a top level element. So how are we going to create a correct MANIFEST?

Eventually we need to fine tune a bit the maven-war plugin. We need to overwrite the default behaviour as specified in the parent pom, and specify a correct entry for this particular dependency. If you happen to have more than one, then you need to append all the jars that are top level elements in the configuration (make sure you do it properly, use a space between entries).So in the sample-war pom we need to add some configuration (extra) on top of the one applied. See the image below.

CapturFiles_6

There is an interesting stackoverflow issue, that you can read more about this, little trick or other potential workarounds in case you use skinny-wars.

That’s it, our ear is ready.

Summary

You can find the final version for this post in this Git Tag.With this post, we are completing a first series of posts, starting from scratch, applying basic maven principles and creating some basic maven modules for a java enterprise application. Feel free to re-use this example and extend it in order to meet your own needs. It is by far complete in terms of covering all your needs, but it is a solid example on getting starting, thinking and configuring in Maven.

I am going to expand on this example, adding more modules and using more features of maven in future posts.

Paris Apostolopoulos

Paris is a senior software engineer focusing on J2EE development, loves Business process modelling and is keen on software quality challenges. He is passionate about Java and Java communities. He is a co-founder and administrator of the first Java User Group in greece(JHUG.gr) and occasional speaker on meet-ups and seminars and regular blogger. For his contributions and involvement on the Java community he has been awarded the title of Java Champion in 2007 by Sun Microsystems.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
bakri anouar
bakri anouar
9 years ago

The property ‘defaultJavaBundleDir’ has been replaced by ‘defaultLibBundleDir’. http://jira.codehaus.org/browse/MEAR-185.

I was working on these exemple in eclipse luna + jboss 7.1.1 + maven 3.2.2 . your exemple work great but when it come to defaultJavaBundleDir the jar files was not placed in the lib folder, they was deployed in the root of project-ear.ear folder in jboss-as-7.1.1.Final\standalone\deployments.
after changing ‘defaultJavaBundleDir’ to ‘defaultLibBundleDir’ the issue was resolved.

S Singh
S Singh
5 years ago

Thanks for posting such a valuable information. Look forward to incorporate in my EAR project.

Back to top button