Nuxeo Core Developer Guide

Maven Usage

Updated: March 18, 2024

Overview and Online Documentation

See http://maven.apache.org/users/index.html.

Maven is a software tool for Java project management and build automation, similar in functionality to the Apache Ant tool coupled to a dependency manager such as Ivy, but Maven is based on different concepts.

Maven uses a construct known as a Project Object Model (POM) to describe the software project being built, its dependencies on other external modules and components, and the build order. It comes with predefined targets for performing certain well defined tasks such as compilation of code and its packaging.

Maven is designed to be network-ready. It dynamically downloads libraries and plugins from one or more repositories. A local cache of downloaded artifacts acts as the primary means of synchronizing the requirements and outputs of projects on a local system.

Online Documentation

http://www.sonatype.com/books/maven-book/reference/

http://maven.apache.org/guides/

http://maven.apache.org/guides/introduction/

http://maven.apache.org/ref/2.1.0/maven-settings/settings.html

Nuxeo Documentation

In the knowledge base: How to debug test run with Maven.

Installing Maven

What is Maven?

Quoting the Wikipedia entry for Maven:

Maven is a software tool for Java programming language project management and automated software build. It is similar in functionality to the Apache Ant tool, but has a simpler build configuration model, based on an XML format. Maven is hosted by the Apache Software Foundation, where it was formerly part of the Jakarta Project.

Maven uses a construct known as a Project Object Model (POM) to describe the software project being built, its dependencies on other external modules and components, and the build order. It comes with predefined targets for performing certain well defined tasks such as compilation of code and its packaging.

A key feature of Maven is that it is network-ready. The core engine can dynamically download plugins from a repository, the same repository that provides access to many versions of different Open Source Java projects, from Apache and other organizations and developers. This repository and its reorganized successor the Maven 2 repository are the de facto distribution mechanism for Java applications. Maven provides built in support not just for retrieving files from this repository, but to upload artifacts at the end of the build. A local cache of downloaded artifacts acts as the primary means of synchronizing the output of projects on a local system.

Nuxeo is fully "Maven managed".

Nuxeo holds a Maven repository here: http://maven.nuxeo.org/.

Installing Maven

Since Nuxeo 5.9.2, Maven 3.1.1+ is required (3.3+ recommended). Maven 2.2.1 is required up to Nuxeo 5.9.1.

You should install Maven on your development box by downloading the latest archive from http://maven.apache.org/download.html and then extract the archive into /opt (for instance).

As usual, you have to put the mvn executable into the path of your environment, include the bin/ sub-directory in your PATH by adding in your .bashrc(or .profile, .zprofile, ...) something like:

export M2_HOME=/opt/apache-maven-3.3.3/
export PATH=/opt/apache-maven-3.3.3/bin:$PATH

In a new shell you can then test:

$ mvn --version
Apache Maven 3.3.3 (...)
Maven home: /opt/apache-maven-3.3.3
Java version: 1.8.0_51, vendor: Oracle Corporation

Maven relies on JAVA_HOME rather than PATH for Java.

Configure Repositories

Nuxeo uses Sonatype Nexus OSS for storing its artifacts at https://maven.nuxeo.org/.

You will find there a lot of "group", "hosted" and "proxy" repositories:

There are three solutions for enabling your Maven build to use Nuxeo repositories:

  • add public and public-snapshot repositories in your POM:
<repositories>
  <repository>
    <id>public</id>
    <url>http://maven.nuxeo.org/nexus/content/groups/public</url>
    <releases>
      <enabled>true</enabled>
    </releases>
    <snapshots>
      <enabled>false</enabled>
    </snapshots>
  </repository>
  <repository>
    <id>public-snapshot</id>
    <url>http://maven.nuxeo.org/nexus/content/groups/public-snapshot</url>
    <releases>
      <enabled>false</enabled>
    </releases>
    <snapshots>
      <updatePolicy>always</updatePolicy>
      <enabled>true</enabled>
    </snapshots>
  </repository>
</repositories>

  • add public and public-snapshot repositories in your $M2_REPO/settings.xml: The same sentence must be put inside a profile.
<?xml version="1.0"?>
<settings>
  <profiles>
    <profile>
      <id>Nuxeo</id>
      <repositories>
        <repository>
          <id>public</id>
          <url>http://maven.nuxeo.org/nexus/content/groups/public</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
        <repository>
          <id>public-snapshot</id>
          <url>http://maven.nuxeo.org/nexus/content/groups/public-snapshot</url>
          <releases>
            <enabled>false</enabled>
          </releases>
          <snapshots>
            <updatePolicy>always</updatePolicy>
            <enabled>true</enabled>
          </snapshots>
        </repository>
      </repositories>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>Nuxeo</activeProfile>
  </activeProfiles>
</settings>

  • define mirrors in your $M2_REPO/settings.xml: If your company is hosting its own Maven repository manager with proxies to Nuxeo repositories, you can define that mirroring in your Maven settings.
<?xml version="1.0"?>
<settings>
  <!-- Bypass POM information to avoid remote repositories and use internal ones instead -->
  <mirrors>
    <mirror>
      <id>public-releases-mirror</id>
      <name>public releases mirror</name>
      <url>http://your.company.com/.../proxy-to-nuxeo-release</url>
      <mirrorOf>public</mirrorOf>
    </mirror>
    <mirror>
      <id>public-snapshots-mirror</id>
      <name>public snapshots mirror</name>
      <url>http://your.company.com/.../proxy-to-nuxeo-snapshot</url>
      <mirrorOf>public-snapshot</mirrorOf>
    </mirror>
  </mirrors>
</settings>

If you are behind an HTTP proxy, see http://maven.apache.org/settings.html#Proxies.

Configure Hotfix Repositories

Nuxeo customers have access to two additional repositories where the "hotfix" artifacts are deployed: hotfix-releases-group and hotfix-snapshots. Contact the Nuxeo support to get an account.

Add the repositories in your POM:

<repositories>
 <!-- Hotfix Repositories For Nuxeo Customers Only -->
  <repository>
    <id>hotfix-releases</id>
    <url>https://maven.nuxeo.org/nexus/content/groups/hotfix-releases-group/</url>
    <releases>
      <enabled>true</enabled>
    </releases>
    <snapshots>
      <enabled>false</enabled>
    </snapshots>
  </repository>
  <repository>
    <id>hotfix-snapshots</id>
    <url>https://maven.nuxeo.org/nexus/content/repositories/hotfix-snapshots</url>
    <releases>
      <enabled>false</enabled>
    </releases>
    <snapshots>
      <updatePolicy>always</updatePolicy>
      <enabled>true</enabled>
    </snapshots>
  </repository>
</repositories>

Set your credentials in your $M2_REPO/settings.xml:

  <!-- In case of redirection error, for instance, from nuxeo.org to nuxeo-us instead of nuxeo-eu -->
  <!--
  <mirrors>
    <mirror>
      <id>hotfix-releases-eu</id>
      <name>hotfix releases EU</name>
      <url>https://maven-eu.nuxeo.org/nexus/content/groups/hotfix-releases-group</url>
      <mirrorOf>hotfix-releases</mirrorOf>
    </mirror>
    <mirror>
      <id>hotfix-snapshots-eu</id>
      <name>hotfix snapshots EU</name>
      <url>https://maven-eu.nuxeo.org/nexus/content/repositories/hotfix-snapshots</url>
      <mirrorOf>hotfix-snapshots</mirrorOf>
    </mirror>
  </mirrors>
  -->

  <servers>
    <server>
      <id>hotfix-releases</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <id>hotfix-snapshots</id>
      <username>****</username>
      <password>****</password>
    </server>
  </servers>

Configuring Plugin Repositories

Maven plugins are considered as special artifacts: their repositories must also be configured. Follow exactly the same instructions as above replacing repositories/repository sections with pluginRepositories/pluginRepository.

Configure Eclipse with Maven

The maven-eclipse-plugin provides a goal (eclipse:configure-workspace) for automatically configuring Eclipse with Maven but it's easy to manually configure: in Eclipse, go to Preferences/Java/Build Path/Classpath Variables, click New, name M2_REPO and browse to your local Maven repository (usually ~/.m2/repository).

Maven usage for LTS 2021

Nuxeo Platform LTS2021 artifacts are private. Consequently, you'll need to create a support ticket to request access to the Nuxeo Central Repository, https://packages.nuxeo.com/.

Once your Okta account is assigned the “Sonatype Nexus“ application, go to https://auth.nuxeo.com and click on the application.

You should then connect to packages using the “single sign-on” feature and then:

  1. Click on its username,
  2. Click on User Token menu,
  3. Push the button Access user token to generate a token to download the artifacts from Maven.

user-token-maven.png
user-token-maven.png

You'll be able to get your credentials for the XML file:

Use the following in your Maven settings.xml:
<server>
<id>${server}</id>
  <username><MY_NEXUS_USERNAME></username>
  <password><MY_NEXUS_PASSWORD></password>
</server>

Use the following for a base64 representation of "user:password":
<MY_BASE_64_REPRESENTATION_CREDENTIALS>

Finally, update your POM file with the credentials you've just generated.

Generate a New Project with the Nuxeo-Archetype-Start Archetype

This is currently deprecated

The goal of the nuxeo-archetype-start template is to setup a development environment to work on a Nuxeo Platform plugin.

The default code provides: a Maven layout for sources, tests and dependencies, a Ant target for deployment. It also customizes the web application a little bit.

To create a project named my-project in the com.company.sandbox.myproject package:

mvn org.apache.maven.plugins:maven-archetype-plugin:1.0-alpha-7:create \
-DartifactId=my-project \
-DgroupId=com.company.sandbox.myproject \
-DarchetypeArtifactId=nuxeo-archetype-start \
-DarchetypeGroupId=org.nuxeo.archetypes \
-DarchetypeVersion=5.1.6 \
-DremoteRepositories=http://maven.nuxeo.org/nuxeo-release

You can see that you need to supply six arguments:

  • artifactId : the name of your project, usually with '-' to separate the words if there are many.
  • groupId : the domain name of your project. Usually it is the package parent name of your classes, you should use '.' to separate the words ('-' is not supported here).
  • archetypeArtifactId : the Maven archetype artifact id. To generate a new project, Nuxeo provides you with nuxeo-archetype-start
  • archetypeGroupId : unique for all Nuxeo Maven archetypes : org.nuxeo.archetypes
  • archetypeVersion : the version of the archetype which is equivalent to the version of Nuxeo Platform without the _GA_ or _RC_ part. (5.1.6, 5.1.5 ...).
  • remoteRepositories : the repository location to download the archetype.

Decrease Build Time

No Clean

Maven is able to decrease build time but it relies on target content for this. If you don't "clean" the target directory, only out of date modules are rebuilt. Issue is not cleaning the target directory may lead to strange results because of deprecated uncleaned resources.

The incremental-build-plugin provides a solution for this: checking the target content during the Maven validation phase, it deletes the target directory when it's needed.

This plugin is automatically activated when using Nuxeo corporate POM (org.nuxeo:nuxeo-ecm since version 5.4.0-SNAPSHOT).

You don't need to "clean" anymore while building sources.

Note that when using for instance mvn install, even if a module is not rebuilt, its target content is installed; which is a good thing for SNAPSHOT consistency but leads to a consequent build time even if there's nothing to recompile.

Multi-Thread

Maven 3 enables multi-threaded builds. Of course, it requires the activated plugins being thread-safe, as well as the Unit Tests code. See below for -T option details.

No SNAPSHOT Updates and Offline Mode

See related -o and -nsu options below.

Skip Tests

Skip all tests execution with -DskipTests(Unit Tests and Integration Tests).

Skip only the Integration Tests execution with -DskipITs.

Skip only the Unit Tests execution with -Dskip.surefire.tests=true.

Avoid use of -Dmaven.test.skip=true which also skips the tests compilation.

In the Maven Surefire Test Framework:

In the Nuxeo Test Framework:

  • There is no difference about the Unit Tests except that they can act like Integration Tests: "unit testing" multiple modules at once, at a higher level than usual per-module Unit Tests. That advanced behavior may disturb analysis tools (like Cobertura, Jacoco, SonarQube...) and require a specific configuration to get relevant coverage reports (see org.nuxeo:nuxeo-ecm POM).
  • Integration Tests designate Functional and Performance Tests.
  • Selenium, FunkLoad and other custom Integration Tests are managed by the ant-assembly-maven-plugin which mimics maven-failsafe-plugin behaviors, honoring its main properties and output format.
  • WebDriver Integration Tests are managed by the maven-failsafe-plugin; the ant-assembly-maven-plugin acts only during pre-integration-test and post-integration-test phases for the environment set up and tear down.

Basic Maven Options

Non Recursive

-N,--non-recursive                     Do not recurse into sub-projects

Offline Mode

-o,--offline                           Work offline

Activate/Deactivate Profiles

-P,--activate-profiles <arg>           Comma-delimited list of profiles to activate

This can also be used to deactivate a profile by preceding the profile to deactivate with a - (dash).

Force Artifacts Update

-U,--update-snapshots                  Forces a check for updated releases and snapshots on remote repositories

Nuxeo default snapshots update policy is "always", so you need this option only if you changed this policy.

No SNAPSHOT updates

 -nsu,--no-snapshot-updates             Suppress SNAPSHOT updates

Advanced Maven Options

Projects list (-pl)

-pl,--projects <arg>                   Build specified reactor projects instead of all projects

For instance, the following will build only modules A and B even if there are others in the modules list of your POM:

mvn install -pl A,B

Useful for building a restricted modules list.

Also Make (Upstream Dependencies)

-am,--also-make                        If project list is specified, also build projects required by the list

For instance, the following will build all modules required by A and B, then A and B:

mvn install -pl A,B -am

Useful for building only modules that may impact the restricted modules list you are currently working on.

Also Make (Downstream) Dependencies

-amd,--also-make-dependents            If project list is specified, also build projects that depend on projects on the list

For instance, the following will build A and B, then all modules depending on A or B:

mvn install -pl A,B -amd

Useful to check collateral impacts of changes you've done on a restricted modules list.

Resume From

-rf,--resume-from <arg>                Resume reactor from specified project

Maven will skip modules until it reaches the named module. Useful when your build failed for some reason you've fixed and you don't want to replay useless previous builds.

Multi-Threading

 -T,--threads <arg>                     Thread count, for instance 2.0C where C is core multiplied

Errors and Debug

-e,--errors                            Produce execution error messages
-X,--debug                             Produce execution debug output

There are two possible errors raised by the Maven plugins: MojoExecutionException and MojoFailureException. A MojoExecutionException is a fatal exception, stopping the build; whereas a MojoFailureException is more like a warning for which you can customize the build result with the following options. Those options are common to all plugins and must be preferably used to plugins' specific options such as -Dmaven.test.haltafterfailure=true or -Dmaven.test.failure.ignore=true.

-fae,--fail-at-end                     Only fail the build afterwards; allow all non-impacted builds to continue

Regarding the tests, with -fae option, Maven will remember the failing tests and skip their downstream (dependent) modules but only report the failures after the whole build. This is more or less equivalent to the maven.test.haltafterfailure Surefire option.

-ff,--fail-fast                        Stop at first failure in reactorized builds
-fn,--fail-never                       NEVER fail the build, regardless of project result

Tips and Workarounds

Do not use activeByDefault - Use Activation by the Absence of a Property

The activeByDefault profile configuration parameter should never be used, instead rely on the activation by the absence of a property as a workaround.

No

     <profile>
       <id>defaultProfile</id>
       <activation>
         <activeByDefault>true</activeByDefault>
       </activation>
     </profile>

mvn help:active-profiles shows that the profile defaultProfile is implicitly deactivated if any other profile is active (which is more than frequent), making the behavior variable, depending on the environment and the POM content.

mvn help:active-profiles -PanotherProfile will implicitly deactivate the "(expected to be) active by default profile" defaultProfile!

Yes

     <profile>
       <id>defaultProfile</id>
       <activation>
         <property>
           <name>!skipDefaultProfile</name>
         </property>
       </activation>
     </profile>

mvn help:active-profiles shows that the profile is always activated unless being explicitly deactivated.

mvn help:active-profiles -P-defaultProfile or mvn help:active-profiles -DskipDefaultProfile both explicitly deactivate the skipDefaultProfile as intuitively expected.

Explanations

The activeByDefault profile configuration parameter should never be used because it is counter-intuitive and actually useless: it activates only if no other profile is activate (explicitly, through Maven settings, based on configuration...). Finally, such a profile is never activated when we want.

The documentation states:

This profile will automatically be active for all builds unless another profile in the same POM is activated using one of the previously described methods (explicitly, through Maven settings, based on environment variables, OS settings, present or missing files).

According to MNG-4917: Profile not active even though it has activeByDefault set to true, this issue won't be fixed as it is not a bug but only a useless counter-intuitive feature.

Fix maven-eclipse-plugin Warning about Workspace's vm (Mac OS X Only)

Because of indefinitely unresolved issue MECLIPSE-668, Mac OS X users get the following warning message when running mvn eclipse:eclipse command: [WARNING] Workspace defines a VM that does not contain a valid jre/lib/rt.jar: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home

Apply that workaround:

cd /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
sudo mkdir -p jre/lib
sudo ln -s /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/classes.jar jre/lib/rt.jar

Common settings.xml File for Nuxeo Core Developers

settings.xml

<settings>
  <!--
  <mirrors>
    <mirror>
      <id>public-releases-mirror</id>
      <name>public releases mirror</name>
      <url>http://mavenin.nuxeo.com/nexus/content/groups/public</url>
      <mirrorOf>public</mirrorOf>
    </mirror>
    <mirror>
      <id>public-snapshots-mirror</id>
      <name>public snapshots mirror</name>
      <url>http://mavenin.nuxeo.com/nexus/content/groups/public-snapshot</url>
      <mirrorOf>public-snapshot</mirrorOf>
    </mirror>
  </mirrors>
  -->
  <servers>
    <!-- See http://maven.apache.org/guides/mini/guide-encryption.html for password encryption -->
    <server>
      <id>nightly-staging</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <id>public-releases</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <!-- private releases -->
      <id>releases</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <id>vendor-releases</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <id>public-snapshots</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <!-- internal snapshots -->
      <id>daily-snapshots</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <!-- private snapshots -->
      <id>snapshots</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <id>vendor-snapshots</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <id>nuxeo-studio</id>
      <username>****</username>
      <password>****</password>
    </server>
    <server>
      <id>maven-website</id>
      <username>mavenweb</username>
    </server>
  </servers>
  <profiles>
    <profile>
      <id>NXP</id>
      <repositories>
        <repository>
          <id>public</id>
          <url>http://maven.nuxeo.org/nexus/content/groups/public</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
        <repository>
          <id>public-snapshot</id>
          <url>http://maven.nuxeo.org/nexus/content/groups/public-snapshot</url>
          <releases>
            <enabled>false</enabled>
          </releases>
          <snapshots>
            <updatePolicy>always</updatePolicy>
            <enabled>true</enabled>
          </snapshots>
        </repository>
      </repositories>
      <properties>
        <connect.password>****</connect.password>
      </properties>
    </profile>
    <profile>
      <id>qa</id>
      <activation>
        <activeByDefault>false</activeByDefault>
      </activation>
      <repositories>
        <repository>
          <id>internal-releases</id>
          <url>http://mavenin.nuxeo.com/nexus/content/groups/internal-releases</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
        <repository>
          <id>internal-snapshots</id>
          <url>http://mavenin.nuxeo.com/nexus/content/groups/internal-snapshots</url>
          <releases>
            <enabled>false</enabled>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>always</updatePolicy>
          </snapshots>
        </repository>
      </repositories>
      <properties>
        <connect.password>****</connect.password>
      </properties>
    </profile>
    <profile>
      <!-- No SNAPSHOT Updates (useless Since Maven 3.1.1 which provides a -nsu option) -->
      <id>qansu</id>
      <repositories>
        <repository>
          <id>internal-snapshots</id>
          <url>http://mavenin.nuxeo.com/nexus/content/groups/internal-snapshots</url>
          <releases>
            <enabled>false</enabled>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </repository>
        <repository>
          <id>public-snapshot</id>
          <url>http://maven.nuxeo.org/public-snapshot</url>
          <releases>
            <enabled>false</enabled>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </repository>
      </repositories>
    </profile>
    <profile>
      <id>qapriv</id>
      <activation>
        <activeByDefault>false</activeByDefault>
      </activation>
      <repositories>
        <repository>
          <id>private-releases</id>
          <url>https://mavenpriv.nuxeo.com/nexus/content/groups/private-releases</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
        <repository>
          <id>private-snapshots</id>
          <url>https://mavenpriv.nuxeo.com/nexus/content/groups/private-snapshots</url>
          <releases>
            <enabled>false</enabled>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>always</updatePolicy>
          </snapshots>
        </repository>
      </repositories>
    </profile>
    <profile>
      <id>release</id>
      <activation>
        <activeByDefault>false</activeByDefault>
      </activation>
      <properties>
        <keystore.path>****</keystore.path>
        <keystore.password>****</keystore.password>
        <keystore.alias>****</keystore.alias>
        <connect.password>****</connect.password>
      </properties>
    </profile>
    (...)
  </profiles>
  <activeProfiles>
    <activeProfile>qa</activeProfile>
  </activeProfiles>
  <pluginGroups>
    <pluginGroup>org.nuxeo.build</pluginGroup>
    <pluginGroup>org.jvnet.hudson.tools</pluginGroup>
    <pluginGroup>org.jenkins-ci.tools</pluginGroup>
    <pluginGroup>com.versioneye</pluginGroup>
  </pluginGroups>
</settings>

Build and Use a Test-Jar with Maven

If you want to expose your test class to be reused in other test classes of projects you can add the following code at the end of your pom.xml:

pom.xml

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>test-jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

If you don't want to include the manifest file of your bundle to use the one defined in your test resources folder, you can add the following code in the execution tag of your pom:

<configuration>
   <archive>
      <manifestFile>src/test/resources/META-INF/MANIFEST.MF</manifestFile>
   </archive>
</configuration>

The aim is to not build a test jar containing the manifest of the main bundle, because the manifest will probably defined a "Nuxeo-Component" item pointing to a service xml file that you don't embedded in the test jar. This will cause the following error when you will deploy your test bundle with maven:

Unknown component 'OSGI-INF/your-service.xml' referenced by bundle 'your.bundle.id'

Then, to use this test package in your code you must define a dependency like this:

<dependency>
      <groupId>the.group.id</groupId>
      <artifactId>the-artifact-name</artifactId>
      <type>test-jar</type>
      <scope>test</scope>
</dependency>