1 package org.apache.maven.plugin.checkstyle;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.ByteArrayInputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.net.MalformedURLException;
27 import java.net.URL;
28 import java.net.URLClassLoader;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32 import java.util.Properties;
33
34 import org.apache.commons.io.IOUtils;
35 import org.apache.maven.artifact.DependencyResolutionRequiredException;
36 import org.apache.maven.model.Resource;
37 import org.apache.maven.project.MavenProject;
38 import org.codehaus.plexus.component.annotations.Component;
39 import org.codehaus.plexus.component.annotations.Requirement;
40 import org.codehaus.plexus.logging.AbstractLogEnabled;
41 import org.codehaus.plexus.resource.ResourceManager;
42 import org.codehaus.plexus.resource.loader.FileResourceCreationException;
43 import org.codehaus.plexus.resource.loader.FileResourceLoader;
44 import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
45 import org.codehaus.plexus.util.FileUtils;
46 import org.codehaus.plexus.util.StringUtils;
47
48 import com.puppycrawl.tools.checkstyle.Checker;
49 import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
50 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
51 import com.puppycrawl.tools.checkstyle.PackageNamesLoader;
52 import com.puppycrawl.tools.checkstyle.PropertiesExpander;
53 import com.puppycrawl.tools.checkstyle.api.AuditListener;
54 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
55 import com.puppycrawl.tools.checkstyle.api.Configuration;
56 import com.puppycrawl.tools.checkstyle.api.FilterSet;
57 import com.puppycrawl.tools.checkstyle.filters.SuppressionsLoader;
58
59
60
61
62
63
64 @Component( role = CheckstyleExecutor.class, hint = "default", instantiationStrategy = "per-lookup" )
65 public class DefaultCheckstyleExecutor
66 extends AbstractLogEnabled
67 implements CheckstyleExecutor
68 {
69 @Requirement
70 private ResourceManager locator;
71
72 private static final File[] EMPTY_FILE_ARRAY = new File[0];
73
74 public CheckstyleResults executeCheckstyle( CheckstyleExecutorRequest request )
75 throws CheckstyleExecutorException, CheckstyleException
76 {
77
78
79
80
81 ClassLoader checkstyleClassLoader = PackageNamesLoader.class.getClassLoader();
82 Thread.currentThread().setContextClassLoader( checkstyleClassLoader );
83
84 if ( getLogger().isDebugEnabled() )
85 {
86 getLogger().debug( "executeCheckstyle start headerLocation : " + request.getHeaderLocation() );
87 }
88 MavenProject project = request.getProject();
89 locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) );
90 File[] files;
91 try
92 {
93 files = getFilesToProcess( request );
94 }
95 catch ( IOException e )
96 {
97 throw new CheckstyleExecutorException( "Error getting files to process", e );
98 }
99
100 FilterSet filterSet = getSuppressions( request );
101
102 Checker checker = new Checker();
103
104
105
106 List<String> classPathStrings = new ArrayList<String>();
107 List<String> outputDirectories = new ArrayList<String>();
108 File sourceDirectory = request.getSourceDirectory();
109 File testSourceDirectory = request.getTestSourceDirectory();
110 if ( request.isAggregate() )
111 {
112 for ( MavenProject childProject : request.getReactorProjects() )
113 {
114 prepareCheckstylePaths( request, childProject, classPathStrings, outputDirectories,
115 new File( childProject.getBuild().getSourceDirectory() ),
116 new File( childProject.getBuild().getTestSourceDirectory() ) );
117 }
118 }
119 else
120 {
121 prepareCheckstylePaths( request, project, classPathStrings, outputDirectories, sourceDirectory,
122 testSourceDirectory );
123 }
124
125 List<URL> urls = new ArrayList<URL>( classPathStrings.size() );
126
127 for ( String path : classPathStrings )
128 {
129 try
130 {
131 urls.add( new File( path ).toURL() );
132 }
133 catch ( MalformedURLException e )
134 {
135 throw new CheckstyleExecutorException( e.getMessage(), e );
136 }
137 }
138
139 for ( String outputDirectoryString : outputDirectories )
140 {
141 try
142 {
143 if ( outputDirectoryString != null )
144 {
145 File outputDirectoryFile = new File( outputDirectoryString );
146 if ( outputDirectoryFile.exists() )
147 {
148 URL outputDirectoryUrl = outputDirectoryFile.toURL();
149 request.getLog().debug(
150 "Adding the outputDirectory " + outputDirectoryUrl.toString()
151 + " to the Checkstyle class path" );
152 urls.add( outputDirectoryUrl );
153 }
154 }
155 }
156 catch ( MalformedURLException e )
157 {
158 throw new CheckstyleExecutorException( e.getMessage(), e );
159 }
160 }
161
162 URLClassLoader projectClassLoader = new URLClassLoader( (URL[]) urls.toArray( new URL[urls.size()] ), null );
163 checker.setClassloader( projectClassLoader );
164
165 checker.setModuleClassLoader( Thread.currentThread().getContextClassLoader() );
166
167 if ( filterSet != null )
168 {
169 checker.addFilter( filterSet );
170 }
171 Configuration configuration = getConfiguration( request );
172 checker.configure( configuration );
173
174 AuditListener listener = request.getListener();
175
176 if ( listener != null )
177 {
178 checker.addListener( listener );
179 }
180
181 if ( request.isConsoleOutput() )
182 {
183 checker.addListener( request.getConsoleListener() );
184 }
185
186 CheckstyleReportListener sinkListener = new CheckstyleReportListener( configuration );
187 if ( request.isAggregate() )
188 {
189 for ( MavenProject childProject : request.getReactorProjects() )
190 {
191 addSourceDirectory( sinkListener, new File( childProject.getBuild().getSourceDirectory() ),
192 new File( childProject.getBuild().getTestSourceDirectory() ),
193 childProject.getResources(), request );
194 }
195 }
196 else
197 {
198 addSourceDirectory( sinkListener, sourceDirectory, testSourceDirectory, request.getResources(), request );
199 }
200
201 checker.addListener( sinkListener );
202
203 List<File> filesList = Arrays.asList( files );
204 int nbErrors = checker.process( filesList );
205
206 checker.destroy();
207
208 if ( request.getStringOutputStream() != null )
209 {
210 request.getLog().info( request.getStringOutputStream().toString() );
211 }
212
213 if ( request.isFailsOnError() && nbErrors > 0 )
214 {
215
216
217
218 throw new CheckstyleExecutorException( "There are " + nbErrors + " checkstyle errors." );
219 }
220 else if ( nbErrors > 0 )
221 {
222 request.getLog().info( "There are " + nbErrors + " checkstyle errors." );
223 }
224
225 return sinkListener.getResults();
226 }
227
228 protected void addSourceDirectory( CheckstyleReportListener sinkListener, File sourceDirectory,
229 File testSourceDirectory, List<Resource> resources,
230 CheckstyleExecutorRequest request )
231 {
232 if ( sourceDirectory != null )
233 {
234 sinkListener.addSourceDirectory( sourceDirectory );
235 }
236
237 if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectory != null )
238 && ( testSourceDirectory.exists() ) && ( testSourceDirectory.isDirectory() ) )
239 {
240 sinkListener.addSourceDirectory( testSourceDirectory );
241 }
242
243 if ( resources != null )
244 {
245 for ( Resource resource : resources )
246 {
247 if ( resource.getDirectory() != null )
248 {
249 File resourcesDirectory = new File( resource.getDirectory() );
250 if ( resourcesDirectory.exists() && resourcesDirectory.isDirectory() )
251 {
252 sinkListener.addSourceDirectory( resourcesDirectory );
253 getLogger().debug( "Added '" + resourcesDirectory.getAbsolutePath()
254 + "' as a source directory." );
255 }
256 }
257 }
258 }
259 }
260
261 public Configuration getConfiguration( CheckstyleExecutorRequest request )
262 throws CheckstyleExecutorException
263 {
264 try
265 {
266
267
268
269 ClassLoader checkstyleClassLoader = PackageNamesLoader.class.getClassLoader();
270 Thread.currentThread().setContextClassLoader( checkstyleClassLoader );
271 String configFile = getConfigFile( request );
272 Properties overridingProperties = getOverridingProperties( request );
273 Configuration config = ConfigurationLoader
274 .loadConfiguration( configFile, new PropertiesExpander( overridingProperties ) );
275 String effectiveEncoding = StringUtils.isNotEmpty( request.getEncoding() ) ? request.getEncoding() : System
276 .getProperty( "file.encoding", "UTF-8" );
277 if ( StringUtils.isEmpty( request.getEncoding() ) )
278 {
279 request.getLog().warn(
280 "File encoding has not been set, using platform encoding " + effectiveEncoding
281 + ", i.e. build is platform dependent!" );
282 }
283
284 if ( "Checker".equals( config.getName() )
285 || "com.puppycrawl.tools.checkstyle.Checker".equals( config.getName() ) )
286 {
287 if ( config instanceof DefaultConfiguration )
288 {
289
290 try
291 {
292 if ( ( (DefaultConfiguration) config ).getAttribute( "charset" ) == null )
293 {
294 ( (DefaultConfiguration) config ).addAttribute( "charset", effectiveEncoding );
295 }
296 }
297 catch ( CheckstyleException ex )
298 {
299
300 ( (DefaultConfiguration) config ).addAttribute( "charset", effectiveEncoding );
301 }
302 }
303 else
304 {
305 request.getLog().warn( "Failed to configure file encoding on module " + config );
306 }
307 }
308 Configuration[] modules = config.getChildren();
309 for ( int i = 0; i < modules.length; i++ )
310 {
311 Configuration module = modules[i];
312 if ( "TreeWalker".equals( module.getName() )
313 || "com.puppycrawl.tools.checkstyle.TreeWalker".equals( module.getName() ) )
314 {
315 if ( module instanceof DefaultConfiguration )
316 {
317
318 try
319 {
320 if ( ( (DefaultConfiguration) module ).getAttribute( "cacheFile" ) == null )
321 {
322 ( (DefaultConfiguration) module ).addAttribute( "cacheFile", request.getCacheFile() );
323 }
324 }
325 catch ( CheckstyleException ex )
326 {
327
328
329 ( (DefaultConfiguration) module ).addAttribute( "cacheFile", request.getCacheFile() );
330 }
331 }
332 else
333 {
334 request.getLog().warn( "Failed to configure cache file on module " + module );
335 }
336 }
337 }
338 return config;
339 }
340 catch ( CheckstyleException e )
341 {
342 throw new CheckstyleExecutorException( "Failed during checkstyle configuration", e );
343 }
344 }
345
346 private void prepareCheckstylePaths( CheckstyleExecutorRequest request, MavenProject project,
347 List<String> classPathStrings, List<String> outputDirectories,
348 File sourceDirectory, File testSourceDirectory )
349 throws CheckstyleExecutorException
350 {
351 try
352 {
353 outputDirectories.add( project.getBuild().getOutputDirectory() );
354
355 if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectory != null )
356 && ( testSourceDirectory.exists() ) && ( testSourceDirectory.isDirectory() ) )
357 {
358 classPathStrings.addAll( project.getTestClasspathElements() );
359 outputDirectories.add( project.getBuild().getTestOutputDirectory() );
360 }
361 else
362 {
363 classPathStrings.addAll( project.getCompileClasspathElements() );
364 }
365 }
366 catch ( DependencyResolutionRequiredException e )
367 {
368 throw new CheckstyleExecutorException( e.getMessage(), e );
369 }
370 }
371
372 private Properties getOverridingProperties( CheckstyleExecutorRequest request )
373 throws CheckstyleExecutorException
374 {
375 Properties p = new Properties();
376
377 try
378 {
379 if ( request.getPropertiesLocation() != null )
380 {
381 if ( getLogger().isDebugEnabled() )
382 {
383 getLogger().debug( "request.getPropertiesLocation() " + request.getPropertiesLocation() );
384 }
385
386 File propertiesFile = locator.getResourceAsFile( request.getPropertiesLocation(),
387 "checkstyle-checker.properties" );
388
389 FileInputStream properties = new FileInputStream( propertiesFile );
390 try
391 {
392 if ( propertiesFile != null )
393 {
394 p.load( properties );
395 }
396 }
397 finally
398 {
399 IOUtils.closeQuietly( properties );
400 }
401 }
402
403 if ( StringUtils.isNotEmpty( request.getPropertyExpansion() ) )
404 {
405 String propertyExpansion = request.getPropertyExpansion();
406
407 propertyExpansion = StringUtils.replace( propertyExpansion, "\\", "\\\\" );
408 p.load( new ByteArrayInputStream( propertyExpansion.getBytes() ) );
409 }
410
411
412
413
414 String headerLocation = request.getHeaderLocation();
415 if ( "config/maven_checks.xml".equals( request.getConfigLocation() ) )
416 {
417
418 if ( "LICENSE.txt".equals( request.getHeaderLocation() ) )
419 {
420 headerLocation = "config/maven-header.txt";
421 }
422 }
423 if ( getLogger().isDebugEnabled() )
424 {
425 getLogger().debug( "headerLocation " + headerLocation );
426 }
427
428 if ( StringUtils.isNotEmpty( headerLocation ) )
429 {
430 try
431 {
432 File headerFile = locator.getResourceAsFile( headerLocation, "checkstyle-header.txt" );
433
434 if ( headerFile != null )
435 {
436 p.setProperty( "checkstyle.header.file", headerFile.getAbsolutePath() );
437 }
438 }
439 catch ( FileResourceCreationException e )
440 {
441 throw new CheckstyleExecutorException( "Unable to process header location: " + headerLocation, e );
442 }
443 catch ( ResourceNotFoundException e )
444 {
445 throw new CheckstyleExecutorException( "Unable to process header location: " + headerLocation, e );
446 }
447 }
448
449 if ( request.getCacheFile() != null )
450 {
451 p.setProperty( "checkstyle.cache.file", request.getCacheFile() );
452 }
453 }
454 catch ( IOException e )
455 {
456 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
457 }
458 catch ( FileResourceCreationException e )
459 {
460 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
461 }
462 catch ( ResourceNotFoundException e )
463 {
464 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
465 }
466 if ( request.getSuppressionsFileExpression() != null )
467 {
468 String suppresionFile = request.getSuppressionsLocation();
469
470 if ( suppresionFile != null )
471 {
472 p.setProperty( request.getSuppressionsFileExpression(), suppresionFile );
473 }
474 }
475
476 return p;
477 }
478
479 private File[] getFilesToProcess( CheckstyleExecutorRequest request )
480 throws IOException
481 {
482 StringBuilder excludesStr = new StringBuilder();
483
484 if ( StringUtils.isNotEmpty( request.getExcludes() ) )
485 {
486 excludesStr.append( request.getExcludes() );
487 }
488
489 String[] defaultExcludes = FileUtils.getDefaultExcludes();
490 for ( int i = 0; i < defaultExcludes.length; i++ )
491 {
492 if ( excludesStr.length() > 0 )
493 {
494 excludesStr.append( "," );
495 }
496
497 excludesStr.append( defaultExcludes[i] );
498 }
499
500 File sourceDirectory = request.getSourceDirectory();
501
502 List<File> files = new ArrayList<File>();
503 if ( request.isAggregate() )
504 {
505 for ( MavenProject project : request.getReactorProjects() )
506 {
507 addFilesToProcess( request, excludesStr, new File( project.getBuild().getSourceDirectory() ),
508 project.getResources(), new File( project.getBuild().getTestSourceDirectory() ),
509 files );
510 }
511 }
512 else
513 {
514 addFilesToProcess( request, excludesStr, sourceDirectory, request.getResources(),
515 request.getTestSourceDirectory(), files );
516 }
517
518 getLogger().debug( "Added " + files.size() + " files to process." );
519
520 return (File[]) files.toArray( EMPTY_FILE_ARRAY );
521 }
522
523 private void addFilesToProcess( CheckstyleExecutorRequest request, StringBuilder excludesStr, File sourceDirectory,
524 List<Resource> resources, File testSourceDirectory, List<File> files )
525 throws IOException
526 {
527 if ( sourceDirectory != null && sourceDirectory.exists() )
528 {
529 final List sourceFiles = FileUtils.getFiles( sourceDirectory,
530 request.getIncludes(),
531 excludesStr.toString() );
532 files.addAll( sourceFiles );
533 getLogger().debug( "Added " + sourceFiles.size() + " source files found in '"
534 + sourceDirectory.getAbsolutePath() + "'." );
535 }
536
537 if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectory != null )
538 && ( testSourceDirectory.exists() ) && ( testSourceDirectory.isDirectory() ) )
539 {
540 final List testSourceFiles = FileUtils.getFiles( testSourceDirectory, request.getIncludes(),
541 excludesStr.toString() );
542 files.addAll( testSourceFiles );
543 getLogger().debug( "Added " + testSourceFiles.size() + " test source files found in '"
544 + testSourceDirectory.getAbsolutePath() + "'." );
545 }
546
547
548 if ( resources != null )
549 {
550 for ( Resource resource : resources )
551 {
552 if ( resource.getDirectory() != null )
553 {
554 File resourcesDirectory = new File( resource.getDirectory() );
555 if ( resourcesDirectory.exists() && resourcesDirectory.isDirectory() )
556 {
557
558 List resourceFiles = FileUtils.getFiles( resourcesDirectory, "**/*.properties", null );
559 files.addAll( resourceFiles );
560 getLogger().debug( "Added " + resourceFiles.size() + " resource files found in '"
561 + resourcesDirectory.getAbsolutePath() + "'." );
562 }
563 else
564 {
565 getLogger().debug( "The resources directory '" + resourcesDirectory.getAbsolutePath()
566 + "' does not exist or is not a directory." );
567 }
568 }
569 }
570 }
571 else
572 {
573 getLogger().debug( "No resources found in this project." );
574 }
575 }
576
577 private FilterSet getSuppressions( CheckstyleExecutorRequest request )
578 throws CheckstyleExecutorException
579 {
580 try
581 {
582 File suppressionsFile = locator.resolveLocation( request.getSuppressionsLocation(),
583 "checkstyle-suppressions.xml" );
584
585 if ( suppressionsFile == null )
586 {
587 return null;
588 }
589
590 return SuppressionsLoader.loadSuppressions( suppressionsFile.getAbsolutePath() );
591 }
592 catch ( CheckstyleException ce )
593 {
594 throw new CheckstyleExecutorException( "failed to load suppressions location: "
595 + request.getSuppressionsLocation(), ce );
596 }
597 catch ( IOException e )
598 {
599 throw new CheckstyleExecutorException( "Failed to process supressions location: "
600 + request.getSuppressionsLocation(), e );
601 }
602 }
603
604 private String getConfigFile( CheckstyleExecutorRequest request )
605 throws CheckstyleExecutorException
606 {
607 try
608 {
609 if ( getLogger().isDebugEnabled() )
610 {
611 getLogger().debug( "request.getConfigLocation() " + request.getConfigLocation() );
612 }
613
614 MavenProject parent = request.getProject();
615 while ( parent != null && parent.getFile() != null )
616 {
617
618
619
620 File dir = parent.getFile().getParentFile();
621 locator.addSearchPath( FileResourceLoader.ID, dir.getAbsolutePath() );
622 parent = parent.getParent();
623 }
624 locator.addSearchPath( "url", "" );
625
626 File configFile = locator.getResourceAsFile( request.getConfigLocation(), "checkstyle-checker.xml" );
627 if ( configFile == null )
628 {
629 throw new CheckstyleExecutorException( "Unable to process config location: "
630 + request.getConfigLocation() );
631 }
632 return configFile.getAbsolutePath();
633 }
634 catch ( org.codehaus.plexus.resource.loader.ResourceNotFoundException e )
635 {
636 throw new CheckstyleExecutorException( "Unable to find configuration file at location "
637 + request.getConfigLocation(), e );
638 }
639 catch ( FileResourceCreationException e )
640 {
641 throw new CheckstyleExecutorException( "Unable to process configuration file location "
642 + request.getConfigLocation(), e );
643 }
644
645 }
646 }