1. Home
  2. Tutorials
  3. Maven
Yolinux.com Tutorial

Maven Configuration And Use:

How to use Apache Maven to build a Java application

Description:

Maven is a build system to compile application artifacts for a Java based application. Apache Maven is different from Apache Ant in that it uses convention over configuration. Maven assumes a Java project directory layout for source, tests, etc rather than explicitly specifying the layout as one does in Apache Ant.

Maven also automatically pulls in dependencies for the Java project from http://central.maven.org/maven2/, allowing one to focus on the CM and management of source code rather than library versions and library dependencies. The specific version of the Java Jar file can be specified and easily upgraded. This dependency management helps reduce the practice of holding Jar file dependencies in a CM system (ie Git, Subversion, CVS) which is best used to manage changes in text files and not binary files.

Maven is directed by a configuration file called pom.xml. This may also be distributed in a project hierarchy where a "parent" pom file calls subsequent pom.xml files lower in the build hierarchy. Maven also has default targets which perform tasks defined by convention. All operations can be modified and expanded with more detail. This is also in contrast to Ant which requires one to define all targets and behavior.

Maven Path Conventions:
Project ArtifactMaven Path
Java SourceApplications: src/main/java/com/megacorp/projectx/...
Servlets:
  • src/main/webapp/WEB-INF/pages/index.jsp
  • src/main/webapp/WEB-INF/web.xml
Unit Testssrc/test/java/com/megacorp/projectx/...
Compiled CodeFrom source: target/classes/com/megacorp/projectx/...
Unit tests: target/test-classes/com/megacorp/projectx/...
Build Artifactseg JAR file:
mvn package: target/projectx-1.0-SNAPSHOT.jar
mvn install: $HOME/.m2/repository/com/megacorp/projectx/projectx/1.0-SNAPSHOT/projectx-1.0-SNAPSHOT.jar

Maven has implicit targets which are steps in its "Lifecycle". The targets are executed in the following order:
Maven TargetDescription
validatevalidate the project is correct and all necessary information is available
compilecompile the source code of the project
testtest the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed
packagetake the compiled code and package it in its distributed format, such as a JAR.
mvn package : Generates projectx/target/projectx-1.0-SNAPSHOT.jar
verifyrun any checks on results of integration tests to ensure quality criteria are met
installinstall the package into the local repository, for use as a dependency in other projects locally.
mvn install : compiles and runs tests. copies jar file to $HOME/.m2/repository/com/megacorp/projectx/projectx/1.0-SNAPSHOT/projectx-1.0-SNAPSHOT.jar
deploydone in the build environment, copies the final package to the remote repository for sharing with other developers and projects.
cleancleans up artifacts created by prior builds.
sitegenerates site documentation for this project.

The target is inclusive of all prior to it. Thus "mvn install" will execute targets validate, compile, package, verify and install.

This tutorial describes how to configure a "Maven" build for a Java application.

Installation:

Maven itself is a Java application and requires the installation of Java. See our Java on Linux tutorial.

Download Maven from the Apache website: Maven Download apache-maven-3.5.3-bin.zip

Install Maven:
  • cd /opt
  • sudo unzip ~/Downloads/apache-maven-3.5.3-bin.zip
Add paths and environment variables to your ~/.bashrc file:
if [ -d /usr/java/latest ]
then
  export JAVA_HOME=/usr/java/latest
  export JDK_HOME=$JAVA_HOME
  export CLASSPATH=$JAVA_HOME/lib/tools.jar:./
fi

if [ -d /opt/apache-maven-3.5.3/bin/ ]
then
  PATH=/opt/apache-maven-3.5.3/bin:$PATH
fi

Try the built-in Maven example:

Maven will generate an example "Hello World" Maven project with the following command:
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false -DgroupId=com.megacorp.projectx -DartifactId=projectx

This will generate the following:
  • Default Maven build file: projectx/pom.xml
  • Hello World default program: projectx/src/main/java/com/megacorp/projectx/App.java
  • JUnit test driver: projectx/src/test/java/com/megacorp/projectx/AppTest.java
File: projectx/pom.xml
<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>
  <groupId>com.megacorp.projectx</groupId>
  <artifactId>projectx</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>projectx</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Build project: mvn package
  • cd projectx
  • mvn package

This will generate target/projectx-1.0-SNAPSHOT.jar

Run: mvn exec:java -Dexec.mainClass="com.megacorp.projectx.App" (with loads of Maven messages) or java -cp target/projectx-1.0-SNAPSHOT.jar com.megacorp.projectx.App
Result: Hello World!

Run tests: mvn verify (with loads of Maven messages)

...
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.megacorp.projectx.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.008 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
...

Using a hierarchy of Maven POM files to build individual JARS:

Top parent POM file: pom.xml Maven configuration file:

<?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>
       <name>ProjectX Parent POM</name>
       <groupId>com.megacorp.projectx</groupId>
       <artifactId>projectx</artifactId>
       <version>1.0-SNAPSHOT</version>
       <packaging>pom</packaging>
       <build>
         <plugins>
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-surefire-plugin</artifactId>
             <version>2.20</version>
             <configuration>
               <redirectTestOutputToFile>true</redirectTestOutputToFile>
             </configuration>
           </plugin>
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-compiler-plugin</artifactId>
             <version>3.0</version>
             <configuration>
               <compilerArgument>${compilerArgument}</compilerArgument>
             </configuration>
           </plugin>
         </plugins>
       </build>

       <properties>
         <skipTests>true</skipTests>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
         <maven.jar.plugin>2.3.2</maven.jar.plugin>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <log4j.version>1.2.16</log4j.version>
         <metainf-services.version>1.6</metainf-services.version>
         <slf4j.version>1.6.1</slf4j.version>
         <spring.version>4.0.5.RELEASE</spring.version>
         <junit.version>4.12</junit.version>
       </properties>
    
       <modules>
         <module>lib/MyLibX</module>
         <module>lib/MyLibY</module>
         <module>lib/MyLibZ</module>
       </modules>

       <dependencyManagement>
             <dependency>
                 <groupId>org.springframework</groupId>
                 <artifactId>spring-context</artifactId>
                 <version>${spring.version}</version>
             </dependency>
             <dependencies>
                 <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-api</artifactId>
                 <version>${slf4j.version}</version>
             </dependency>
             <!--Test-->
             <dependency>
                 <groupId>junit</groupId>
                 <artifactId>junit</artifactId>
                 <version>${junit.version}</version>
             </dependency>
             <dependency>
                 <groupId>org.hamcrest</groupId>
                 <artifactId>hamcrest-core</artifactId>
                 <version>1.3</version>
             </dependency>
       </dependencyManagement>

</project>
Note:
  • The directive "<packaging>pom</packaging>" states the the parent pom is just responsible for calling other pom files.
  • The "child" pom Maven modules in hierarchy:
    • projectx/lib/MyLibX/pom.xml
    • projectx/lib/MyLibY/pom.xml
    • projectx/lib/MyLibZ/pom.xml
    Where MyLibX has the following:
    • projectx/lib/MyLibX/README.txt
    • projectx/lib/MyLibX/src/main/java/com/megacorp/...
    • projectx/lib/MyLibX/src/test/java/com/megacorp/...

Child POM: projectx/lib/MyLibX/pom.xml

<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>
        <groupId>com.megacorp.projectx</groupId>
        <artifactId>projectx</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>
    
    <groupId>com.megacorp.lib</groupId>
    <artifactId>MyLibX</artifactId>
    <version>1.10.0</version>
    <packaging>jar</packaging>

  <dependencies>
    <dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<scope>test</scope>
    </dependency>
    <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
            <scope>test</scope>
        </dependency>
  </dependencies>

</project>
Note:
  • <parent> : relativePath to parent pom.xml
  • Show full hierarchy from parent directory: mvn dependency:display-ancestors
  • Jar files are downloaded from the Maven repository to your local cache in $HOME/.m2/ for example the JUnit jar gets downloaded to: $HOME/.m2/repository/junit/junit/4.12/junit-4.12.jar
Breaking from Maven directory conventions:

Maven convention: projectx/src/com/megacorp/projectx/App.java

To do this acknowledge default then modify/over-ride the default:

<project>
    ...
    <properties>
        <src.dir>src/main/java</src.dir>
    </properties>
    <build>
        <sourceDirectory>${src.dir}</sourceDirectory>
        ...
    </build>
    <profiles>
        <profile>
            <id>projectx</id>
            <properties>
                <src.dir>${project.build.directory}/src</src.dir>
            </properties>
        </profile>
    </profiles>
</project>

or specify on the command line: mvn -DtestSourceDirectory=src install


Specify an alternate target directory:

<properties>
  <deploy.path>/opt/apache-tomcat-6.0.33/webapps/projectx.war</deploy.path>
</properties>

Maven and GIT SCM:

We can look at an existing GitHub library example: https://github.com/fiji/Jama/blob/master/pom.xml

...

        <ciManagement>
                <system>Jenkins</system>
                <url>http://jenkins.imagej.net/job/Jama/</url>
        </ciManagement>

        <scm>
                <connection>scm:git:git://github.com/fiji/Jama</connection>
                <developerConnection>scm:git:git@github.com:fiji/Jama</developerConnection>
                <tag>HEAD</tag>
                <url>https://github.com/fiji/Jama</url>
        </scm>

...

Proxies:

The modern corporate network environment increasingly makes use of the proxy gateway which requires local SSL certificates so that the gateway can examine the contents for viruses and malware. This also makes it difficult to use use https for anything other than the web browser. Maven often gets stuck in this complicated mess and the easiest way out is to use http rather than https. The following configuration is to point Maven to the proxy gateway and to use http to access the public Maven repository.

Edit the Maven global configuration file /opt/apache-maven-3.5.3/conf/settings.xml or your local user configuration ~/.m2/settings.xml:
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <proxies>
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <host>proxy.megacorp.com</host>
      <port>80</port>
      <nonProxyHosts>*.megacorp.com|localhost</nonProxyHosts>
    </proxy>
  </proxies>

  <profiles>
    <profile>
      <id>http-override</id>
      <!--Override the default https repository (and pluginRepository) from the Maven Super POM -->
      <repositories>
        <repository>
          <id>central</id>
          <url>http://central.maven.org/maven2</url>
          <releases>
            <enabled>true</enabled>
          </releases>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>central</id>
          <url>http://central.maven.org/maven2</url>
          <releases>
            <enabled>true</enabled>
          </releases>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>

  <activeProfiles>
    <activeProfile>http-override</activeProfile>
  </activeProfiles>
</settings>

Links:

Book imageBooks:

"Maven Essentials"
by Prabath Siriwardena
ISBN # 178398676X, Packt Publishing

For Java developers

Amazon.com