This guide is intended to assist users in developing reporting plugins for Maven in Java that will contribute to sites generated by maven-site-plugin or site pdf documents generated by maven-pdf-site.
First and foremost, a report plugin is a Maven plugin and it is strongly advised to first read the Guide to Developing Java Plugins to properly understand its core mechanisms.
A plugin is actually not a report plugin in itself. But one (or several) of its goals or Mojos may be specialized to be invoked by maven-site-plugin, typically during the site build life cycle.
A Maven plugin can therefore implement regular goals and report goals. The below details how to write a Mojo that will get invoked as a report by maven-site-plugin.
<project> ... <reporting> <plugins> <plugin> <groupId>com.mycompany.maven</groupId> <artifactId>simple-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> </plugin> </plugins> </reporting> ...
Each goal or Mojo is implemented with a separate Java class. For a Mojo to become a report Mojo, it needs to implement MavenReport (in addition to org.apache.maven.plugin.Mojo). An easy way to implement both interfaces is to extend the org.apache.maven.reporting.AbstractMavenReport class (instead of org.apache.maven.plugin.AbstractMojo for a regular Mojo).
The class will also need to implement the following methods:
To build a Maven plugin that includes report Mojos, the pom.xml of your project will need declare the project as a regular plugin, and include specific dependencies required by the report Mojos:
Let's write a very simple report Mojo in a very simple Maven plugin:
Our Maven plugin project has 2 files only:
The below examples can be copied and pasted as a template.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.maven</groupId> <artifactId>simple-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> <packaging>maven-plugin</packaging> <name>Simple Plugin</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- reporting API --> <dependency> <groupId>org.apache.maven.reporting</groupId> <artifactId>maven-reporting-impl</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.apache.maven.reporting</groupId> <artifactId>maven-reporting-api</artifactId> <version>3.0</version> </dependency> <!-- plugin API and plugin-tools --> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.apache.maven.plugin-tools</groupId> <artifactId>maven-plugin-annotations</artifactId> <version>3.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.maven.shared</groupId> <artifactId>maven-shared-utils</artifactId> <version>3.2.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugin-plugin</artifactId> <version>3.5.1</version> <configuration> <goalPrefix>simple</goalPrefix> </configuration> <executions> <execution> <id>default-descriptor</id> <phase>process-classes</phase> </execution> <execution> <id>generated-helpmojo</id> <goals> <goal>helpmojo</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
package com.mycompany.maven; import java.util.Locale; import org.apache.maven.doxia.sink.Sink; import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import org.apache.maven.reporting.AbstractMavenReport; import org.apache.maven.reporting.MavenReportException; /** * Builds an simple report page as an example. * * <p> * This example show how easy it is to build your own reporting plugin * (or, for that matter, your own reporting Mojo) * */ @Mojo( name = "simple", defaultPhase = LifecyclePhase.SITE, requiresDependencyResolution = ResolutionScope.RUNTIME, requiresProject = true, threadSafe = true ) public class SimpleReport extends AbstractMavenReport { public String getOutputName() { // This report will generate simple-report.html when invoked in a project with `mvn site` return "simple-report"; } public String getName(Locale locale) { // Name of the report when listed in the project-reports.html page of a project return "Simple Report"; } public String getDescription(Locale locale) { // Description of the report when listed in the project-reports.html page of a project return "This simple report is a very simple report that does nothing but " + "shows off Maven's wonderful reporting capabilities."; } /** * Practical reference to the Maven project */ @Parameter(defaultValue = "${project}", readonly = true) private MavenProject project; @Override protected void executeReport(Locale locale) throws MavenReportException { // Get the logger Log logger = getLog(); // Some info logger.info("Generating " + getOutputName() + ".html" + " for " + project.getName() + " " + project.getVersion()); // Get the Maven Doxia Sink, which will be used to generate the // various elements of the document Sink mainSink = getSink(); if (mainSink == null) { throw new MavenReportException("Could not get the Doxia sink"); } // Page title mainSink.head(); mainSink.title(); mainSink.text("Simple Report for " + project.getName() + " " + project.getVersion()); mainSink.title_(); mainSink.head_(); mainSink.body(); // Heading 1 mainSink.section1(); mainSink.sectionTitle1(); mainSink.text("Simple Report for " + project.getName() + " " + project.getVersion()); mainSink.sectionTitle1_(); // Content mainSink.paragraph(); mainSink.text("This page provides simple information, like its location: "); mainSink.text(project.getBasedir().getAbsolutePath()); mainSink.paragraph_(); // Close mainSink.section1_(); mainSink.body_(); } }
Building the plugin is done by executing the below command in the root directory of the plugin project:
$ mvn install
This command will:
To make sure everything went well and is properly declared, you can now execute the below command in any other Maven project directory:
$ mvn com.mycompany.maven:simple-maven-plugin:1.0-SNAPSHOT:help [INFO] --- simple-maven-plugin:1.0-SNAPSHOT:help (default-cli) @ hardware-connectors --- [INFO] Simple Plugin 1.0-SNAPSHOT This plugin has 2 goals: simple:help Display help information on simple-maven-plugin. Call mvn simple:help -Ddetail=true -Dgoal=<goal-name> to display parameter details. simple:simple Builds an simple report page as an example. This example show how easy it is to build your own reporting plugin (or, for that matter, your own reporting Mojo) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------
To invoke the report Mojo of our plugin in another Maven project, we just need to declare the plugin in the <reporting> section of its pom.xml as in the example below:
<project> ... <reporting> <plugins> <plugin> <groupId>com.mycompany.maven</groupId> <artifactId>simple-maven-plugin</artifactId> <version>1.0-SNAPSHOT</version> </plugin> </plugins> </reporting> ...
Note: When no specific report is specified, all of the Mojos in the plugin, that are declared as "reporting" will be executed. More information about configuring reports.
In your executeReport() method, you will leverage the Doxia Sink API to add elements to the report document.
You will use the Sink object associated to the report:
Sink sink = getSink();
This object allows you to append new elements to the report document (initially empty). Unlike some DOM manipulation APIs, you cannot insert elements in already existing elements, or remove elements.
The elements that you append to the document will look familiar if you have basic knowledge of HTML. Most of the elements have opening and closing tags, like sink.body() (opening) and sink.body_() (closing).
Do not forget to close elements!
At the very least, a document should include the following:
The Sink object allows you to add raw text with the rawText() method. More precisely, it allows you to add raw HTML code into the document for full flexibility. However, you should limit the usage of this method as you may add elements that are not supported by non-HTML renderers (like maven-pdf-site).
The Doxia Sink API allows you to specify SinkEventAttributes to each element, i.e. HTML properties, notably the class and the ID of an object, which allows for easy customization with an appropriate CSS (either provided by the specified Maven Skin, or by the project itself).
You may need to create not just one HTML file, but several of them (like Javadoc produces one HTML file for each Java class). To do so, you will need to get a new Sink for each HTML file you need to produce. This is achieved by using the SinkFactory object that you can easily obtain with the getSinkFactory() method of your AbstractMavenReport instance, as in the example below.
public class SimpleReport extends AbstractMavenReport { ... /** * Where the HTML pages of the report will be created. */ @Parameter( defaultValue = "${project.reporting.outputDirectory}", property = "outputDirectory", required = true ) private File outputDirectory; ... @Override protected void executeReport(Locale locale) throws MavenReportException { ... // Create a new sink String pageFilename = "other-report.html"; Sink otherSink; try { otherSink = getSinkFactory().createSink(outputDirectory, pageFilename); } catch (IOException e) { throw new MavenReportException("Could not create sink for " + pageFilename + " in " + outputDirectory.getAbsolutePath(), e); } // Create the "other" report otherSink.head(); otherSink.title(); otherSink.text("The Other Report"); ...
The above example will create a other-report.html HTML file along with simple-report.html.
Note: Despite the fact that you will be creating additional HTML files, the Velocity variable $currentFileName passed to the site.vm script of the Maven Skin will keep the name of the original report (i.e. the result of your getOutputName() method). More information about the Velocity variables.