blob: 8c3d26d9692b0535ac104841d2f253f5e88e2054 [file] [log] [blame]
------
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.
[]