View Javadoc
1   package org.apache.maven.plugins.checkstyle.exec;
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.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.net.URL;
29  import java.net.URLClassLoader;
30  import java.security.AccessController;
31  import java.security.PrivilegedAction;
32  import java.util.ArrayList;
33  import java.util.Collection;
34  import java.util.HashMap;
35  import java.util.LinkedHashSet;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Properties;
39  import java.util.Set;
40  
41  import org.apache.maven.artifact.Artifact;
42  import org.apache.maven.artifact.DependencyResolutionRequiredException;
43  import org.apache.maven.model.Resource;
44  import org.apache.maven.project.MavenProject;
45  import org.codehaus.plexus.component.annotations.Component;
46  import org.codehaus.plexus.component.annotations.Requirement;
47  import org.codehaus.plexus.logging.AbstractLogEnabled;
48  import org.codehaus.plexus.resource.ResourceManager;
49  import org.codehaus.plexus.resource.loader.FileResourceCreationException;
50  import org.codehaus.plexus.resource.loader.FileResourceLoader;
51  import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
52  import org.codehaus.plexus.util.FileUtils;
53  import org.codehaus.plexus.util.StringUtils;
54  
55  import com.puppycrawl.tools.checkstyle.Checker;
56  import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
57  import com.puppycrawl.tools.checkstyle.ConfigurationLoader.IgnoredModulesOptions;
58  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
59  import com.puppycrawl.tools.checkstyle.PackageNamesLoader;
60  import com.puppycrawl.tools.checkstyle.PropertiesExpander;
61  import com.puppycrawl.tools.checkstyle.api.AuditListener;
62  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
63  import com.puppycrawl.tools.checkstyle.api.Configuration;
64  import com.puppycrawl.tools.checkstyle.api.FilterSet;
65  import com.puppycrawl.tools.checkstyle.filters.SuppressionsLoader;
66  
67  /**
68   * @author Olivier Lamy
69   * @since 2.5
70   *
71   */
72  @Component( role = CheckstyleExecutor.class, hint = "default", instantiationStrategy = "per-lookup" )
73  public class DefaultCheckstyleExecutor
74      extends AbstractLogEnabled
75      implements CheckstyleExecutor
76  {
77      @Requirement( hint = "default" )
78      private ResourceManager locator;
79      
80      @Requirement( hint = "license" )
81      private ResourceManager licenseLocator;
82  
83      public CheckstyleResults executeCheckstyle( CheckstyleExecutorRequest request )
84          throws CheckstyleExecutorException, CheckstyleException
85      {
86          if ( getLogger().isDebugEnabled() )
87          {
88              getLogger().debug( "executeCheckstyle start headerLocation : " + request.getHeaderLocation() );
89          }
90  
91          MavenProject project = request.getProject();
92  
93          configureResourceLocator( locator, request, null );
94          
95          configureResourceLocator( licenseLocator, request, request.getLicenseArtifacts() );
96  
97          // Config is less critical than License, locator can still be used.
98          // configureResourceLocator( configurationLocator, request, request.getConfigurationArtifacts() );
99  
100         List<File> files;
101         try
102         {
103             files = getFilesToProcess( request );
104         }
105         catch ( IOException e )
106         {
107             throw new CheckstyleExecutorException( "Error getting files to process", e );
108         }
109 
110         final String suppressionsFilePath = getSuppressionsFilePath( request );
111         FilterSet filterSet = getSuppressionsFilterSet( suppressionsFilePath );
112 
113         Checker checker = new Checker();
114 
115         // setup classloader, needed to avoid "Unable to get class information for ..." errors
116         List<String> classPathStrings = new ArrayList<>();
117         List<String> outputDirectories = new ArrayList<>();
118         
119         // stand-alone
120         Collection<File> sourceDirectories = null;
121         Collection<File> testSourceDirectories = request.getTestSourceDirectories();
122         
123         // aggregator
124         Map<MavenProject, Collection<File>> sourceDirectoriesByProject = new HashMap<>();
125         Map<MavenProject, Collection<File>> testSourceDirectoriesByProject = new HashMap<>();
126         
127         if ( request.isAggregate() )
128         {
129             for ( MavenProject childProject : request.getReactorProjects() )
130             {
131                 sourceDirectories = new ArrayList<>( childProject.getCompileSourceRoots().size() );
132                 List<String> compileSourceRoots = childProject.getCompileSourceRoots();
133                 for ( String compileSourceRoot : compileSourceRoots )
134                 {
135                     sourceDirectories.add( new File( compileSourceRoot ) );
136                 }
137                 sourceDirectoriesByProject.put( childProject, sourceDirectories );
138                 
139                 testSourceDirectories = new ArrayList<>( childProject.getTestCompileSourceRoots().size() );
140                 List<String> testCompileSourceRoots = childProject.getTestCompileSourceRoots();
141                 for ( String testCompileSourceRoot : testCompileSourceRoots )
142                 {
143                     testSourceDirectories.add( new File( testCompileSourceRoot ) );
144                 }
145                 testSourceDirectoriesByProject.put( childProject, testSourceDirectories );
146                 
147                 prepareCheckstylePaths( request, childProject, classPathStrings, outputDirectories,
148                                         sourceDirectories, testSourceDirectories );
149             }
150         }
151         else
152         {
153             sourceDirectories = request.getSourceDirectories();
154             prepareCheckstylePaths( request, project, classPathStrings, outputDirectories, sourceDirectories,
155                                     testSourceDirectories );
156         }
157 
158         setUpCheckstyleClassloader( checker, classPathStrings, outputDirectories );
159 
160         checker.setModuleClassLoader( Thread.currentThread().getContextClassLoader() );
161 
162         if ( filterSet != null )
163         {
164             checker.addFilter( filterSet );
165         }
166         Configuration configuration = getConfiguration( request );
167         checker.configure( configuration );
168 
169         AuditListener listener = request.getListener();
170 
171         if ( listener != null )
172         {
173             checker.addListener( listener );
174         }
175 
176         if ( request.isConsoleOutput() )
177         {
178             checker.addListener( request.getConsoleListener() );
179         }
180 
181         CheckstyleCheckerListener checkerListener = new CheckstyleCheckerListener( configuration );
182         if ( request.isAggregate() )
183         {
184             for ( MavenProject childProject : request.getReactorProjects() )
185             {
186                 sourceDirectories = sourceDirectoriesByProject.get( childProject );
187                 testSourceDirectories = testSourceDirectoriesByProject.get( childProject );
188                 addSourceDirectory( checkerListener, sourceDirectories,
189                                     testSourceDirectories,
190                                     childProject.getResources(), request );
191             }
192         }
193         else
194         {
195             addSourceDirectory( checkerListener, sourceDirectories, testSourceDirectories, request.getResources(),
196                                 request );
197         }
198 
199         checker.addListener( checkerListener );
200 
201         int nbErrors = checker.process( files );
202 
203         checker.destroy();
204 
205         if ( request.getStringOutputStream() != null )
206         {
207             String message = request.getStringOutputStream().toString().trim();
208 
209             if ( message.length() > 0 )
210             {
211                 getLogger().info( message );
212             }
213         }
214 
215         if ( nbErrors > 0 )
216         {
217             StringBuilder message = new StringBuilder( "There " );
218             if ( nbErrors == 1 )
219             {
220                 message.append( "is" );
221             }
222             else
223             {
224                 message.append( "are" );
225             }
226             message.append( " " );
227             message.append( nbErrors );
228             message.append( " error" );
229             if ( nbErrors != 1 )
230             {
231                 message.append( "s" );
232             }
233             message.append( " reported by Checkstyle" );
234             String version = getCheckstyleVersion();
235             if ( version != null )
236             {
237                 message.append( " " );
238                 message.append( version );
239             }
240             message.append( " with " );
241             message.append( request.getConfigLocation() );
242             message.append( " ruleset." );
243 
244             if ( request.isFailsOnError() )
245             {
246                 // TODO: should be a failure, not an error. Report is not meant to
247                 // throw an exception here (so site would
248                 // work regardless of config), but should record this information
249                 throw new CheckstyleExecutorException( message.toString() );
250             }
251             else
252             {
253                 getLogger().info( message.toString() );
254             }
255         }
256 
257         return checkerListener.getResults();
258     }
259 
260     private void setUpCheckstyleClassloader( Checker checker,
261                                              List<String> classPathStrings,
262                                              List<String> outputDirectories )
263         throws CheckstyleExecutorException
264     {
265         final List<URL> urls = new ArrayList<>( classPathStrings.size() );
266 
267         for ( String path : classPathStrings )
268         {
269             try
270             {
271                 urls.add( new File( path ).toURI().toURL() );
272             }
273             catch ( MalformedURLException e )
274             {
275                 throw new CheckstyleExecutorException( e.getMessage(), e );
276             }
277         }
278 
279         for ( String outputDirectoryString : outputDirectories )
280         {
281             try
282             {
283                 if ( outputDirectoryString != null )
284                 {
285                     File outputDirectoryFile = new File( outputDirectoryString );
286                     if ( outputDirectoryFile.exists() )
287                     {
288                         URL outputDirectoryUrl = outputDirectoryFile.toURI().toURL();
289                         getLogger().debug( "Adding the outputDirectory " + outputDirectoryUrl.toString()
290                                                + " to the Checkstyle class path" );
291                         urls.add( outputDirectoryUrl );
292                     }
293                 }
294             }
295             catch ( MalformedURLException e )
296             {
297                 throw new CheckstyleExecutorException( e.getMessage(), e );
298             }
299         }
300 
301         URLClassLoader projectClassLoader = AccessController.doPrivileged( new PrivilegedAction<URLClassLoader>()
302         {
303             public URLClassLoader run()
304             {
305                 return new URLClassLoader( urls.toArray( new URL[0] ), null );
306             }
307         } );
308 
309         /*
310          * MCHECKSTYLE-381: More recent Checkstyle versions will drop the setClassLoader() method.
311          * However, it was used before Checkstyle 8.25.
312          */
313         try
314         {
315             checker.setClassLoader( projectClassLoader );
316             /*
317              * MCHECKSTYLE-387: If the previous method call was successful, emit a warning that the user is using
318              * an old version of checkstyle.
319              */
320             getLogger().warn( "Old version of checkstyle detected. Consider updating to >= v8.30" );
321             getLogger().warn( "For more information see: "
322                 + "https://maven.apache.org/plugins/maven-checkstyle-plugin/examples/upgrading-checkstyle.html" );
323         }
324         catch ( NoSuchMethodError ignored )
325         {
326             /*
327              * The current checkstyle version does not support the method setClassLoader anymore.
328              * This is expected. The method call is being retained for less recent versions of checkstyle.
329              */
330         }
331 
332     }
333 
334     protected void addSourceDirectory( CheckstyleCheckerListener sinkListener, Collection<File> sourceDirectories,
335                                        Collection<File> testSourceDirectories, List<Resource> resources,
336                                        CheckstyleExecutorRequest request )
337     {
338         if ( sourceDirectories != null )
339         {
340             for ( File sourceDirectory : sourceDirectories )
341             {
342                 if ( sourceDirectory.exists() )
343                 {
344                     sinkListener.addSourceDirectory( sourceDirectory );
345                 }
346             }
347         }
348 
349         if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectories != null ) )
350         {
351             for ( File testSourceDirectory : testSourceDirectories )
352             {
353                 if ( testSourceDirectory.isDirectory() )
354                 {
355                     sinkListener.addSourceDirectory( testSourceDirectory );
356                 }
357             }
358         }
359 
360         if ( resources != null )
361         {
362             for ( Resource resource : resources )
363             {
364                 if ( resource.getDirectory() != null )
365                 {
366                     File resourcesDirectory = new File( resource.getDirectory() );
367                     if ( resourcesDirectory.exists() && resourcesDirectory.isDirectory() )
368                     {
369                         sinkListener.addSourceDirectory( resourcesDirectory );
370                         getLogger().debug( "Added '" + resourcesDirectory.getAbsolutePath()
371                                 + "' as a source directory." );
372                     }
373                 }
374             }
375         }
376     }
377 
378     public Configuration getConfiguration( CheckstyleExecutorRequest request )
379         throws CheckstyleExecutorException
380     {
381         try
382         {
383             // Checkstyle will always use the context classloader in order
384             // to load resources (dtds),
385             // so we have to fix it
386             ClassLoader checkstyleClassLoader = PackageNamesLoader.class.getClassLoader();
387             Thread.currentThread().setContextClassLoader( checkstyleClassLoader );
388             String configFile = getConfigFile( request );
389             Properties overridingProperties = getOverridingProperties( request );
390             IgnoredModulesOptions omitIgnoredModules;
391             if ( request.isOmitIgnoredModules() )
392             {
393                 omitIgnoredModules = IgnoredModulesOptions.OMIT;
394             }
395             else
396             {
397                 omitIgnoredModules = IgnoredModulesOptions.EXECUTE;
398             }
399             Configuration config =
400                 ConfigurationLoader.loadConfiguration( configFile, new PropertiesExpander( overridingProperties ),
401                                                        omitIgnoredModules );
402             String effectiveEncoding = StringUtils.isNotEmpty( request.getEncoding() ) ? request.getEncoding() : System
403                 .getProperty( "file.encoding", "UTF-8" );
404             
405             if ( StringUtils.isEmpty( request.getEncoding() ) )
406             {
407                 getLogger().warn( "File encoding has not been set, using platform encoding " + effectiveEncoding
408                                       + ", i.e. build is platform dependent!" );
409             }
410 
411             if ( "Checker".equals( config.getName() )
412                     || "com.puppycrawl.tools.checkstyle.Checker".equals( config.getName() ) )
413             {
414                 if ( config instanceof DefaultConfiguration )
415                 {
416                     // MCHECKSTYLE-173 Only add the "charset" attribute if it has not been set
417                     addAttributeIfNotExists( (DefaultConfiguration) config, "charset", effectiveEncoding );
418                     addAttributeIfNotExists( (DefaultConfiguration) config, "cacheFile", request.getCacheFile() );
419                 }
420                 else
421                 {
422                     getLogger().warn( "Failed to configure file encoding on module " + config );
423                 }
424             }
425             return config;
426         }
427         catch ( CheckstyleException e )
428         {
429             throw new CheckstyleExecutorException( "Failed during checkstyle configuration", e );
430         }
431     }
432 
433     private void addAttributeIfNotExists( DefaultConfiguration config, String name, String value )
434     {
435         try
436         {
437             // MCHECKSTYLE-132 DefaultConfiguration addAttribute has changed in checkstyle 5.3
438             if ( config.getAttribute( name ) == null )
439             {
440                 config.addAttribute( name, value );
441             }
442         }
443         catch ( CheckstyleException ex )
444         {
445             // MCHECKSTYLE-159 Checkstyle 5.4+ throws an exception when trying to access an attribute that doesn't exist
446             config.addAttribute( name, value );
447         }
448     }
449 
450     private void prepareCheckstylePaths( CheckstyleExecutorRequest request, MavenProject project,
451                                          List<String> classPathStrings, List<String> outputDirectories,
452                                          Collection<File> sourceDirectories, Collection<File> testSourceDirectories )
453         throws CheckstyleExecutorException
454     {
455         try
456         {
457             outputDirectories.add( project.getBuild().getOutputDirectory() );
458 
459             if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectories != null )
460                 && anyDirectoryExists( testSourceDirectories ) )
461             {
462                 classPathStrings.addAll( project.getTestClasspathElements() );
463                 outputDirectories.add( project.getBuild().getTestOutputDirectory() );
464             }
465             else
466             {
467                 classPathStrings.addAll( project.getCompileClasspathElements() );
468             }
469         }
470         catch ( DependencyResolutionRequiredException e )
471         {
472             throw new CheckstyleExecutorException( e.getMessage(), e );
473         }
474     }
475     
476     private boolean anyDirectoryExists( Collection<File> files )
477     {
478         for ( File file : files )
479         {
480             if ( file.isDirectory() )
481             {
482                 return true;
483             }
484         }
485         return false;
486     }
487 
488     /**
489      * Get the effective Checkstyle version at runtime.
490      * @return the MANIFEST implementation version of Checkstyle API package (can be <code>null</code>)
491      *
492      *@todo Copied from CheckstyleReportGenerator - move to a utility class
493      */
494     private String getCheckstyleVersion()
495     {
496         Package checkstyleApiPackage = Configuration.class.getPackage();
497 
498         return ( checkstyleApiPackage == null ) ? null : checkstyleApiPackage.getImplementationVersion();
499     }
500 
501     private Properties getOverridingProperties( CheckstyleExecutorRequest request )
502         throws CheckstyleExecutorException
503     {
504         Properties p = new Properties();
505         try
506         {
507             if ( request.getPropertiesLocation() != null )
508             {
509                 if ( getLogger().isDebugEnabled() )
510                 {
511                     getLogger().debug( "request.getPropertiesLocation() " + request.getPropertiesLocation() );
512                 }
513 
514                 File propertiesFile = locator.getResourceAsFile( request.getPropertiesLocation(),
515                                                                  "checkstyle-checker.properties" );
516 
517                 if ( propertiesFile != null )
518                 {
519                     try ( InputStream in = new FileInputStream( propertiesFile ) )
520                     {
521                         p.load( in );
522                     }
523                 }
524             }
525 
526             if ( StringUtils.isNotEmpty( request.getPropertyExpansion() ) )
527             {
528                 String propertyExpansion = request.getPropertyExpansion();
529                 // Convert \ to \\, so that p.load will convert it back properly
530                 propertyExpansion = StringUtils.replace( propertyExpansion, "\\", "\\\\" );
531                 p.load( new ByteArrayInputStream( propertyExpansion.getBytes() ) );
532             }
533 
534             // Workaround for MCHECKSTYLE-48
535             // Make sure that "config/maven-header.txt" is the default value
536             // for headerLocation, if configLocation="config/maven_checks.xml"
537             String headerLocation = request.getHeaderLocation();
538             if ( "config/maven_checks.xml".equals( request.getConfigLocation() ) )
539             {
540 
541                 if ( "LICENSE.txt".equals( request.getHeaderLocation() ) )
542                 {
543                     headerLocation = "config/maven-header.txt";
544                 }
545             }
546             if ( getLogger().isDebugEnabled() )
547             {
548                 getLogger().debug( "headerLocation " + headerLocation );
549             }
550 
551             if ( StringUtils.isNotEmpty( headerLocation ) )
552             {
553                 try
554                 {
555                     File headerFile = licenseLocator.getResourceAsFile( headerLocation, "checkstyle-header.txt" );
556 
557                     if ( headerFile != null )
558                     {
559                         p.setProperty( "checkstyle.header.file", headerFile.getAbsolutePath() );
560                     }
561                 }
562                 catch ( FileResourceCreationException | ResourceNotFoundException e )
563                 {
564                     getLogger().debug( "Unable to process header location: " + headerLocation );
565                     getLogger().debug( "Checkstyle will throw exception if ${checkstyle.header.file} is used" );
566                 }
567             }
568 
569             if ( request.getCacheFile() != null )
570             {
571                 p.setProperty( "checkstyle.cache.file", request.getCacheFile() );
572             }
573         }
574         catch ( IOException | ResourceNotFoundException | FileResourceCreationException e )
575         {
576             throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
577         }
578         if ( request.getSuppressionsFileExpression() != null )
579         {
580             String suppressionsFilePath = getSuppressionsFilePath( request );
581 
582             if ( suppressionsFilePath != null )
583             {
584                 p.setProperty( request.getSuppressionsFileExpression(), suppressionsFilePath );
585             }
586         }
587 
588         return p;
589     }
590 
591     private List<File> getFilesToProcess( CheckstyleExecutorRequest request )
592         throws IOException
593     {
594         StringBuilder excludesStr = new StringBuilder();
595 
596         if ( StringUtils.isNotEmpty( request.getExcludes() ) )
597         {
598             excludesStr.append( request.getExcludes() );
599         }
600 
601         String[] defaultExcludes = FileUtils.getDefaultExcludes();
602         for ( String defaultExclude : defaultExcludes )
603         {
604             if ( excludesStr.length() > 0 )
605             {
606                 excludesStr.append( "," );
607             }
608 
609             excludesStr.append( defaultExclude );
610         }
611 
612         Set<File> files = new LinkedHashSet<>();
613         if ( request.isAggregate() )
614         {
615             for ( MavenProject project : request.getReactorProjects() )
616             {
617                 Set<File> sourceDirectories = new LinkedHashSet<>();
618                 
619                 // CompileSourceRoots are absolute paths
620                 List<String> compileSourceRoots = project.getCompileSourceRoots(); 
621                 for ( String compileSourceRoot : compileSourceRoots )
622                 {
623                     sourceDirectories.add( new File( compileSourceRoot ) );
624                 }
625 
626                 Set<File> testSourceDirectories = new LinkedHashSet<>();
627                 // CompileSourceRoots are absolute paths
628                 List<String> testCompileSourceRoots = project.getTestCompileSourceRoots(); 
629                 for ( String testCompileSourceRoot : testCompileSourceRoots )
630                 {
631                     testSourceDirectories.add( new File( testCompileSourceRoot ) );
632                 }
633 
634                 addFilesToProcess( request, sourceDirectories, project.getResources(), project.getTestResources(),
635                                    files, testSourceDirectories );
636             }
637         }
638         else
639         {
640             Collection<File> sourceDirectories = request.getSourceDirectories();
641             addFilesToProcess( request, sourceDirectories, request.getResources(),
642                 request.getTestResources(), files, request.getTestSourceDirectories() );
643         }
644 
645         getLogger().debug( "Added " + files.size() + " files to process." );
646 
647         return new ArrayList<>( files );
648     }
649 
650     private void addFilesToProcess( CheckstyleExecutorRequest request, Collection<File> sourceDirectories,
651                                     List<Resource> resources, List<Resource> testResources, Collection<File> files,
652                                     Collection<File> testSourceDirectories )
653         throws IOException
654     {
655         if ( sourceDirectories != null )
656         {
657             for ( File sourceDirectory : sourceDirectories )
658             {
659                 if ( sourceDirectory.isDirectory() )
660                 {
661                     final List<File> sourceFiles =
662                         FileUtils.getFiles( sourceDirectory, request.getIncludes(), request.getExcludes() );
663                     files.addAll( sourceFiles );
664                     getLogger().debug( "Added " + sourceFiles.size() + " source files found in '"
665                                            + sourceDirectory.getAbsolutePath() + "'." );
666                 }
667             }
668         }
669 
670         if ( request.isIncludeTestSourceDirectory() && testSourceDirectories != null )
671         {
672             for ( File testSourceDirectory : testSourceDirectories )
673             {
674                 if ( testSourceDirectory.isDirectory() )
675                 {
676                     final List<File> testSourceFiles =
677                         FileUtils.getFiles( testSourceDirectory, request.getIncludes(), request.getExcludes() );
678                     
679                     files.addAll( testSourceFiles );
680                     getLogger().debug( "Added " + testSourceFiles.size() + " test source files found in '"
681                             + testSourceDirectory.getAbsolutePath() + "'." );
682                 }
683             }
684         }
685 
686         if ( resources != null && request.isIncludeResources() )
687         {
688             addResourceFilesToProcess( request, resources, files );
689         }
690         else
691         {
692             getLogger().debug( "No resources found in this project." );
693         }
694 
695         if ( testResources != null && request.isIncludeTestResources() )
696         {
697             addResourceFilesToProcess( request, testResources, files );
698         }
699         else
700         {
701             getLogger().debug( "No test resources found in this project." );
702         }
703     }
704 
705     private void addResourceFilesToProcess( CheckstyleExecutorRequest request, List<Resource> resources,
706                                             Collection<File> files )
707         throws IOException
708     {
709         for ( Resource resource : resources )
710         {
711             if ( resource.getDirectory() != null )
712             {
713                 File resourcesDirectory = new File( resource.getDirectory() );
714                 if ( resourcesDirectory.isDirectory() )
715                 {
716                     String includes = request.getResourceIncludes();
717                     String excludes = request.getResourceExcludes();
718 
719                     // MCHECKSTYLE-214: Only with project-root respect in/excludes, otherwise you'll get every file
720                     if ( resourcesDirectory.equals( request.getProject().getBasedir() ) )
721                     {
722                         String resourceIncludes = StringUtils.join( resource.getIncludes().iterator(), "," );
723                         if ( StringUtils.isEmpty( includes ) )
724                         {
725                             includes = resourceIncludes;
726                         }
727                         else
728                         {
729                             includes += "," + resourceIncludes;
730                         }
731                         
732                         String resourceExcludes = StringUtils.join( resource.getExcludes().iterator(), "," );
733                         if ( StringUtils.isEmpty( excludes ) )
734                         {
735                             excludes = resourceExcludes;
736                         }
737                         else
738                         {
739                             excludes += "," + resourceExcludes;
740                         }
741                     }
742                     
743                     List<File> resourceFiles =
744                         FileUtils.getFiles( resourcesDirectory, includes, excludes );
745                     files.addAll( resourceFiles );
746                     getLogger().debug( "Added " + resourceFiles.size() + " resource files found in '"
747                             + resourcesDirectory.getAbsolutePath() + "'." );
748                 }
749                 else
750                 {
751                     getLogger().debug( "The resources directory '" + resourcesDirectory.getAbsolutePath()
752                             + "' does not exist or is not a directory." );
753                 }
754             }
755         }
756     }
757 
758     private FilterSet getSuppressionsFilterSet( final String suppressionsFilePath )
759         throws CheckstyleExecutorException
760     {
761         if ( suppressionsFilePath == null )
762         {
763             return null;
764         }
765 
766         try
767         {
768             return SuppressionsLoader.loadSuppressions( suppressionsFilePath );
769         }
770         catch ( CheckstyleException ce )
771         {
772             throw new CheckstyleExecutorException( "Failed to load suppressions file from: "
773                 + suppressionsFilePath, ce );
774         }
775     }
776 
777     private String getSuppressionsFilePath( final CheckstyleExecutorRequest request )
778         throws CheckstyleExecutorException
779     {
780         final String suppressionsLocation = request.getSuppressionsLocation();
781         if ( StringUtils.isEmpty( suppressionsLocation ) )
782         {
783             return null;
784         }
785         
786         try
787         {
788             File suppressionsFile = locator.getResourceAsFile( suppressionsLocation, "checkstyle-suppressions.xml" );
789             return suppressionsFile == null ? null : suppressionsFile.getAbsolutePath();
790         }
791         catch ( ResourceNotFoundException e )
792         {
793             throw new CheckstyleExecutorException( "Unable to find suppressions file at location: "
794                 + suppressionsLocation, e );
795         }
796         catch ( FileResourceCreationException e )
797         {
798             throw new CheckstyleExecutorException( "Unable to process suppressions file location: "
799                 + suppressionsLocation, e );
800         }
801     }
802 
803     private String getConfigFile( CheckstyleExecutorRequest request )
804         throws CheckstyleExecutorException
805     {
806         try
807         {
808             if ( getLogger().isDebugEnabled() )
809             {
810                 getLogger().debug( "request.getConfigLocation() " + request.getConfigLocation() );
811             }
812 
813             File configFile = locator.getResourceAsFile( request.getConfigLocation(), "checkstyle-checker.xml" );
814             if ( configFile == null )
815             {
816                 throw new CheckstyleExecutorException( "Unable to process config location: "
817                     + request.getConfigLocation() );
818             }
819             return configFile.getAbsolutePath();
820         }
821         catch ( ResourceNotFoundException e )
822         {
823             throw new CheckstyleExecutorException( "Unable to find configuration file at location: "
824                 + request.getConfigLocation(), e );
825         }
826         catch ( FileResourceCreationException e )
827         {
828             throw new CheckstyleExecutorException( "Unable to process configuration file at location: "
829                 + request.getConfigLocation(), e );
830         }
831 
832     }
833 
834     /**
835      * Configures search paths in the resource locator.
836      * This method should only be called once per execution.
837      *
838      * @param request executor request data.
839      */
840     private void configureResourceLocator( final ResourceManager resourceManager,
841                                            final CheckstyleExecutorRequest request,
842                                            final List<Artifact> additionalArtifacts )
843     {
844         final MavenProject project = request.getProject();
845         resourceManager.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
846 
847         // Recurse up the parent hierarchy and add project directories to the search roots
848         MavenProject parent = project;
849         while ( parent != null && parent.getFile() != null )
850         {
851             // MCHECKSTYLE-131 ( olamy ) I don't like this hack.
852             // (dkulp) Me either.   It really pollutes the location stuff
853             // by allowing searches of stuff outside the current module.
854             File dir = parent.getFile().getParentFile();
855             resourceManager.addSearchPath( FileResourceLoader.ID, dir.getAbsolutePath() );
856             parent = parent.getParent();
857         }
858         resourceManager.addSearchPath( "url", "" );
859         
860         // MCHECKSTYLE-225: load licenses from additional artifacts, not from classpath
861         if ( additionalArtifacts != null )
862         {
863             for ( Artifact licenseArtifact : additionalArtifacts )
864             {
865                 try
866                 {
867                     resourceManager.addSearchPath( "jar", "jar:" + licenseArtifact.getFile().toURI().toURL() );
868                 }
869                 catch ( MalformedURLException e )
870                 {
871                     // noop
872                 }
873             }
874         }
875     }
876 }