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