1 package org.apache.maven.plugins.checkstyle.exec;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.ByteArrayInputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.net.MalformedURLException;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.LinkedHashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Properties;
35 import java.util.Set;
36
37 import org.apache.maven.artifact.Artifact;
38 import org.apache.maven.artifact.DependencyResolutionRequiredException;
39 import org.apache.maven.model.Resource;
40 import org.apache.maven.project.MavenProject;
41 import org.codehaus.plexus.component.annotations.Component;
42 import org.codehaus.plexus.component.annotations.Requirement;
43 import org.codehaus.plexus.logging.AbstractLogEnabled;
44 import org.codehaus.plexus.resource.ResourceManager;
45 import org.codehaus.plexus.resource.loader.FileResourceCreationException;
46 import org.codehaus.plexus.resource.loader.FileResourceLoader;
47 import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
48 import org.codehaus.plexus.util.FileUtils;
49 import org.codehaus.plexus.util.StringUtils;
50
51 import com.puppycrawl.tools.checkstyle.Checker;
52 import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
53 import com.puppycrawl.tools.checkstyle.ConfigurationLoader.IgnoredModulesOptions;
54 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
55 import com.puppycrawl.tools.checkstyle.PackageNamesLoader;
56 import com.puppycrawl.tools.checkstyle.PropertiesExpander;
57 import com.puppycrawl.tools.checkstyle.api.AuditListener;
58 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
59 import com.puppycrawl.tools.checkstyle.api.Configuration;
60 import com.puppycrawl.tools.checkstyle.api.FilterSet;
61 import com.puppycrawl.tools.checkstyle.filters.SuppressionsLoader;
62
63
64
65
66
67
68 @Component( role = CheckstyleExecutor.class, hint = "default", instantiationStrategy = "per-lookup" )
69 public class DefaultCheckstyleExecutor
70 extends AbstractLogEnabled
71 implements CheckstyleExecutor
72 {
73 @Requirement( hint = "default" )
74 private ResourceManager locator;
75
76 @Requirement( hint = "license" )
77 private ResourceManager licenseLocator;
78
79 public CheckstyleResults executeCheckstyle( CheckstyleExecutorRequest request )
80 throws CheckstyleExecutorException, CheckstyleException
81 {
82 if ( getLogger().isDebugEnabled() )
83 {
84 getLogger().debug( "executeCheckstyle start headerLocation : " + request.getHeaderLocation() );
85 }
86
87 MavenProject project = request.getProject();
88
89 configureResourceLocator( locator, request, null );
90
91 configureResourceLocator( licenseLocator, request, request.getLicenseArtifacts() );
92
93
94
95
96 List<File> files;
97 try
98 {
99 files = getFilesToProcess( request );
100 }
101 catch ( IOException e )
102 {
103 throw new CheckstyleExecutorException( "Error getting files to process", e );
104 }
105
106 final String suppressionsFilePath = getSuppressionsFilePath( request );
107 FilterSet filterSet = getSuppressionsFilterSet( suppressionsFilePath );
108
109 Checker checker = new Checker();
110
111
112 List<String> classPathStrings = new ArrayList<>();
113 List<String> outputDirectories = new ArrayList<>();
114
115
116 Collection<File> sourceDirectories = null;
117 Collection<File> testSourceDirectories = request.getTestSourceDirectories();
118
119
120 Map<MavenProject, Collection<File>> sourceDirectoriesByProject = new HashMap<>();
121 Map<MavenProject, Collection<File>> testSourceDirectoriesByProject = new HashMap<>();
122
123 if ( request.isAggregate() )
124 {
125 for ( MavenProject childProject : request.getReactorProjects() )
126 {
127 sourceDirectories = new ArrayList<>( childProject.getCompileSourceRoots().size() );
128 List<String> compileSourceRoots = childProject.getCompileSourceRoots();
129 for ( String compileSourceRoot : compileSourceRoots )
130 {
131 sourceDirectories.add( new File( compileSourceRoot ) );
132 }
133 sourceDirectoriesByProject.put( childProject, sourceDirectories );
134
135 testSourceDirectories = new ArrayList<>( childProject.getTestCompileSourceRoots().size() );
136 List<String> testCompileSourceRoots = childProject.getTestCompileSourceRoots();
137 for ( String testCompileSourceRoot : testCompileSourceRoots )
138 {
139 testSourceDirectories.add( new File( testCompileSourceRoot ) );
140 }
141 testSourceDirectoriesByProject.put( childProject, testSourceDirectories );
142
143 prepareCheckstylePaths( request, childProject, classPathStrings, outputDirectories,
144 sourceDirectories, testSourceDirectories );
145 }
146 }
147 else
148 {
149 sourceDirectories = request.getSourceDirectories();
150 prepareCheckstylePaths( request, project, classPathStrings, outputDirectories, sourceDirectories,
151 testSourceDirectories );
152 }
153
154 checker.setModuleClassLoader( Thread.currentThread().getContextClassLoader() );
155
156 if ( filterSet != null )
157 {
158 checker.addFilter( filterSet );
159 }
160 Configuration configuration = getConfiguration( request );
161 checker.configure( configuration );
162
163 AuditListener listener = request.getListener();
164
165 if ( listener != null )
166 {
167 checker.addListener( listener );
168 }
169
170 if ( request.isConsoleOutput() )
171 {
172 checker.addListener( request.getConsoleListener() );
173 }
174
175 CheckstyleCheckerListener checkerListener = new CheckstyleCheckerListener( configuration );
176 if ( request.isAggregate() )
177 {
178 for ( MavenProject childProject : request.getReactorProjects() )
179 {
180 sourceDirectories = sourceDirectoriesByProject.get( childProject );
181 testSourceDirectories = testSourceDirectoriesByProject.get( childProject );
182 addSourceDirectory( checkerListener, sourceDirectories,
183 testSourceDirectories,
184 childProject.getResources(), request );
185 }
186 }
187 else
188 {
189 addSourceDirectory( checkerListener, sourceDirectories, testSourceDirectories, request.getResources(),
190 request );
191 }
192
193 checker.addListener( checkerListener );
194
195 int nbErrors = checker.process( files );
196
197 checker.destroy();
198
199 if ( request.getStringOutputStream() != null )
200 {
201 String message = request.getStringOutputStream().toString().trim();
202
203 if ( message.length() > 0 )
204 {
205 getLogger().info( message );
206 }
207 }
208
209 if ( nbErrors > 0 )
210 {
211 StringBuilder message = new StringBuilder( "There " );
212 if ( nbErrors == 1 )
213 {
214 message.append( "is" );
215 }
216 else
217 {
218 message.append( "are" );
219 }
220 message.append( " " );
221 message.append( nbErrors );
222 message.append( " error" );
223 if ( nbErrors != 1 )
224 {
225 message.append( "s" );
226 }
227 message.append( " reported by Checkstyle" );
228 String version = getCheckstyleVersion();
229 if ( version != null )
230 {
231 message.append( " " );
232 message.append( version );
233 }
234 message.append( " with " );
235 message.append( request.getConfigLocation() );
236 message.append( " ruleset." );
237
238 if ( request.isFailsOnError() )
239 {
240
241
242
243 throw new CheckstyleExecutorException( message.toString() );
244 }
245 else
246 {
247 getLogger().info( message.toString() );
248 }
249 }
250
251 return checkerListener.getResults();
252 }
253
254 protected void addSourceDirectory( CheckstyleCheckerListener sinkListener, Collection<File> sourceDirectories,
255 Collection<File> testSourceDirectories, List<Resource> resources,
256 CheckstyleExecutorRequest request )
257 {
258 if ( sourceDirectories != null )
259 {
260 for ( File sourceDirectory : sourceDirectories )
261 {
262 if ( sourceDirectory.exists() )
263 {
264 sinkListener.addSourceDirectory( sourceDirectory );
265 }
266 }
267 }
268
269 if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectories != null ) )
270 {
271 for ( File testSourceDirectory : testSourceDirectories )
272 {
273 if ( testSourceDirectory.isDirectory() )
274 {
275 sinkListener.addSourceDirectory( testSourceDirectory );
276 }
277 }
278 }
279
280 if ( resources != null )
281 {
282 for ( Resource resource : resources )
283 {
284 if ( resource.getDirectory() != null )
285 {
286 File resourcesDirectory = new File( resource.getDirectory() );
287 if ( resourcesDirectory.exists() && resourcesDirectory.isDirectory() )
288 {
289 sinkListener.addSourceDirectory( resourcesDirectory );
290 getLogger().debug( "Added '" + resourcesDirectory.getAbsolutePath()
291 + "' as a source directory." );
292 }
293 }
294 }
295 }
296 }
297
298 public Configuration getConfiguration( CheckstyleExecutorRequest request )
299 throws CheckstyleExecutorException
300 {
301 try
302 {
303
304
305
306 ClassLoader checkstyleClassLoader = PackageNamesLoader.class.getClassLoader();
307 Thread.currentThread().setContextClassLoader( checkstyleClassLoader );
308 String configFile = getConfigFile( request );
309 Properties overridingProperties = getOverridingProperties( request );
310 IgnoredModulesOptions omitIgnoredModules;
311 if ( request.isOmitIgnoredModules() )
312 {
313 omitIgnoredModules = IgnoredModulesOptions.OMIT;
314 }
315 else
316 {
317 omitIgnoredModules = IgnoredModulesOptions.EXECUTE;
318 }
319 Configuration config =
320 ConfigurationLoader.loadConfiguration( configFile, new PropertiesExpander( overridingProperties ),
321 omitIgnoredModules );
322 String effectiveEncoding = StringUtils.isNotEmpty( request.getEncoding() ) ? request.getEncoding() : System
323 .getProperty( "file.encoding", "UTF-8" );
324
325 if ( StringUtils.isEmpty( request.getEncoding() ) )
326 {
327 getLogger().warn( "File encoding has not been set, using platform encoding " + effectiveEncoding
328 + ", i.e. build is platform dependent!" );
329 }
330
331 if ( "Checker".equals( config.getName() )
332 || "com.puppycrawl.tools.checkstyle.Checker".equals( config.getName() ) )
333 {
334 if ( config instanceof DefaultConfiguration )
335 {
336
337 addAttributeIfNotExists( (DefaultConfiguration) config, "charset", effectiveEncoding );
338 addAttributeIfNotExists( (DefaultConfiguration) config, "cacheFile", request.getCacheFile() );
339 }
340 else
341 {
342 getLogger().warn( "Failed to configure file encoding on module " + config );
343 }
344 }
345 return config;
346 }
347 catch ( CheckstyleException e )
348 {
349 throw new CheckstyleExecutorException( "Failed during checkstyle configuration", e );
350 }
351 }
352
353 private void addAttributeIfNotExists( DefaultConfiguration config, String name, String value )
354 {
355 try
356 {
357
358 if ( config.getAttribute( name ) == null )
359 {
360 config.addAttribute( name, value );
361 }
362 }
363 catch ( CheckstyleException ex )
364 {
365
366 config.addAttribute( name, value );
367 }
368 }
369
370 private void prepareCheckstylePaths( CheckstyleExecutorRequest request, MavenProject project,
371 List<String> classPathStrings, List<String> outputDirectories,
372 Collection<File> sourceDirectories, Collection<File> testSourceDirectories )
373 throws CheckstyleExecutorException
374 {
375 try
376 {
377 outputDirectories.add( project.getBuild().getOutputDirectory() );
378
379 if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectories != null )
380 && anyDirectoryExists( testSourceDirectories ) )
381 {
382 classPathStrings.addAll( project.getTestClasspathElements() );
383 outputDirectories.add( project.getBuild().getTestOutputDirectory() );
384 }
385 else
386 {
387 classPathStrings.addAll( project.getCompileClasspathElements() );
388 }
389 }
390 catch ( DependencyResolutionRequiredException e )
391 {
392 throw new CheckstyleExecutorException( e.getMessage(), e );
393 }
394 }
395
396 private boolean anyDirectoryExists( Collection<File> files )
397 {
398 for ( File file : files )
399 {
400 if ( file.isDirectory() )
401 {
402 return true;
403 }
404 }
405 return false;
406 }
407
408
409
410
411
412
413
414 private String getCheckstyleVersion()
415 {
416 Package checkstyleApiPackage = Configuration.class.getPackage();
417
418 return ( checkstyleApiPackage == null ) ? null : checkstyleApiPackage.getImplementationVersion();
419 }
420
421 private Properties getOverridingProperties( CheckstyleExecutorRequest request )
422 throws CheckstyleExecutorException
423 {
424 Properties p = new Properties();
425 try
426 {
427 if ( request.getPropertiesLocation() != null )
428 {
429 if ( getLogger().isDebugEnabled() )
430 {
431 getLogger().debug( "request.getPropertiesLocation() " + request.getPropertiesLocation() );
432 }
433
434 File propertiesFile = locator.getResourceAsFile( request.getPropertiesLocation(),
435 "checkstyle-checker.properties" );
436
437 if ( propertiesFile != null )
438 {
439 try ( InputStream in = new FileInputStream( propertiesFile ) )
440 {
441 p.load( in );
442 }
443 }
444 }
445
446 if ( StringUtils.isNotEmpty( request.getPropertyExpansion() ) )
447 {
448 String propertyExpansion = request.getPropertyExpansion();
449
450 propertyExpansion = StringUtils.replace( propertyExpansion, "\\", "\\\\" );
451 p.load( new ByteArrayInputStream( propertyExpansion.getBytes() ) );
452 }
453
454
455
456
457 String headerLocation = request.getHeaderLocation();
458 if ( "config/maven_checks.xml".equals( request.getConfigLocation() ) )
459 {
460
461 if ( "LICENSE.txt".equals( request.getHeaderLocation() ) )
462 {
463 headerLocation = "config/maven-header.txt";
464 }
465 }
466 if ( getLogger().isDebugEnabled() )
467 {
468 getLogger().debug( "headerLocation " + headerLocation );
469 }
470
471 if ( StringUtils.isNotEmpty( headerLocation ) )
472 {
473 try
474 {
475 File headerFile = licenseLocator.getResourceAsFile( headerLocation, "checkstyle-header.txt" );
476
477 if ( headerFile != null )
478 {
479 p.setProperty( "checkstyle.header.file", headerFile.getAbsolutePath() );
480 }
481 }
482 catch ( FileResourceCreationException | ResourceNotFoundException e )
483 {
484 getLogger().debug( "Unable to process header location: " + headerLocation );
485 getLogger().debug( "Checkstyle will throw exception if ${checkstyle.header.file} is used" );
486 }
487 }
488
489 if ( request.getCacheFile() != null )
490 {
491 p.setProperty( "checkstyle.cache.file", request.getCacheFile() );
492 }
493 }
494 catch ( IOException | ResourceNotFoundException | FileResourceCreationException e )
495 {
496 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
497 }
498 if ( request.getSuppressionsFileExpression() != null )
499 {
500 String suppressionsFilePath = getSuppressionsFilePath( request );
501
502 if ( suppressionsFilePath != null )
503 {
504 p.setProperty( request.getSuppressionsFileExpression(), suppressionsFilePath );
505 }
506 }
507
508 return p;
509 }
510
511 private List<File> getFilesToProcess( CheckstyleExecutorRequest request )
512 throws IOException
513 {
514 StringBuilder excludesStr = new StringBuilder();
515
516 if ( StringUtils.isNotEmpty( request.getExcludes() ) )
517 {
518 excludesStr.append( request.getExcludes() );
519 }
520
521 String[] defaultExcludes = FileUtils.getDefaultExcludes();
522 for ( String defaultExclude : defaultExcludes )
523 {
524 if ( excludesStr.length() > 0 )
525 {
526 excludesStr.append( "," );
527 }
528
529 excludesStr.append( defaultExclude );
530 }
531
532 Set<File> files = new LinkedHashSet<>();
533 if ( request.isAggregate() )
534 {
535 for ( MavenProject project : request.getReactorProjects() )
536 {
537 Set<File> sourceDirectories = new LinkedHashSet<>();
538
539
540 List<String> compileSourceRoots = project.getCompileSourceRoots();
541 for ( String compileSourceRoot : compileSourceRoots )
542 {
543 sourceDirectories.add( new File( compileSourceRoot ) );
544 }
545
546 Set<File> testSourceDirectories = new LinkedHashSet<>();
547
548 List<String> testCompileSourceRoots = project.getTestCompileSourceRoots();
549 for ( String testCompileSourceRoot : testCompileSourceRoots )
550 {
551 testSourceDirectories.add( new File( testCompileSourceRoot ) );
552 }
553
554 addFilesToProcess( request, sourceDirectories, project.getResources(), project.getTestResources(),
555 files, testSourceDirectories );
556 }
557 }
558 else
559 {
560 Collection<File> sourceDirectories = request.getSourceDirectories();
561 addFilesToProcess( request, sourceDirectories, request.getResources(),
562 request.getTestResources(), files, request.getTestSourceDirectories() );
563 }
564
565 getLogger().debug( "Added " + files.size() + " files to process." );
566
567 return new ArrayList<>( files );
568 }
569
570 private void addFilesToProcess( CheckstyleExecutorRequest request, Collection<File> sourceDirectories,
571 List<Resource> resources, List<Resource> testResources, Collection<File> files,
572 Collection<File> testSourceDirectories )
573 throws IOException
574 {
575 if ( sourceDirectories != null )
576 {
577 for ( File sourceDirectory : sourceDirectories )
578 {
579 if ( sourceDirectory.isDirectory() )
580 {
581 final List<File> sourceFiles =
582 FileUtils.getFiles( sourceDirectory, request.getIncludes(), request.getExcludes() );
583 files.addAll( sourceFiles );
584 getLogger().debug( "Added " + sourceFiles.size() + " source files found in '"
585 + sourceDirectory.getAbsolutePath() + "'." );
586 }
587 }
588 }
589
590 if ( request.isIncludeTestSourceDirectory() && testSourceDirectories != null )
591 {
592 for ( File testSourceDirectory : testSourceDirectories )
593 {
594 if ( testSourceDirectory.isDirectory() )
595 {
596 final List<File> testSourceFiles =
597 FileUtils.getFiles( testSourceDirectory, request.getIncludes(), request.getExcludes() );
598
599 files.addAll( testSourceFiles );
600 getLogger().debug( "Added " + testSourceFiles.size() + " test source files found in '"
601 + testSourceDirectory.getAbsolutePath() + "'." );
602 }
603 }
604 }
605
606 if ( resources != null && request.isIncludeResources() )
607 {
608 addResourceFilesToProcess( request, resources, files );
609 }
610 else
611 {
612 getLogger().debug( "No resources found in this project." );
613 }
614
615 if ( testResources != null && request.isIncludeTestResources() )
616 {
617 addResourceFilesToProcess( request, testResources, files );
618 }
619 else
620 {
621 getLogger().debug( "No test resources found in this project." );
622 }
623 }
624
625 private void addResourceFilesToProcess( CheckstyleExecutorRequest request, List<Resource> resources,
626 Collection<File> files )
627 throws IOException
628 {
629 for ( Resource resource : resources )
630 {
631 if ( resource.getDirectory() != null )
632 {
633 File resourcesDirectory = new File( resource.getDirectory() );
634 if ( resourcesDirectory.isDirectory() )
635 {
636 String includes = request.getResourceIncludes();
637 String excludes = request.getResourceExcludes();
638
639
640 if ( resourcesDirectory.equals( request.getProject().getBasedir() ) )
641 {
642 String resourceIncludes = StringUtils.join( resource.getIncludes().iterator(), "," );
643 if ( StringUtils.isEmpty( includes ) )
644 {
645 includes = resourceIncludes;
646 }
647 else
648 {
649 includes += "," + resourceIncludes;
650 }
651
652 String resourceExcludes = StringUtils.join( resource.getExcludes().iterator(), "," );
653 if ( StringUtils.isEmpty( excludes ) )
654 {
655 excludes = resourceExcludes;
656 }
657 else
658 {
659 excludes += "," + resourceExcludes;
660 }
661 }
662
663 List<File> resourceFiles =
664 FileUtils.getFiles( resourcesDirectory, includes, excludes );
665 files.addAll( resourceFiles );
666 getLogger().debug( "Added " + resourceFiles.size() + " resource files found in '"
667 + resourcesDirectory.getAbsolutePath() + "'." );
668 }
669 else
670 {
671 getLogger().debug( "The resources directory '" + resourcesDirectory.getAbsolutePath()
672 + "' does not exist or is not a directory." );
673 }
674 }
675 }
676 }
677
678 private FilterSet getSuppressionsFilterSet( final String suppressionsFilePath )
679 throws CheckstyleExecutorException
680 {
681 if ( suppressionsFilePath == null )
682 {
683 return null;
684 }
685
686 try
687 {
688 return SuppressionsLoader.loadSuppressions( suppressionsFilePath );
689 }
690 catch ( CheckstyleException ce )
691 {
692 throw new CheckstyleExecutorException( "Failed to load suppressions file from: "
693 + suppressionsFilePath, ce );
694 }
695 }
696
697 private String getSuppressionsFilePath( final CheckstyleExecutorRequest request )
698 throws CheckstyleExecutorException
699 {
700 final String suppressionsLocation = request.getSuppressionsLocation();
701 if ( StringUtils.isEmpty( suppressionsLocation ) )
702 {
703 return null;
704 }
705
706 try
707 {
708 File suppressionsFile = locator.getResourceAsFile( suppressionsLocation, "checkstyle-suppressions.xml" );
709 return suppressionsFile == null ? null : suppressionsFile.getAbsolutePath();
710 }
711 catch ( ResourceNotFoundException e )
712 {
713 throw new CheckstyleExecutorException( "Unable to find suppressions file at location: "
714 + suppressionsLocation, e );
715 }
716 catch ( FileResourceCreationException e )
717 {
718 throw new CheckstyleExecutorException( "Unable to process suppressions file location: "
719 + suppressionsLocation, e );
720 }
721 }
722
723 private String getConfigFile( CheckstyleExecutorRequest request )
724 throws CheckstyleExecutorException
725 {
726 try
727 {
728 if ( getLogger().isDebugEnabled() )
729 {
730 getLogger().debug( "request.getConfigLocation() " + request.getConfigLocation() );
731 }
732
733 File configFile = locator.getResourceAsFile( request.getConfigLocation(), "checkstyle-checker.xml" );
734 if ( configFile == null )
735 {
736 throw new CheckstyleExecutorException( "Unable to process config location: "
737 + request.getConfigLocation() );
738 }
739 return configFile.getAbsolutePath();
740 }
741 catch ( ResourceNotFoundException e )
742 {
743 throw new CheckstyleExecutorException( "Unable to find configuration file at location: "
744 + request.getConfigLocation(), e );
745 }
746 catch ( FileResourceCreationException e )
747 {
748 throw new CheckstyleExecutorException( "Unable to process configuration file at location: "
749 + request.getConfigLocation(), e );
750 }
751
752 }
753
754
755
756
757
758
759
760 private void configureResourceLocator( final ResourceManager resourceManager,
761 final CheckstyleExecutorRequest request,
762 final List<Artifact> additionalArtifacts )
763 {
764 final MavenProject project = request.getProject();
765 resourceManager.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
766
767
768 MavenProject parent = project;
769 while ( parent != null && parent.getFile() != null )
770 {
771
772
773
774 File dir = parent.getFile().getParentFile();
775 resourceManager.addSearchPath( FileResourceLoader.ID, dir.getAbsolutePath() );
776 parent = parent.getParent();
777 }
778 resourceManager.addSearchPath( "url", "" );
779
780
781 if ( additionalArtifacts != null )
782 {
783 for ( Artifact licenseArtifact : additionalArtifacts )
784 {
785 try
786 {
787 resourceManager.addSearchPath( "jar", "jar:" + licenseArtifact.getFile().toURI().toURL() );
788 }
789 catch ( MalformedURLException e )
790 {
791
792 }
793 }
794 }
795 }
796 }