1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.checkstyle;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31
32 import com.puppycrawl.tools.checkstyle.DefaultLogger;
33 import com.puppycrawl.tools.checkstyle.XMLLogger;
34 import com.puppycrawl.tools.checkstyle.api.AuditListener;
35 import com.puppycrawl.tools.checkstyle.api.AutomaticBean.OutputStreamOptions;
36 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
37 import org.apache.commons.lang3.StringUtils;
38 import org.apache.maven.artifact.Artifact;
39 import org.apache.maven.execution.MavenSession;
40 import org.apache.maven.model.Dependency;
41 import org.apache.maven.model.Plugin;
42 import org.apache.maven.model.PluginManagement;
43 import org.apache.maven.model.ReportPlugin;
44 import org.apache.maven.model.Resource;
45 import org.apache.maven.plugin.descriptor.PluginDescriptor;
46 import org.apache.maven.plugins.annotations.Component;
47 import org.apache.maven.plugins.annotations.Parameter;
48 import org.apache.maven.plugins.checkstyle.exec.CheckstyleExecutor;
49 import org.apache.maven.plugins.checkstyle.exec.CheckstyleExecutorException;
50 import org.apache.maven.plugins.checkstyle.exec.CheckstyleExecutorRequest;
51 import org.apache.maven.plugins.checkstyle.exec.CheckstyleResults;
52 import org.apache.maven.reporting.AbstractMavenReport;
53 import org.apache.maven.reporting.MavenReportException;
54 import org.codehaus.plexus.configuration.PlexusConfiguration;
55 import org.codehaus.plexus.i18n.I18N;
56 import org.codehaus.plexus.resource.ResourceManager;
57 import org.codehaus.plexus.resource.loader.FileResourceLoader;
58 import org.codehaus.plexus.util.FileUtils;
59 import org.codehaus.plexus.util.PathTool;
60
61
62
63
64
65
66 public abstract class AbstractCheckstyleReport extends AbstractMavenReport {
67 protected static final String JAVA_FILES = "**\\/*.java";
68
69 private static final String DEFAULT_CONFIG_LOCATION = "sun_checks.xml";
70
71 @Parameter(defaultValue = "${session}", readonly = true, required = true)
72 private MavenSession session;
73
74
75
76
77 @Parameter(defaultValue = "${project.build.directory}/checkstyle-cachefile")
78 protected String cacheFile;
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 @Parameter(property = "checkstyle.config.location", defaultValue = DEFAULT_CONFIG_LOCATION)
102 protected String configLocation;
103
104
105
106
107 @Parameter(property = "checkstyle.consoleOutput", defaultValue = "false")
108 protected boolean consoleOutput;
109
110
111
112
113 @Parameter(defaultValue = "false")
114 protected boolean failsOnError;
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 @Parameter(property = "checkstyle.header.file", defaultValue = "LICENSE.txt")
135 protected String headerLocation;
136
137
138
139
140
141
142 @Parameter(property = "checkstyle.skip", defaultValue = "false")
143 protected boolean skip;
144
145
146
147
148
149
150 @Parameter(property = "checkstyle.output.file", defaultValue = "${project.build.directory}/checkstyle-result.xml")
151 private File outputFile;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 @Parameter(property = "checkstyle.properties.location")
170 protected String propertiesLocation;
171
172
173
174
175 @Parameter
176 protected String propertyExpansion;
177
178
179
180
181
182
183 @Parameter(defaultValue = "${project.resources}", readonly = true)
184 protected List<Resource> resources;
185
186
187
188
189
190
191 @Parameter(defaultValue = "${project.testResources}", readonly = true)
192 protected List<Resource> testResources;
193
194
195
196
197 @Parameter(property = "checkstyle.includes", defaultValue = JAVA_FILES, required = true)
198 protected String includes;
199
200
201
202
203
204 @Parameter(property = "checkstyle.excludes")
205 protected String excludes;
206
207
208
209
210
211 @Parameter(property = "checkstyle.resourceIncludes", defaultValue = "**/*.properties", required = true)
212 protected String resourceIncludes;
213
214
215
216
217
218
219 @Parameter(property = "checkstyle.resourceExcludes")
220 protected String resourceExcludes;
221
222
223
224
225
226 @Parameter(property = "checkstyle.includeResources", defaultValue = "true", required = true)
227 protected boolean includeResources;
228
229
230
231
232
233 @Parameter(property = "checkstyle.includeTestResources", defaultValue = "true", required = true)
234 protected boolean includeTestResources;
235
236
237
238
239
240
241
242 @Deprecated
243 @Parameter
244 private File sourceDirectory;
245
246
247
248
249
250
251
252 @Parameter
253 private List<String> sourceDirectories;
254
255
256
257
258
259
260
261
262 @Parameter
263 @Deprecated
264 private File testSourceDirectory;
265
266
267
268
269
270
271
272 @Parameter
273 private List<String> testSourceDirectories;
274
275
276
277
278
279
280 @Parameter(defaultValue = "false")
281 protected boolean includeTestSourceDirectory;
282
283
284
285
286
287
288 @Parameter(property = "checkstyle.suppression.expression", defaultValue = "checkstyle.suppressions.file")
289 protected String suppressionsFileExpression;
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305 @Parameter(property = "checkstyle.suppressions.location")
306 protected String suppressionsLocation;
307
308
309
310
311
312 @Parameter
313 private File useFile;
314
315
316
317
318
319 @Parameter(property = "checkstyle.output.format", defaultValue = "xml")
320 private String outputFileFormat;
321
322
323
324
325 @Parameter(property = "checkstyle.enable.rules.summary", defaultValue = "true")
326 private boolean enableRulesSummary;
327
328
329
330
331 @Parameter(property = "checkstyle.enable.severity.summary", defaultValue = "true")
332 private boolean enableSeveritySummary;
333
334
335
336
337 @Parameter(property = "checkstyle.enable.files.summary", defaultValue = "true")
338 private boolean enableFilesSummary;
339
340
341
342
343 @Parameter(defaultValue = "${plugin}", readonly = true, required = true)
344 private PluginDescriptor plugin;
345
346
347
348
349
350
351
352 @Parameter(property = "linkXRef", defaultValue = "true")
353 private boolean linkXRef;
354
355
356
357
358 @Parameter(defaultValue = "${project.reporting.outputDirectory}/xref")
359 private File xrefLocation;
360
361
362
363
364 @Parameter(defaultValue = "${project.reporting.outputDirectory}/xref-test")
365 private File xrefTestLocation;
366
367
368
369
370
371
372
373 @Parameter
374 private List<String> treeWalkerNames;
375
376
377
378
379
380
381
382 @Parameter(defaultValue = "false")
383 private boolean omitIgnoredModules;
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409 @Parameter
410 private PlexusConfiguration checkstyleRules;
411
412
413
414
415 @Parameter(
416 property = "checkstyle.output.rules.file",
417 defaultValue = "${project.build.directory}/checkstyle-rules.xml")
418 private File rulesFiles;
419
420
421
422
423
424 @Parameter(
425 defaultValue = "<?xml version=\"1.0\"?>\n"
426 + "<!DOCTYPE module PUBLIC \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\"\n"
427 + " \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n")
428 private String checkstyleRulesHeader;
429
430
431
432 @Component
433 protected ResourceManager locator;
434
435
436
437
438 @Component(role = CheckstyleExecutor.class, hint = "default")
439 protected CheckstyleExecutor checkstyleExecutor;
440
441
442
443
444 @Component
445 private I18N i18n;
446
447 protected ByteArrayOutputStream stringOutputStream;
448
449
450 public String getName(Locale locale) {
451 return getI18nString(locale, "name");
452 }
453
454
455 public String getDescription(Locale locale) {
456 return getI18nString(locale, "description");
457 }
458
459
460
461
462
463
464 protected String getI18nString(Locale locale, String key) {
465 return i18n.getString("checkstyle-report", locale, "report.checkstyle." + key);
466 }
467
468
469 public void executeReport(Locale locale) throws MavenReportException {
470 checkDeprecatedParameterUsage(sourceDirectory, "sourceDirectory", "sourceDirectories");
471 checkDeprecatedParameterUsage(testSourceDirectory, "testSourceDirectory", "testSourceDirectories");
472
473 locator.addSearchPath(
474 FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath());
475 locator.addSearchPath("url", "");
476
477 locator.setOutputDirectory(new File(project.getBuild().getDirectory()));
478
479
480
481
482
483 String effectiveConfigLocation = configLocation;
484 if (checkstyleRules != null) {
485 if (!DEFAULT_CONFIG_LOCATION.equals(configLocation)) {
486 throw new MavenReportException(
487 "If you use inline configuration for rules, don't specify " + "a configLocation");
488 }
489 if (checkstyleRules.getChildCount() > 1) {
490 throw new MavenReportException("Currently only one root module is supported");
491 }
492 PlexusConfiguration checkerModule = checkstyleRules.getChild(0);
493
494 try {
495 FileUtils.forceMkdir(rulesFiles.getParentFile());
496 FileUtils.fileWrite(rulesFiles, checkstyleRulesHeader + checkerModule.toString());
497 } catch (final IOException e) {
498 throw new MavenReportException(e.getMessage(), e);
499 }
500 effectiveConfigLocation = rulesFiles.getAbsolutePath();
501 }
502 ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
503
504 try {
505 CheckstyleExecutorRequest request = createRequest()
506 .setLicenseArtifacts(collectArtifacts("license"))
507 .setConfigurationArtifacts(collectArtifacts("configuration"))
508 .setOmitIgnoredModules(omitIgnoredModules)
509 .setConfigLocation(effectiveConfigLocation);
510
511 CheckstyleResults results = checkstyleExecutor.executeCheckstyle(request);
512
513 CheckstyleReportRenderer r = new CheckstyleReportRenderer(
514 getSink(),
515 i18n,
516 locale,
517 project,
518 siteTool,
519 effectiveConfigLocation,
520 enableRulesSummary,
521 enableSeveritySummary,
522 enableFilesSummary,
523 results);
524 if (linkXRef) {
525 initializeXrefLocation(r);
526 if (r.getXrefLocation() == null && results.getFileCount() > 0) {
527 getLog().warn("Unable to locate Source XRef to link to - DISABLED");
528 }
529
530 initializeXrefTestLocation(r);
531 if (r.getXrefTestLocation() == null && results.getFileCount() > 0) {
532 getLog().warn("Unable to locate Test Source XRef to link to - DISABLED");
533 }
534
535 r.setTestSourceDirectories(getTestSourceDirectories());
536 }
537 if (treeWalkerNames != null) {
538 r.setTreeWalkerNames(treeWalkerNames);
539 }
540 r.render();
541 } catch (CheckstyleException e) {
542 throw new MavenReportException("Failed during checkstyle configuration", e);
543 } catch (CheckstyleExecutorException e) {
544 throw new MavenReportException("Failed during checkstyle execution", e);
545 } finally {
546
547 Thread.currentThread().setContextClassLoader(currentClassLoader);
548 }
549 }
550
551 private void checkDeprecatedParameterUsage(Object parameter, String name, String replacement)
552 throws MavenReportException {
553 if (parameter != null) {
554 throw new MavenReportException("You are using '" + name + "' which has been removed"
555 + " from the maven-checkstyle-plugin. " + "Please use '" + replacement
556 + "' and refer to the >>Major Version Upgrade to version 3.0.0<< " + "on the plugin site.");
557 }
558 }
559
560
561
562
563
564
565
566 protected abstract CheckstyleExecutorRequest createRequest() throws MavenReportException;
567
568 private List<Artifact> collectArtifacts(String hint) {
569 List<Artifact> artifacts = new ArrayList<>();
570
571 PluginManagement pluginManagement = project.getBuild().getPluginManagement();
572 if (pluginManagement != null) {
573 artifacts.addAll(getCheckstylePluginDependenciesAsArtifacts(pluginManagement.getPluginsAsMap(), hint));
574 }
575
576 artifacts.addAll(
577 getCheckstylePluginDependenciesAsArtifacts(project.getBuild().getPluginsAsMap(), hint));
578
579 return artifacts;
580 }
581
582 private List<Artifact> getCheckstylePluginDependenciesAsArtifacts(Map<String, Plugin> plugins, String hint) {
583 List<Artifact> artifacts = new ArrayList<>();
584
585 Plugin checkstylePlugin = plugins.get(plugin.getGroupId() + ":" + plugin.getArtifactId());
586 if (checkstylePlugin != null) {
587 for (Dependency dep : checkstylePlugin.getDependencies()) {
588
589 String depKey = dep.getGroupId() + ":" + dep.getArtifactId();
590 artifacts.add(plugin.getArtifactMap().get(depKey));
591 }
592 }
593 return artifacts;
594 }
595
596
597
598
599
600
601
602 protected AuditListener getListener() throws MavenReportException {
603 AuditListener listener = null;
604
605 if (StringUtils.isNotEmpty(outputFileFormat)) {
606 File resultFile = outputFile;
607
608 OutputStream out = getOutputStream(resultFile);
609
610 if ("xml".equals(outputFileFormat)) {
611 listener = new XMLLogger(out, OutputStreamOptions.CLOSE);
612 } else if ("plain".equals(outputFileFormat)) {
613 listener = new DefaultLogger(out, OutputStreamOptions.CLOSE);
614 } else {
615
616 throw new MavenReportException(
617 "Invalid output file format: (" + outputFileFormat + "). Must be 'plain' or 'xml'.");
618 }
619 }
620
621 return listener;
622 }
623
624 private OutputStream getOutputStream(File file) throws MavenReportException {
625 File parentFile = file.getAbsoluteFile().getParentFile();
626
627 if (!parentFile.exists()) {
628 parentFile.mkdirs();
629 }
630
631 FileOutputStream fileOutputStream;
632 try {
633 fileOutputStream = new FileOutputStream(file);
634 } catch (FileNotFoundException e) {
635 throw new MavenReportException("Unable to create output stream: " + file, e);
636 }
637 return fileOutputStream;
638 }
639
640
641
642
643
644
645
646 protected DefaultLogger getConsoleListener() throws MavenReportException {
647 DefaultLogger consoleListener;
648
649 if (useFile == null) {
650 stringOutputStream = new ByteArrayOutputStream();
651 consoleListener = new DefaultLogger(stringOutputStream, OutputStreamOptions.NONE);
652 } else {
653 OutputStream out = getOutputStream(useFile);
654
655 consoleListener = new DefaultLogger(out, OutputStreamOptions.CLOSE);
656 }
657
658 return consoleListener;
659 }
660
661 private void initializeXrefLocation(CheckstyleReportRenderer renderer) {
662 String relativePath = determineRelativePath(xrefLocation);
663 if (xrefLocation.exists() || checkMavenJxrPluginIsConfigured()) {
664
665
666 renderer.setXrefLocation(relativePath);
667 }
668 }
669
670 private void initializeXrefTestLocation(CheckstyleReportRenderer renderer) {
671 String relativePath = determineRelativePath(xrefTestLocation);
672 if (xrefTestLocation.exists() || checkMavenJxrPluginIsConfigured()) {
673
674
675 renderer.setXrefTestLocation(relativePath);
676 }
677 }
678
679 private String determineRelativePath(File location) {
680 String relativePath = PathTool.getRelativePath(getOutputDirectory(), location.getAbsolutePath());
681 if (relativePath == null || relativePath.trim().isEmpty()) {
682 relativePath = ".";
683 }
684
685 return relativePath + "/" + location.getName();
686 }
687
688 private boolean checkMavenJxrPluginIsConfigured() {
689 for (ReportPlugin report : (Iterable<ReportPlugin>) getProject().getReportPlugins()) {
690 String artifactId = report.getArtifactId();
691 if ("maven-jxr-plugin".equals(artifactId) || "jxr-maven-plugin".equals(artifactId)) {
692 return true;
693 }
694 }
695
696 return false;
697 }
698
699 protected List<File> getSourceDirectories() {
700 if (sourceDirectories == null) {
701 sourceDirectories = project.getCompileSourceRoots();
702 }
703 List<File> sourceDirs = new ArrayList<>(sourceDirectories.size());
704 for (String sourceDir : sourceDirectories) {
705 sourceDirs.add(FileUtils.resolveFile(project.getBasedir(), sourceDir));
706 }
707 return sourceDirs;
708 }
709
710 protected List<File> getTestSourceDirectories() {
711 if (testSourceDirectories == null) {
712 testSourceDirectories = project.getTestCompileSourceRoots();
713 }
714 List<File> testSourceDirs = new ArrayList<>(testSourceDirectories.size());
715 for (String testSourceDir : testSourceDirectories) {
716 testSourceDirs.add(FileUtils.resolveFile(project.getBasedir(), testSourceDir));
717 }
718 return testSourceDirs;
719 }
720 }