Maven Tips: What I Wish I Had Been Told from the Start

Maven is still very much alive, but still suffers from a serious lack of understanding. It can appear very daunting at first, and going through the documentation does not do much to make that feeling go away. Here are some tips that I would have loved being told when I first started using Maven.

A Quick Introduction

I won’t go into too much details (you probably already know about Maven’s dependency management) here, but will quickly describe what I think is important to know before using Maven. Maven helps you build a project. The way it does that is through the build lifecycle and the plugins.

The lifecycle is made of phases that you can call explicitly on the command line, for example:

mvn package

package is a phase part of the default build lifecycle, like compile or deploy. All the phases of the default lifecycle can be found in the reference. At each phase, Maven calls a goal in a plugin that does something for you. For example, the maven-compiler-plugin has a compile goal that compiles your java code during the compile phase of the lifecycle. You can also explicitly call a plugin on the command line, like:

mvn clean:clean

which calls the clean goal on the maven-clean-plugin. By default this goal is bound to the clean phase, so you can call it by executing mvn clean. You can call any plugin by using its group id, its artifact id, its version and the goal you want to execute, e.g.:

mvn org.codehaus.mojo:versions-maven-plugin:2.1:set -DnewVersion=1.2.3

(A plugin named blah-maven-plugin can be called by the shortened version of its name, blah. See also the Guide to Developing Java Plugins)

To “bind” a plugin goal to a phase, you just need to define your plugin in your pom.xml, and define its execution to a phase of the lifecycle:

      <plugin>
        <groupId>org.mirah.maven</groupId>
        <artifactId>maven-mirah-plugin</artifactId>
        <version>1.1-SNAPSHOT</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals><goal>compile</goal></goals>
          </execution>
        </executions>
      </plugin>

Here we are attaching the mirah compile goal to the compile phase of the lifecycle. When Maven executes the compile phase, it will the compile the Mirah code for us.

That’s pretty much the most important stuff you need to understand to get going with Maven: build lifecycle and plugin goals. Once you understand this, the hardest part is to find the plugin that does what you want, and going through its documentation to see how to configure it.

Here are the Tips!

In no particular order.

How to find a plugin goals?

Use the help goal for that plugin, for example:

mvn dependency:help

You can also use the @help@ plugin:
mvn help:describe -Dplugin=de.saumya.mojo:rake-maven-plugin:1.0.0-rc3

How to get completion on the command line?

Check out this project in GitHub. After a while it becomes impossible to do without.

How to find a project dependencies?

To list all the dependencies of the project:

mvn dependency:list

To display a tree of the transitive dependencies of the project:

mvn dependency:tree

How to download dependencies’ sources?

 mvn dependency:sources

How to copy dependencies locally?

Use:

mvn dependency:copy-dependencies -DoutputDirectory=/tmp

This copies all the dependencies into the /tmp folder. To do this automatically during the package phase, add the plugin to your pom, as follows:

      <plugin>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.8</version>
        <executions>
          <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}</outputDirectory>
              <overWriteReleases>false</overWriteReleases>
              <overWriteSnapshots>false</overWriteSnapshots>
              <overWriteIfNewer>true</overWriteIfNewer>
            </configuration>
          </execution>
        </executions>
      </plugin>

See the maven-dependency-plugin page for more details.

How to remove the dependencies from the local repo?

mvn dependency:purge-local-repository

By default, purge-local-repository will then re-resolve your dependencies. If you don’t want that behaviour, add -DreResolve=false on the command line.

This is particularly handy when, for some reason, Maven is choking on a dependency that was temporarily unavailable, stubbornly refusing to download it again.

How to find a dependency?

To find the coordinates of a dependency, you can use http://mvnrepository.com. You can also use a ruby gem I have written, called mvnizer. Once installed (gem install mvnizer), you can easily search for existing artefacts:

$ mvnizer search slf4j-simple
  org.slf4j:slf4j-simple:1.7.5:jar
  com.googlecode.sli4j:sli4j-slf4j-simple:2.0:jar

How to add a dependency?

You can copy/paste the coordinates from mvnrepository. You can also use mvnizer:

mvnizer add org.slf4j:slf4j-simple:1.7.5:jar

Beware, mvnizer applies its own formatting to your pom file.

How to update dependencies to their latest versions?

mvn versions:use-latest-releases

See versions-maven-plugin page for more details.

How to see if there are recent versions of the plugins you use?

mvn versions:display-plugin-updates

See versions-maven-plugin page for more details.

How to change the version of my project?

Use:

mvn versions:set -DnewVersion=1.2.3

Generally speaking, for anything that deals with the version of your project (and its submodules) or its dependencies, use versions-maven-plugin.

How to add locations for code or tests?

Use build-helper-maven-plugin. Example:

         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <version>1.8</version>
            <executions>
              <execution>
                <id>add-source</id>
                <phase>generate-sources</phase>
                <goals>
                  <goal>add-test-source</goal>
                </goals>
                <configuration>
                  <sources>
                    <source>src/it/java</source>
                  </sources>
                </configuration>
              </execution>
            </executions>
          </plugin>

How to store properties in external files?

Use properties-maven-plugin. Example:

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>properties-maven-plugin</artifactId>
        <version>1.0-alpha-2</version>
        <executions>
          <execution>
            <phase>initialize</phase>
            <goals>
              <goal>read-project-properties</goal>
            </goals>
            <configuration>
              <files>
                <file>example.properties</file>
              </files>
            </configuration>
          </execution>
        </executions>
      </plugin>

Properties can then be stored in example.properties as key/value pairs:

key1=value1
key2=value2

How to set the Main-Class in the MANIFEST file?

Use the maven-jar-plugin.

      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <mainClass>org.example.YourMainClass</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>

How to create a standalone executable jar (i.e. an über-jar)?

Use the maven-shade-plugin. Example:

        <plugin>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.0</version>
        <configuration>
          <transformers>
            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
              <mainClass>org.example.YourMainClass</mainClass>
            </transformer>
          </transformers>
          <artifactSet>
            <excludes>
              <exclude>rubygems:*</exclude>
            </excludes>
          </artifactSet>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

How to change the name of my artefact (name of war or jar)?

Use finalName:

<build>
    <finalName>new-name</finalName>
 . . .
</build>

How to start a jetty server for your integration tests?

Bind the run goal of the jetty-maven-plugin to the pre-integration-test phase, and bind stop to the post-integration-test phase:

 <plugin>
  <groupId>org.mortbay.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <version>8.1.14.v20131031</version>
  <configuration>
    <stopKey>ungeduldig</stopKey>
    <stopPort>9995</stopPort>
  </configuration>
  <executions>
    <execution>
      <id>start-jetty</id>
      <phase>pre-integration-test</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <daemon>true</daemon>
      </configuration>
    </execution>
    <execution>
      <id>stop-jetty</id>
      <phase>post-integration-test</phase>
      <goals>
        <goal>stop</goal>
      </goals>
    </execution>
  </executions>
</plugin>
 
---

Comment

  1. A great post, summarizing a lot of otherwise spare information.

    Alberto Cabello Sanchez · 2014-01-13 13:29 · #

  2. Your explanation on the build process is awesome.
    Thanks for sharing!

    Leon · 2015-06-05 00:14 · #

your_ip_is_blacklisted_by sbl.spamhaus.org

---