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(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 ( 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 (Configuration module : modules) {
310 if ("TreeWalker".equals(module.getName())
311 || "com.puppycrawl.tools.checkstyle.TreeWalker".equals(module.getName())) {
312 if (module instanceof DefaultConfiguration) {
313
314 try {
315 if (module.getAttribute("cacheFile") == null) {
316 ((DefaultConfiguration) module).addAttribute("cacheFile", request.getCacheFile());
317 }
318 } catch (CheckstyleException ex) {
319
320
321 ((DefaultConfiguration) module).addAttribute("cacheFile", request.getCacheFile());
322 }
323 } else {
324 request.getLog().warn("Failed to configure cache file on module " + module);
325 }
326 }
327 }
328 return config;
329 }
330 catch ( CheckstyleException e )
331 {
332 throw new CheckstyleExecutorException( "Failed during checkstyle configuration", e );
333 }
334 }
335
336 private void prepareCheckstylePaths( CheckstyleExecutorRequest request, MavenProject project,
337 List<String> classPathStrings, List<String> outputDirectories,
338 File sourceDirectory, File testSourceDirectory )
339 throws CheckstyleExecutorException
340 {
341 try
342 {
343 outputDirectories.add( project.getBuild().getOutputDirectory() );
344
345 if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectory != null )
346 && ( testSourceDirectory.exists() ) && ( testSourceDirectory.isDirectory() ) )
347 {
348 classPathStrings.addAll( project.getTestClasspathElements() );
349 outputDirectories.add( project.getBuild().getTestOutputDirectory() );
350 }
351 else
352 {
353 classPathStrings.addAll( project.getCompileClasspathElements() );
354 }
355 }
356 catch ( DependencyResolutionRequiredException e )
357 {
358 throw new CheckstyleExecutorException( e.getMessage(), e );
359 }
360 }
361
362 private Properties getOverridingProperties( CheckstyleExecutorRequest request )
363 throws CheckstyleExecutorException
364 {
365 Properties p = new Properties();
366
367 try
368 {
369 if ( request.getPropertiesLocation() != null )
370 {
371 if ( getLogger().isDebugEnabled() )
372 {
373 getLogger().debug( "request.getPropertiesLocation() " + request.getPropertiesLocation() );
374 }
375
376 File propertiesFile = locator.getResourceAsFile( request.getPropertiesLocation(),
377 "checkstyle-checker.properties" );
378
379 FileInputStream properties = new FileInputStream( propertiesFile );
380 try
381 {
382 if ( propertiesFile != null )
383 {
384 p.load( properties );
385 }
386 }
387 finally
388 {
389 IOUtils.closeQuietly( properties );
390 }
391 }
392
393 if ( StringUtils.isNotEmpty( request.getPropertyExpansion() ) )
394 {
395 String propertyExpansion = request.getPropertyExpansion();
396
397 propertyExpansion = StringUtils.replace( propertyExpansion, "\\", "\\\\" );
398 p.load( new ByteArrayInputStream( propertyExpansion.getBytes() ) );
399 }
400
401
402
403
404 String headerLocation = request.getHeaderLocation();
405 if ( "config/maven_checks.xml".equals( request.getConfigLocation() ) )
406 {
407
408 if ( "LICENSE.txt".equals( request.getHeaderLocation() ) )
409 {
410 headerLocation = "config/maven-header.txt";
411 }
412 }
413 if ( getLogger().isDebugEnabled() )
414 {
415 getLogger().debug( "headerLocation " + headerLocation );
416 }
417
418 if ( StringUtils.isNotEmpty( headerLocation ) )
419 {
420 try
421 {
422 File headerFile = locator.getResourceAsFile( headerLocation, "checkstyle-header.txt" );
423
424 if ( headerFile != null )
425 {
426 p.setProperty( "checkstyle.header.file", headerFile.getAbsolutePath() );
427 }
428 }
429 catch ( FileResourceCreationException e )
430 {
431 throw new CheckstyleExecutorException( "Unable to process header location: " + headerLocation, e );
432 }
433 catch ( ResourceNotFoundException e )
434 {
435 throw new CheckstyleExecutorException( "Unable to process header location: " + headerLocation, e );
436 }
437 }
438
439 if ( request.getCacheFile() != null )
440 {
441 p.setProperty( "checkstyle.cache.file", request.getCacheFile() );
442 }
443 }
444 catch ( IOException e )
445 {
446 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
447 }
448 catch ( FileResourceCreationException e )
449 {
450 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
451 }
452 catch ( ResourceNotFoundException e )
453 {
454 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
455 }
456 if ( request.getSuppressionsFileExpression() != null )
457 {
458 String suppresionFile = request.getSuppressionsLocation();
459
460 if ( suppresionFile != null )
461 {
462 p.setProperty( request.getSuppressionsFileExpression(), suppresionFile );
463 }
464 }
465
466 return p;
467 }
468
469 private File[] getFilesToProcess( CheckstyleExecutorRequest request )
470 throws IOException
471 {
472 StringBuilder excludesStr = new StringBuilder();
473
474 if ( StringUtils.isNotEmpty( request.getExcludes() ) )
475 {
476 excludesStr.append( request.getExcludes() );
477 }
478
479 String[] defaultExcludes = FileUtils.getDefaultExcludes();
480 for (String defaultExclude : defaultExcludes) {
481 if (excludesStr.length() > 0) {
482 excludesStr.append(",");
483 }
484
485 excludesStr.append(defaultExclude);
486 }
487
488 File sourceDirectory = request.getSourceDirectory();
489
490 List<File> files = new ArrayList<File>();
491 if ( request.isAggregate() )
492 {
493 for ( MavenProject project : request.getReactorProjects() )
494 {
495 addFilesToProcess( request, new File( project.getBuild().getSourceDirectory() ),
496 project.getResources(), project.getTestResources(),
497 files, new File( project.getBuild().getTestSourceDirectory() )
498 );
499 }
500 }
501 else
502 {
503 addFilesToProcess( request, sourceDirectory, request.getResources(),
504 request.getTestResources(), files, request.getTestSourceDirectory() );
505 }
506
507 getLogger().debug( "Added " + files.size() + " files to process." );
508
509 return files.toArray( new File[files.size()] );
510 }
511
512 private void addFilesToProcess( CheckstyleExecutorRequest request, File sourceDirectory,
513 List<Resource> resources, List<Resource> testResources, List<File> files, File testSourceDirectory )
514 throws IOException
515 {
516 if ( sourceDirectory != null && sourceDirectory.exists() )
517 {
518 final List sourceFiles = FileUtils.getFiles( sourceDirectory,
519 request.getIncludes(),
520 request.getExcludes() );
521 files.addAll( sourceFiles );
522 getLogger().debug( "Added " + sourceFiles.size() + " source files found in '"
523 + sourceDirectory.getAbsolutePath() + "'." );
524 }
525
526 if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectory != null )
527 && ( testSourceDirectory.exists() ) && ( testSourceDirectory.isDirectory() ) )
528 {
529 final List testSourceFiles = FileUtils.getFiles( testSourceDirectory, request.getIncludes(),
530 request.getExcludes() );
531 files.addAll( testSourceFiles );
532 getLogger().debug( "Added " + testSourceFiles.size() + " test source files found in '"
533 + testSourceDirectory.getAbsolutePath() + "'." );
534 }
535
536 if ( resources != null && request.isIncludeResources() )
537 {
538 addResourceFilesToProcess( request, resources, files );
539 }
540 else
541 {
542 getLogger().debug( "No resources found in this project." );
543 }
544
545 if ( testResources != null && request.isIncludeTestResources() )
546 {
547 addResourceFilesToProcess( request, testResources, files );
548 }
549 else
550 {
551 getLogger().debug( "No test resources found in this project." );
552 }
553 }
554
555 private void addResourceFilesToProcess( CheckstyleExecutorRequest request, List<Resource> resources, List<File> files ) throws IOException
556 {
557 for ( Resource resource : resources )
558 {
559 if ( resource.getDirectory() != null )
560 {
561 File resourcesDirectory = new File( resource.getDirectory() );
562 if ( resourcesDirectory.exists() && resourcesDirectory.isDirectory() )
563 {
564 List resourceFiles = FileUtils.getFiles( resourcesDirectory, request.getResourceIncludes(), request.getResourceExcludes() );
565 files.addAll( resourceFiles );
566 getLogger().debug( "Added " + resourceFiles.size() + " resource files found in '"
567 + resourcesDirectory.getAbsolutePath() + "'." );
568 }
569 else
570 {
571 getLogger().debug( "The resources directory '" + resourcesDirectory.getAbsolutePath()
572 + "' does not exist or is not a directory." );
573 }
574 }
575 }
576 }
577
578 private FilterSet getSuppressions( CheckstyleExecutorRequest request )
579 throws CheckstyleExecutorException
580 {
581 try
582 {
583 File suppressionsFile = locator.resolveLocation( request.getSuppressionsLocation(),
584 "checkstyle-suppressions.xml" );
585
586 if ( suppressionsFile == null )
587 {
588 return null;
589 }
590
591 return SuppressionsLoader.loadSuppressions( suppressionsFile.getAbsolutePath() );
592 }
593 catch ( CheckstyleException ce )
594 {
595 throw new CheckstyleExecutorException( "failed to load suppressions location: "
596 + request.getSuppressionsLocation(), ce );
597 }
598 catch ( IOException e )
599 {
600 throw new CheckstyleExecutorException( "Failed to process supressions location: "
601 + request.getSuppressionsLocation(), e );
602 }
603 }
604
605 private String getConfigFile( CheckstyleExecutorRequest request )
606 throws CheckstyleExecutorException
607 {
608 try
609 {
610 if ( getLogger().isDebugEnabled() )
611 {
612 getLogger().debug( "request.getConfigLocation() " + request.getConfigLocation() );
613 }
614
615 MavenProject parent = request.getProject();
616 while ( parent != null && parent.getFile() != null )
617 {
618
619
620
621 File dir = parent.getFile().getParentFile();
622 locator.addSearchPath( FileResourceLoader.ID, dir.getAbsolutePath() );
623 parent = parent.getParent();
624 }
625 locator.addSearchPath( "url", "" );
626
627 File configFile = locator.getResourceAsFile( request.getConfigLocation(), "checkstyle-checker.xml" );
628 if ( configFile == null )
629 {
630 throw new CheckstyleExecutorException( "Unable to process config location: "
631 + request.getConfigLocation() );
632 }
633 return configFile.getAbsolutePath();
634 }
635 catch ( org.codehaus.plexus.resource.loader.ResourceNotFoundException e )
636 {
637 throw new CheckstyleExecutorException( "Unable to find configuration file at location "
638 + request.getConfigLocation(), e );
639 }
640 catch ( FileResourceCreationException e )
641 {
642 throw new CheckstyleExecutorException( "Unable to process configuration file location "
643 + request.getConfigLocation(), e );
644 }
645
646 }
647 }