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