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