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 ( (DefaultConfiguration) config ).addAttribute( "charset", effectiveEncoding );
265 }
266 else
267 {
268 request.getLog().warn( "Failed to configure file encoding on module " + config );
269 }
270 }
271 Configuration[] modules = config.getChildren();
272 for ( int i = 0; i < modules.length; i++ )
273 {
274 Configuration module = modules[i];
275 if ( "TreeWalker".equals( module.getName() )
276 || "com.puppycrawl.tools.checkstyle.TreeWalker".equals( module.getName() ) )
277 {
278 if ( module instanceof DefaultConfiguration )
279 {
280
281 try
282 {
283 if ( ( (DefaultConfiguration) module ).getAttribute( "cacheFile" ) == null )
284 {
285 ( (DefaultConfiguration) module ).addAttribute( "cacheFile", request.getCacheFile() );
286 }
287 }
288 catch ( CheckstyleException ex )
289 {
290
291
292 ( (DefaultConfiguration) module ).addAttribute( "cacheFile", request.getCacheFile() );
293 }
294 }
295 else
296 {
297 request.getLog().warn( "Failed to configure cache file on module " + module );
298 }
299 }
300 }
301 return config;
302 }
303 catch ( CheckstyleException e )
304 {
305 throw new CheckstyleExecutorException( "Failed during checkstyle configuration", e );
306 }
307 }
308
309 private void prepareCheckstylePaths( CheckstyleExecutorRequest request, MavenProject project,
310 List<String> classPathStrings, List<String> outputDirectories,
311 File sourceDirectory, File testSourceDirectory )
312 throws CheckstyleExecutorException
313 {
314 try
315 {
316 outputDirectories.add( project.getBuild().getOutputDirectory() );
317
318 if ( request.isIncludeTestSourceDirectory() && ( sourceDirectory != null )
319 && ( testSourceDirectory.exists() ) && ( testSourceDirectory.isDirectory() ) )
320 {
321 classPathStrings.addAll( project.getTestClasspathElements() );
322 outputDirectories.add( project.getBuild().getTestOutputDirectory() );
323 }
324 else
325 {
326 classPathStrings.addAll( project.getCompileClasspathElements() );
327 }
328 }
329 catch ( DependencyResolutionRequiredException e )
330 {
331 throw new CheckstyleExecutorException( e.getMessage(), e );
332 }
333 }
334
335 private Properties getOverridingProperties( CheckstyleExecutorRequest request )
336 throws CheckstyleExecutorException
337 {
338 Properties p = new Properties();
339
340 try
341 {
342 if ( request.getPropertiesLocation() != null )
343 {
344 if ( getLogger().isDebugEnabled() )
345 {
346 getLogger().debug( "request.getPropertiesLocation() " + request.getPropertiesLocation() );
347 }
348
349 File propertiesFile = locator.getResourceAsFile( request.getPropertiesLocation(),
350 "checkstyle-checker.properties" );
351
352 FileInputStream properties = new FileInputStream( propertiesFile );
353 try
354 {
355 if ( propertiesFile != null )
356 {
357 p.load( properties );
358 }
359 }
360 finally
361 {
362 IOUtils.closeQuietly( properties );
363 }
364 }
365
366 if ( StringUtils.isNotEmpty( request.getPropertyExpansion() ) )
367 {
368 String propertyExpansion = request.getPropertyExpansion();
369
370 propertyExpansion = StringUtils.replace( propertyExpansion, "\\", "\\\\" );
371 p.load( new ByteArrayInputStream( propertyExpansion.getBytes() ) );
372 }
373
374
375
376
377 String headerLocation = request.getHeaderLocation();
378 if ( "config/maven_checks.xml".equals( request.getConfigLocation() ) )
379 {
380
381 if ( "LICENSE.txt".equals( request.getHeaderLocation() ) )
382 {
383 headerLocation = "config/maven-header.txt";
384 }
385 }
386 if ( getLogger().isDebugEnabled() )
387 {
388 getLogger().debug( "headerLocation " + headerLocation );
389 }
390
391 if ( StringUtils.isNotEmpty( headerLocation ) )
392 {
393 try
394 {
395 File headerFile = locator.getResourceAsFile( headerLocation, "checkstyle-header.txt" );
396
397 if ( headerFile != null )
398 {
399 p.setProperty( "checkstyle.header.file", headerFile.getAbsolutePath() );
400 }
401 }
402 catch ( FileResourceCreationException e )
403 {
404 throw new CheckstyleExecutorException( "Unable to process header location: " + headerLocation, e );
405 }
406 catch ( ResourceNotFoundException e )
407 {
408 throw new CheckstyleExecutorException( "Unable to process header location: " + headerLocation, e );
409 }
410 }
411
412 if ( request.getCacheFile() != null )
413 {
414 p.setProperty( "checkstyle.cache.file", request.getCacheFile() );
415 }
416 }
417 catch ( IOException e )
418 {
419 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
420 }
421 catch ( FileResourceCreationException e )
422 {
423 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
424 }
425 catch ( ResourceNotFoundException e )
426 {
427 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
428 }
429 if ( request.getSuppressionsFileExpression() != null )
430 {
431 String suppresionFile = request.getSuppressionsLocation();
432
433 if ( suppresionFile != null )
434 {
435 p.setProperty( request.getSuppressionsFileExpression(), suppresionFile );
436 }
437 }
438
439 return p;
440 }
441
442 private File[] getFilesToProcess( CheckstyleExecutorRequest request )
443 throws IOException
444 {
445 StringBuffer excludesStr = new StringBuffer();
446
447 if ( StringUtils.isNotEmpty( request.getExcludes() ) )
448 {
449 excludesStr.append( request.getExcludes() );
450 }
451
452 String[] defaultExcludes = FileUtils.getDefaultExcludes();
453 for ( int i = 0; i < defaultExcludes.length; i++ )
454 {
455 if ( excludesStr.length() > 0 )
456 {
457 excludesStr.append( "," );
458 }
459
460 excludesStr.append( defaultExcludes[i] );
461 }
462
463 File sourceDirectory = request.getSourceDirectory();
464
465 List<File> files = new ArrayList<File>();
466 addFilesToProcess( request, excludesStr, sourceDirectory, files );
467 if ( request.isAggregate() )
468 {
469 for ( MavenProject project : request.getReactorProjects() )
470 {
471 addFilesToProcess( request, excludesStr, new File( project.getBuild().getSourceDirectory() ), files );
472 }
473 }
474
475 return (File[]) files.toArray( EMPTY_FILE_ARRAY );
476 }
477
478 private void addFilesToProcess( CheckstyleExecutorRequest request, StringBuffer excludesStr, File sourceDirectory,
479 List<File> files )
480 throws IOException
481 {
482 if ( sourceDirectory == null || !sourceDirectory.exists() )
483 {
484 return;
485 }
486 files.addAll(
487 FileUtils.getFiles( sourceDirectory, request.getIncludes(), excludesStr.toString() ) );
488 File testSourceDirectory = request.getTestSourceDirectory();
489 if ( request.isIncludeTestSourceDirectory() && ( testSourceDirectory != null )
490 && ( testSourceDirectory.exists() ) && ( testSourceDirectory.isDirectory() ) )
491 {
492 files.addAll( FileUtils.getFiles( testSourceDirectory, request.getIncludes(),
493 excludesStr.toString() ) );
494 }
495 }
496
497 private FilterSet getSuppressions( CheckstyleExecutorRequest request )
498 throws CheckstyleExecutorException
499 {
500 try
501 {
502 File suppressionsFile = locator.resolveLocation( request.getSuppressionsLocation(),
503 "checkstyle-suppressions.xml" );
504
505 if ( suppressionsFile == null )
506 {
507 return null;
508 }
509
510 return SuppressionsLoader.loadSuppressions( suppressionsFile.getAbsolutePath() );
511 }
512 catch ( CheckstyleException ce )
513 {
514 throw new CheckstyleExecutorException( "failed to load suppressions location: "
515 + request.getSuppressionsLocation(), ce );
516 }
517 catch ( IOException e )
518 {
519 throw new CheckstyleExecutorException( "Failed to process supressions location: "
520 + request.getSuppressionsLocation(), e );
521 }
522 }
523
524 private String getConfigFile( CheckstyleExecutorRequest request )
525 throws CheckstyleExecutorException
526 {
527 try
528 {
529 if ( getLogger().isDebugEnabled() )
530 {
531 getLogger().debug( "request.getConfigLocation() " + request.getConfigLocation() );
532 }
533
534 MavenProject parent = request.getProject();
535 while ( parent != null && parent.getFile() != null )
536 {
537
538
539
540 File dir = parent.getFile().getParentFile();
541 locator.addSearchPath( FileResourceLoader.ID, dir.getAbsolutePath() );
542 parent = parent.getParent();
543 }
544 locator.addSearchPath( "url", "" );
545
546 File configFile = locator.getResourceAsFile( request.getConfigLocation(), "checkstyle-checker.xml" );
547 if ( configFile == null )
548 {
549 throw new CheckstyleExecutorException( "Unable to process config location: "
550 + request.getConfigLocation() );
551 }
552 return configFile.getAbsolutePath();
553 }
554 catch ( org.codehaus.plexus.resource.loader.ResourceNotFoundException e )
555 {
556 throw new CheckstyleExecutorException( "Unable to find configuration file at location "
557 + request.getConfigLocation(), e );
558 }
559 catch ( FileResourceCreationException e )
560 {
561 throw new CheckstyleExecutorException( "Unable to process configuration file location "
562 + request.getConfigLocation(), e );
563 }
564
565 }
566 }