Maven



Development
Topics
Overview
Philosophy
Architecture
Source code
Project management
Coding style
Debugging
Tools
GitHub
Git
Maven
IDEs
Travis
AppVeyor
Dotfiles
Guides
Writing plugins
ImageJ Ops
Contributing to a plugin
Distributing your plugins
Development lifecycle
Building a POM
Developing with Eclipse
Hands-on debugging
Adding new ops
Adding new formats
Using native libraries
Tips for developers
Tips for C++ developers
ImageJ 1.x plugins
Versioning
Logging
Uber-JARs

Apache Maven is a convention over configuration build automation tool.
ImageJ, Fiji and other SciJava projects use Maven for their project infrastructure.

Maven artifacts are published to the ImageJ Maven repository.

Introduction

Maven is a powerful tool to build Java projects and to manage their dependencies. It can build dependencies from sources, but if the sources are not available, it will look into Maven repositories from which to download the dependencies.

Example: let's assume that you want to build a new plugin for ImageJ 1.x that builds on, say, the 3D Viewer and commons-math. You do not want to rebuild them from scratch unless you need to debug issues that are suspect bugs in said components. This is where Maven comes in: you tell it that the dependencies are ImageJ 1.x, 3D Viewer and commons-math and what version(s) you require. It is Maven's job to find and get them, no matter whether you just built them locally or not.

Many convenient IDEs (integrated development environments) including Eclipse, NetBeans and IntelliJ support Maven projects; therefore, using Maven is an excellent choice when trying to let every developer choose their preferred development environment.

What does it take to make a new Maven project?

POM and directory structure

All it really takes is a pom.xml file and a certain directory structure:

pom.xml
src/
   main/
       java/
           <package>/
                    <name>.java
                    ...
       resources/
                <other-files>
                ...


Technically, you can override the default directory layout in the pom.xml, but why do so? It only breaks expectations and is more hassle than it is worth, really.

So the directory structure is: you put your .java files under src/main/java/ and the other files you need to be included into src/main/resources/. Should you want to apply the best practices called "regression tests" or even "test-driven development": your tests' .java files go to src/test/java/ and the non-.java files you might require unsurprisingly go into src/test/resources/.

So what does a pom.xml look like? This is a very simple example:

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

  <groupId>org.mywebsite</groupId>
  <artifactId>my-uber-library</artifactId>
  <version>2.0.0-SNAPSHOT</version>
</project>

The first 6 lines are of course just a way to say "Hi, Maven? How are you today? This is what I would like you to do...".

The only relevant parts are the groupId, which by convention is something like the inverted domain name (similar to the Java package convention), the name of the artifact to build (it will be put into target/, under the name <artifactId>-<version>.jar). And of course the version.

Dependencies

Maven is not only a build tool, but also a dependency management tool.

To depend on another library, you must declare the dependencies in your project's pom.xml file. For example, every ImageJ 1.x plugin will depend on ImageJ 1.x. So let's add that (before the final </project> line):

<dependencies>
  <dependency>
    <groupId>net.imagej</groupId>
    <artifactId>ij</artifactId>
    <version>1.45b</version>
  </dependency>
</dependencies>

As you can see, dependencies are referenced using the same groupId, artifactId and version triplet (also known as GAV parameters) that you declared for your project itself.

Repositories

Once your dependencies are declared, Maven will download them on demand from the Internet. However, for Maven to find the dependencies, it has to know where to look.

Out of the box, Maven will look in the so-called Maven Central repository. Some ImageJ and SciJava components are deployed there, including the pom-scijava parent POM which declares important metadata, such as the Bill of Materials: current artifact versions intended to work together.

However, many other SciJava and ImageJ components are not yet deployed to Maven Central, but instead to the ImageJ Maven repository. To gain access to this repository from your project, add the following configuration block to your pom.xml:

<repositories>
  <!-- NB: for project parent -->
  <repository>
    <id>imagej.public</id>
    <url>http://maven.imagej.net/content/groups/public</url>
  </repository>
</repositories>

As a rule of thumb: components versioned at 0.x are deployed to the ImageJ Maven repository, while those at 1.x or later are deployed to Maven Central.

Releases and snapshots

There are two different sorts of Maven artifacts (i.e., JAR files): releases and snapshots. The snapshot versions are "in-progress" versions. If you declare a dependency with a -SNAPSHOT suffix in the version, Maven will look once a day for new artifacts of the same versions; otherwise, Maven will look whether it has that version already and not bother re-downloading.

Producing multiple JAR files

So what if you have multiple .jar files you want to build in the same project? Then these need to live in their own subdirectories and there needs to be a common parent POM, a so-called aggregator or multi-module POM (only this POM needs to have the SciJava POM as parent, of course). Here is an example. Basically, it is adding the <packaging>pom</packaging> entry at the top, as well as some subdirectory names to the <modules> section.

Note, however, that most projects of the SciJava component collection (e.g., SciJava, ImgLib2, SCIFIO, ImageJ and Fiji) now structure each component as its own single-module project in its own Git repository, since using multi-module projects can complicate versioning.

Convention over configuration

There are many more things you can do with Maven, but chances are you will not need them.

The simplicity of the pom.xml you need comes from the fact that Maven defines implicit defaults. It calls that convention over configuration. For many reasons, it is strongly recommended to stay with the defaults as much as possible.

In the context of SciJava, you will most likely never write a pom.xml from scratch. You will rather more likely edit an existing one, possibly after having copied it. We recommend using the ImageJ "Load and Display a Dataset" tutorial as a starting point.

How to find a dependency's groupId/artifactId/version (GAV)?

Most popular open source libraries upon which you might want to depend are stored in the Maven Central repository. However, the ImageJ and Fiji JARs are not yet stored there, but in the ImageJ Maven repository. Fortunately, you can search both at once, by visiting:

 http://maven.imagej.net/

For example, let's suppose you want to depend on the snakeyaml library. Typing "snakeyaml" into the search box at maven.imagej.net tells us to use a groupId of org.yaml, artifactId of snakeyaml, with available versions ranging from 1.4 to 1.10. In the case of many results, you can click the "Drill down" link to view more details of that specific GAV combination. You can also click an entry to get a formatted dependency block for direct copy-pasting into your POM.


Tip: If your dependencies are in Maven Central, you can use the quickdeps tool to quickly generate dependency blocks, by scanning your project's bytecode.


Depending on libraries outside the core repositories

If you need to depend on a library that is not present in either Maven Central or the ImageJ Maven repository, first double check the project's web site for any documentation on using their library with Maven. They might provide their own public Maven repository which you could use instead (by adding a <repository> to the <repositories> section of your POM).

If there are no public repositories containing your dependency, you have two options:

Finally, for local testing you can install the dependency into your local Maven repository cache yourself. The command is mvn install:install-file. For example, if you have a library foo.jar to install, you could run:

 mvn install:install-file -Dfile=/path/to/foo.jar -DgroupId=org.foo \
   -DartifactId=foo -Dversion=1.0.0 -Dpackaging=jar

For the groupId, it is typically best to use the reversed domain name of the library's web site. For libraries that are not explicitly versioned, you may want to use a datestamp such as "20120920" for the version, rather than inventing your own versioning scheme.

WARNING: If you use install:install-file, others will not be able to build your code unless they also use install:install-file to install the library on their systems.

When in doubt, contact the community with your questions.

Further reading