10

I have a MOJO I would like executed once, and once only after the test phase of the last project in the reactor to run.

Using:

if (!getProject().isExecutionRoot()) {
        return ;
}

at the start of the execute() method means my mojo gets executed once, however at the very beginning of the build - before all other child modules.

Rich Seller
  • 83,208
  • 23
  • 172
  • 177
npellow
  • 1,985
  • 1
  • 16
  • 23
  • Let me make sure I understand you. You have a parent project and some children. You want this mojo to run after test in the last child project. Right? – sblundy Sep 25 '08 at 12:48
  • Also, do you need to guarantee that it runs? – sblundy Sep 25 '08 at 12:50
  • 1
    ~9 years later... for Maven 3x see @Konrad Windszus answer re: `org.apache.maven.AbstractMavenLifecycleParticipant` – earcam Jul 31 '17 at 17:45

8 Answers8

10

The best solution I have found for this is:

/**
 * The projects in the reactor.
 *
 * @parameter expression="${reactorProjects}"
 * @readonly
 */
private List reactorProjects;

public void execute() throws MojoExecutionException {

    // only execute this mojo once, on the very last project in the reactor
    final int size = reactorProjects.size();
    MavenProject lastProject = (MavenProject) reactorProjects.get(size - 1);
    if (lastProject != getProject()) {
        return;
    }
   // do work
   ...
}

This appears to work on the small build hierarchies I've tested with.

npellow
  • 1,985
  • 1
  • 16
  • 23
  • +1 This actually works, but it looks like a workaround to me. Mojo is still executed for each project which is reported by maven. That's a tiny detail when you've got 5 projects, but becomes a flaw when you've got lots. – Krzysztof Jabłoński Apr 10 '13 at 13:28
  • I think the real solution is to make a Maven extension as @KonradWindszus suggests in another answer – Francois Marot Jul 04 '18 at 08:05
3

The best solution is relying on a lifecycle extension by extending your class from org.apache.maven.AbstractMavenLifecycleParticipant (see also https://maven.apache.org/examples/maven-3-lifecycle-extensions.html) which got a method afterSessionEnd added with https://issues.apache.org/jira/browse/MNG-5640 (fixed in Maven 3.2.2).

Konrad Windszus
  • 241
  • 3
  • 7
2

There is a Sonatype blog entry that describes how to do this. The last project to be run will be the root project as it will contain module references to the rest. Thereforec you need a test in your mojo to check if the current project's directory is the same as the directory from where Maven was launched:

boolean result = mavenSession.getExecutionRootDirectory().equalsIgnoreCase(basedir.toString());

In the referenced entry there is a pretty comprehensive example of how to use this in your mojo.

Rich Seller
  • 83,208
  • 23
  • 172
  • 177
1

I think you might get what you need if you use the @aggregator tag and bind your mojo to one of the following lifecycle phases:

  • prepare-package
  • package
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install
  • deploy
Brian Matthews
  • 8,506
  • 7
  • 46
  • 68
  • That may work, however, the MOJO needs to be executed after the test phase. ie: mvn test should run all the tests in all sub-modules and then run the MOJO of the execution root. – npellow Sep 26 '08 at 00:03
  • Also, watch out for @aggregator when binding to lifecycle phases. "When bound to a lifecycle, an aggregator mojo can have some nasty side-effects. It can force the execution of the ... lifecycle phase to execute ahead of time, and can result in builds which end up executing the ... phase twice." [ref](http://www.sonatype.com/books/mvnref-book/reference/assemblies-sect-basics.html) – seanf Dec 13 '11 at 06:48
  • +1 Your answer actually saved me a lot of time. Thanks for mentioning @aggregator tag. – Krzysztof Jabłoński Apr 10 '13 at 13:30
1

The solution with using session.getEventDispatcher() no longer works since Maven 3.x. The whole eventing has been removed in this commit: https://github.com/apache/maven/commit/505423e666b9a8814e1c1aa5d50f4e73b8d710f4

jkdev
  • 11,360
  • 15
  • 54
  • 77
Konrad Windszus
  • 241
  • 3
  • 7
  • Upvoted but this is not an answer - should be a comment or an edit or @Konrad Windszus other answer (which is what I'd been trying to find for ages and is valid in Maven 3.5.0) – earcam Jul 31 '17 at 17:42
0

Check out maven-monitor API

You can add an EventMonitor to the dispatcher, and then trap the END of the 'reactor-execute' event: this is dispatched after everything is completed, i.e. even after you see the BUILD SUCCESSFUL/FAILED output.

Here's how I used it recently to print a summary right at the end:

/**
 * The Maven Project Object
 *
 * @parameter expression="${project}"
 * @required
 * @readonly
 */
protected MavenProject project;


/**
 * The Maven Session.
 *
 * @parameter expression="${session}"
 * @required
 * @readonly
 */
protected MavenSession session;

...


@Override
public void execute() throws MojoExecutionException, MojoFailureException
{
    //Register the event handler right at the start only
    if (project.isExecutionRoot())
        registerEventMonitor();
    ...
}


/**
 * Register an {@link EventMonitor} with Maven so that we can respond to certain lifecycle events
 */
protected void registerEventMonitor()
{
    session.getEventDispatcher().addEventMonitor(
            new EventMonitor() {

                @Override
                public void endEvent(String eventName, String target, long arg2) {
                    if (eventName.equals("reactor-execute"))
                        printSummary();
                }

                @Override
                public void startEvent(String eventName, String target, long arg2) {}

                @Override
                public void errorEvent(String eventName, String target, long arg2, Throwable arg3) {}


            }
    );
}


/**
 * Print summary at end
 */
protected void printSummary()
{
    ...
}
Cornel Masson
  • 1,352
  • 2
  • 17
  • 33
0

you can use mavensession to solve this

public boolean isThisTheLastProject() {
        return session.getProjectDependencyGraph().getSortedProjects().
                get(session.getProjectDependencyGraph().getSortedProjects().size()-1).getArtifactId().equalsIgnoreCase(project.getArtifactId());
    }
-1

Normally, this is a matter of configuration. You might have to setup a project just for the mojo and make it dependent on all of the other projects. Or you could force one of the child projects to be last by making it dependent on all of the other children.

sblundy
  • 60,628
  • 22
  • 121
  • 123