Example reactor project

Consider an ordinary multi-module reactor build:

|-- pom.xml
|-- fooUI
|   `-- pom.xml
|-- barBusinessLogic
|   `-- pom.xml
`-- bazDataAccess
    `-- pom.xml

Suppose project "fooUI" depends on project "barBusinessLogic", which depends on project "bazDataAccess".

fooUI --> barBusinessLogic --> bazDataAccess

Ordinarily, when you run mvn install from my-root-project, you'll first build bazDataAccess, then barBusinessLogic, then fooUI.

Resuming the build with reactor:resume

Suppose you're working on your code and you attempt to build your code with mvn install from my-root-project, and suppose you get a test failure in barBusinessLogic. You make additional changes to barBusinessLogic without changing bazDataAccess; you know that bazDataAccess is fine, so there's no need to rebuild/test it. You can then use reactor:resume, like this:

mvn reactor:resume -Dfrom=barBusinessLogic

That will skip over bazDataAccess and pick up the build where you left off in barBusinessLogic. If barBusinessLogic succeeds, it will go on to build fooUI.

Making barBusinessLogic without building fooUI using reactor:make

Suppose you're a developer working on barBusinessLogic; you don't want to work on fooUI right now, but just want to get a working build of barBusinessLogic. You can use reactor:make, like this:

mvn reactor:make -Dmake.folders=barBusinessLogic

reactor:make will examine barBusinessLogic and walk down its dependency tree, finding all of the projects that it needs to build. In this case, it will automatically build bazDataAccess and then barBusinessLogic, without building fooUI.

Changing barBusinessLogic and verifying you didn't break anything using reactor:make-dependents

Suppose you've made a change to barBusinessLogic; you want to make sure you didn't break any of the projects that depend on you. (In this case, you want to make sure that you didn't break fooUI, but in a more complex reactor that might not be so obvious.) You also want to avoid rebuilding/testing projects that you know you haven't changed. In this case, you want to avoid building bazDataAccess. You can use reactor:make-dependents, like this:

mvn reactor:make-dependents -Dmake.folders=barBusinessLogic

reactor:make-dependents will examine all of the projects in your reactor to find projects that depend on barBusinessLogic, and automatically build those and nothing else. In this case, it will automatically build barBusinessLogic and then fooUI.

Building anything that you personally have changed using reactor:make-scm-changes

Suppose you've made a change to some source files in barBusinessLogic but you've forgotten what you've changed. You want to make sure you're safe to check-in, and that you haven't broken any projects that depend on you. You can use reactor:make-scm-changes, like this:

mvn reactor:make-scm-changes

reactor:make-scm-changes determines which files have changed using your SCM (Source Configuration Management) tool, e.g. Subversion, Perforce, Git, etc. To use it, you'll need to configure an SCM connection in your root project POM file:


The reactor plugin will use Maven's built-in SCM providers to attempt to figure out which files you've modified. (By default, it will ignore "unknown" files: files that have been created in a source directory but which haven't been explicitly added to source control.)

Once the reactor plugin computes the list of changed files, it will figure out which of those files correspond to which projects in the reactor, and basically do a reactor:make-dependents on those projects that contain changed files.

In this example, if you only modified files in barBusinessLogic, then running reactor:make-scm-changes is equivalent to running reactor:make-dependents -Dmake.folders=barBusinessLogic -- it builds barBusinessLogic and anything that depends on barBusinessLogic (in this case, it will build fooUI).

If you find that reactor:make-scm-changes is doing somthing you don't expect, try running Maven in debug mode with --debug or -X like this:

mvn reactor:make-scm-changes -X

You'll see a log of all the changed files and a depiction of how we computed which projects to pass to reactor:make-dependents.

Doing a "dry run" with the reactor plugin

All of the reactor plugin goals take in an argument -Dmake.printOnly that you can use to see what the goal would have done without actually doing it. For example:

> mvn reactor:make -Dmake.folders=barBusinesslogic -Dmake.printOnly
[INFO] Scanning for projects...
[INFO] [reactor:make]
[INFO] Executing: /Users/danfabulich/svn/redfin/buildtools/maven/bin/mvn -B -N -r -D maven.reactor.includes=bazDataAcecss/pom.xml,barBusinessLogic/pom.xml install
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------

Running a different goal/lifecycle ("test", "package", "eclipse:eclipse", "clean", etc.)

By default, all of the reactor plugin goals will run mvn install on the appropriate projects. That's a pretty reasonable default, but sometimes you want to run a different command on a bunch of projects. All of the reactor plugin goals will accept a -Dmake.goals argument that will let you run other goals instead. You can separate multiple goals with commas:

mvn reactor:make -Dmake.folders=barBusinessLogic -Dmake.goals=eclipse:eclipse
mvn reactor:make-dependents -Dmake.folders=barBusinessLogic -Dmake.goals=package,clean
mvn reactor:resume -Dmake.folders=barBusinessLogic -Dmake.goals=test

Skipping tests and passing flags to the spawned Maven

The reactor plugin launches a second copy of Maven to do its magic. This copy of Maven doesn't necessarily have all of the flags and options that you passed to your first copy of Maven, including the --debug flag, system properties, and -DskipTests.

You can pass additional arguments to the spawned Maven by treating them as goals with -Dmake.goals, like this:

mvn reactor:resume -Dmake.folders=barBusinessLogic -Dmake.goals=install,-DskipTests

In other words, the "goals" are just extra command-line parameters passed to the spawned Maven; they don't necessarily have to be "goals."

If you want to get really fancy, you may prefer to just dry run the reactor plugin in -Dmake.printOnly mode, described above. That will print out the command that the plugin would have used to build, but you can tweak that command line to your heart's content!

Resuming a "make" build

When you use reactor:make, you run a subset of projects, but that doesn't mean stuff won't fail halfway through the build. You can resume a reactor:make build from the project that stopped the build by passing -Dfrom to the reactor:make goal, like this:

mvn reactor:make -Dmake.folders=fooUI -Dfrom=barBusinessLogic

The -Dfrom argument also works with reactor:make-dependents and reactor:make-scm-changes.

Nested directories

Let's consider a more complex project:

|-- pom.xml
|-- fooUI
|   `-- pom.xml
|-- barBusinessLogic
|   `-- pom.xml
|-- quz
|   |-- pom.xml
|   |-- quzAdditionalLogic
|   |   `-- pom.xml
|   `-- quzUI
|       `-- pom.xml
`-- bazDataAccess
    `-- pom.xml

Again suppose project "fooUI" depends on project "barBusinessLogic", which depends on project "bazDataAccess".

fooUI --> barBusinessLogic --> bazDataAccess

But furthermore, suppose "quzUI" depends on "quzAdditionalLogic", which depends on "barBusinessLogic."

quzUI --> quzAdditionalLogic --> barBusinessLogic --> bazDataAccess

If you try to run mvn reactor:make -Dmake.folders=quzUI, you'll get an error:

> mvn reactor:make -Dmake.folders=quzUI
[INFO] Folder doesn't exist: /home/person/svn/trunk/quzUI

Naturally, you'll have to specify the complete relative path to quzUI, like this:

> mvn reactor:make -Dmake.folders=quz/quzUI

That would build everything in the reactor except "fooUI". Note that in this project, if you ran mvn reactor:make-dependents -Dmake.folders=barBusinessLogic it would build everything in the reactor except bazDataAccess.

Identifying projects by artifact, instead of by folder name

Sometimes projects can be nested pretty far down the tree; it can be easier in that case to just identify them by their artifactId instead of by their full relative path. Assuming that the artifactId for "quzUI" is just "quzUI", you can do this:

mvn reactor:make -Dmake.artifacts=com.mycompany:quzUI

or, if you want to resume:

mvn reactor:resume -DfromArtifact=com.mycompany:quzAdditionalLogic

If quzUI has the same groupId as the root POM, you can even omit the "com.mycompany:" groupId and just say:

mvn reactor:make -Dmake.artifacts=quzUI