| ------ |
| Guide to Developing Java Report Plugins |
| ------ |
| Hervé Boutemy |
| Bertrand Martin |
| ------ |
| 2018-01-21 |
| ------ |
| |
| ~~ Licensed to the Apache Software Foundation (ASF) under one |
| ~~ or more contributor license agreements. See the NOTICE file |
| ~~ distributed with this work for additional information |
| ~~ regarding copyright ownership. The ASF licenses this file |
| ~~ to you under the Apache License, Version 2.0 (the |
| ~~ "License"); you may not use this file except in compliance |
| ~~ with the License. You may obtain a copy of the License at |
| ~~ |
| ~~ http://www.apache.org/licenses/LICENSE-2.0 |
| ~~ |
| ~~ Unless required by applicable law or agreed to in writing, |
| ~~ software distributed under the License is distributed on an |
| ~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| ~~ KIND, either express or implied. See the License for the |
| ~~ specific language governing permissions and limitations |
| ~~ under the License. |
| |
| ~~ NOTE: For help with the syntax of this file, see: |
| ~~ http://maven.apache.org/doxia/references/apt-format.html |
| |
| Introduction |
| |
| This guide is intended to assist users in developing reporting plugins for |
| Maven in Java that will contribute to sites generated by {{{/plugins/maven-site-plugin/}<<<maven-site-plugin>>>}} |
| or site pdf documents generated by {{{/plugins/maven-pdf-plugin/}<<<maven-pdf-site>>>}}. |
| |
| First and foremost, a <report plugin> is a <Maven plugin> and it is strongly advised to first read the |
| {{{./guide-java-plugin-development.html}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 {{{/plugins/maven-site-plugin/}<<<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 {{{/plugins/maven-site-plugin/}<<<maven-site-plugin>>>}}. |
| |
| * How It Works |
| |
| [[1]] |
| A regular Maven project usually invokes a <reporting goal> of a plugin by declaring such plugin in the |
| {{{/plugins/maven-site-plugin/examples/configuring-reports.html}<<<<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> |
| ... |
| +---+ |
| |
| [[2]] |
| When {{{/plugins/maven-site-plugin/}<<<maven-site-plugin>>>}} is invoked (for example with the <<<mvn site>>> command), |
| the specified plugins are loaded and the <generate()> method of each class that implements |
| {{{/shared/maven-reporting-api/apidocs/org/apache/maven/reporting/MavenReport.html}<MavenReport>}} |
| is executed. |
| |
| [[3]] |
| The <generate()> method generates a document through Maven's {{{/doxia/doxia/doxia-sink-api/}Doxia Sink API}}. |
| This document is comprised of basic elements like title, headings, text, links, tables, etc. |
| This is where you will put the logic of your report, assembling elements, based on the content of the Maven project. |
| |
| [[4]] |
| These document elements are passed to Doxia to generate an HTML document, which itself gets wrapped into a {{{/skins/}Maven Skin}}, |
| as specified in the projects {{{/guides/mini/guide-site.html}<<<./src/site/site.xml>>>}}. |
| |
| [[5]] |
| The result produces an HTML file in the <<<./target/site>>> directory of your project. |
| |
| [] |
| |
| {{{/doxia/doxia-sitetools/doxia-site-renderer/}More details about Doxia Site Renderer}} |
| |
| * Basic Requirements of a Report <Mojo> |
| |
| Each goal or <Mojo> is implemented with a separate Java class. For a <Mojo> to become a <report Mojo>, it needs to |
| implement {{{/shared/maven-reporting-api/apidocs/org/apache/maven/reporting/MavenReport.html}<MavenReport>}} |
| (in addition to {{{/ref/current/apidocs/org/apache/maven/plugin/Mojo.html}<org.apache.maven.plugin.Mojo>}}). |
| An easy way to implement both interfaces is to extend |
| the {{{/shared/maven-reporting-impl/apidocs/org/apache/maven/reporting/AbstractMavenReport.html}<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: |
| |
| * <<<public String getOutputName()>>>: returns the name of page that will be produced |
| |
| * <<<public String getName(Locale locale)>>>: returns the display name of the report |
| |
| * <<<public String getDescription(Locale locale)>>>: returns the description of the report |
| |
| * <<<protected void executeReport(Locale locale) throws MavenReportException>>>: produces the actual report |
| |
| [] |
| |
| 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>: |
| |
| * {{{/shared/maven-reporting-impl/dependency-info.html}<<<org.apache.maven.reporting:maven-reporting-impl>>>}} |
| |
| * {{{/shared/maven-reporting-api/project-summary.html}<<<org.apache.maven.reporting:maven-reporting-api>>>}} |
| |
| [] |
| |
| * A (Very) Simple Report |
| |
| Let's write a very simple <report Mojo> in a very simple Maven plugin: |
| |
| * Plugin's name: <<Simple Plugin>> |
| |
| * Plugin's artifact coordinates: <<<com.mycompany.maven:simple-maven-plugin:1.0-SNAPSHOT>>> |
| |
| * One goal: <<simple>> |
| |
| * One result: <<<simple-report.html>>> |
| |
| [] |
| |
| Our Maven plugin project has 2 files only: |
| |
| * <<<./pom.xml>>> |
| |
| * <<<./src/main/java/com/mycompany/maven/SimpleReport.java>>> |
| |
| [] |
| |
| The below examples can be copied and pasted as a template. |
| |
| ** ./pom.xml |
| |
| +--- |
| <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> |
| +--- |
| |
| ** ./src/main/java/com/mycompany/maven/SimpleReport.java |
| |
| +--- |
| 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 Simple Plugin |
| |
| Building the plugin is done by executing the below command in the root directory of the plugin project: |
| |
| +--- |
| $ mvn install |
| +--- |
| |
| This command will: |
| |
| * compile your code |
| |
| * produces the plugin JAR artifact (<<<./target/simple-maven-plugin-1.0-SNAPSHOT.jar>>>) |
| |
| * copy the artifact to your local repository so that it can be "consumed" by other projects (which |
| is the purpose of a plugin, right?). |
| |
| [] |
| |
| 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] ------------------------------------------------------------------------ |
| +--- |
| |
| ** Invoking the Simple Plugin |
| |
| To invoke the <report Mojo> of our plugin in another Maven project, we just need to declare the plugin in the |
| {{{/plugins/maven-site-plugin/examples/configuring-reports.html}<<<<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. |
| {{{/plugins/maven-site-plugin/examples/configuring-reports.html}More information about configuring reports}}. |
| |
| |
| * More Information |
| |
| ** The Doxia Sink API |
| |
| In your <executeReport()> method, you will leverage the {{{/doxia/doxia/doxia-sink-api/}Doxia Sink API}} |
| to add elements to the report document. |
| |
| You will use the {{{/doxia/doxia/doxia-sink-api/apidocs/org/apache/maven/doxia/sink/Sink.html}<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). |
| |
| * <sink.head()> and <sink.head_()> |
| |
| * <sink.paragraph()> and <sink.paragraph_()> |
| |
| * <sink.section1()> and <sink.section1_()> |
| |
| * <sink.bold()> and <sink.bold_()> |
| |
| * etc. |
| |
| [] |
| |
| <<Do not forget to close elements!>> |
| |
| At the very least, a document should include the following: |
| |
| * Head and title (<sink.head()> and <sink.title()>) |
| |
| * Body (<sink.body()>) |
| |
| * Section 1 with title (<sink.section1()> and <sink.sectionTitle1()>) |
| |
| [] |
| |
| The {{{/doxia/doxia/doxia-sink-api/apidocs/org/apache/maven/doxia/sink/Sink.html}<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 {{{/plugins/maven-pdf-plugin/}<<<maven-pdf-site>>>}}). |
| |
| The Doxia Sink API allows you to specify {{{/doxia/doxia/doxia-sink-api/apidocs/org/apache/maven/doxia/sink/SinkEventAttributes.html}<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). |
| |
| ** Creating more than one document |
| |
| 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 |
| {{{/doxia/doxia/doxia-sink-api/apidocs/org/apache/maven/doxia/sink/SinkFactory.html}<SinkFactory>}} object |
| that you can easily obtain with the |
| {{{/shared/maven-reporting-impl/apidocs/org/apache/maven/reporting/AbstractMavenReport.html#getSinkFactory()}<getSinkFactory()>}} |
| method of your |
| {{{/shared/maven-reporting-impl/apidocs/org/apache/maven/reporting/AbstractMavenReport.html}<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). {{{/doxia/doxia-sitetools/doxia-site-renderer/}More information about the Velocity |
| variables}}. |
| |
| * Resources |
| |
| [[1]] {{{./guide-java-plugin-development.html}Guide to Developing Java Plugins}}: Starting point, since a reporting plugin is a plugin... |
| |
| [[2]] {{{/shared/maven-reporting-api/}Maven Reporting API}}: The Reporting API to implement when a Mojo provides reporting for site. |
| |
| [[3]] {{{/shared/maven-reporting-impl/}Maven Reporting Implementation}}: Base implementation of both Reporting API and Plugin API. |
| |
| [[4]] {{{/doxia/doxia/doxia-sink-api/}Doxia Sink API}}: API to generate content. |
| |
| [] |