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