In this article we will develop a simple custom maven plugin.
Maven plugin example in this article
- Attach a goal to a maven plugin.
- Access dependency list of project.
- Add/modify files in target directory.
- Build & use plugin.
Create maven plugin project
Refer to these slides to get steps to create maven plugin project. Go to next section in this article for code & explanation.
Concepts of maven plugin
Here are few concepts involved in maven plugin development
- Mojo
- As documented in maven documentation MOJO is a play on POJO (Plain-old-Java-object), substituting “Maven” for “Plain”.
- Mojo java class is nothing but a goal in maven. Maven build consists of lot of phases & goals so through mojo you can introduce custom goal.
- General mojo class is annotated with @Mojo(name = “<goal-name>”) & it extends org.apache.maven.plugin.AbstractMojo class whcih provides several ready to use methods.
- Parameters
- parameters provide easy way to access objects associated with pom.xml, maven execution, project etc.
- Inside mojo, @Parameter annotation can be used on instance field to get necessary objects injected.
- Refer this to get few examples of object injection using parameters.
- Maven Plugin Plugin (2 times plugin, not a typing mistake 🙂 )
- This plugin generates maven plugin descriptor for our custom mojo.
- Plugin descriptor is generated & stored in META-INF/maven/plugin.xml in a plugin’s jar artifact.
Example code
- This is a mojo class for our plugin annotated with @Mojo & custom goal ‘modify-target”. This is extending AbstractMojo
- We have injected MavenProject to access dependencies & target directory to add/modify files in target.
- getLog() – This method from AbstractMojo provdes the logger of maven build so that logs from plugin are printed in same manner as that of maven build log.
- As a best practice wrap exceptions in MojoExecutionException or MojoFailureException. MojoExecutionException causes build error (like unexpected error) & MojoFailureException causes build failure (like expected exceptions like compile issues.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
package com.itsallbinary.maven_target_modifier; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.stream.Collectors; import org.apache.maven.model.Dependency; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; @Mojo(name = "modify-target") public class MavenTargetModifier extends AbstractMojo { @Parameter(defaultValue = "${project}", readonly = true) private MavenProject project; @Parameter(defaultValue = "${project.build.directory}", readonly = true) private File target; public void execute() throws MojoExecutionException, MojoFailureException { /* * getLog() from AbstractMojo provides logger of maven so that we can see it in * build log. */ getLog().info("Execution for maven target modifier started"); /* * Iterate dependencies & collect information about dependencies */ String dependenciesText = ""; for (Object dep : project.getDependencies()) { Dependency dependency = (Dependency) dep; dependenciesText = dependenciesText + " Group = " + dependency.getGroupId() + " Artifact = " + dependency.getArtifactId() + " Version = " + dependency.getVersion() + " \n"; } /* * Create a new file in target & add above dependency information to that. */ File newFile = new File(target, "generatedFile.txt"); try { newFile.createNewFile(); Files.write(newFile.toPath(), dependenciesText.getBytes(), StandardOpenOption.APPEND); getLog().info("Generated - " + newFile.getAbsolutePath()); } catch (IOException e) { getLog().error("Failed to generate new file", e); throw new MojoExecutionException("Failed to generate new file", e); } /* * Modify existing file from target & append text to it. Assume that * test.properties exists in project for example purpose. */ File classes = new File(target.getAbsolutePath() + System.getProperty("file.separator") + "classes"); File testProp = Arrays.stream(classes.listFiles()).filter(f -> f.getName().equalsIgnoreCase("test.properties")) .collect(Collectors.toList()).get(0); try { Files.write(testProp.toPath(), "Added by plugin".getBytes(), StandardOpenOption.APPEND); getLog().info("Updated - " + testProp.getAbsolutePath()); } catch (IOException e) { getLog().error("Failed to modify properties", e); throw new MojoExecutionException("Failed to modify properties", e); } } } |
Build, publish & use
Slides in earlier section covers steps for building, publishing & using plugin. Here is the code that you have to put in other projects to use this plugin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!-- ADD PLUGIN WITH GOAL SAME AS @mojo ANNOTATION --> <build> <plugins> <plugin> <groupId>com.itsallbinary</groupId> <artifactId>maven-target-modifier</artifactId> <version>0.0.1-SNAPSHOT</version> <executions> <execution> <phase>package</phase> <goals> <goal>modify-target</goal> </goals> </execution> </executions> </plugin> </plugins> </build> |
Here is the output generated by test project.
1 2 3 4 |
[INFO] --- maven-target-modifier:0.0.1-SNAPSHOT:modify-target (default) @ Test --- [INFO] Execution for maven target modifier started [INFO] Generated - C:\MSTD\Workspaces\MavenPlugin\Test\target\generatedFile.txt [INFO] Updated - C:\MSTD\Workspaces\MavenPlugin\Test\target\classes\test.properties |
Get complete source code on GitHub repository