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.artifact.Artifact;
23  import org.apache.maven.artifact.repository.ArtifactRepository;
24  import org.apache.maven.doxia.sink.Sink;
25  import org.apache.maven.doxia.sink.SinkFactory;
26  import org.apache.maven.doxia.site.decoration.DecorationModel;
27  import org.apache.maven.doxia.siterenderer.Renderer;
28  import org.apache.maven.doxia.siterenderer.RendererException;
29  import org.apache.maven.doxia.siterenderer.RenderingContext;
30  import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
31  import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
32  import org.apache.maven.doxia.tools.SiteTool;
33  import org.apache.maven.doxia.tools.SiteToolException;
34  import org.apache.maven.plugin.AbstractMojo;
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.plugins.annotations.Component;
37  import org.apache.maven.plugins.annotations.Parameter;
38  import org.apache.maven.project.MavenProject;
39  import org.apache.maven.shared.utils.WriterFactory;
40  import org.codehaus.plexus.util.ReaderFactory;
41  
42  import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
43  
44  import java.io.File;
45  import java.io.FileOutputStream;
46  import java.io.IOException;
47  import java.io.OutputStreamWriter;
48  import java.io.Writer;
49  import java.util.HashMap;
50  import java.util.List;
51  import java.util.Locale;
52  import java.util.Map;
53  
54  /**
55   * The basis for a Maven report which can be generated both as part of a site generation or
56   * as a direct standalone goal invocation.
57   * Both invocations are delegated to <code>abstract executeReport( Locale )</code> from:
58   * <ul>
59   * <li>Mojo's <code>execute()</code> method, see maven-plugin-api</li>
60   * <li>MavenMultiPageReport's <code>generate( Sink, SinkFactory, Locale )</code>, see maven-reporting-api</li>
61   * </ul>
62   *
63   * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
64   * @since 2.0
65   * @see #execute() <code>Mojo.execute()</code>, from maven-plugin-api
66   * @see #generate(Sink, SinkFactory, Locale) <code>MavenMultiPageReport.generate( Sink, SinkFactory, Locale )</code>,
67   *  from maven-reporting-api
68   * @see #executeReport(Locale) <code>abstract executeReport( Locale )</code>
69   */
70  public abstract class AbstractMavenReport
71      extends AbstractMojo
72      implements MavenMultiPageReport
73  {
74      /**
75       * The output directory for the report. Note that this parameter is only evaluated if the goal is run directly from
76       * the command line. If the goal is run indirectly as part of a site generation, the output directory configured in
77       * the Maven Site Plugin is used instead.
78       */
79      @Parameter( defaultValue = "${project.reporting.outputDirectory}", readonly = true, required = true )
80      protected File outputDirectory;
81  
82      /**
83       * The Maven Project.
84       */
85      @Parameter( defaultValue = "${project}", readonly = true, required = true )
86      protected MavenProject project;
87  
88      /**
89       * Specifies the input encoding.
90       */
91      @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}", readonly = true )
92      private String inputEncoding;
93  
94      /**
95       * Specifies the output encoding.
96       */
97      @Parameter( property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}", readonly = true )
98      private String outputEncoding;
99  
100     /**
101      * The local repository.
102      */
103     @Parameter( defaultValue = "${localRepository}", readonly = true, required = true )
104     protected ArtifactRepository localRepository;
105 
106     /**
107      * Remote repositories used for the project.
108      */
109     @Parameter( defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true )
110     protected List<ArtifactRepository> remoteRepositories;
111 
112     /**
113      * SiteTool.
114      */
115     @Component
116     protected SiteTool siteTool;
117 
118     /**
119      * Doxia Site Renderer component.
120      */
121     @Component
122     protected Renderer siteRenderer;
123 
124     /** The current sink to use */
125     private Sink sink;
126 
127     /** The sink factory to use */
128     private SinkFactory sinkFactory;
129 
130     /** The current report output directory to use */
131     private File reportOutputDirectory;
132 
133     /**
134      * This method is called when the report generation is invoked directly as a standalone Mojo.
135      *
136      * @throws MojoExecutionException if an error occurs when generating the report
137      * @see org.apache.maven.plugin.Mojo#execute()
138      */
139     @Override
140     public void execute()
141         throws MojoExecutionException
142     {
143         if ( !canGenerateReport() )
144         {
145             return;
146         }
147 
148         File outputDirectory = new File( getOutputDirectory() );
149 
150         String filename = getOutputName() + ".html";
151 
152         Locale locale = Locale.getDefault();
153 
154         try
155         {
156             SiteRenderingContext siteContext = createSiteRenderingContext( locale );
157 
158             // copy resources
159             getSiteRenderer().copyResources( siteContext, outputDirectory );
160 
161             // TODO Replace null with real value
162             RenderingContext docRenderingContext = new RenderingContext( outputDirectory, filename, null );
163 
164             SiteRendererSink sink = new SiteRendererSink( docRenderingContext );
165 
166             generate( sink, null, locale );
167 
168             if ( !isExternalReport() ) // MSHARED-204: only render Doxia sink if not an external report
169             {
170                 outputDirectory.mkdirs();
171 
172                 try ( Writer writer =
173                       new OutputStreamWriter( new FileOutputStream( new File( outputDirectory, filename ) ),
174                                               getOutputEncoding() ) )
175                 {
176                     // render report
177                     getSiteRenderer().mergeDocumentIntoSite( writer, sink, siteContext );
178                 }
179             }
180 
181             // copy generated resources also
182             getSiteRenderer().copyResources( siteContext, outputDirectory );
183         }
184         catch ( RendererException | IOException | MavenReportException e )
185         {
186             throw new MojoExecutionException(
187                 "An error has occurred in " + getName( Locale.ENGLISH ) + " report generation.", e );
188         }
189     }
190 
191     private SiteRenderingContext createSiteRenderingContext( Locale locale )
192         throws MavenReportException, IOException
193     {
194         DecorationModel decorationModel = new DecorationModel();
195 
196         Map<String, Object> templateProperties = new HashMap<>();
197         // We tell the skin that we are rendering in standalone mode
198         templateProperties.put( "standalone", Boolean.TRUE );
199         templateProperties.put( "project", getProject() );
200         templateProperties.put( "inputEncoding", getInputEncoding() );
201         templateProperties.put( "outputEncoding", getOutputEncoding() );
202         // Put any of the properties in directly into the Velocity context
203         for ( Map.Entry<Object, Object> entry : getProject().getProperties().entrySet() )
204         {
205             templateProperties.put( (String) entry.getKey(), entry.getValue() );
206         }
207 
208         SiteRenderingContext context;
209         try
210         {
211            Artifact skinArtifact =
212                siteTool.getSkinArtifactFromRepository( localRepository, remoteRepositories, decorationModel );
213 
214            getLog().info( buffer().a( "Rendering content with " ).strong( skinArtifact.getId()
215                + " skin" ).a( '.' ).toString() );
216 
217             context = siteRenderer.createContextForSkin( skinArtifact, templateProperties, decorationModel,
218                                                          project.getName(), locale );
219         }
220         catch ( SiteToolException e )
221         {
222             throw new MavenReportException( "Failed to retrieve skin artifact", e );
223         }
224         catch ( RendererException e )
225         {
226             throw new MavenReportException( "Failed to create context for skin", e );
227         }
228 
229         // Generate static site
230         context.setRootDirectory( project.getBasedir() );
231 
232         return context;
233     }
234 
235     /**
236      * Generate a report.
237      *
238      * @param sink the sink to use for the generation.
239      * @param locale the wanted locale to generate the report, could be null.
240      * @throws MavenReportException if any
241      * @deprecated use {@link #generate(Sink, SinkFactory, Locale)} instead.
242      */
243     @Deprecated
244     @Override
245     public void generate( org.codehaus.doxia.sink.Sink sink, Locale locale )
246         throws MavenReportException
247     {
248         generate( sink, null, locale );
249     }
250 
251     /**
252      * Generate a report.
253      *
254      * @param sink
255      * @param locale
256      * @throws MavenReportException
257      * @deprecated use {@link #generate(Sink, SinkFactory, Locale)} instead.
258      */
259     @Deprecated
260     public void generate( Sink sink, Locale locale )
261         throws MavenReportException
262     {
263         generate( sink, null, locale );
264     }
265 
266     /**
267      * This method is called when the report generation is invoked by maven-site-plugin.
268      *
269      * @param sink
270      * @param sinkFactory
271      * @param locale
272      * @throws MavenReportException
273      */
274     @Override
275     public void generate( Sink sink, SinkFactory sinkFactory, Locale locale )
276         throws MavenReportException
277     {
278         if ( !canGenerateReport() )
279         {
280             getLog().info( "This report cannot be generated as part of the current build. "
281                            + "The report name should be referenced in this line of output." );
282             return;
283         }
284 
285         this.sink = sink;
286 
287         this.sinkFactory = sinkFactory;
288 
289         executeReport( locale );
290 
291         closeReport();
292     }
293 
294     /**
295      * @return CATEGORY_PROJECT_REPORTS
296      */
297     @Override
298     public String getCategoryName()
299     {
300         return CATEGORY_PROJECT_REPORTS;
301     }
302 
303     @Override
304     public File getReportOutputDirectory()
305     {
306         if ( reportOutputDirectory == null )
307         {
308             reportOutputDirectory = new File( getOutputDirectory() );
309         }
310 
311         return reportOutputDirectory;
312     }
313 
314     @Override
315     public void setReportOutputDirectory( File reportOutputDirectory )
316     {
317         this.reportOutputDirectory = reportOutputDirectory;
318         this.outputDirectory = reportOutputDirectory;
319     }
320 
321     protected String getOutputDirectory()
322     {
323         return outputDirectory.getAbsolutePath();
324     }
325 
326     protected MavenProject getProject()
327     {
328         return project;
329     }
330 
331     protected Renderer getSiteRenderer()
332     {
333         return siteRenderer;
334     }
335 
336     /**
337      * Gets the input files encoding.
338      *
339      * @return The input files encoding, never <code>null</code>.
340      */
341     protected String getInputEncoding()
342     {
343         return ( inputEncoding == null ) ? ReaderFactory.FILE_ENCODING : inputEncoding;
344     }
345 
346     /**
347      * Gets the effective reporting output files encoding.
348      *
349      * @return The effective reporting output file encoding, never <code>null</code>.
350      */
351     protected String getOutputEncoding()
352     {
353         return ( outputEncoding == null ) ? WriterFactory.UTF_8 : outputEncoding;
354     }
355 
356     /**
357      * Actions when closing the report.
358      */
359     protected void closeReport()
360     {
361         getSink().close();
362     }
363 
364     /**
365      * @return the sink used
366      */
367     public Sink getSink()
368     {
369         return sink;
370     }
371 
372     /**
373      * @return the sink factory used
374      */
375     public SinkFactory getSinkFactory()
376     {
377         return sinkFactory;
378     }
379 
380     /**
381      * @see org.apache.maven.reporting.MavenReport#isExternalReport()
382      * @return {@code false} by default.
383      */
384     @Override
385     public boolean isExternalReport()
386     {
387         return false;
388     }
389 
390     @Override
391     public boolean canGenerateReport()
392     {
393         return true;
394     }
395 
396     /**
397      * Execute the generation of the report.
398      *
399      * @param locale the wanted locale to return the report's description, could be <code>null</code>.
400      * @throws MavenReportException if any
401      */
402     protected abstract void executeReport( Locale locale )
403         throws MavenReportException;
404 }