View Javadoc

1   package org.apache.maven.report.projectinfo;
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.factory.ArtifactFactory;
24  import org.apache.maven.artifact.repository.ArtifactRepository;
25  import org.apache.maven.artifact.resolver.ArtifactResolver;
26  import org.apache.maven.doxia.sink.render.RenderingContext;
27  import org.apache.maven.doxia.site.decoration.Body;
28  import org.apache.maven.doxia.site.decoration.DecorationModel;
29  import org.apache.maven.doxia.siterenderer.Renderer;
30  import org.apache.maven.doxia.siterenderer.RendererException;
31  import org.apache.maven.doxia.siterenderer.SiteRenderingContext;
32  import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
33  import org.apache.maven.doxia.tools.SiteTool;
34  import org.apache.maven.doxia.tools.SiteToolException;
35  import org.apache.maven.model.Plugin;
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.plugins.annotations.Component;
38  import org.apache.maven.plugins.annotations.Parameter;
39  import org.apache.maven.project.MavenProject;
40  import org.apache.maven.reporting.AbstractMavenReport;
41  import org.apache.maven.reporting.MavenReportException;
42  import org.apache.maven.settings.Settings;
43  import org.codehaus.plexus.i18n.I18N;
44  import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
45  import org.codehaus.plexus.interpolation.InterpolationException;
46  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
47  import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
48  import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
49  import org.codehaus.plexus.util.IOUtil;
50  import org.codehaus.plexus.util.StringUtils;
51  import org.codehaus.plexus.util.xml.Xpp3Dom;
52  
53  
54  import java.io.File;
55  import java.io.FileOutputStream;
56  import java.io.IOException;
57  import java.io.OutputStreamWriter;
58  import java.io.Writer;
59  import java.net.MalformedURLException;
60  import java.net.URL;
61  import java.net.URLClassLoader;
62  import java.text.MessageFormat;
63  import java.util.HashMap;
64  import java.util.List;
65  import java.util.Locale;
66  import java.util.Map;
67  import java.util.MissingResourceException;
68  import java.util.ResourceBundle;
69  
70  /**
71   * Base class with the things that should be in AbstractMavenReport anyway.
72   *
73   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
74   * @version $Id: AbstractProjectInfoReport.html 861765 2013-05-12 18:46:29Z hboutemy $
75   * @since 2.0
76   */
77  public abstract class AbstractProjectInfoReport
78      extends AbstractMavenReport
79  {
80      // ----------------------------------------------------------------------
81      // Mojo components
82      // ----------------------------------------------------------------------
83  
84      /**
85       * SiteTool component.
86       *
87       * @since 2.1
88       */
89      @Component
90      protected SiteTool siteTool;
91  
92      /**
93       * Doxia Site Renderer component.
94       */
95      @Component
96      protected Renderer siteRenderer;
97  
98      /**
99       * Artifact Resolver component.
100      */
101     @Component
102     protected ArtifactResolver resolver;
103 
104     /**
105      * Artifact Factory component.
106      */
107     @Component
108     protected ArtifactFactory factory;
109 
110     /**
111      * Internationalization component, could support also custom bundle using {@link #customBundle}.
112      */
113     @Component
114     private I18N i18n;
115 
116     // ----------------------------------------------------------------------
117     // Mojo parameters
118     // ----------------------------------------------------------------------
119 
120     /**
121      * The output directory for the report. Note that this parameter is only evaluated if the goal is run directly from
122      * the command line. If the goal is run indirectly as part of a site generation, the output directory configured in
123      * the Maven Site Plugin is used instead.
124      */
125     @Parameter( property = "project.reporting.outputDirectory", required = true )
126     protected File outputDirectory;
127 
128     /**
129      * The Maven Project.
130      */
131     @Component
132     protected MavenProject project;
133 
134     /**
135      * Local Repository.
136      */
137     @Parameter( property = "localRepository", required = true, readonly = true )
138     protected ArtifactRepository localRepository;
139 
140     /**
141      * Remote repositories used for the project.
142      *
143      * @since 2.1
144      */
145     @Parameter( property = "project.remoteArtifactRepositories" )
146     protected List<ArtifactRepository> remoteRepositories;
147 
148     /**
149      * The current user system settings for use in Maven.
150      *
151      * @since 2.3
152      */
153     @Component
154     protected Settings settings;
155 
156     /**
157      * Path for a custom bundle instead of using the default one. <br/>
158      * Using this field, you could change the texts in the generated reports.
159      *
160      * @since 2.3
161      */
162     @Parameter( defaultValue = "${project.basedir}/src/site/custom/project-info-report.properties" )
163     protected String customBundle;
164 
165     // ----------------------------------------------------------------------
166     // Public methods
167     // ----------------------------------------------------------------------
168 
169     @Override
170     public void execute()
171         throws MojoExecutionException
172     {
173         if ( !canGenerateReport() )
174         {
175             return;
176         }
177 
178         // TODO: push to a helper? Could still be improved by taking more of the site information from the site plugin
179         Writer writer = null;
180         try
181         {
182             String filename = getOutputName() + ".html";
183 
184             DecorationModel model = new DecorationModel();
185             model.setBody( new Body() );
186 
187             Map<String, Object> attributes = new HashMap<String, Object>();
188             attributes.put( "outputEncoding", "UTF-8" );
189             attributes.put( "project", project );
190 
191             Locale locale = Locale.getDefault();
192             Artifact defaultSkin =
193                 siteTool.getDefaultSkinArtifact( localRepository, project.getRemoteArtifactRepositories() );
194 
195             SiteRenderingContext siteContext =
196                 siteRenderer.createContextForSkin( defaultSkin.getFile(), attributes, model, getName( locale ), locale );
197 
198             RenderingContext context = new RenderingContext( outputDirectory, filename );
199 
200             SiteRendererSink sink = new SiteRendererSink( context );
201 
202             generate( sink, null, locale );
203 
204             outputDirectory.mkdirs();
205 
206             writer = new OutputStreamWriter( new FileOutputStream( new File( outputDirectory, filename ) ), "UTF-8" );
207 
208             siteRenderer.generateDocument( writer, sink, siteContext );
209 
210             siteRenderer.copyResources( siteContext, new File( project.getBasedir(), "src/site/resources" ),
211                                         outputDirectory );
212         }
213         catch ( RendererException e )
214         {
215             throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
216                 + " report generation.", e );
217         }
218         catch ( IOException e )
219         {
220             throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
221                 + " report generation.", e );
222         }
223         catch ( SiteToolException e )
224         {
225             throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
226                 + " report generation.", e );
227         }
228         catch ( MavenReportException e )
229         {
230             throw new MojoExecutionException( "An error has occurred in " + getName( Locale.ENGLISH )
231                 + " report generation.", e );
232         }
233         finally
234         {
235             IOUtil.close( writer );
236         }
237     }
238 
239     @Override
240     public String getCategoryName()
241     {
242         return CATEGORY_PROJECT_INFORMATION;
243     }
244 
245     // ----------------------------------------------------------------------
246     // Protected methods
247     // ----------------------------------------------------------------------
248 
249     @Override
250     protected String getOutputDirectory()
251     {
252         return outputDirectory.getAbsolutePath();
253     }
254 
255     @Override
256     public File getReportOutputDirectory()
257     {
258         return outputDirectory;
259     }
260 
261     @Override
262     public void setReportOutputDirectory( File reportOutputDirectory )
263     {
264         this.outputDirectory = reportOutputDirectory;
265     }
266 
267     @Override
268     protected MavenProject getProject()
269     {
270         return project;
271     }
272     
273     protected Plugin getPlugin( String pluginId )
274     {
275         if ( ( getProject().getBuild() == null ) || ( getProject().getBuild().getPluginsAsMap() == null ) )
276         {
277             return null;
278         }
279 
280         Plugin plugin = (Plugin) getProject().getBuild().getPluginsAsMap().get( pluginId );
281 
282         if ( ( plugin == null ) && ( getProject().getBuild().getPluginManagement() != null ) && (
283                 getProject().getBuild().getPluginManagement().getPluginsAsMap() != null ) )
284         {
285             plugin = (Plugin) getProject().getBuild().getPluginManagement().getPluginsAsMap().get( pluginId );
286         }
287 
288         return plugin;
289     }
290     
291     protected String getPluginParameter( String pluginId, String param )
292     {
293         Plugin plugin = getPlugin( pluginId );
294         if ( plugin != null )
295         {
296             Xpp3Dom xpp3Dom = (Xpp3Dom) plugin.getConfiguration();
297             if ( xpp3Dom != null && xpp3Dom.getChild( param ) != null
298                 && StringUtils.isNotEmpty( xpp3Dom.getChild( param ).getValue() ) )
299             {
300                 return xpp3Dom.getChild( param ).getValue();
301             }
302         }
303 
304         return null;
305     }
306 
307     @Override
308     protected Renderer getSiteRenderer()
309     {
310         return siteRenderer;
311     }
312 
313     protected String getI18nString( Locale locale, String key )
314     {
315         return getI18N( locale ).getString( "project-info-report", locale, "report." + getI18Nsection() + '.' + key );
316     }
317 
318     protected I18N getI18N( Locale locale )
319     {
320         if ( customBundle != null )
321         {
322             File customBundleFile = new File( customBundle );
323             if ( customBundleFile.isFile() && customBundleFile.getName().endsWith( ".properties" ) )
324             {
325                 if ( !i18n.getClass().isAssignableFrom( CustomI18N.class ) )
326                 {
327                     // first load
328                     i18n = new CustomI18N( project, settings, customBundleFile, locale, i18n );
329                 }
330                 else if ( !i18n.getDefaultLanguage().equals( locale.getLanguage() ) )
331                 {
332                     i18n = new CustomI18N( project, settings, customBundleFile, locale, i18n );
333                 }
334             }
335         }
336 
337         return i18n;
338     }
339 
340     protected abstract String getI18Nsection();
341 
342     /** {@inheritDoc} */
343     public String getName( Locale locale )
344     {
345         return getI18nString( locale, "name" );
346     }
347 
348     /** {@inheritDoc} */
349     public String getDescription( Locale locale )
350     {
351         return getI18nString( locale, "description" );
352     }
353 
354     private static class CustomI18N
355         implements I18N
356     {
357         private final MavenProject project;
358 
359         private final Settings settings;
360 
361         private final String bundleName;
362 
363         private final Locale locale;
364 
365         private final I18N i18nOriginal;
366 
367         private ResourceBundle bundle;
368 
369         private final static Object[] NO_ARGS = new Object[0];
370 
371         public CustomI18N( MavenProject project, Settings settings, File customBundleFile, Locale locale,
372                            I18N i18nOriginal )
373         {
374             super();
375             this.project = project;
376             this.settings = settings;
377             this.locale = locale;
378             this.i18nOriginal = i18nOriginal;
379             this.bundleName =
380                 customBundleFile.getName().substring( 0, customBundleFile.getName().indexOf( ".properties" ) );
381 
382             URLClassLoader classLoader = null;
383             try
384             {
385                 classLoader = new URLClassLoader( new URL[] { customBundleFile.getParentFile().toURI().toURL() } );
386             }
387             catch ( MalformedURLException e )
388             {
389             }
390 
391             this.bundle = ResourceBundle.getBundle( this.bundleName, locale, classLoader );
392             if ( !this.bundle.getLocale().getLanguage().equals( locale.getLanguage() ) )
393             {
394                 this.bundle = ResourceBundle.getBundle( this.bundleName, Locale.getDefault(), classLoader );
395             }
396         }
397 
398         /** {@inheritDoc} */
399         public String getDefaultLanguage()
400         {
401             return locale.getLanguage();
402         }
403 
404         /** {@inheritDoc} */
405         public String getDefaultCountry()
406         {
407             return locale.getCountry();
408         }
409 
410         /** {@inheritDoc} */
411         public String getDefaultBundleName()
412         {
413             return bundleName;
414         }
415 
416         /** {@inheritDoc} */
417         public String[] getBundleNames()
418         {
419             return new String[] { bundleName };
420         }
421 
422         /** {@inheritDoc} */
423         public ResourceBundle getBundle()
424         {
425             return bundle;
426         }
427 
428         /** {@inheritDoc} */
429         public ResourceBundle getBundle( String bundleName )
430         {
431             return bundle;
432         }
433 
434         /** {@inheritDoc} */
435         public ResourceBundle getBundle( String bundleName, String languageHeader )
436         {
437             return bundle;
438         }
439 
440         /** {@inheritDoc} */
441         public ResourceBundle getBundle( String bundleName, Locale locale )
442         {
443             return bundle;
444         }
445 
446         /** {@inheritDoc} */
447         public Locale getLocale( String languageHeader )
448         {
449             return new Locale( languageHeader );
450         }
451 
452         /** {@inheritDoc} */
453         public String getString( String key )
454         {
455             return getString( bundleName, locale, key );
456         }
457 
458         /** {@inheritDoc} */
459         public String getString( String key, Locale locale )
460         {
461             return getString( bundleName, locale, key );
462         }
463 
464         /** {@inheritDoc} */
465         public String getString( String bundleName, Locale locale, String key )
466         {
467             String value;
468 
469             if ( locale == null )
470             {
471                 locale = getLocale( null );
472             }
473 
474             ResourceBundle rb = getBundle( bundleName, locale );
475             value = getStringOrNull( rb, key );
476 
477             if ( value == null )
478             {
479                 // try to load default
480                 value = i18nOriginal.getString( bundleName, locale, key );
481             }
482 
483             if ( value.indexOf( "${" ) < 0 )
484             {
485                 return value;
486             }
487 
488             final RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
489             try
490             {
491                 interpolator.addValueSource( new EnvarBasedValueSource() );
492             }
493             catch ( final IOException e )
494             {
495             }
496 
497             interpolator.addValueSource( new PropertiesBasedValueSource( System.getProperties() ) );
498             interpolator.addValueSource( new PropertiesBasedValueSource( project.getProperties() ) );
499             interpolator.addValueSource( new PrefixedObjectValueSource( "project", project ) );
500             interpolator.addValueSource( new PrefixedObjectValueSource( "pom", project ) );
501             interpolator.addValueSource( new PrefixedObjectValueSource( "settings", settings ) );
502 
503             try
504             {
505                 value = interpolator.interpolate( value );
506             }
507             catch ( final InterpolationException e )
508             {
509             }
510 
511             return value;
512         }
513 
514         /** {@inheritDoc} */
515         public String format( String key, Object arg1 )
516         {
517             return format( bundleName, locale, key, new Object[] { arg1 } );
518         }
519 
520         /** {@inheritDoc} */
521         public String format( String key, Object arg1, Object arg2 )
522         {
523             return format( bundleName, locale, key, new Object[] { arg1, arg2 } );
524         }
525 
526         /** {@inheritDoc} */
527         public String format( String bundleName, Locale locale, String key, Object arg1 )
528         {
529             return format( bundleName, locale, key, new Object[] { arg1 } );
530         }
531 
532         /** {@inheritDoc} */
533         public String format( String bundleName, Locale locale, String key, Object arg1, Object arg2 )
534         {
535             return format( bundleName, locale, key, new Object[] { arg1, arg2 } );
536         }
537 
538         /** {@inheritDoc} */
539         public String format( String bundleName, Locale locale, String key, Object[] args )
540         {
541             if ( locale == null )
542             {
543                 locale = getLocale( null );
544             }
545 
546             String value = getString( bundleName, locale, key );
547             if ( args == null )
548             {
549                 args = NO_ARGS;
550             }
551 
552             MessageFormat messageFormat = new MessageFormat( "" );
553             messageFormat.setLocale( locale );
554             messageFormat.applyPattern( value );
555 
556             return messageFormat.format( args );
557         }
558 
559         private String getStringOrNull( ResourceBundle rb, String key )
560         {
561             if ( rb != null )
562             {
563                 try
564                 {
565                     return rb.getString( key );
566                 }
567                 catch ( MissingResourceException ignored )
568                 {
569                     // intentional
570                 }
571             }
572             return null;
573         }
574     }
575 }