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