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 com.puppycrawl.tools.checkstyle.DefaultLogger;
23 import com.puppycrawl.tools.checkstyle.XMLLogger;
24 import com.puppycrawl.tools.checkstyle.api.AuditListener;
25 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
26 import org.apache.maven.plugin.AbstractMojo;
27 import org.apache.maven.plugin.MojoExecutionException;
28 import org.apache.maven.plugin.MojoFailureException;
29 import org.apache.maven.plugins.annotations.Component;
30 import org.apache.maven.plugins.annotations.LifecyclePhase;
31 import org.apache.maven.plugins.annotations.Mojo;
32 import org.apache.maven.plugins.annotations.Parameter;
33 import org.apache.maven.plugins.annotations.ResolutionScope;
34 import org.apache.maven.project.MavenProject;
35 import org.codehaus.plexus.util.ReaderFactory;
36 import org.codehaus.plexus.util.StringUtils;
37 import org.codehaus.plexus.util.xml.pull.MXParser;
38 import org.codehaus.plexus.util.xml.pull.XmlPullParser;
39 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
40
41 import java.io.BufferedReader;
42 import java.io.ByteArrayOutputStream;
43 import java.io.File;
44 import java.io.FileNotFoundException;
45 import java.io.FileOutputStream;
46 import java.io.IOException;
47 import java.io.OutputStream;
48 import java.io.Reader;
49
50
51
52
53
54
55
56
57
58 @Mojo( name = "check", defaultPhase = LifecyclePhase.VERIFY, requiresDependencyResolution = ResolutionScope.TEST,
59 threadSafe = true )
60 public class CheckstyleViolationCheckMojo
61 extends AbstractMojo
62 {
63
64 private static final String JAVA_FILES = "**\\/*.java";
65
66
67
68
69
70
71 @Parameter( property = "checkstyle.output.file", defaultValue = "${project.build.directory}/checkstyle-result.xml" )
72 private File outputFile;
73
74
75
76
77
78 @Parameter( property = "checkstyle.output.format", defaultValue = "xml" )
79 private String outputFileFormat;
80
81
82
83
84 @Parameter( property = "checkstyle.failOnViolation", defaultValue = "true" )
85 private boolean failOnViolation;
86
87
88
89
90
91
92
93 @Parameter( property = "checkstyle.maxAllowedViolations", defaultValue = "0" )
94 private int maxAllowedViolations = 0;
95
96
97
98
99
100
101
102 @Parameter( property = "checkstyle.violationSeverity", defaultValue = "error" )
103 private String violationSeverity = "error";
104
105
106
107
108
109
110 @Parameter( property = "checkstyle.skip", defaultValue = "false" )
111 private boolean skip;
112
113
114
115
116
117
118 @Parameter( property = "checkstyle.skipExec", defaultValue = "false" )
119 private boolean skipExec;
120
121
122
123
124
125
126 @Parameter( property = "checkstyle.console", defaultValue = "false" )
127 private boolean logViolationsToConsole;
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 @Parameter( property = "checkstyle.config.location", defaultValue = "config/sun_checks.xml" )
163 private String configLocation;
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186 @Parameter( property = "checkstyle.properties.location" )
187 private String propertiesLocation;
188
189
190
191
192 @Parameter
193 private String propertyExpansion;
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 @Parameter( property = "checkstyle.header.file", defaultValue = "LICENSE.txt" )
219 private String headerLocation;
220
221
222
223
224 @Parameter( defaultValue = "${project.build.directory}/checkstyle-cachefile" )
225 private String cacheFile;
226
227
228
229
230
231
232 @Parameter( property = "checkstyle.suppression.expression", defaultValue = "checkstyle.suppressions.file" )
233 private String suppressionsFileExpression;
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 @Parameter( property = "checkstyle.suppressions.location" )
255 private String suppressionsLocation;
256
257
258
259
260
261
262
263
264 @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
265 private String encoding;
266
267
268
269
270 @Component( role = CheckstyleExecutor.class, hint = "default" )
271 protected CheckstyleExecutor checkstyleExecutor;
272
273
274
275
276 @Parameter( defaultValue = "false" )
277 private boolean consoleOutput;
278
279
280
281
282 @Component
283 protected MavenProject project;
284
285
286
287
288
289 @Parameter
290 private File useFile;
291
292
293
294
295
296 @Parameter( property = "checkstyle.excludes" )
297 private String excludes;
298
299
300
301
302
303
304 @Parameter( property = "checkstyle.includes", defaultValue = JAVA_FILES, required = true )
305 private String includes;
306
307
308
309
310 @Parameter( defaultValue = "false" )
311 private boolean failsOnError;
312
313
314
315
316
317
318
319 @Parameter( defaultValue = "${project.build.testSourceDirectory}" )
320 private File testSourceDirectory;
321
322
323
324
325
326
327 @Parameter( defaultValue = "false" )
328 private boolean includeTestSourceDirectory;
329
330
331
332
333 @Parameter( defaultValue = "${project.build.sourceDirectory}", required = true )
334 private File sourceDirectory;
335
336 private ByteArrayOutputStream stringOutputStream;
337
338
339
340 public void execute()
341 throws MojoExecutionException, MojoFailureException
342 {
343
344 if ( !skip )
345 {
346
347 if ( !skipExec )
348 {
349
350 ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
351
352 try
353 {
354 CheckstyleExecutorRequest request = new CheckstyleExecutorRequest();
355 request.setConsoleListener( getConsoleListener() ).setConsoleOutput( consoleOutput )
356 .setExcludes( excludes ).setFailsOnError( failsOnError ).setIncludes( includes )
357 .setIncludeTestSourceDirectory( includeTestSourceDirectory ).setListener( getListener() )
358 .setLog( getLog() ).setProject( project ).setSourceDirectory( sourceDirectory )
359 .setStringOutputStream( stringOutputStream ).setSuppressionsLocation( suppressionsLocation )
360 .setTestSourceDirectory( testSourceDirectory ).setConfigLocation( configLocation )
361 .setPropertyExpansion( propertyExpansion ).setHeaderLocation( headerLocation )
362 .setCacheFile( cacheFile ).setSuppressionsFileExpression( suppressionsFileExpression )
363 .setEncoding( encoding ).setPropertiesLocation( propertiesLocation );
364
365 checkstyleExecutor.executeCheckstyle( request );
366
367 }
368 catch ( CheckstyleException e )
369 {
370 throw new MojoExecutionException( "Failed during checkstyle configuration", e );
371 }
372 catch ( CheckstyleExecutorException e )
373 {
374 throw new MojoExecutionException( "Failed during checkstyle execution", e );
375 }
376 finally
377 {
378
379 Thread.currentThread().setContextClassLoader( currentClassLoader );
380 }
381
382 }
383 if ( !"xml".equals( outputFileFormat ) )
384 {
385 throw new MojoExecutionException( "Output format is '" + outputFileFormat
386 + "', checkstyle:check requires format to be 'xml'." );
387 }
388
389 if ( !outputFile.exists() )
390 {
391 getLog().info(
392 "Unable to perform checkstyle:check, "
393 + "unable to find checkstyle:checkstyle outputFile." );
394 return;
395 }
396
397 try
398 {
399 XmlPullParser xpp = new MXParser();
400 Reader freader = ReaderFactory.newXmlReader( outputFile );
401 BufferedReader breader = new BufferedReader( freader );
402 xpp.setInput( breader );
403
404 int violations = countViolations( xpp );
405 if ( violations > maxAllowedViolations )
406 {
407 if ( failOnViolation )
408 {
409 String msg = "You have " + violations + " Checkstyle violation"
410 + ( ( violations > 1 ) ? "s" : "" ) + ".";
411 if ( maxAllowedViolations > 0 )
412 {
413 msg += " The maximum number of allowed violations is " + maxAllowedViolations + ".";
414 }
415 throw new MojoFailureException( msg );
416 }
417
418 getLog().warn( "checkstyle:check violations detected but failOnViolation set to false" );
419 }
420 }
421 catch ( IOException e )
422 {
423 throw new MojoExecutionException( "Unable to read Checkstyle results xml: "
424 + outputFile.getAbsolutePath(), e );
425 }
426 catch ( XmlPullParserException e )
427 {
428 throw new MojoExecutionException( "Unable to read Checkstyle results xml: "
429 + outputFile.getAbsolutePath(), e );
430 }
431 }
432 }
433
434 private int countViolations( XmlPullParser xpp )
435 throws XmlPullParserException, IOException
436 {
437 int count = 0;
438
439 int eventType = xpp.getEventType();
440 String file = "";
441 while ( eventType != XmlPullParser.END_DOCUMENT )
442 {
443 if ( eventType == XmlPullParser.START_TAG && "file".equals( xpp.getName() ) )
444 {
445 file = xpp.getAttributeValue( "", "name" );
446 file = file.substring( file.lastIndexOf( File.separatorChar ) + 1 );
447 }
448
449 if ( eventType == XmlPullParser.START_TAG && "error".equals( xpp.getName() )
450 && isViolation( xpp.getAttributeValue( "", "severity" ) ) )
451 {
452 if ( logViolationsToConsole )
453 {
454 StringBuilder stb = new StringBuilder();
455 stb.append( file );
456 stb.append( '[' );
457 stb.append( xpp.getAttributeValue( "", "line" ) );
458 stb.append( ':' );
459 stb.append( xpp.getAttributeValue( "", "column" ) );
460 stb.append( "] " );
461 stb.append( xpp.getAttributeValue( "", "message" ) );
462 getLog().error( stb.toString() );
463 }
464 count++;
465 }
466 eventType = xpp.next();
467 }
468
469 return count;
470 }
471
472
473
474
475
476
477
478 private boolean isViolation( String severity )
479 {
480 if ( "error".equals( severity ) )
481 {
482 return "error".equals( violationSeverity ) || "warning".equals( violationSeverity )
483 || "info".equals( violationSeverity );
484 }
485 else if ( "warning".equals( severity ) )
486 {
487 return "warning".equals( violationSeverity ) || "info".equals( violationSeverity );
488 }
489 else if ( "info".equals( severity ) )
490 {
491 return "info".equals( violationSeverity );
492 }
493 else
494 {
495 return false;
496 }
497 }
498 private DefaultLogger getConsoleListener()
499 throws MojoExecutionException
500 {
501 DefaultLogger consoleListener;
502
503 if ( useFile == null )
504 {
505 stringOutputStream = new ByteArrayOutputStream();
506 consoleListener = new DefaultLogger( stringOutputStream, false );
507 }
508 else
509 {
510 OutputStream out = getOutputStream( useFile );
511
512 consoleListener = new DefaultLogger( out, true );
513 }
514
515 return consoleListener;
516 }
517
518 private OutputStream getOutputStream( File file )
519 throws MojoExecutionException
520 {
521 File parentFile = file.getAbsoluteFile().getParentFile();
522
523 if ( !parentFile.exists() )
524 {
525 parentFile.mkdirs();
526 }
527
528 FileOutputStream fileOutputStream;
529 try
530 {
531 fileOutputStream = new FileOutputStream( file );
532 }
533 catch ( FileNotFoundException e )
534 {
535 throw new MojoExecutionException( "Unable to create output stream: " + file, e );
536 }
537 return fileOutputStream;
538 }
539
540 private AuditListener getListener()
541 throws MojoFailureException, MojoExecutionException
542 {
543 AuditListener listener = null;
544
545 if ( StringUtils.isNotEmpty( outputFileFormat ) )
546 {
547 File resultFile = outputFile;
548
549 OutputStream out = getOutputStream( resultFile );
550
551 if ( "xml".equals( outputFileFormat ) )
552 {
553 listener = new XMLLogger( out, true );
554 }
555 else if ( "plain".equals( outputFileFormat ) )
556 {
557 listener = new DefaultLogger( out, true );
558 }
559 else
560 {
561 throw new MojoFailureException( "Invalid output file format: (" + outputFileFormat
562 + "). Must be 'plain' or 'xml'." );
563 }
564 }
565
566 return listener;
567 }
568
569 }