1   package org.apache.maven.plugins.checkstyle;
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
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.ArrayList;
29  import java.util.Calendar;
30  import java.util.List;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.ResourceBundle;
34  
35  import org.apache.commons.lang3.StringUtils;
36  import org.apache.maven.artifact.Artifact;
37  import org.apache.maven.execution.MavenSession;
38  import org.apache.maven.model.Dependency;
39  import org.apache.maven.model.Plugin;
40  import org.apache.maven.model.PluginManagement;
41  import org.apache.maven.model.ReportPlugin;
42  import org.apache.maven.model.Resource;
43  import org.apache.maven.plugin.descriptor.PluginDescriptor;
44  import org.apache.maven.plugins.annotations.Component;
45  import org.apache.maven.plugins.annotations.Parameter;
46  import org.apache.maven.plugins.checkstyle.exec.CheckstyleExecutor;
47  import org.apache.maven.plugins.checkstyle.exec.CheckstyleExecutorException;
48  import org.apache.maven.plugins.checkstyle.exec.CheckstyleExecutorRequest;
49  import org.apache.maven.plugins.checkstyle.exec.CheckstyleResults;
50  import org.apache.maven.plugins.checkstyle.rss.CheckstyleRssGenerator;
51  import org.apache.maven.plugins.checkstyle.rss.CheckstyleRssGeneratorRequest;
52  import org.apache.maven.reporting.AbstractMavenReport;
53  import org.apache.maven.reporting.MavenReportException;
54  import org.codehaus.plexus.configuration.PlexusConfiguration;
55  import org.codehaus.plexus.resource.ResourceManager;
56  import org.codehaus.plexus.resource.loader.FileResourceLoader;
57  import org.codehaus.plexus.util.FileUtils;
58  import org.codehaus.plexus.util.PathTool;
59  
60  import com.puppycrawl.tools.checkstyle.DefaultLogger;
61  import com.puppycrawl.tools.checkstyle.XMLLogger;
62  import com.puppycrawl.tools.checkstyle.api.AuditListener;
63  import com.puppycrawl.tools.checkstyle.api.AutomaticBean.OutputStreamOptions;
64  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
65  
66  
67  
68  
69  
70  
71  public abstract class AbstractCheckstyleReport
72      extends AbstractMavenReport
73  {
74      public static final String PLUGIN_RESOURCES = "org/apache/maven/plugins/checkstyle";
75  
76      protected static final String JAVA_FILES = "**\\/*.java";
77  
78      private static final String DEFAULT_CONFIG_LOCATION = "sun_checks.xml";
79  
80      @Parameter( defaultValue = "${session}", readonly = true, required = true )
81      private MavenSession session;
82  
83      
84  
85  
86      @Parameter( defaultValue = "${project.build.directory}/checkstyle-cachefile" )
87      protected String cacheFile;
88  
89      
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110     @Parameter( property = "checkstyle.config.location", defaultValue = DEFAULT_CONFIG_LOCATION )
111     protected String configLocation;
112 
113     
114 
115 
116     @Parameter( property = "checkstyle.consoleOutput", defaultValue = "false" )
117     protected boolean consoleOutput;
118 
119     
120 
121 
122     @Parameter( defaultValue = "false" )
123     protected boolean failsOnError;
124 
125     
126 
127 
128 
129 
130 
131 
132 
133 
134 
135 
136 
137 
138 
139 
140 
141 
142 
143     @Parameter( property = "checkstyle.header.file", defaultValue = "LICENSE.txt" )
144     protected String headerLocation;
145 
146     
147 
148 
149 
150 
151     @Parameter( property = "checkstyle.skip", defaultValue = "false" )
152     protected boolean skip;
153 
154     
155 
156 
157 
158 
159     @Parameter( property = "checkstyle.output.file", defaultValue = "${project.build.directory}/checkstyle-result.xml" )
160     private File outputFile;
161 
162     
163 
164 
165 
166 
167 
168 
169 
170 
171 
172 
173 
174 
175 
176 
177 
178     @Parameter( property = "checkstyle.properties.location" )
179     protected String propertiesLocation;
180 
181     
182 
183 
184     @Parameter
185     protected String propertyExpansion;
186 
187     
188 
189 
190 
191 
192     @Parameter( defaultValue = "${project.resources}", readonly = true )
193     protected List<Resource> resources;
194 
195     
196 
197 
198 
199 
200     @Parameter( defaultValue = "${project.testResources}", readonly = true )
201     protected List<Resource> testResources;
202 
203     
204 
205 
206     @Parameter( property = "checkstyle.includes", defaultValue = JAVA_FILES, required = true )
207     protected String includes;
208 
209     
210 
211 
212 
213     @Parameter( property = "checkstyle.excludes" )
214     protected String excludes;
215 
216     
217 
218 
219 
220     @Parameter( property = "checkstyle.resourceIncludes", defaultValue = "**/*.properties", required = true )
221     protected String resourceIncludes;
222 
223     
224 
225 
226 
227 
228     @Parameter( property = "checkstyle.resourceExcludes" )
229     protected String resourceExcludes;
230 
231     
232 
233 
234 
235     @Parameter( property = "checkstyle.includeResources", defaultValue = "true", required = true )
236     protected boolean includeResources;
237 
238     
239 
240 
241 
242     @Parameter( property = "checkstyle.includeTestResources", defaultValue = "true", required = true )
243     protected boolean includeTestResources;
244 
245     
246 
247 
248 
249 
250 
251     @Deprecated
252     @Parameter
253     private File sourceDirectory;
254 
255     
256 
257 
258 
259 
260     
261     @Parameter
262     private List<String> sourceDirectories;
263 
264     
265 
266 
267 
268 
269 
270 
271     @Parameter
272     @Deprecated
273     private File testSourceDirectory;
274 
275     
276 
277 
278 
279 
280     
281     @Parameter
282     private List<String> testSourceDirectories;
283 
284     
285 
286 
287 
288 
289     @Parameter( defaultValue = "false" )
290     protected boolean includeTestSourceDirectory;
291 
292     
293 
294 
295 
296 
297     @Parameter( property = "checkstyle.suppression.expression", defaultValue = "checkstyle.suppressions.file" )
298     protected String suppressionsFileExpression;
299 
300     
301 
302 
303 
304 
305 
306 
307 
308 
309 
310 
311 
312 
313 
314     @Parameter( property = "checkstyle.suppressions.location" )
315     protected String suppressionsLocation;
316 
317     
318 
319 
320 
321     @Parameter
322     private File useFile;
323 
324     
325 
326 
327 
328     @Parameter( property = "checkstyle.output.format", defaultValue = "xml" )
329     private String outputFileFormat;
330 
331     
332 
333 
334     @Parameter( property = "checkstyle.enable.rules.summary", defaultValue = "true" )
335     private boolean enableRulesSummary;
336 
337     
338 
339 
340     @Parameter( property = "checkstyle.enable.severity.summary", defaultValue = "true" )
341     private boolean enableSeveritySummary;
342 
343     
344 
345 
346     @Parameter( property = "checkstyle.enable.files.summary", defaultValue = "true" )
347     private boolean enableFilesSummary;
348 
349     
350 
351 
352 
353 
354     @Parameter( property = "checkstyle.enable.rss", defaultValue = "false" )
355     @Deprecated
356     private boolean enableRSS;
357 
358     
359 
360 
361     @Parameter( defaultValue = "${plugin}", readonly = true, required = true )
362     private PluginDescriptor plugin;
363 
364     
365 
366 
367 
368 
369 
370     @Parameter( property = "linkXRef", defaultValue = "true" )
371     private boolean linkXRef;
372 
373     
374 
375 
376     @Parameter( defaultValue = "${project.reporting.outputDirectory}/xref" )
377     private File xrefLocation;
378 
379     
380 
381 
382     @Parameter( defaultValue = "${project.reporting.outputDirectory}/xref-test" )
383     private File xrefTestLocation;
384 
385     
386 
387 
388 
389 
390 
391     @Parameter
392     private List<String> treeWalkerNames;
393 
394     
395 
396 
397 
398 
399 
400     @Parameter( defaultValue = "false" )
401     private boolean omitIgnoredModules;
402 
403     
404 
405 
406 
407 
408 
409 
410 
411 
412 
413 
414 
415 
416 
417 
418 
419 
420 
421 
422 
423 
424 
425 
426 
427     @Parameter
428     private PlexusConfiguration checkstyleRules;
429 
430     
431 
432 
433     @Parameter( property = "checkstyle.output.rules.file",
434                     defaultValue = "${project.build.directory}/checkstyle-rules.xml" )
435     private File rulesFiles;
436 
437     
438 
439 
440 
441     @Parameter( defaultValue = "<?xml version=\"1.0\"?>\n"
442             + "<!DOCTYPE module PUBLIC \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\"\n"
443             + "        \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n" )
444     private String checkstyleRulesHeader;
445 
446     
447 
448     @Component
449     protected ResourceManager locator;
450 
451     
452 
453 
454 
455 
456     @Component( role = CheckstyleRssGenerator.class, hint = "default" )
457     @Deprecated
458     protected CheckstyleRssGenerator checkstyleRssGenerator;
459 
460     
461 
462 
463     @Component( role = CheckstyleExecutor.class, hint = "default" )
464     protected CheckstyleExecutor checkstyleExecutor;
465 
466     protected ByteArrayOutputStream stringOutputStream;
467 
468     
469     public String getName( Locale locale )
470     {
471         return getBundle( locale ).getString( "report.checkstyle.name" );
472     }
473 
474     
475     public String getDescription( Locale locale )
476     {
477         return getBundle( locale ).getString( "report.checkstyle.description" );
478     }
479 
480     
481     public void executeReport( Locale locale )
482         throws MavenReportException
483     {
484         checkDeprecatedParameterUsage( sourceDirectory, "sourceDirectory", "sourceDirectories" );
485         checkDeprecatedParameterUsage( testSourceDirectory, "testSourceDirectory", "testSourceDirectories" );
486 
487         locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() );
488         locator.addSearchPath( "url", "" );
489 
490         locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
491 
492         
493         
494 
495         
496         String effectiveConfigLocation = configLocation;
497         if ( checkstyleRules != null )
498         {
499             if ( !DEFAULT_CONFIG_LOCATION.equals( configLocation ) )
500             {
501                 throw new MavenReportException( "If you use inline configuration for rules, don't specify "
502                         + "a configLocation" );
503             }
504             if ( checkstyleRules.getChildCount() > 1 )
505             {
506                 throw new MavenReportException( "Currently only one root module is supported" );
507             }
508             PlexusConfiguration checkerModule = checkstyleRules.getChild( 0 );
509 
510             try
511             {
512                 FileUtils.forceMkdir( rulesFiles.getParentFile() );
513                 FileUtils.fileWrite( rulesFiles, checkstyleRulesHeader + checkerModule.toString() );
514             }
515             catch ( final IOException e )
516             {
517                 throw new MavenReportException( e.getMessage(), e );
518             }
519             effectiveConfigLocation = rulesFiles.getAbsolutePath();
520         }
521         ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
522 
523         try
524         {
525             CheckstyleExecutorRequest request = createRequest().setLicenseArtifacts( collectArtifacts( "license" ) )
526                             .setConfigurationArtifacts( collectArtifacts( "configuration" ) )
527                             .setOmitIgnoredModules( omitIgnoredModules )
528                             .setConfigLocation( effectiveConfigLocation );
529 
530             CheckstyleResults results = checkstyleExecutor.executeCheckstyle( request );
531 
532             ResourceBundle bundle = getBundle( locale );
533             generateReportStatics();
534             generateMainReport( results, bundle, effectiveConfigLocation );
535             if ( enableRSS )
536             {
537                 CheckstyleRssGeneratorRequest checkstyleRssGeneratorRequest =
538                     new CheckstyleRssGeneratorRequest( this.project, this.getCopyright(), outputDirectory, getLog() );
539                 checkstyleRssGenerator.generateRSS( results, checkstyleRssGeneratorRequest );
540             }
541 
542         }
543         catch ( CheckstyleException e )
544         {
545             throw new MavenReportException( "Failed during checkstyle configuration", e );
546         }
547         catch ( CheckstyleExecutorException e )
548         {
549             throw new MavenReportException( "Failed during checkstyle execution", e );
550         }
551         finally
552         {
553             
554             Thread.currentThread().setContextClassLoader( currentClassLoader );
555         }
556     }
557 
558     private void checkDeprecatedParameterUsage( Object parameter, String name, String replacement )
559         throws MavenReportException
560     {
561         if ( parameter != null )
562         {
563             throw new MavenReportException( "You are using '" + name + "' which has been removed"
564                 + " from the maven-checkstyle-plugin. " + "Please use '" + replacement
565                 + "' and refer to the >>Major Version Upgrade to version 3.0.0<< " + "on the plugin site." );
566         }
567     }
568 
569     
570 
571 
572 
573 
574 
575     protected abstract CheckstyleExecutorRequest createRequest()
576             throws MavenReportException;
577 
578     private List<Artifact> collectArtifacts( String hint )
579     {
580         List<Artifact> artifacts = new ArrayList<>();
581 
582         PluginManagement pluginManagement = project.getBuild().getPluginManagement();
583         if ( pluginManagement != null )
584         {
585             artifacts.addAll( getCheckstylePluginDependenciesAsArtifacts( pluginManagement.getPluginsAsMap(), hint ) );
586         }
587 
588         artifacts.addAll( getCheckstylePluginDependenciesAsArtifacts( project.getBuild().getPluginsAsMap(), hint ) );
589 
590         return artifacts;
591     }
592 
593     private List<Artifact> getCheckstylePluginDependenciesAsArtifacts( Map<String, Plugin> plugins, String hint )
594     {
595         List<Artifact> artifacts = new ArrayList<>();
596 
597         Plugin checkstylePlugin = plugins.get( plugin.getGroupId() + ":" + plugin.getArtifactId() );
598         if ( checkstylePlugin != null )
599         {
600             for ( Dependency dep : checkstylePlugin.getDependencies() )
601             {
602              
603                 String depKey = dep.getGroupId() + ":" + dep.getArtifactId();
604                 artifacts.add( plugin.getArtifactMap().get( depKey ) );
605             }
606         }
607         return artifacts;
608     }
609 
610     
611 
612 
613 
614 
615 
616     protected AuditListener getListener()
617         throws MavenReportException
618     {
619         AuditListener listener = null;
620 
621         if ( StringUtils.isNotEmpty( outputFileFormat ) )
622         {
623             File resultFile = outputFile;
624 
625             OutputStream out = getOutputStream( resultFile );
626 
627             if ( "xml".equals( outputFileFormat ) )
628             {
629                 listener = new XMLLogger( out, OutputStreamOptions.CLOSE );
630             }
631             else if ( "plain".equals( outputFileFormat ) )
632             {
633                 listener = new DefaultLogger( out, OutputStreamOptions.CLOSE );
634             }
635             else
636             {
637                 
638                 throw new MavenReportException( "Invalid output file format: (" + outputFileFormat
639                     + "). Must be 'plain' or 'xml'." );
640             }
641         }
642 
643         return listener;
644     }
645 
646     private OutputStream getOutputStream( File file )
647         throws MavenReportException
648     {
649         File parentFile = file.getAbsoluteFile().getParentFile();
650 
651         if ( !parentFile.exists() )
652         {
653             parentFile.mkdirs();
654         }
655 
656         FileOutputStream fileOutputStream;
657         try
658         {
659             fileOutputStream = new FileOutputStream( file );
660         }
661         catch ( FileNotFoundException e )
662         {
663             throw new MavenReportException( "Unable to create output stream: " + file, e );
664         }
665         return fileOutputStream;
666     }
667 
668     
669 
670 
671 
672 
673 
674     protected DefaultLogger getConsoleListener()
675         throws MavenReportException
676     {
677         DefaultLogger consoleListener;
678 
679         if ( useFile == null )
680         {
681             stringOutputStream = new ByteArrayOutputStream();
682             consoleListener = new DefaultLogger( stringOutputStream, OutputStreamOptions.NONE );
683         }
684         else
685         {
686             OutputStream out = getOutputStream( useFile );
687 
688             consoleListener = new DefaultLogger( out, OutputStreamOptions.CLOSE );
689         }
690 
691         return consoleListener;
692     }
693 
694     private void generateReportStatics()
695         throws MavenReportException
696     {
697         ReportResource rresource = new ReportResource( PLUGIN_RESOURCES, outputDirectory );
698         try
699         {
700             rresource.copy( "images/rss.png" );
701         }
702         catch ( IOException e )
703         {
704             throw new MavenReportException( "Unable to copy static resources.", e );
705         }
706     }
707 
708 
709     private String getCopyright()
710     {
711         String copyright;
712         int currentYear = Calendar.getInstance().get( Calendar.YEAR );
713         if ( StringUtils.isNotEmpty( project.getInceptionYear() )
714             && !String.valueOf( currentYear ).equals( project.getInceptionYear() ) )
715         {
716             copyright = project.getInceptionYear() + " - " + currentYear;
717         }
718         else
719         {
720             copyright = String.valueOf( currentYear );
721         }
722 
723         if ( ( project.getOrganization() != null ) && StringUtils.isNotEmpty( project.getOrganization().getName() ) )
724         {
725             copyright = copyright + " " + project.getOrganization().getName();
726         }
727         return copyright;
728     }
729 
730     private void generateMainReport( CheckstyleResults results, ResourceBundle bundle, String configLocation )
731     {
732         CheckstyleReportGenerator generator =
733             new CheckstyleReportGenerator( getSink(), bundle, project.getBasedir(), siteTool, configLocation );
734 
735         generator.setLog( getLog() );
736         generator.setEnableRulesSummary( enableRulesSummary );
737         generator.setEnableSeveritySummary( enableSeveritySummary );
738         generator.setEnableFilesSummary( enableFilesSummary );
739         generator.setEnableRSS( enableRSS );
740         generator.setCheckstyleConfig( results.getConfiguration() );
741         if ( linkXRef )
742         {
743             initializeXrefLocation( generator );
744             if ( generator.getXrefLocation() == null && results.getFileCount() > 0 )
745             {
746                 getLog().warn( "Unable to locate Source XRef to link to - DISABLED" );
747             }
748 
749             initializeXrefTestLocation( generator );
750             if ( generator.getXrefTestLocation() == null && results.getFileCount() > 0 )
751             {
752                 getLog().warn( "Unable to locate Test Source XRef to link to - DISABLED" );
753             }
754 
755             generator.setTestSourceDirectories( getTestSourceDirectories() );
756         }
757         if ( treeWalkerNames != null )
758         {
759             generator.setTreeWalkerNames( treeWalkerNames );
760         }
761         generator.generateReport( results );
762     }
763 
764     private void initializeXrefLocation( CheckstyleReportGenerator generator )
765     {
766       String relativePath = determineRelativePath( xrefLocation );
767       if ( xrefLocation.exists() || checkMavenJxrPluginIsConfigured() )
768       {
769           
770           
771           generator.setXrefLocation( relativePath );
772       }
773     }
774 
775     private void initializeXrefTestLocation( CheckstyleReportGenerator generator )
776     {
777       String relativePath = determineRelativePath( xrefTestLocation );
778       if ( xrefTestLocation.exists() || checkMavenJxrPluginIsConfigured() )
779       {
780           
781           
782           generator.setXrefTestLocation( relativePath );
783       }
784     }
785 
786     private String determineRelativePath( File location )
787     {
788       String relativePath = PathTool.getRelativePath( getOutputDirectory(), location.getAbsolutePath() );
789       if ( relativePath == null || relativePath.trim().isEmpty() )
790       {
791           relativePath = ".";
792       }
793 
794       return relativePath + "/" + location.getName();
795     }
796 
797     private boolean checkMavenJxrPluginIsConfigured()
798     {
799         for ( ReportPlugin report : (Iterable<ReportPlugin>) getProject().getReportPlugins() )
800         {
801             String artifactId = report.getArtifactId();
802             if ( "maven-jxr-plugin".equals( artifactId ) || "jxr-maven-plugin".equals( artifactId ) )
803             {
804                 return true;
805             }
806         }
807 
808         return false;
809     }
810 
811     private static ResourceBundle getBundle( Locale locale )
812     {
813         return ResourceBundle.getBundle( "checkstyle-report", locale, AbstractCheckstyleReport.class.getClassLoader() );
814     }
815 
816     protected List<File> getSourceDirectories()
817     {
818         if ( sourceDirectories == null )
819         {
820             sourceDirectories = project.getCompileSourceRoots();
821         }
822         List<File> sourceDirs = new ArrayList<>( sourceDirectories.size() );
823         for ( String sourceDir : sourceDirectories )
824         {
825             sourceDirs.add( FileUtils.resolveFile( project.getBasedir(), sourceDir ) );
826         }
827         return sourceDirs;
828     }
829 
830     protected List<File> getTestSourceDirectories()
831     {
832         if ( testSourceDirectories == null )
833         {
834             testSourceDirectories = project.getTestCompileSourceRoots();
835         }
836         List<File> testSourceDirs = new ArrayList<>( testSourceDirectories.size() );
837         for ( String testSourceDir : testSourceDirectories )
838         {
839             testSourceDirs.add( FileUtils.resolveFile( project.getBasedir(), testSourceDir ) );
840         }
841         return testSourceDirs;
842     }
843 }