ImageJ, Fiji and other SciJava projects use Maven for their project infrastructure.
Maven artifacts are published to the ImageJ Maven repository.
- 1 Why do we use Maven?
- 2 Introduction
- 3 What does it take to make a new Maven project?
- 4 How to find a dependency's groupId/artifactId/version (GAV)?
- 5 Depending on libraries outside the core repositories
- 6 Further reading
Why do we use Maven?
- We need something to take source code and package it into a useable format, i.e. jar files.
- Maven organizes dependencies for us, declaring those dependencies… as opposed to manually tracking each individual piece-of-the-puzzle.
- Maven is a central storage location for all developers, providing the same tools to many folks.
- Maven has an established concept of immutable release and development versioning – both of which are essential for reproducible science.
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.
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.
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
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:
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
snakeyaml, with available versions ranging from
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.
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:
- If the dependency is itself an ImageJ plugin, consider contributing it to Fiji. Plugins distributed with Fiji are made available as Maven artifacts, and thus will benefit both users and developers.
- If the dependency is narrower in scope, you could contact the ImageJ & Fiji maintainers to get your needed dependency added to the ImageJ Maven repository. Note that you will then be responsible for distributing the dependency with your code—so ensure it is licensed appropriately.
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
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.