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