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