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