1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.rat.mp;
20
21 import static org.apache.rat.mp.util.ExclusionHelper.addEclipseDefaults;
22 import static org.apache.rat.mp.util.ExclusionHelper.addIdeaDefaults;
23 import static org.apache.rat.mp.util.ExclusionHelper.addMavenDefaults;
24 import static org.apache.rat.mp.util.ExclusionHelper.addPlexusAndScmDefaults;
25
26 import java.io.BufferedInputStream;
27 import java.io.BufferedReader;
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.InputStreamReader;
32 import java.io.Reader;
33 import java.lang.reflect.UndeclaredThrowableException;
34 import java.net.MalformedURLException;
35 import java.net.URL;
36 import java.nio.file.Files;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.Collections;
41 import java.util.List;
42 import java.util.Objects;
43 import java.util.SortedSet;
44 import java.util.function.Consumer;
45 import java.util.stream.Collectors;
46 import java.util.stream.Stream;
47
48 import org.apache.commons.lang3.StringUtils;
49 import org.apache.maven.plugin.AbstractMojo;
50 import org.apache.maven.plugin.MojoExecutionException;
51 import org.apache.maven.plugin.logging.Log;
52 import org.apache.maven.plugins.annotations.Parameter;
53 import org.apache.maven.project.MavenProject;
54 import org.apache.rat.ConfigurationException;
55 import org.apache.rat.Defaults;
56 import org.apache.rat.ReportConfiguration;
57 import org.apache.rat.analysis.license.DeprecatedConfig;
58 import org.apache.rat.config.SourceCodeManagementSystems;
59 import org.apache.rat.configuration.Format;
60 import org.apache.rat.configuration.LicenseReader;
61 import org.apache.rat.configuration.MatcherReader;
62 import org.apache.rat.license.ILicense;
63 import org.apache.rat.license.ILicenseFamily;
64 import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
65 import org.apache.rat.license.SimpleLicenseFamily;
66 import org.apache.rat.mp.util.ScmIgnoreParser;
67 import org.apache.rat.mp.util.ignore.GlobIgnoreMatcher;
68 import org.apache.rat.mp.util.ignore.IgnoreMatcher;
69 import org.apache.rat.mp.util.ignore.IgnoringDirectoryScanner;
70 import org.apache.rat.report.IReportable;
71 import org.codehaus.plexus.util.DirectoryScanner;
72
73
74
75
76 public abstract class AbstractRatMojo extends AbstractMojo {
77
78
79
80
81 @Parameter(property = "rat.basedir", defaultValue = "${basedir}", required = true)
82 private File basedir;
83
84
85
86
87
88
89
90 @Parameter
91 private String[] defaultLicenseFiles;
92
93 @Parameter
94 private String[] additionalLicenseFiles;
95
96
97
98
99 @Parameter(property = "rat.addDefaultLicenses", defaultValue = "true")
100 private boolean addDefaultLicenses;
101
102
103
104
105 @Parameter(property = "rat.addDefaultLicenseMatchers", defaultValue = "true")
106 private boolean addDefaultLicenseMatchers;
107
108 @Parameter(required = false)
109 private String[] approvedLicenses;
110
111 @Parameter(property = "rat.approvedFile")
112 private String approvedLicenseFile;
113
114
115
116
117
118
119
120 @Deprecated
121 @Parameter
122 private SimpleLicenseFamily[] licenseFamilies;
123
124
125
126
127 @Deprecated
128 @Parameter
129 private Object[] licenses;
130
131 @Parameter
132 private Family[] families;
133
134
135
136
137
138 @Parameter
139 private String[] includes;
140
141
142
143
144
145 @Parameter(property = "rat.includesFile")
146 private String includesFile;
147
148
149
150
151
152 @Parameter(property = "rat.includesFileCharset", defaultValue = "${project.build.sourceEncoding}")
153 private String includesFileCharset;
154
155
156
157
158
159 @Parameter
160 private String[] excludes;
161
162
163
164
165
166
167 @Parameter(property = "rat.excludesFile")
168 private String excludesFile;
169
170
171
172
173
174 @Parameter(property = "rat.excludesFileCharset", defaultValue = "${project.build.sourceEncoding}")
175 private String excludesFileCharset;
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191 @Parameter(property = "rat.useDefaultExcludes", defaultValue = "true")
192 private boolean useDefaultExcludes;
193
194
195
196
197
198
199
200 @Parameter(property = "rat.useMavenDefaultExcludes", defaultValue = "true")
201 private boolean useMavenDefaultExcludes;
202
203
204
205
206
207
208
209 @Parameter(property = "rat.parseSCMIgnoresAsExcludes", defaultValue = "true")
210 private boolean parseSCMIgnoresAsExcludes;
211
212
213
214
215
216
217
218 @Parameter(property = "rat.useEclipseDefaultExcludes", defaultValue = "true")
219 private boolean useEclipseDefaultExcludes;
220
221
222
223
224
225
226
227 @Parameter(property = "rat.useIdeaDefaultExcludes", defaultValue = "true")
228 private boolean useIdeaDefaultExcludes;
229
230
231
232
233
234 @Parameter(property = "rat.excludeSubprojects", defaultValue = "true")
235 private boolean excludeSubProjects;
236
237
238
239
240
241
242
243 @Parameter(property = "rat.skip", defaultValue = "false")
244 protected boolean skip;
245
246
247
248
249
250 @Parameter(defaultValue = "${project}", required = true, readonly = true)
251 protected MavenProject project;
252
253
254
255
256 protected MavenProject getProject() {
257 return project;
258 }
259
260 protected Defaults.Builder getDefaultsBuilder() {
261 Defaults.Builder result = Defaults.builder();
262 if (defaultLicenseFiles != null) {
263 for (String defaultLicenseFile : defaultLicenseFiles) {
264 try {
265 result.add(defaultLicenseFile);
266 } catch (MalformedURLException e) {
267 throw new ConfigurationException(defaultLicenseFile + " is not a valid license file", e);
268 }
269 }
270 }
271 return result;
272 }
273
274 @Deprecated
275 private Stream<License> getLicenses() {
276 if (licenses == null) {
277 return Stream.empty();
278 }
279 return Arrays.stream(licenses).filter( s -> {return s instanceof License;}).map(License.class::cast);
280 }
281
282 @Deprecated
283 private Stream<DeprecatedConfig> getDeprecatedConfigs() {
284 if (licenses == null) {
285 return Stream.empty();
286 }
287 return Arrays.stream(licenses).filter( s -> {return s instanceof DeprecatedConfig;}).map(DeprecatedConfig.class::cast);
288 }
289
290 @Deprecated
291 private void reportDeprecatedProcessing()
292 {
293 if (getDeprecatedConfigs().findAny().isPresent()) {
294 Log log = getLog();
295 log.warn("Configuration uses deprecated configuration. Please upgrade to v0.17 configuration options");
296 }
297 }
298
299 @Deprecated
300 private void processLicenseFamilies(ReportConfiguration config) {
301 List<ILicenseFamily> families = getDeprecatedConfigs().map(DeprecatedConfig::getLicenseFamily).filter(Objects::nonNull).collect(Collectors.toList());
302 if (licenseFamilies != null) {
303 for (SimpleLicenseFamily slf : licenseFamilies) {
304 if (StringUtils.isBlank(slf.getFamilyCategory())) {
305 families.stream().filter( f -> f.getFamilyName().equalsIgnoreCase(slf.getFamilyName())).findFirst()
306 .ifPresent(config::addApprovedLicenseCategory);
307 } else {
308 config.addApprovedLicenseCategory(ILicenseFamily.builder().setLicenseFamilyCategory(slf.getFamilyCategory())
309 .setLicenseFamilyName(StringUtils.defaultIfBlank(slf.getFamilyName(), slf.getFamilyCategory()))
310 .build());
311 }
312 }
313 }
314 }
315
316 private org.apache.rat.utils.Log makeLog() {
317 return new org.apache.rat.utils.Log() {
318 Log log = getLog();
319 @Override
320 public void log(Level level, String msg) {
321 switch (level)
322 {
323 case DEBUG:
324 log.debug(msg);
325 break;
326 case INFO:
327 log.info(msg);;
328 break;
329 case WARN:
330 log.warn(msg);
331 break;
332 case ERROR:
333 log.error(msg);
334 break;
335 }
336 }};
337 }
338
339 protected ReportConfiguration getConfiguration() throws MojoExecutionException {
340 ReportConfiguration config = new ReportConfiguration(makeLog());
341 reportDeprecatedProcessing();
342 if (addDefaultLicenses) {
343 config.setFrom(getDefaultsBuilder().build());
344 } else {
345 config.setStyleSheet(Defaults.getPlainStyleSheet());
346 }
347 if (additionalLicenseFiles != null) {
348 for (String licenseFile : additionalLicenseFiles) {
349 try {
350 URL url = new File(licenseFile).toURI().toURL();
351 Format fmt = Format.fromName(licenseFile);
352 MatcherReader mReader = fmt.matcherReader();
353 if (mReader != null) {
354 mReader.addMatchers(url);
355 }
356 LicenseReader lReader = fmt.licenseReader();
357 if (lReader != null) {
358 lReader.addLicenses(url);
359 config.addLicenses(lReader.readLicenses());
360 config.addApprovedLicenseCategories(lReader.approvedLicenseId());
361 }
362 } catch (MalformedURLException e) {
363 throw new ConfigurationException(licenseFile + " is not a valid license file", e);
364 }
365 }
366 }
367 if (families != null || getDeprecatedConfigs().findAny().isPresent()) {
368 Log log = getLog();
369 if (log.isDebugEnabled()) {
370 log.debug(String.format("%s license families loaded from pom", families.length));
371 }
372 Consumer<ILicenseFamily> logger = log.isDebugEnabled() ? (l) -> log.debug(String.format("Family: %s", l))
373 : (l) -> {
374 };
375
376 Consumer<ILicenseFamily> process = logger.andThen(config::addFamily);
377 getDeprecatedConfigs().map(DeprecatedConfig::getLicenseFamily).filter(Objects::nonNull).forEach(process);
378 if (families != null) {
379 Arrays.stream(families).map(Family::build).forEach(process);
380 }
381 }
382
383 processLicenseFamilies(config);
384
385 if (approvedLicenses != null && approvedLicenses.length > 0) {
386 Arrays.stream(approvedLicenses).forEach(config::addApprovedLicenseCategory);
387 }
388
389 if (licenses != null) {
390 Log log = getLog();
391 if (log.isDebugEnabled()) {
392 log.debug(String.format("%s licenses loaded from pom", licenses.length));
393 }
394 Consumer<ILicense> logger = log.isDebugEnabled() ? (l) -> log.debug(String.format("License: %s", l))
395 : (l) -> {
396 };
397 Consumer<ILicense> addApproved = (approvedLicenses == null || approvedLicenses.length == 0)
398 ? (l) -> config.addApprovedLicenseCategory(l.getLicenseFamily())
399 : (l) -> {
400 };
401
402 Consumer<ILicense> process = logger.andThen(config::addLicense).andThen(addApproved);
403 SortedSet<ILicenseFamily> families = config.getLicenseFamilies(LicenseFilter.all);
404 getDeprecatedConfigs().map(DeprecatedConfig::getLicense).filter(Objects::nonNull)
405 .map(x -> x.build(families)).forEach(process);
406 getLicenses().map(x -> x.build(families)).forEach(process);
407 }
408
409 config.setReportable(getReportable());
410 return config;
411 }
412
413 protected void logLicenses(Collection<ILicense> licenses) {
414 if (getLog().isDebugEnabled()) {
415 getLog().debug("The following " + licenses.size() + " licenses are activated:");
416 for (ILicense license : licenses) {
417 getLog().debug("* " + license.toString());
418 }
419 }
420 }
421
422
423
424
425
426
427
428
429 private IReportable getReportable() throws MojoExecutionException {
430 final IgnoringDirectoryScanner ds = new IgnoringDirectoryScanner();
431 ds.setBasedir(basedir);
432 setExcludes(ds);
433 setIncludes(ds);
434 ds.scan();
435 whenDebuggingLogExcludedFiles(ds);
436 final String[] files = ds.getIncludedFiles();
437 logAboutIncludedFiles(files);
438 try {
439 return new FilesReportable(basedir, files);
440 } catch (final IOException e) {
441 throw new UndeclaredThrowableException(e);
442 }
443 }
444
445 private void logAboutIncludedFiles(final String[] files) {
446 if (files.length == 0) {
447 getLog().warn("No resources included.");
448 } else {
449 getLog().debug(files.length + " resources included");
450 if (getLog().isDebugEnabled()) {
451 for (final String resource : files) {
452 getLog().debug(" - included " + resource);
453 }
454 }
455 }
456 }
457
458 private void whenDebuggingLogExcludedFiles(final DirectoryScanner ds) {
459 if (getLog().isDebugEnabled()) {
460 final String[] excludedFiles = ds.getExcludedFiles();
461 if (excludedFiles.length == 0) {
462 getLog().debug("No excluded resources.");
463 } else {
464 getLog().debug("Excluded " + excludedFiles.length + " resources:");
465 for (final String resource : excludedFiles) {
466 getLog().debug(" - excluded " + resource);
467 }
468 }
469 }
470 }
471
472 private void setIncludes(DirectoryScanner ds) throws MojoExecutionException {
473 if ((includes != null && includes.length > 0) || includesFile != null) {
474 final List<String> includeList = new ArrayList<>();
475 if (includes != null) {
476 includeList.addAll(Arrays.asList(includes));
477 }
478 if (includesFile != null) {
479 final String charset = includesFileCharset == null ? "UTF8" : includesFileCharset;
480 final File f = new File(includesFile);
481 if (!f.isFile()) {
482 getLog().error("IncludesFile not found: " + f.getAbsolutePath());
483 } else {
484 getLog().debug("Includes loaded from file " + includesFile + ", using character set " + charset);
485 }
486 includeList.addAll(getPatternsFromFile(f, charset));
487 }
488 ds.setIncludes(includeList.toArray(new String[includeList.size()]));
489 }
490 }
491
492 private List<String> getPatternsFromFile(File pFile, String pCharset) throws MojoExecutionException {
493 InputStream is = null;
494 BufferedInputStream bis = null;
495 Reader r = null;
496 BufferedReader br = null;
497 Throwable th = null;
498 final List<String> patterns = new ArrayList<>();
499 try {
500 is = Files.newInputStream(pFile.toPath());
501 bis = new BufferedInputStream(is);
502 r = new InputStreamReader(bis, pCharset);
503 br = new BufferedReader(r);
504 for (;;) {
505 final String s = br.readLine();
506 if (s == null) {
507 break;
508 }
509 patterns.add(s);
510 }
511 br.close();
512 br = null;
513 r.close();
514 r = null;
515 bis.close();
516 bis = null;
517 is.close();
518 is = null;
519 } catch (Throwable t) {
520 th = t;
521 } finally {
522 if (br != null) {
523 try {
524 br.close();
525 } catch (Throwable t) {
526 if (th == null) {
527 th = t;
528 }
529 }
530 }
531 if (r != null) {
532 try {
533 r.close();
534 } catch (Throwable t) {
535 if (th == null) {
536 th = t;
537 }
538 }
539 }
540 if (bis != null) {
541 try {
542 bis.close();
543 } catch (Throwable t) {
544 if (th == null) {
545 th = t;
546 }
547 }
548 }
549 if (is != null) {
550 try {
551 is.close();
552 } catch (Throwable t) {
553 if (th == null) {
554 th = t;
555 }
556 }
557 }
558 }
559 if (th != null) {
560 if (th instanceof RuntimeException) {
561 throw (RuntimeException) th;
562 }
563 if (th instanceof Error) {
564 throw (Error) th;
565 }
566 throw new MojoExecutionException(th.getMessage(), th);
567 }
568 return patterns;
569 }
570
571 private void setExcludes(IgnoringDirectoryScanner ds) throws MojoExecutionException {
572 final List<IgnoreMatcher> ignoreMatchers = mergeDefaultExclusions();
573 if (excludes == null || excludes.length == 0) {
574 getLog().debug("No excludes explicitly specified.");
575 } else {
576 getLog().debug(excludes.length + " explicit excludes.");
577 for (final String exclude : excludes) {
578 getLog().debug("Exclude: " + exclude);
579 }
580 }
581
582 final List<String> globExcludes = new ArrayList<>();
583 for (IgnoreMatcher ignoreMatcher : ignoreMatchers) {
584 if (ignoreMatcher instanceof GlobIgnoreMatcher) {
585
586 globExcludes.addAll(((GlobIgnoreMatcher) ignoreMatcher).getExclusionLines());
587 } else {
588
589 ds.addIgnoreMatcher(ignoreMatcher);
590 }
591 }
592
593 if (excludes != null) {
594 Collections.addAll(globExcludes, excludes);
595 }
596 if (!globExcludes.isEmpty()) {
597 final String[] allExcludes = globExcludes.toArray(new String[globExcludes.size()]);
598 ds.setExcludes(allExcludes);
599 }
600 }
601
602 private List<IgnoreMatcher> mergeDefaultExclusions() throws MojoExecutionException {
603 List<IgnoreMatcher> ignoreMatchers = new ArrayList<>();
604
605 final GlobIgnoreMatcher basicRules = new GlobIgnoreMatcher();
606
607 basicRules.addRules(addPlexusAndScmDefaults(getLog(), useDefaultExcludes));
608 basicRules.addRules(addMavenDefaults(getLog(), useMavenDefaultExcludes));
609 basicRules.addRules(addEclipseDefaults(getLog(), useEclipseDefaultExcludes));
610 basicRules.addRules(addIdeaDefaults(getLog(), useIdeaDefaultExcludes));
611
612 if (parseSCMIgnoresAsExcludes) {
613 getLog().debug("Will parse SCM ignores for exclusions...");
614 ignoreMatchers.addAll(ScmIgnoreParser.getExclusionsFromSCM(getLog(), project.getBasedir()));
615 getLog().debug("Finished adding exclusions from SCM ignore files.");
616 }
617
618 if (excludeSubProjects && project != null && project.getModules() != null) {
619 for (final String moduleSubPath : project.getModules()) {
620 if (new File(basedir, moduleSubPath).isDirectory()) {
621 basicRules.addRule(moduleSubPath + "/**/*");
622 } else {
623 basicRules.addRule(StringUtils.substringBeforeLast(moduleSubPath, "/") + "/**/*");
624 }
625 }
626 }
627
628 if (getLog().isDebugEnabled()) {
629 getLog().debug("Finished creating list of implicit excludes.");
630 if (basicRules.getExclusionLines().isEmpty() && ignoreMatchers.isEmpty()) {
631 getLog().debug("No excludes implicitly specified.");
632 } else {
633 if (!basicRules.getExclusionLines().isEmpty()) {
634 getLog().debug(basicRules.getExclusionLines().size() + " implicit excludes.");
635 for (final String exclude : basicRules.getExclusionLines()) {
636 getLog().debug("Implicit exclude: " + exclude);
637 }
638 }
639 for (IgnoreMatcher ignoreMatcher : ignoreMatchers) {
640 if (ignoreMatcher instanceof GlobIgnoreMatcher) {
641 GlobIgnoreMatcher globIgnoreMatcher = (GlobIgnoreMatcher) ignoreMatcher;
642 if (!globIgnoreMatcher.getExclusionLines().isEmpty()) {
643 getLog().debug(globIgnoreMatcher.getExclusionLines().size() + " implicit excludes from SCM.");
644 for (final String exclude : globIgnoreMatcher.getExclusionLines()) {
645 getLog().debug("Implicit exclude: " + exclude);
646 }
647 }
648 } else {
649 getLog().debug("Implicit exclude: \n" + ignoreMatcher);
650 }
651 }
652 }
653 }
654 if (excludesFile != null) {
655 final File f = new File(excludesFile);
656 if (!f.isFile()) {
657 getLog().error("Excludes file not found: " + f.getAbsolutePath());
658 }
659 if (!f.canRead()) {
660 getLog().error("Excludes file not readable: " + f.getAbsolutePath());
661 }
662 final String charset = excludesFileCharset == null ? "UTF8" : excludesFileCharset;
663 getLog().debug("Loading excludes from file " + f + ", using character set " + charset);
664 basicRules.addRules(getPatternsFromFile(f, charset));
665 }
666
667 if (!basicRules.isEmpty()) {
668 ignoreMatchers.add(basicRules);
669 }
670
671 return ignoreMatchers;
672 }
673 }