------ 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/}<<>>}} or site pdf documents generated by {{{/plugins/maven-pdf-plugin/}<<>>}}. First and foremost, a is a 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 in itself. But one (or several) of its goals or may be specialized to be invoked by {{{/plugins/maven-site-plugin/}<<>>}}, typically during the <<>> build life cycle. A Maven plugin can therefore implement goals and goals. The below details how to write a that will get invoked as a by {{{/plugins/maven-site-plugin/}<<>>}}. * How It Works [[1]] A regular Maven project usually invokes a of a plugin by declaring such plugin in the {{{/plugins/maven-site-plugin/examples/configuring-reports.html}<<<>>>}} section of its <<>> as in the example below: +---+ ... com.mycompany.maven simple-maven-plugin 1.0-SNAPSHOT ... +---+ [[2]] When {{{/plugins/maven-site-plugin/}<<>>}} is invoked (for example with the <<>> command), the specified plugins are loaded and the method of each class that implements {{{/shared/maven-reporting-api/apidocs/org/apache/maven/reporting/MavenReport.html}}} is executed. [[3]] The 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 Each goal or is implemented with a separate Java class. For a to become a , it needs to implement {{{/shared/maven-reporting-api/apidocs/org/apache/maven/reporting/MavenReport.html}}} (in addition to {{{/ref/current/apidocs/org/apache/maven/plugin/Mojo.html}}}). An easy way to implement both interfaces is to extend the {{{/shared/maven-reporting-impl/apidocs/org/apache/maven/reporting/AbstractMavenReport.html}}} class (instead of for a regular ). The class will also need to implement the following methods: * <<>>: returns the name of page that will be produced * <<>>: returns the display name of the report * <<>>: returns the description of the report * <<>>: produces the actual report [] To build a Maven plugin that includes , the <<>> of your project will need declare the project as a regular plugin, and include specific dependencies required by the report : * {{{/shared/maven-reporting-impl/dependency-info.html}<<>>}} * {{{/shared/maven-reporting-api/project-summary.html}<<>>}} [] * A (Very) Simple Report Let's write a very simple in a very simple Maven plugin: * Plugin's name: <> * Plugin's artifact coordinates: <<>> * One goal: <> * One result: <<>> [] 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 +--- 4.0.0 com.mycompany.maven simple-maven-plugin 1.0-SNAPSHOT maven-plugin Simple Plugin UTF-8 org.apache.maven.reporting maven-reporting-impl 3.0.0 org.apache.maven.reporting maven-reporting-api 3.0 org.apache.maven maven-plugin-api 3.5.2 org.apache.maven.plugin-tools maven-plugin-annotations 3.5 provided org.apache.maven.shared maven-shared-utils 3.2.0 maven-compiler-plugin 3.7.0 maven-install-plugin 2.5.2 org.apache.maven.plugins maven-plugin-plugin 3.5.1 simple default-descriptor process-classes generated-helpmojo helpmojo +--- ** ./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. * *

* 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= 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 of our plugin in another Maven project, we just need to declare the plugin in the {{{/plugins/maven-site-plugin/examples/configuring-reports.html}<<<>>>}} section of its <<>> as in the example below: +--- ... com.mycompany.maven simple-maven-plugin 1.0-SNAPSHOT ... +--- Note: When no specific report is specified, all of the 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 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}}} 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 (opening) and (closing). * and * and * and * and * etc. [] <> At the very least, a document should include the following: * Head and title ( and ) * Body () * Section 1 with title ( and ) [] The {{{/doxia/doxia/doxia-sink-api/apidocs/org/apache/maven/doxia/sink/Sink.html}}} object allows you to add raw text with the 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/}<<>>}}). The Doxia Sink API allows you to specify {{{/doxia/doxia/doxia-sink-api/apidocs/org/apache/maven/doxia/sink/SinkEventAttributes.html}}} 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 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}}} object that you can easily obtain with the {{{/shared/maven-reporting-impl/apidocs/org/apache/maven/reporting/AbstractMavenReport.html#getSinkFactory()}}} method of your {{{/shared/maven-reporting-impl/apidocs/org/apache/maven/reporting/AbstractMavenReport.html}}} 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 <<>> HTML file along with <<>>. Note: Despite the fact that you will be creating additional HTML files, the Velocity variable <<<$currentFileName>>> passed to the <<>> script of the Maven Skin will keep the name of the original report (i.e. the result of your 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. []