4. Maven Project Basics
Maven provides conventions and a standard directory layout for all of its projects. As discussed in Chapter 1, this standardization provides a uniform build interface, and it also makes it easy for developers to jump from one project to another. This chapter will explain the basics of a Maven project and the pom.xml file.
Basic Project Organization
The best way to understand Maven project structure is to look at one. Figure 4-1 illustrates a bare-bones Maven-based Java project.
Figure 4-1 Maven Java project structure
Now let’s look at each of the components in the project:
-
The
gswmis the root folder of the project. Typically, the name of the root folder matches the name of the generated artifact. -
The
srcfolder contains project-related artifacts such as source code or property files, which you typically would like to manage in a source control management (SCM) system, such as SVN or Git. -
The
src/main/javafolder contains the Java source code. -
The
src/test/javafolder contains the Java unit test code. -
The
targetfolder holds generated artifacts, such as.classfiles. Generated artifacts are typically not stored in SCM, so you don’t commit the target folder and its contents into SCM. -
The
LICENSE.txtfile contains license information related to project. -
The
README.txtfile contains information/instructions about the project. -
The
NOTICE.txtfile contains notices required by third-party libraries used by this project. -
Every Maven project has a
pom.xmlfile at the root of the project. It holds project and configuration information, such as dependencies and plug-ins.
In addition to the src/main and src/test directories, Maven recommends several other directories to hold additional files and resources. Table 4-1 lists those directories along with the content that goes into them.
Table 4-1 Maven Directories
| Directory Name | Description |
|---|---|
| src/main/resources | Holds resources, such as Spring configuration files and velocity templates, that need to end up in the generated artifact. |
| src/main/config | Holds configuration files, such as Tomcat context files, James Mail Server configuration files, and so on. These files will not end up in the generated artifact. |
| src/main/scripts | Holds any scripts that system administrators and developers need for the application. |
| src/test/resources | Holds configuration files needed for testing. |
| src/main/webapp | Holds web assets such as .jsp files, style sheets, and images. |
| src/it | Holds integration tests for the application. |
| src/main/db | Holds database files, such as SQL scripts. |
| src/site | Holds files required during the generation of the project site. |
Maven provides archetypes (as discussed in Chapter 6) to bootstrap projects quickly. However, in this chapter, you will manually assemble a Maven-based Java project. Use the instructions that follow to create the project:
-
Using a command line, go to the folder where you would like to create the project. In this book, we assume that directory to be c:\apress\gswm-book\chapter4.
-
Run the command mkdir gswm .
-
cd into the newly created directory and create an empty pom.xml file.
-
Create the src directory under gswm, then create the main directory in src, and finally create the java directory under main.
The starting project structure should resemble that shown in Figure 4-2
Figure 4-2 Starting project structure
Understanding the pom.xml File
The pom.xml file is the most important file in a Maven project. As we have discussed so far in the book, the pom.xml file holds the configuration information needed by Maven. Listing 4-1 shows the pom.xml file with the basic project information. We start the pom.xml file with the project element. Then we provide the groupId, artifactId, and version coordinates. The packaging element tells Maven that it needs to create a JAR archive for this project. Finally, we use the developers element to add information about the developers who are working on this project.
<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>
<groupId>com.apress.gswmbook</groupId>
<artifactId>gswm</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Getting Started with Maven</name>
<url>http://apress.com</url>
<developers>
<developer>
<id>balaji</id>
<name>Balaji Varanasi</name>
<email>balaji@inflinx.com</email>
<properties>
<active>true</active>
</properties>
</developer>
<developer>
<id>sudha</id>
<name>Sudha Belida</name>
<email>sudha@inflinx.com</email>
<properties>
<active>true</active>
</properties>
</developer>
</developers>
</project>
Listing 4-1 pom.xml File Configuration
We will be looking at other elements in the pom.xml file later in this chapter and throughout the rest of the book.
Maven Versioning
It is recommended that Maven projects use the following conventions for versioning:
<major-version>.<minor-version>.<incremental-version>-qualifier
The major, minor, and incremental values are numeric, and the qualifier can have values such as RC, alpha, beta, and SNAPSHOT. Some examples that follow this convention are 1.0.0, 2.4.5-SNAPSHOT, 3.1.1-RC1, and so forth.
The SNAPSHOT qualifier in the project’s version carries a special meaning. It indicates that the project is in a development stage. When a project uses a SNAPSHOT dependency, every time the project is built, Maven will fetch and use the latest SNAPSHOT artifact.
Most repository managers accept release builds only once. However, when you are developing an application in a continuous integration environment, you want to build often and push your latest build to the repository manager. Thus, it is the best practice to suffix your version with SNAPSHOT during development.
Building a Project
Before we look at building a project, let’s add the HelloWorld Java class under src/main/java folder. Listing 4-2 shows the code for the HelloWorld class .
public class HelloWorld {
public void sayHello() {
System.out.print("Hello World");
}
}
Listing 4-2 Code for HelloWorld Java Class
Figure 4-3shows the project structure after adding the class.
Figure 4-3 Project structure with Java class added
Now you’re ready to build the application, so let’s run the mvn package from gswm. You should see output similar to that shown in Listing 4-3.
C:\apress\gswm-book\chapter4\gswm>mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------------------------------------
[INFO] Building Getting Started with Maven 1.0.0-SNAPSHOT
[INFO] --------------------------------------------------------
...................
[INFO] Compiling 1 source file to C:\apress\gswm-book\chapter4\gswm\target\classes
...................
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ gswm ---
[INFO] Building jar: C:\apress\gswm-book\chapter4\gswm\target\gswm-1.0.0-SNAPSHOT.jar
[INFO] --------------------------------------------------------
[INFO] BUILD SUCCESS
Listing 4-3 Output for Maven Package Command for Building the Application
Note: If this is your first time running Maven, it will download the plug-ins and dependencies required for it to run. Thus, your first build might take longer than you would expect.
The package suffix after the mvn command is a Maven phase that compiles Java code and packages it into the JAR file. The packaged JAR file ends up in the gswm\target folder, as shown in Figure 4-4.
Figure 4-4 Packaged JAR located under the target folder
Testing the Project
Now that you have completed the project build, let’s add a JUnit test that tests the sayHello() method. Let’s start this process by adding JUnit dependency to the pom.xml file. You accomplish this by using the dependencies element. Listing 4-4 shows the updated pom.xml file with JUnit 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>
<groupId>com.apress.gswmbook</groupId>
<artifactId>gswm</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Getting Started with Maven</name>
<url>http://apress.com</url>
<developers>
<developer>
<id>balaji</id>
<name>Balaji Varanasi</name>
<email>balaji@inflinx.com</email>
<properties>
<active>true</active>
</properties>
</developer>
<developer>
<id>sudha</id>
<name>Sudha Belida</name>
<email>sudha@inflinx.com</email>
<properties>
<active>true</active>
</properties>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Listing 4-4 Updated POM with JUnit Dependency
Notice that you have used the scope test, indicating that the JUnit .jar is needed only during the testing phase. Let’s make sure that this dependency has been successfully added by running mvn dependency:tree in the command line. Listing 4-5 shows the output of this operation.
C:\apress\gswm-book\chapter4\gswm>mvn dependency:tree
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ gswm ---
[INFO] com.apress.gswmbook:gswm:jar:1.0.0-SNAPSHOT
[INFO] \- junit:junit:jar:4.12:test
[INFO] \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] --------------------------------------------------------
[INFO] BUILD SUCCESS
Listing 4-5 Maven Tree Command Output
The tree goal in the dependency plug-in displays the project’s dependencies as tree. Notice that the JUnit dependency pulled in a transitive dependency named hamcrest, which is an open source project that makes it easy to write matcher objects.
Now that you have the JUnit dependency in the class path, let’s add a unit test HelloWorldTest.java to the project. Create the folders test/java under src and add HelloWorldTest.java beneath it. The updated project structure is shown in Figure 4-5
Figure 4-5
Maven structure with test class
The source code for HelloWorldTest is shown in Listing 4-6
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class HelloWorldTest {
private final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
@Before
public void setUp() {
System.setOut(new PrintStream(outStream));
}
@Test
public void testSayHello() {
HelloWorld hw = new HelloWorld();
hw.sayHello();
Assert.assertEquals("Hello World", outStream.toString());
}
@After
public void cleanUp() {
System.setOut(null);
}
}
Listing 4-6 Code for HelloWorldTest Java Class
You now have everything set up in this project, so you can run the mvn package one more time. After you run it, you will see an output similar to that shown in Listing 4-7
C:\apress\gswm-book\chapter4\gswm>mvn package
[INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ gswm ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ gswm ---
---------------------------------------------------------------
[INFO] Surefire report directory: C:\apress\gswm-book\chapter4\gswm\target\surefire-reports
---------------------------------------------------------------
T E S T S
---------------------------------------------------------------
Running HelloWorldTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.038 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ gswm ---
[INFO] Building jar: C:\apress\gswmbook\chapter4\gswm\target\gswm-1.0.0-SNAPSHOT.jar
[INFO] --------------------------------------------------------
[INFO] BUILD SUCCESS
Listing 4-7 Output for Maven Command for Building the Project
Note the Tests section in Listing 4-7. It shows that Maven has run the test and that it has successfully completed.
Figure 4-6 shows the updated target folder. You can see that you now have a test-classes folder with their associated reports in that folder.
Figure 4-6 Target folder with test classes
Properties in pom.xml
Maven provides properties AKA placeholders that can be used inside pom.xml file. Maven properties are referenced in pom.xml file using the ${property_name} notation. There are two types of properties – implicit and user-defined properties.
IMPLICIT PROPERTIES
Implicit properties are properties that are available by default to any Maven project. For example, Maven exposes its Project Object Model properties using the “project.” prefix. To access the artifactId value inside the pom.xml file, you can use the ${project. artifactId} as shown in the following:
<build>
<finalName>${project.artifactId}</finalName>
</build>
Simillarly, to access properties from settings.xml file, you can use the “settings.” prefix. Finally, the “env.” prefix can be used to access environment variable values. For example, ${env.PATH} will return the value of PATH environment variable.
USER-DEFINED PROPERTIES
Maven allows you to declare custom properties in the pom.xml file using the <properties /> element. These properties are highly useful for declaring dependency versions. Listing 4-8 shows the updated pom.xml file with the JUnit version declared as a property. This is especially useful when pom.xml has a lot of dependencies and you need to know or change a version of a particular 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>
<groupId>com.apress.gswmbook</groupId>
<!-- Removed for brevity -->
<properties>
<junit.version>4.12</junit.version>
</properties>
<developers>
<!-- Removed for brevity -->
</developers>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Listing 4-8 pom.xml File with Properties
Summary
Maven’s CoC prescribes a standard directory layout for all of its projects. It provides several sensible directories such as src\main\java and src\test, along with recommendations on the content that goes into each one of them. You learned about the mandatory pom.xml file and some of its elements, which are used to configure Maven project’s behavior.
In the next chapter, you will look at Maven’s lifecycle, plug-ins, build phases, goals, and how to leverage them effectively.