Invoking maven plugin as a part of build lifecycle - maven

I’m new to maven. I’m trying to integrate a plugin into my build so that it would execute automatically as part of phase execution.
Say I want to plug into clean lifecycle phase.
The mojo I’m using was annotated specifying that it should be injected into clean phase:
/**
*
* #goal clean
* #phase clean
* #requiresProject
*/
public class CleanMojo extends AbstractSCAMojo {
This mojo was installed following instructions in Using Plugin Tools Java5 Annotations.
I added plugin to my pom.xml:
<build>
<plugins>
<plugin>
<groupId>myclean.plugin</groupId>
<artifactId>myclean-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<logfile>C:/temp/clean.log</logfile>
</configuration>
</plugin>
</plugins>
</build>
In my understanding having lifecycle binding in Mojo java code eliminates the need to provide executions in build-plugins-plugin. Is that correct?
I was expecting that after invoking mvn clean configured above myclean.plugin:myclean-maven-plugin will be executed as part of the clean goal, but nothing happens besides regular maven clean procedure.
When pom is changed to specify executions myclean.plugin:myclean-maven-plugin is invoked so I’m certain mojo code doesn’t contain blocking errors – this is just a question of configuration.
There is probably something more I need to specify to make plugin executed automatically (i.e. without specifying executions), but what?

As per the documentation, you should be adding the following annotation before the class definition:
#Mojo(name = "clean", defaultPhase = LifecyclePhase.clean)
#goal and #phase are for javadocs.

Related

Run a Maven plugin when the build fails

I am using a plugin to send a Slack message through Maven. I am wondering if it's possible to use a plugin when the build failed so I get automatically notified about the failed build?
You could do that within Maven itself, through the EventSpy mechanism, built-in from Maven 3.0.2. At each step of the build, several events are raised by Maven itself, or by custom code, and it is possible to listen to those events to perform some actions. The execution event raised by Maven are represented by the class ExecutionEvent. Each event has a type, that describes what kind of event it represents: project failure, Mojo failure, project skipped, etc. In this case, the project failure event is what you're looking for.
A custom spy on events is just a Java class that implements the EventSpy interface. Preferably, it should inherit from the AbstractEventSpy helper class. As an example, create a new project (let's call it my-spy), and add the following Java class under a package:
import org.apache.maven.eventspy.AbstractEventSpy;
import org.apache.maven.eventspy.EventSpy;
import org.apache.maven.execution.ExecutionEvent;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
#Component(role = EventSpy.class)
public class BuildFailureEventSpy extends AbstractEventSpy {
#Requirement
private Logger logger;
#Override
public void onEvent(Object event) throws Exception {
if (event instanceof ExecutionEvent) {
ExecutionEvent executionEvent = (ExecutionEvent) event;
if (executionEvent.getType() == ExecutionEvent.Type.ProjectFailed) {
logger.info("My spy detected a build failure, do the necessary here!");
}
}
}
}
This code simply registers the spy through the Plexus' #Component annotation, and logs a message when a project failed to build. To compile that class, you just need to add to the my-spy project a dependency on Maven Core and an execution of the plexus-component-metadata plugin to create the right Plexus metadata for the component.
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.0.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
<version>1.6</version>
<executions>
<execution>
<goals>
<goal>generate-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Once this project is compiled and installed into your local repository (through mvn clean install), you can add it to the build of another project through the core extensions mechanism.
Before Maven 3.3.1, you had to drop the my-spy JAR into your ${MAVEN_HOME}/lib/ext folder, so that Maven could find it. As of 3.3.1, you don't need to fiddle with your Maven installation, and can create a file .mvn/extensions.xml in your project base directory (${maven.multiModuleProjectDirectory}/.mvn/extensions.xml). Its content would be
<?xml version="1.0" encoding="UTF-8"?>
<extensions>
<extension>
<groupId>my.spy</groupId>
<artifactId>my-spy</artifactId>
<version>0.0.1</version>
</extension>
</extensions>
which just declares an extension pointing to the Maven coordinates of the spy project. Maven (≥ 3.3.1) will by default look for that file, and, as such, your spy will be correctly registered and invoked throughout the build.
The only remaining thing to do, is to code what the spy should do. In your case, it should invoke a Maven plugin, so you take a look at the Mojo Executor library, which makes that very easy to do.

How to disable jar creation in commandline in a maven project?

I have a maven project for which I'm running two separate builds.
In one build I want to save the build time by disabling the jar creation of maven modules in it.(There are 45 maven modules). There is a Maven-Jar-Plugin that is being used to create the jars.
I want to conditionally disable the jar creation at the command line, that is, looking for something similar to -Dskiptests used to skip the unit tests though there is a surefire plugin by default.
The maven-jar-plugin does not provide any skip option.
However, several ways are possible to achieve your requirement.
You may just skip the phase which brings by default (via default mappings) the jar creation, that is, the package phase, and as such simply invoke
mvn clean test
The additional phases would not make sense if you do not create a jar file anyway: package, install, deploy would not have anything to process. Moreover, the additional integration phases may also be impacted depending on your strategy for integration tests, if any.
Alternatively, you can configure your pom as following:
<properties>
<jar.creation>package</jar.creation>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>default-jar</id>
<phase>${jar.creation}</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
As such, the default behavior would still provide a jar creation, while executing maven as following:
mvn clean install -Djar.creation=false
Would instead skip the creation of the jar.
What we are actually doing:
We are re-defining the default execution of the maven-jar-plugin
We are overriding its execution id, as such getting more control over it
We are placing its execution phase binding to a configurable (via property) phase
Default phase (property value) keeps on being package
At command line time you can still change it to any value different than a standard maven phase. That is, -Djar.creation=none would also work.

Where should I put interface class for Junit #Category?

I'd like to define project-wide interfaces, to be used in #Category annotations, and configure Maven to exclude their annotated tests when building the whole project.
In the application project there's a test I'd like to categorize:
#Category(Integration.class)
#Test
public void testExternalResource() { ... }
The setting:
I've set up a multi-module maven project:
/container (has a pom with <modules> element)
/parent (all other modules inherit from its pom. has no source, only pom)
/util (other modules are depending on it)
/infra
/application (depending on infra and util, inherits from parent)
Being an infrastructure freak :), I'd like to configure my whole project to exclude groups in any module. So, in parent module I've defined:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
<configuration>
<excludedGroups>com.mycompany.test.Integration</excludedGroups>
</configuration>
</plugin>
And I've put the Integration interface in the util module (in the util/src/test/java), for all modules to see:
package com.mycompany.test;
public interface Integration {}
And because it's a test-jar, I've made the right configuration for both util and application .
The error:
When running mvn clean install, I'm getting
[ERROR] Failed to execute goal org.apache.maven.plugins:
maven-surefire-plugin:2.16:test
(default-test) on project util: Execution default-test of goal
org.apache.maven.plugins:maven-surefire-plugin:2.16:test
failed: There was an error in the forked process
[ERROR] java.lang.RuntimeException:
Unable to load category: com.mycompany.test.Integration
Well, of course the class isn't available - I've crammed it in a test-jar in the first module of the project. :(
Where should I put my Integration interface so it will be available to my mvn configuration and my source code?
Create a new Maven module and put the interface under src/main/java/. Make sure junit is available as a compile time dependency (<scope>compile</scope>) - you're basically building a dependency that other tests can use. So your code that helps other tests must go into main while the tests for this go in the src/test/java, as usual.
This step feels a bit weird but think about how you would package junit itself. From the point of view of junit, it's just a normal Java library, so all its code goes into main.
When you need this module, use it as a test dependency with <scope>test</scope>

Maven: Let "mvn -Pjenkins" be the same as "mvn clean install pmd:pmd javadoc:aggregate"

We have a number of Maven jobs in our Jenkins instance, each with their own particular invocation string specified in the build configuration similar to
mvn clean install -DDISABLED="javadoc:aggregate" checkstyle:checkstyle pmd:pmd findbugs:findbugs
I would like to consolidate this so that the invocation string is stored somewhere in the POM along with suitable profile information so we can replace the invocation strings of all these slightly different jobs with a
mvn -Pjenkins
standard invocation. To my understanding the defaultGoal entry only supports a single goal which on first glance seems to be insufficient for representing our multiple goals, but might be enough if we can make it correspond to multiple entries instead. If at all possible I would like avoid setting up profile specific bindings to standard lifecycle phases if a simple invocation string will do.
You can configure additional mojos ina profile, and you can bind mojos to life cycle phases. These two things combined will allow you to run additional mojos when a certain profile is given.
This is a standard techniqued used throughout in Maven. For example, when you run "mvn release:perform", it runs a nested Maven session with "-Prelease" that does additional things, such as GPG-signing binaries.
<profile>
<id>jenkins</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>findbugs</goal>
</goals>
</execution>
</executions>
</plugin>
... other mojos ...
The findbugs mojo is bound by default to the compile phase, so this gets invoked automatically at the compilation phase. If you want to use a mojo that doesn't bind to any lifecycle phase by default, you add <phase>...</phase>.
See our POM in the Jenkins core for a complete example where we invoke FindBugs. The other mojos are the basically the same.
Note that for this to work, your default goal needs to invoke the life cycle phase to the certain point (say package or install.)
I don't think there's a direct way to give Maven an invocation string. As you say, you can add a custom 'jenkins' profile in which you configure the checkstyle, pmd, and findbugs plugins to be bound to a build phase (e.g. their default phase). You would still need to run mvn -Pjenkins clean install though. However this has the advantage that you can also add custom config to those plugins (e.g. to include test code in the PMD coverage).
I think you can add the settings for the checkstyle, pmd, findbugs and javadoc plugins in a profile's <build><plugins/></build> section along with a <properties> bit where you can define the javadoc property. Also, add an explicit invocation of the maven-clean-plugin attached to the clean phase. Then just invoke the build like:
mvn -Pjenkins site:site
You could even set the defaultGoal to site, if you like.

How to rename goals in Maven?

In the Maven document Introduction to the Build Lifecycle, a goal of display:time is described that outputs the current time. The plugin is as follows:
...
<plugin>
<groupId>com.mycompany.example</groupId>
<artifactId>maven-touch-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>timestamp</goal>
</goals>
</execution>
</executions>
</plugin>
...
I have several questions relating to this plugin:
How can I change the name of the goal to, for example, foo:bar? (Why does neither display nor time appear anywhere in the XML fragment? How can you tell, from looking at the fragment, what goals it defines?)
How can I manually run this goal? (For similar constructs, the equivalent of mvn display:time sometimes works, but this doesn't work consistently.)
How can I see if this goal exists? (i.e. list available goals; this question suggests this is impossible.)
How can I change the name of the goal to, for example, foo:bar? (Why does neither display nor time appear anywhere in the XML fragment? How can you tell, from looking at the fragment, what goals it defines?)
To be precise, in foo:bar, foo is the "plugin goal prefix" and bar is the "goal". And while the later is derived from naming conventions (or can be configured1), the former comes from an annotation of the BarMojo, the class that implements the plugin logic. Something like this:
/**
* Do the wonderful bar.
* #goal bar
* #requiresProject false
*/
public class BarMojo extends AbstractMojo
{
...
}
Changing the goal requires modifying the annotation of the mojo of a plugin and rebuilding it.
Regarding the documentation you linked to, there is a clearly mismatch between the goal time and the XML fragment which binds a timestamp goal to the process-test-resources phase. This must be a typo.
How can I manually run this goal? (For similar constructs, the equivalent of mvn display:time sometimes works, but this doesn't work consistently.)
You can call it like this:
mvn com.mycompany.example:maven-touch-plugin:1.0:timestamp
You could make this command shorter by adding com.mycompany.example to the Plugin Groups in your settings.xml (org.apache.maven.plugins and org.codehaus.mojo are declared by default in case you wonder how it works for those plugins)
<settings>
...
<pluginGroups>
<pluginGroup>com.mycompany.example</pluginGroup>
</pluginGroups>
</settings>
Then, because the plugin name matches the ${prefix}-maven-plugin or maven-${prefix}-plugin patterns, you could execute:
mvn touch:timestamp
Following the convention is recommended but, as I said, the prefix can also be configured.
How can I see if this goal exists?
Check the plugin documentation (derived from the plugin sources) or the plugin sources.
1 Note that there is also a typo at the bottom of the mentioned page of the Maven Book. The way to execute the plugin with a custom prefix should be mvn blah:echo (see MVNREF-145).
I think there's a chance that the documentation may have a typo. Plugin goals are specified by plugin-name:goal-name. That XML would bind the goal touch:timestamp to the process-test-resources phase. That fragment doesn't have anything to do with display:time.
As far as I know, it is impossible to rename maven goals. (It seems like it would just make things more confusing.) The plugin source code is what defines goals, not the pom. The <executions> tag in the pom merely allows you to bind plugin goals to phases, or rebind goals if they already have a default phase.
You should be able to run a goal with prefix:goalName. Most often, the prefix is simply whatever is between "maven-" and "-plugin" in the artifactId. e.g. touch:timestamp. This is complicated in a few situations since plugin authors can specify a "goalPrefix" different from the plugin name, but I haven't ever run into problems with this.
The best way to find out what goals a plugin defines, and what phases they are bound to by default is to read the plugin documentation.
To your first one. The name of the goal is defined by the plugin (there is an annotation for that). If have the source code you change that.
Taking a look at the XML you can't know what kind of goals a plugin defines only the ones which are given in the XML. The best location is to look at the documentation of the plugin.
Second one: You have to check the docs. Usually pluginname:goal ...May be you have to specify the full path of the plugin (groupId).
To the third: Usually should be able to use the help plugin take a look at the docs.
How can I change the name of the goal to, for example, foo:bar? (Why does neither display nor time appear anywhere in the XML fragment? How can you tell, from looking at the fragment, what goals it defines?)
To change the name of the prefix to "foo" you need to configure the maven plugin "plugin".
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<goalPrefix>foo</goalPrefix>
</configuration>
</plugin>
To change the name of the goal, you need to edit the javadoc within the plugin java source
/**
* Prints a timestamp to console
*
* #goal bar
*/
public class TimestampMojo extends AbstractMojo {
// ...
}
Add the plugin prefix to your settings.xml file
<pluginGroups>
<pluginGroup>com.mycompany.example</pluginGroup>
</pluginGroups>
How can I manually run this goal?
mvn com.mycompany.example:foo:bar
or with plugin prefix setting (above)
mvn foo:bar
How can I see if this goal exists? (i.e. list available goals)
Add the following to the maven plugin plugin element and you can use foo:help to print out the list of goals.
<plugin>
...
<executions>
<execution>
<id>generated-helpmojo</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>

Resources