Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Maven and Java EE 7: More Than Meets the Eye

DZone's Guide to

Maven and Java EE 7: More Than Meets the Eye

Maven is an incredibly useful tool, so let's break it down into its component parts and try to unlock it's full potential and see just what you can do with it.

· Java Zone
Free Resource

Navigate the Maze of the End-User Experience and pick up this APM Essential guide, brought to you in partnership with CA Technologies

If you are not using Maven, then you should. If you are using Maven, then you are likely cutting and pasting snippets of pom.xml file examples and hoping for the best. In this article, I will present the pom.xml file that I require my students to use in an e-commerce project course. Not everything in it is required, and you likely have libraries you need that are not in my example. After reviewing my pom.xml, however, you should be able to make all the changes you need. You can find the complete file at the end of this article.

This is my third article on the pom.xml file that I have written. The last time I wrote about it was two years ago, and while Maven has not changed, I have learned more about the pom.xml. If you are not using Maven because a practical explanation of the elements required in the real world is rare, then hopefully this will change your mind.

Whether you work from the command line or with an IDE such as NetBeans (my favorite), one issue comes up over and over again. How do I make sure all the libraries I need get included in my project? It used to be, for me, that I needed to track down the website for every library I wanted to use, download the JAR and manually paste it into a location on the classpath or a specific folder in IDE. Packaging an application for distribution was also an issue because I needed to ensure that all the right JARs were included in the uber archive, JAR or WAR, I planned to distribute.

There is more to Maven than just including libraries. I use it to upload Java programs to my Raspberry Pi and execute them. I use Maven to control when I want unit testing to occur. There is much, much more it can do. The one task that drives my continual writing about Maven is that I use it in the classroom. I teach software development at Dawson College in the final year of the program. I like teaching coding, but I am not fond of teaching the necessary plumbing to make systems work. I use NetBeans in my courses because it does not need to be managed the way Eclipse or IntelliJ needs to be. I use Maven for the same reason. I provide my students with a standard Maven pom.xml file that ensures that they all begin from the same baseline. Here is that pom.xml file.


<?xml version="1.0" encoding="UTF-8"?>

Every XML document should begin with an XML declaration. While the version has remained 1.0, it is the encoding attribute that is important. UTF-8 means that this document is encoded using the Unicode standard in which the first 128 characters map to Unicode. For more information on UTF-8, I recommend checking this out: http://www.fileformat.info/info/unicode/utf8.htm.


<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">

This is the root tag for this XML document. An XML document must be well-formed and valid. Well-formed means that all tags are properly nested and have a matching closing tag. Valid means that the tags and attributes you are using are part of this specific XML language. Testing for validity requires a schema and that is what is referred to in the root project tag. The schema is also used by the IDE to validate while you enter the tags into the file.


<!-- Maven version of the xml document currently only 4.0.0 is valid -->
<modelVersion>4.0.0</modelVersion>

As the comment says, it's always 4.0.0.


<!-- The GAV consists of an arbitrary descriptor that is usually in the
form of a reverse domain name. -->
<groupId>com.kfwebstandard</groupId>
 
<!-- This is the name given to the packaged build -->
<artifactId>KFWebStandardProject</artifactId>

Maven manages the thousands of libraries available by using the combination of the groupId and artifactId as a unique identifier for each library. This is commonly called the GAV. These are usually expressed using the naming convention for Java packages or as a reverse domain name but they can be anything. You’ll see a non-package name for the MySQL dependency. When a library is a dependency for a project, it is identified by this combination. This is also the directory in your local repository where this JAR or other file type is stored.


<!-- The version of the build. Any value is valid though a number and a
string are common. SNAPSHOT means a project under development. FINAL is commonly
used to refer to stable production version -->
<version> 0.0.1-SNAPSHOT</version>

The version number for a project is an important piece of data that you should be updating during development. Over time, there may be many versions of a project, and other developers may depend on a specific version. The most recent version may contain changes that could break the work of other developers who have a dependency on your work. Two special optional terms are SNAPSHOT for a project in development and FINAL for a production-ready project.


<!-- Default value is jar but may be war or ear -->
<packaging>war</packaging>

Select the appropriate archive packaging type, either JAR, WAR, or EAR.


<!-- The name given to the project. Unlike groupId and artifactId a name
may have spaces -->
<name>${project.artifactId}</name>

I like to use the artifactId for the project name but you can use any name you want. Notice that you can refer to a tag in the file using substitution notation and by appending it to the term ‘project’.


<!-- A description of the program -->
<description>Standard starting point for a JEE Web Profile application using Java Server Faces
    for students of Ken Fogel</description>

<!-- Identifies the programmer or programmers who worked on the project -->
<developers>
    <developer>
        <id>Enter your school id</id>
        <name>Enter your name</name>
        <email>Enter your email address</email>
    </developer>
</developers>

<!-- The company or organization that the programmer(s) work for -->
<organization>
    <name>Enter school name</name>
</organization>

The three sections above are used to identify you. They are optional if you are an amateur. If you are a professional they are mandatory.  


<!-- Global settings for the project. Settings can be accessed in the pom
by placing the tag name in ${...} ex. ${endorsed.dir} -->
<properties>
    <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>

Properties represent data that another section of the POM may need access to. This way this data can be stored in one place and not repeated where it is needed. Endorsed represents a directory where files with the same name and type as those present in the Java installation are stored. This way the file in the endorsed folder will be used rather than the one supplied in the Java installation. The encoding of the project appears here, as does the version of Java you will be using.


<!-- Dependencies listed here are usually bom or bill of materials-->
<!-- The bom lists all the child dependencies that could be used and -->
<!-- lists the current version number for each -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.jboss.arquillian</groupId>
            <artifactId>arquillian-bom</artifactId>
            <version>1.1.12.Final</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>org.jboss.shrinkwrap.resolver</groupId>
            <artifactId>shrinkwrap-resolver-bom</artifactId>
            <version>2.2.6</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

When using Arquillian there are numerous libraries that may be required. Exactly what they are may change from version to version. A dependencyManagement section refers to special files classified as a bill of materials that in turn will include the necessary Arquillian libraries.  


<!-- Dependencies are libraries that either must be included in the -->
<!-- jar/war file or are expected to be found in the container such as -->
<!-- GlassFish -->

As my comment above says, the dependencies are libraries that must be available to your project. They will all be downloaded to your local repository. This will allow your IDE to ensure you are using classes and methods from these libraries correctly. They are further classified by the scope tag. If there is no scope tag then the file associated with the dependency will be added to your final archive file. If the scope tag shows ‘provided’ then the required files are in the container you will be running in, such as GlassFish/Payara, and do not need to be added to your archive. If the scope is ‘test’ then the files are only added to an archive used for testing such as what Arquillian produces.


<dependencies>
    <!-- These dependencies are required to run the project on the server -->
    <!-- Java EE 7.0 Web profile dependency -->
    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-web-api</artifactId>
        <version>7.0</version>
        <scope>provided</scope>
    </dependency>

    <!-- MySQL dependency -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.40</version>
        <scope>provided</scope>
    </dependency>

    <!-- PrimeFaces dependency -->
    <dependency>
        <groupId>org.primefaces</groupId>
        <artifactId>primefaces</artifactId>
        <version>6.0</version>
    </dependency>

    <!-- EclipseLink dependency for the static metamodel generator -->
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
        <version>2.6.4</version>
        <scope>provided</scope>
    </dependency>

The metamodel generator dependency invokes a process to create meta model classes dynamically. This will allow you to use identifiers rather than strings in certain types of JPA queries so that refactoring (renaming) is supported.


<!-- These dependencies are required for testing only -->
<!-- Arquillian dependency for running tests on a remote GlassFish server -->
<dependency>
    <groupId>org.jboss.arquillian.container</groupId>
    <artifactId>arquillian-glassfish-remote-3.1</artifactId>
    <version>1.0.0.Final</version>
    <scope>test</scope>
</dependency>

<!-- Resolves dependencies from the pom.xml when explicitly referred to
in the Arquillian deploy method -->
<dependency>
    <groupId>org.jboss.shrinkwrap.resolver</groupId>
    <artifactId>shrinkwrap-resolver-depchain</artifactId>
    <type>pom</type>
    <scope>test</scope>
</dependency>

Not every required Arquillian dependency that my students need is in one of the two bom dependencyManagement entries so we need to add these two dependencies. If you are using Arquillian then you need these, too.


<!-- Connects Arquillian to JUnit -->
<dependency>
    <groupId>org.jboss.arquillian.junit</groupId>
    <artifactId>arquillian-junit-container</artifactId>
    <scope>test</scope>
</dependency>
 
<!-- JUnit dependency -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

<!-- Selenium dependencies -->
<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>htmlunit-driver</artifactId>
    <version>2.25</version>
    <scope>test</scope>
</dependency>

<!-- You will need a driver dependency for every browser you use in
testing. You can find the meven dependencies at
https://mvnrepository.com/artifact/org.seleniumhq.selenium -->
<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-chrome-driver</artifactId>
    <version>3.2.0</version>
    <scope>test</scope>
</dependency>

<!-- Normally you must download an exe for each browser. This library
will retrieve the the necessary file and place it in the classpath for
selenium to use -->
<dependency>
    <groupId>io.github.bonigarcia</groupId>
    <artifactId>webdrivermanager</artifactId>
    <version>1.6.0</version>
    <scope>test</scope>
</dependency>

For the first time, I will be having my students use Selenium as part of their testing. Selenium is actually an independent system that can even be placed in a Java desktop application. Here it will be run as a unit test. Selenium requires, in addition to the Java library, a standalone executable program to connect it to a specific browser. I found this extremely convenient library, webdrivermanager, that will download all the required executables and run them as required.


    <!-- A better way to write assertions -->
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>1.7.1</version>
        <scope>test</scope>
    </dependency>

    <!-- Selenium uses SLF4J and log4j so these dependencies are needed
    but just for test scope -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.23</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.8</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.8</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.8</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Selenium uses slf4j as the interface to log4j. That is why these dependencies are all listed as test scope. A student recently ran into a problem when a library they were using in their project also needed slf4j. The solution was to remove scope so that the libraries were added to both the production and test archive files.  


<!-- Information for compiling, testing and packaging are define here -->
<build>
    <testResources>
        <!-- Folders in the project required during testing -->
        <testResource>
            <directory>src/test/resources</directory>
        </testResource>
        <testResource>
            <directory>src/test/resources-glassfish-remote</directory>
        </testResource>
    </testResources>

Here in the build section of the pom.xml. Here you can identify files or folders that are required but are not part of a standard Maven project.  


<!-- Plugins are components that Maven uses for specific purposes beyond
the basic tasks -->
<plugins>
    <!-- Presence of this plugin suppress a warning about the existence 
    of the web.xml file -->
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.0.0</version>
    </plugin>

Plugins are modules that add features to Maven. The Maven war plugin is used when we wish to modify how the war file is built. It is not required for the projects my students build. Unfortunately, leaving this plugin out results in a warning that reads “Warning: selected war files include a WEB-INF/web.xml which will be ignored”. This statement is false and the project’s web.xml is properly processed. Adding this plugin without any settings eliminates the warning message.  


            <plugin>
                <!-- Executes JUnit tests and writes the results as an xml and
                txt file Test classes must include one of the following in their
                name: Test* *Test *TestCase -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19.1</version>
                <configuration>
                    <argLine>-Dfile.encoding=${project.build.sourceEncoding}</argLine>
                    <skipTests>false</skipTests>
                </configuration>
            </plugin>
        </pluginn>
    </build>
</project>

The Surefire plugin controls running tests, and it allows me to easily turn on or off testing with the skipTests tag.

Final Word

In preparing this article, I removed two plugins that proved to be redundant and removed unnecessary tags from a third. These plugins were there because the articles that I read in researching Maven showed them. These Maven entries continue to appear on StackOverflow and in other articles. May I suggest that when writing about Maven or answering a question you consider testing your code by removing plugins. Otherwise, we will propagate inflated Maven pom.xml files.

Here is the full 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">
    <!-- pom.xml February 2017 version 1.0 -->

    <!-- Maven version of the xml document currently only 4.0.0 is valid -->
    <modelVersion>4.0.0</modelVersion>

    <!-- The GAV consists of an arbitrary descriptor that is usually in the
    form of a reverse domain name. -->
    <groupId>com.kfwebstandard</groupId>

    <!-- This is the name given to the packaged build -->
    <artifactId>KFWebStandardProject</artifactId>

    <!-- The version of the build. Any value is valid though a number and a
    string are common. SNAPSHOT means a project under development. FINAL is commonly
    used to refer to stable production version -->
    <version>0.1</version>

    <!-- Default value is jar but may be war or ear -->
    <packaging>war</packaging>

    <!-- The name given to the project. Unlike groupId and artifactId a name
    may have spaces -->
    <name>${project.artifactId}</name>

    <!-- A description of the program -->
    <description>Standard starting point for a JEE Web Profile application using Java Server Faces
        for students of Ken Fogel</description>

    <!-- Identifies the programmer or programmers who worked on the project -->
    <developers>
        <developer>
            <id>Enter your school id</id>
            <name>Enter your name</name>
            <email>Enter your email address</email>
        </developer>
    </developers>

    <!-- The company or organization that the programmer(s) work for -->
    <organization>
        <name>Enter school name</name>
    </organization>

    <!-- Global settings for the project. Settings can be accessed in the pom
    by placing the tag name in ${...} ex. ${endorsed.dir} -->
    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <!-- Dependencies listed here are usually bom or bill of materials-->
    <!-- The bom lists all the child dependencies that could be used and -->
    <!-- lists the current version number for each -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.arquillian</groupId>
                <artifactId>arquillian-bom</artifactId>
                <version>1.1.12.Final</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>

            <dependency>
                <groupId>org.jboss.shrinkwrap.resolver</groupId>
                <artifactId>shrinkwrap-resolver-bom</artifactId>
                <version>2.2.6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- Dependencies are libraries that either must be included in the -->
    <!-- jar/war file or are expected to be found in the container such as -->
    <!-- GlassFish -->
    <dependencies>
        <!-- These dependencies are required to run the project on the server -->

        <!-- Java EE 7.0 Web profile dependency -->
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- MySQL dependency -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
            <scope>provided</scope>
        </dependency>

        <!-- PrimeFaces dependency -->
        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>6.0</version>
        </dependency>

        <!-- EclipseLink dependency for the static metamodel generator -->
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
            <version>2.6.4</version>
            <scope>provided</scope>
        </dependency>

        <!-- These dependencies are required for testing only -->

        <!-- Arquillian dependency for running tests on a remote GlassFish server -->
        <dependency>
            <groupId>org.jboss.arquillian.container</groupId>
            <artifactId>arquillian-glassfish-remote-3.1</artifactId>
            <version>1.0.0.Final</version>
            <scope>test</scope>
        </dependency>

        <!-- Resolves dependencies from the pom.xml when explicitly referred to
        in the Arquillian deploy method -->
        <dependency>
            <groupId>org.jboss.shrinkwrap.resolver</groupId>
            <artifactId>shrinkwrap-resolver-depchain</artifactId>
            <type>pom</type>
            <scope>test</scope>
        </dependency>

        <!-- Connects Arquillian to JUnit -->
        <dependency>
            <groupId>org.jboss.arquillian.junit</groupId>
            <artifactId>arquillian-junit-container</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- JUnit dependency -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- Selenium dependencies -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>htmlunit-driver</artifactId>
            <version>2.25</version>
            <scope>test</scope>
        </dependency>

        <!-- You will need a driver dependency for every browser you use in
        testing. You can find the meven dependencies at
        https://mvnrepository.com/artifact/org.seleniumhq.selenium -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-chrome-driver</artifactId>
            <version>3.2.0</version>
            <scope>test</scope>
        </dependency>

        <!-- Normally you must download an exe for each browser. This library
        will retrieve the the necessary file and place it in the classpath for
        selenium to use -->
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
            <version>1.6.0</version>
            <scope>test</scope>
        </dependency>

        <!-- A better way to write assertions -->
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>1.7.1</version>
            <scope>test</scope>
        </dependency>

        <!-- Selenium uses SLF4J and log4j so these dependencies are needed
        but just for test scope -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.23</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.8</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.8</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.8</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <!-- Information for compiling, testing and packaging are define here -->
    <build>
        <!-- Folders in the project required during testing -->
        <testResources>
            <testResource>
                <directory>src/test/resources</directory>
            </testResource>
            <testResource>
                <directory>src/test/resources-glassfish-remote</directory>
            </testResource>
        </testResources>

        <!-- Plugins are components that Maven uses for specific purposes beyond
        the basic tasks -->
        <plugins>
            <!-- Presence of this plugin suppress a warning about the existence
            of the web.xml file -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>

            <plugin>
                <!-- Executes JUnit tests and writes the results as an xml and
                txt file Test classes must include one of the following in their
                name: Test* *Test *TestCase -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19.1</version>
                <configuration>
                    <argLine>-Dfile.encoding=${project.build.sourceEncoding}</argLine>
                    <skipTests>false</skipTests>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>


Thrive in the application economy with an APM model that is strategic. Be E.P.I.C. with CA APM.  Brought to you in partnership with CA Technologies.

Topics:
maven ,java ,build management ,tutorial ,dependencies

Published at DZone with permission of Ken Fogel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}