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