View Javadoc
1   package org.apache.maven.reporting;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.doxia.sink.Sink;
23  import org.apache.maven.doxia.sink.SinkFactory;
24  import org.apache.maven.doxia.site.decoration.DecorationModel;
25  import org.apache.maven.doxia.siterenderer.Renderer;
26  import org.apache.maven.doxia.siterenderer.RendererException;
27  import org.apache.maven.doxia.siterenderer.RenderingContext;
28  import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
29  import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
30  import org.apache.maven.plugin.AbstractMojo;
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugins.annotations.Component;
33  import org.apache.maven.plugins.annotations.Parameter;
34  import org.apache.maven.project.MavenProject;
35  import org.apache.maven.shared.utils.WriterFactory;
36  import org.codehaus.plexus.util.ReaderFactory;
37  
38  import java.io.File;
39  import java.io.FileOutputStream;
40  import java.io.IOException;
41  import java.io.OutputStreamWriter;
42  import java.io.Writer;
43  import java.util.HashMap;
44  import java.util.Locale;
45  import java.util.Map;
46  
47  /**
48   * The basis for a Maven report which can be generated both as part of a site generation or
49   * as a direct standalone goal invocation.
50   * Both invocations are delegated to <code>abstract executeReport( Locale )</code> from:
51   * <ul>
52   * <li>Mojo's <code>execute()</code> method, see maven-plugin-api</li>
53   * <li>MavenMultiPageReport's <code>generate( Sink, SinkFactory, Locale )</code>, see maven-reporting-api</li>
54   * </ul>
55   *
56   * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
57   * @since 2.0
58   * @see #execute() <code>Mojo.execute()</code>, from maven-plugin-api
59   * @see #generate(Sink, SinkFactory, Locale) <code>MavenMultiPageReport.generate( Sink, SinkFactory, Locale )</code>,
60   *  from maven-reporting-api
61   * @see #executeReport(Locale) <code>abstract executeReport( Locale )</code>
62   */
63  public abstract class AbstractMavenReport
64      extends AbstractMojo
65      implements MavenMultiPageReport
66  {
67      /**
68       * The output directory for the report. Note that this parameter is only evaluated if the goal is run directly from
69       * the command line. If the goal is run indirectly as part of a site generation, the output directory configured in
70       * the Maven Site Plugin is used instead.
71       */
72      @Parameter( defaultValue = "${project.reporting.outputDirectory}", readonly = true, required = true )
73      protected File outputDirectory;
74  
75      /**
76       * The Maven Project.
77       */
78      @Parameter( defaultValue = "${project}", readonly = true, required = true )
79      protected MavenProject project;
80  
81      /**
82       * Specifies the input encoding.
83       */
84      @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}", readonly = true )
85      private String inputEncoding;
86  
87      /**
88       * Specifies the output encoding.
89       */
90      @Parameter( property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}", readonly = true )
91      private String outputEncoding;
92  
93      /**
94       * Doxia Site Renderer component.
95       */
96      @Component
97      protected Renderer siteRenderer;
98  
99      /** The current sink to use */
100     private Sink sink;
101 
102     /** The sink factory to use */
103     private SinkFactory sinkFactory;
104 
105     /** The current report output directory to use */
106     private File reportOutputDirectory;
107 
108     /**
109      * This method is called when the report generation is invoked directly as a standalone Mojo.
110      *
111      * @throws MojoExecutionException if an error occurs when generating the report
112      * @see org.apache.maven.plugin.Mojo#execute()
113      */
114     @Override
115     public void execute()
116         throws MojoExecutionException
117     {
118         if ( !canGenerateReport() )
119         {
120             return;
121         }
122 
123         File outputDirectory = new File( getOutputDirectory() );
124 
125         String filename = getOutputName() + ".html";
126 
127         Locale locale = Locale.getDefault();
128 
129         SiteRenderingContext siteContext = new SiteRenderingContext();
130         siteContext.setDecoration( new DecorationModel() );
131         siteContext.setTemplateName( "org/apache/maven/doxia/siterenderer/resources/default-site.vm" );
132         siteContext.setLocale( locale );
133         siteContext.setTemplateProperties( getTemplateProperties() );
134 
135         // TODO Replace null with real value
136         RenderingContext context = new RenderingContext( outputDirectory, filename, null );
137 
138         SiteRendererSink sink = new SiteRendererSink( context );
139 
140         try
141         {
142 
143             generate( sink, null, locale );
144 
145             if ( !isExternalReport() ) // MSHARED-204: only render Doxia sink if not an external report
146             {
147                 outputDirectory.mkdirs();
148 
149                 try ( Writer writer =
150                     new OutputStreamWriter( new FileOutputStream( new File( outputDirectory, filename ) ),
151                                             getOutputEncoding() ) )
152                 {
153                     getSiteRenderer().mergeDocumentIntoSite( writer, sink, siteContext );
154                 }
155             }
156         }
157         catch ( RendererException | IOException | MavenReportException e )
158         {
159             throw new MojoExecutionException(
160                 "An error has occurred in " + getName( Locale.ENGLISH ) + " report generation.", e );
161         }
162     }
163 
164     /**
165      * create template properties like done in maven-site-plugin's
166      * <code>AbstractSiteRenderingMojo.createSiteRenderingContext( Locale )</code>
167      * @return properties
168      */
169     private Map<String, Object> getTemplateProperties()
170     {
171         Map<String, Object> templateProperties = new HashMap<>();
172         templateProperties.put( "project", getProject() );
173         templateProperties.put( "inputEncoding", getInputEncoding() );
174         templateProperties.put( "outputEncoding", getOutputEncoding() );
175         // Put any of the properties in directly into the Velocity context
176         for ( Map.Entry<Object, Object> entry : getProject().getProperties().entrySet() )
177         {
178             templateProperties.put( (String) entry.getKey(), entry.getValue() );
179         }
180         return templateProperties;
181     }
182 
183     /**
184      * Generate a report.
185      *
186      * @param sink the sink to use for the generation.
187      * @param locale the wanted locale to generate the report, could be null.
188      * @throws MavenReportException if any
189      * @deprecated use {@link #generate(Sink, SinkFactory, Locale)} instead.
190      */
191     @Deprecated
192     public void generate( org.codehaus.doxia.sink.Sink sink, Locale locale )
193         throws MavenReportException
194     {
195         generate( sink, null, locale );
196     }
197 
198     /**
199      * Generate a report.
200      *
201      * @param sink
202      * @param locale
203      * @throws MavenReportException
204      * @deprecated use {@link #generate(Sink, SinkFactory, Locale)} instead.
205      */
206     @Deprecated
207     @Override
208     public void generate( Sink sink, Locale locale )
209         throws MavenReportException
210     {
211         generate( sink, null, locale );
212     }
213 
214     /**
215      * This method is called when the report generation is invoked by maven-site-plugin.
216      *
217      * @param sink
218      * @param sinkFactory
219      * @param locale
220      * @throws MavenReportException
221      */
222     @Override
223     public void generate( Sink sink, SinkFactory sinkFactory, Locale locale )
224         throws MavenReportException
225     {
226         if ( !canGenerateReport() )
227         {
228             getLog().info( "This report cannot be generated as part of the current build. "
229                            + "The report name should be referenced in this line of output." );
230             return;
231         }
232 
233         this.sink = sink;
234 
235         this.sinkFactory = sinkFactory;
236 
237         executeReport( locale );
238 
239         closeReport();
240     }
241 
242     /**
243      * @return CATEGORY_PROJECT_REPORTS
244      */
245     @Override
246     public String getCategoryName()
247     {
248         return CATEGORY_PROJECT_REPORTS;
249     }
250 
251     @Override
252     public File getReportOutputDirectory()
253     {
254         if ( reportOutputDirectory == null )
255         {
256             reportOutputDirectory = new File( getOutputDirectory() );
257         }
258 
259         return reportOutputDirectory;
260     }
261 
262     @Override
263     public void setReportOutputDirectory( File reportOutputDirectory )
264     {
265         this.reportOutputDirectory = reportOutputDirectory;
266         this.outputDirectory = reportOutputDirectory;
267     }
268 
269     protected String getOutputDirectory()
270     {
271         return outputDirectory.getAbsolutePath();
272     }
273 
274     protected MavenProject getProject()
275     {
276         return project;
277     }
278 
279     protected Renderer getSiteRenderer()
280     {
281         return siteRenderer;
282     }
283 
284     /**
285      * Gets the input files encoding.
286      *
287      * @return The input files encoding, never <code>null</code>.
288      */
289     protected String getInputEncoding()
290     {
291         return ( inputEncoding == null ) ? ReaderFactory.FILE_ENCODING : inputEncoding;
292     }
293 
294     /**
295      * Gets the effective reporting output files encoding.
296      *
297      * @return The effective reporting output file encoding, never <code>null</code>.
298      */
299     protected String getOutputEncoding()
300     {
301         return ( outputEncoding == null ) ? WriterFactory.UTF_8 : outputEncoding;
302     }
303 
304     /**
305      * Actions when closing the report.
306      */
307     protected void closeReport()
308     {
309         getSink().close();
310     }
311 
312     /**
313      * @return the sink used
314      */
315     public Sink getSink()
316     {
317         return sink;
318     }
319 
320     /**
321      * @return the sink factory used
322      */
323     public SinkFactory getSinkFactory()
324     {
325         return sinkFactory;
326     }
327 
328     /**
329      * @see org.apache.maven.reporting.MavenReport#isExternalReport()
330      * @return {@code false} by default.
331      */
332     @Override
333     public boolean isExternalReport()
334     {
335         return false;
336     }
337 
338     @Override
339     public boolean canGenerateReport()
340     {
341         return true;
342     }
343 
344     /**
345      * Execute the generation of the report.
346      *
347      * @param locale the wanted locale to return the report's description, could be <code>null</code>.
348      * @throws MavenReportException if any
349      */
350     protected abstract void executeReport( Locale locale )
351         throws MavenReportException;
352 }