View Javadoc

1   package org.apache.maven.plugin.checkstyle;
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 java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.OutputStream;
28  import java.util.Calendar;
29  import java.util.Iterator;
30  import java.util.Locale;
31  import java.util.ResourceBundle;
32  
33  import org.apache.maven.doxia.siterenderer.Renderer;
34  import org.apache.maven.doxia.tools.SiteTool;
35  import org.apache.maven.model.ReportPlugin;
36  import org.apache.maven.plugin.checkstyle.rss.CheckstyleRssGenerator;
37  import org.apache.maven.plugin.checkstyle.rss.CheckstyleRssGeneratorRequest;
38  import org.apache.maven.project.MavenProject;
39  import org.apache.maven.reporting.AbstractMavenReport;
40  import org.apache.maven.reporting.MavenReportException;
41  import org.codehaus.plexus.resource.ResourceManager;
42  import org.codehaus.plexus.resource.loader.FileResourceLoader;
43  import org.codehaus.plexus.util.PathTool;
44  import org.codehaus.plexus.util.StringUtils;
45  
46  import com.puppycrawl.tools.checkstyle.DefaultLogger;
47  import com.puppycrawl.tools.checkstyle.XMLLogger;
48  import com.puppycrawl.tools.checkstyle.api.AuditListener;
49  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
50  
51  /**
52   * Base abstract class for Checkstyle reports.
53   *
54   * @version $Id: AbstractCheckstyleReport.html 816667 2012-05-08 14:02:08Z hboutemy $
55   * @since 2.8
56   */
57  public abstract class AbstractCheckstyleReport
58      extends AbstractMavenReport
59  {
60      public static final String PLUGIN_RESOURCES = "org/apache/maven/plugin/checkstyle";
61  
62      /**
63       * Skip entire check.
64       *
65       * @parameter expression="${checkstyle.skip}" default-value="false"
66       * @since 2.2
67       */
68      protected boolean skip;
69  
70      /**
71       * The output directory for the report. Note that this parameter is only
72       * evaluated if the goal is run directly from the command line. If the goal
73       * is run indirectly as part of a site generation, the output directory
74       * configured in Maven Site Plugin is used instead.
75       *
76       * @parameter default-value="${project.reporting.outputDirectory}"
77       * @required
78       */
79      private File outputDirectory;
80  
81      /**
82       * Specifies the path and filename to save the checkstyle output. The format
83       * of the output file is determined by the <code>outputFileFormat</code>
84       * parameter.
85       *
86       * @parameter expression="${checkstyle.output.file}"
87       *            default-value="${project.build.directory}/checkstyle-result.xml"
88       */
89      private File outputFile;
90  
91      /**
92       * If <code>null</code>, the Checkstyle plugin will display violations on stdout.
93       * Otherwise, a text file will be created with the violations.
94       *
95       * @parameter
96       */
97      private File useFile;
98  
99      /**
100      * Specifies the format of the output to be used when writing to the output
101      * file. Valid values are "plain" and "xml".
102      *
103      * @parameter expression="${checkstyle.output.format}" default-value="xml"
104      */
105     private String outputFileFormat;
106 
107     /**
108      * Specifies if the Rules summary should be enabled or not.
109      *
110      * @parameter expression="${checkstyle.enable.rules.summary}"
111      *            default-value="true"
112      */
113     private boolean enableRulesSummary;
114 
115     /**
116      * Specifies if the Severity summary should be enabled or not.
117      *
118      * @parameter expression="${checkstyle.enable.severity.summary}"
119      *            default-value="true"
120      */
121     private boolean enableSeveritySummary;
122 
123     /**
124      * Specifies if the Files summary should be enabled or not.
125      *
126      * @parameter expression="${checkstyle.enable.files.summary}"
127      *            default-value="true"
128      */
129     private boolean enableFilesSummary;
130 
131     /**
132      * Specifies if the RSS should be enabled or not.
133      *
134      * @parameter expression="${checkstyle.enable.rss}" default-value="true"
135      */
136     private boolean enableRSS;
137 
138     /**
139      * SiteTool.
140      *
141      * @since 2.2
142      * @component role="org.apache.maven.doxia.tools.SiteTool"
143      * @required
144      * @readonly
145      */
146     protected SiteTool siteTool;
147 
148     /**
149      * The Maven Project Object.
150      *
151      * @parameter default-value="${project}"
152      * @required
153      * @readonly
154      */
155     protected MavenProject project;
156 
157     /**
158      * Link the violation line numbers to the source xref. Will link
159      * automatically if Maven JXR plugin is being used.
160      *
161      * @parameter expression="${linkXRef}" default-value="true"
162      * @since 2.1
163      */
164     private boolean linkXRef;
165 
166     /**
167      * Location of the Xrefs to link to.
168      *
169      * @parameter default-value="${project.reporting.outputDirectory}/xref"
170      */
171     private File xrefLocation;
172 
173     /**
174      * @component
175      * @required
176      * @readonly
177      */
178     private Renderer siteRenderer;
179 
180     /**
181      * @component
182      * @required
183      * @readonly
184      */
185     protected ResourceManager locator;
186 
187     /**
188      * CheckstyleRssGenerator.
189      *
190      * @since 2.4
191      * @component role="org.apache.maven.plugin.checkstyle.rss.CheckstyleRssGenerator" role-hint="default"
192      * @required
193      * @readonly
194      */
195     protected CheckstyleRssGenerator checkstyleRssGenerator;
196 
197     /**
198      * @since 2.5
199      * @component role="org.apache.maven.plugin.checkstyle.CheckstyleExecutor" role-hint="default"
200      * @required
201      * @readonly
202      */
203     protected CheckstyleExecutor checkstyleExecutor;
204 
205     protected ByteArrayOutputStream stringOutputStream;
206 
207     /** {@inheritDoc} */
208     public String getName( Locale locale )
209     {
210         return getBundle( locale ).getString( "report.checkstyle.name" );
211     }
212 
213     /** {@inheritDoc} */
214     public String getDescription( Locale locale )
215     {
216         return getBundle( locale ).getString( "report.checkstyle.description" );
217     }
218 
219     /** {@inheritDoc} */
220     protected String getOutputDirectory()
221     {
222         return outputDirectory.getAbsolutePath();
223     }
224 
225     /** {@inheritDoc} */
226     protected MavenProject getProject()
227     {
228         return project;
229     }
230 
231     /** {@inheritDoc} */
232     protected Renderer getSiteRenderer()
233     {
234         return siteRenderer;
235     }
236 
237     /** {@inheritDoc} */
238     public void executeReport( Locale locale )
239         throws MavenReportException
240     {
241         locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() );
242         locator.addSearchPath( "url", "" );
243 
244         locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
245 
246         // for when we start using maven-shared-io and
247         // maven-shared-monitor...
248         // locator = new Locator( new MojoLogMonitorAdaptor( getLog() ) );
249 
250         // locator = new Locator( getLog(), new File(
251         // project.getBuild().getDirectory() ) );
252 
253         ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
254 
255         try
256         {
257             CheckstyleExecutorRequest request = createRequest();
258 
259             CheckstyleResults results = checkstyleExecutor.executeCheckstyle( request );
260 
261             ResourceBundle bundle = getBundle( locale );
262             generateReportStatics();
263             generateMainReport( results, bundle );
264             if ( enableRSS )
265             {
266                 CheckstyleRssGeneratorRequest checkstyleRssGeneratorRequest =
267                     new CheckstyleRssGeneratorRequest( this.project, this.getCopyright(), outputDirectory, getLog() );
268                 checkstyleRssGenerator.generateRSS( results, checkstyleRssGeneratorRequest );
269             }
270 
271         }
272         catch ( CheckstyleException e )
273         {
274             throw new MavenReportException( "Failed during checkstyle configuration", e );
275         }
276         catch ( CheckstyleExecutorException e )
277         {
278             throw new MavenReportException( "Failed during checkstyle execution", e );
279         }
280         finally
281         {
282             //be sure to restore original context classloader
283             Thread.currentThread().setContextClassLoader( currentClassLoader );
284         }
285     }
286 
287     /**
288      * Create the Checkstyle executor request.
289      *
290      * @return The executor request.
291      * @throws MavenReportException If something goes wrong during creation.
292      */
293     protected abstract CheckstyleExecutorRequest createRequest()
294             throws MavenReportException;
295 
296     /**
297      * Creates and returns the report generation listener.
298      *
299      * @return The audit listener.
300      * @throws MavenReportException If something goes wrong.
301      */
302     protected AuditListener getListener()
303         throws MavenReportException
304     {
305         AuditListener listener = null;
306 
307         if ( StringUtils.isNotEmpty( outputFileFormat ) )
308         {
309             File resultFile = outputFile;
310 
311             OutputStream out = getOutputStream( resultFile );
312 
313             if ( "xml".equals( outputFileFormat ) )
314             {
315                 listener = new XMLLogger( out, true );
316             }
317             else if ( "plain".equals( outputFileFormat ) )
318             {
319                 listener = new DefaultLogger( out, true );
320             }
321             else
322             {
323                 // TODO: failure if not a report
324                 throw new MavenReportException( "Invalid output file format: (" + outputFileFormat
325                     + "). Must be 'plain' or 'xml'." );
326             }
327         }
328 
329         return listener;
330     }
331 
332     private OutputStream getOutputStream( File file )
333         throws MavenReportException
334     {
335         File parentFile = file.getAbsoluteFile().getParentFile();
336 
337         if ( !parentFile.exists() )
338         {
339             parentFile.mkdirs();
340         }
341 
342         FileOutputStream fileOutputStream;
343         try
344         {
345             fileOutputStream = new FileOutputStream( file );
346         }
347         catch ( FileNotFoundException e )
348         {
349             throw new MavenReportException( "Unable to create output stream: " + file, e );
350         }
351         return fileOutputStream;
352     }
353 
354     /**
355      * Creates and returns the console listener.
356      *
357      * @return The console listener.
358      * @throws MavenReportException If something goes wrong.
359      */
360     protected DefaultLogger getConsoleListener()
361         throws MavenReportException
362     {
363         DefaultLogger consoleListener;
364 
365         if ( useFile == null )
366         {
367             stringOutputStream = new ByteArrayOutputStream();
368             consoleListener = new DefaultLogger( stringOutputStream, false );
369         }
370         else
371         {
372             OutputStream out = getOutputStream( useFile );
373 
374             consoleListener = new DefaultLogger( out, true );
375         }
376 
377         return consoleListener;
378     }
379 
380     private void generateReportStatics()
381         throws MavenReportException
382     {
383         ReportResource rresource = new ReportResource( PLUGIN_RESOURCES, outputDirectory );
384         try
385         {
386             rresource.copy( "images/rss.png" );
387         }
388         catch ( IOException e )
389         {
390             throw new MavenReportException( "Unable to copy static resources.", e );
391         }
392     }
393 
394 
395     private String getCopyright()
396     {
397         String copyright;
398         int currentYear = Calendar.getInstance().get( Calendar.YEAR );
399         if ( StringUtils.isNotEmpty( project.getInceptionYear() )
400             && !String.valueOf( currentYear ).equals( project.getInceptionYear() ) )
401         {
402             copyright = project.getInceptionYear() + " - " + currentYear;
403         }
404         else
405         {
406             copyright = String.valueOf( currentYear );
407         }
408 
409         if ( ( project.getOrganization() != null ) && StringUtils.isNotEmpty( project.getOrganization().getName() ) )
410         {
411             copyright = copyright + " " + project.getOrganization().getName();
412         }
413         return copyright;
414     }
415 
416     private void generateMainReport( CheckstyleResults results, ResourceBundle bundle )
417     {
418         CheckstyleReportGenerator generator =
419             new CheckstyleReportGenerator( getSink(), bundle, project.getBasedir(), siteTool );
420 
421         generator.setLog( getLog() );
422         generator.setEnableRulesSummary( enableRulesSummary );
423         generator.setEnableSeveritySummary( enableSeveritySummary );
424         generator.setEnableFilesSummary( enableFilesSummary );
425         generator.setEnableRSS( enableRSS );
426         generator.setCheckstyleConfig( results.getConfiguration() );
427         if ( linkXRef )
428         {
429             String relativePath = PathTool.getRelativePath( getOutputDirectory(), xrefLocation.getAbsolutePath() );
430             if ( StringUtils.isEmpty( relativePath ) )
431             {
432                 relativePath = ".";
433             }
434             relativePath = relativePath + "/" + xrefLocation.getName();
435             if ( xrefLocation.exists() )
436             {
437                 // XRef was already generated by manual execution of a lifecycle
438                 // binding
439                 generator.setXrefLocation( relativePath );
440             }
441             else
442             {
443                 // Not yet generated - check if the report is on its way
444                 for ( Iterator<ReportPlugin> reports = getProject().getReportPlugins().iterator(); reports.hasNext(); )
445                 {
446                     ReportPlugin report = reports.next();
447 
448                     String artifactId = report.getArtifactId();
449                     if ( "maven-jxr-plugin".equals( artifactId ) || "jxr-maven-plugin".equals( artifactId ) )
450                     {
451                         generator.setXrefLocation( relativePath );
452                     }
453                 }
454             }
455 
456             if ( generator.getXrefLocation() == null )
457             {
458                 getLog().warn( "Unable to locate Source XRef to link to - DISABLED" );
459             }
460         }
461         generator.generateReport( results );
462     }
463 
464     private static ResourceBundle getBundle( Locale locale )
465     {
466         return ResourceBundle.getBundle( "checkstyle-report", locale, AbstractCheckstyleReport.class.getClassLoader() );
467     }
468 
469     /** {@inheritDoc} */
470     public void setReportOutputDirectory( File reportOutputDirectory )
471     {
472         super.setReportOutputDirectory( reportOutputDirectory );
473         this.outputDirectory = reportOutputDirectory;
474     }
475 }