View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.rat.mp;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  import java.net.URI;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Objects;
31  import java.util.SortedSet;
32  import java.util.function.Consumer;
33  import java.util.stream.Collectors;
34  import java.util.stream.Stream;
35  
36  import org.apache.commons.cli.Option;
37  import org.apache.commons.lang3.StringUtils;
38  import org.apache.maven.plugin.MojoExecutionException;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.project.MavenProject;
41  import org.apache.rat.Defaults;
42  import org.apache.rat.OptionCollection;
43  import org.apache.rat.ReportConfiguration;
44  import org.apache.rat.analysis.license.DeprecatedConfig;
45  import org.apache.rat.commandline.Arg;
46  import org.apache.rat.config.exclusion.StandardCollection;
47  import org.apache.rat.configuration.Format;
48  import org.apache.rat.configuration.LicenseReader;
49  import org.apache.rat.configuration.MatcherReader;
50  import org.apache.rat.document.DocumentName;
51  import org.apache.rat.document.FileDocument;
52  import org.apache.rat.license.ILicense;
53  import org.apache.rat.license.ILicenseFamily;
54  import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
55  import org.apache.rat.license.SimpleLicenseFamily;
56  import org.apache.rat.plugin.BaseRatMojo;
57  import org.apache.rat.utils.DefaultLog;
58  import org.apache.rat.utils.Log;
59  import org.apache.rat.walker.DirectoryWalker;
60  
61  import static java.lang.String.format;
62  
63  /**
64   * Abstract base class for Mojos, which are running Rat.
65   */
66  public abstract class AbstractRatMojo extends BaseRatMojo {
67      /** Report configuration for report */
68      private ReportConfiguration reportConfiguration;
69      /**
70       * The base directory, in which to search for files.
71       */
72      @Parameter(property = "rat.basedir", defaultValue = "${basedir}", required = true)
73      protected File basedir;
74  
75      /**
76       * Specifies the verbose output.
77       * @since 0.8
78       */
79      @Parameter(property = "rat.verbose", defaultValue = "false")
80      protected boolean verbose;
81  
82      /**
83       * Specifies the licenses to accept. By default, these are added to the default
84       * licenses, unless you set <addDefaultLicenseMatchers> to false. Arguments should be
85       * file name of <Configs> file structure.
86       * @deprecated Use specific configuration under <configuration>.
87       * @since 0.8
88       */
89      @Parameter
90      @Deprecated
91      private String[] defaultLicenseFiles;
92  
93      /**
94       * Specifies the additional licenses file.
95       * @deprecated Use specific configuration under <configuration>.
96       */
97      @Parameter
98      @Deprecated
99      private String[] additionalLicenseFiles;
100 
101     /**
102      * Whether to add the default list of licenses.
103      * @deprecated Deprecated for removal since 0.17: Use <configurationNoDefaults> instead (note the change of state).
104      */
105     @Deprecated
106     @Parameter(property = "rat.addDefaultLicenses", name = "addDefaultLicenses")
107     public void setAddDefaultLicenses(final boolean addDefaultLicenses) {
108         setNoDefaultLicenses(!addDefaultLicenses);
109     }
110 
111     /**
112      * Whether to add the default list of license matchers.
113      * @deprecated Use specific configuration under <configuration>.
114      */
115     @Deprecated
116     @Parameter(property = "rat.addDefaultLicenseMatchers")
117     private boolean addDefaultLicenseMatchers;
118 
119     /** The list of approved licenses
120      * @deprecated Use specific configuration under <configuration>.
121      */
122     @Deprecated
123     @Parameter
124     private String[] approvedLicenses;
125 
126     /** The file of approved licenses
127      * @deprecated Use specific configuration under <configuration>.
128      */
129     @Deprecated
130     @Parameter(property = "rat.approvedFile")
131     private String approvedLicenseFile;
132 
133     /**
134      * Specifies the license families to accept.
135      *
136      * @since 0.8
137      * @deprecated Use LicenseFamily section of configuration file.
138      */
139     @Deprecated
140     @Parameter
141     private SimpleLicenseFamily[] licenseFamilies;
142 
143     /** The list of license definitions.
144      * @deprecated Deprecated for removal since 0.17: Use specific configuration under <configuration>. See configuration file documentation.
145      */
146     @Deprecated
147     @Parameter
148     private Object[] licenses;
149 
150     /** The list of family definitions.
151      * @deprecated Use specific configuration under <configuration>.
152      */
153     @Deprecated
154     @Parameter
155     private Family[] families;
156 
157     /**
158      * Specifies the include files character set.
159      * If ${project.build.sourceEncoding} is not set defaults to UTF-8.
160      */
161     @Parameter(property = "rat.includesFileCharset", defaultValue = "${project.build.sourceEncoding}")
162     private String includesFileCharset;
163 
164     /**
165      * Specifies the include files character set.
166      * If ${project.build.sourceEncoding} is not set defaults to UTF-8.
167      */
168     @Parameter(property = "rat.excludesFileCharset", defaultValue = "${project.build.sourceEncoding}")
169     private String excludesFileCharset;
170 
171     /**
172      * Whether to use the default excludes when scanning for files. The default
173      * excludes are:
174      * <ul>
175      * <li>meta data files for source code management / revision control systems,
176      * see {@link org.apache.rat.config.exclusion.StandardCollection}</li>
177      * <li>temporary files used by Maven, see
178      * <a href="#useMavenDefaultExcludes">useMavenDefaultExcludes</a></li>
179      * <li>configuration files for Eclipse, see
180      * <a href="#useEclipseDefaultExcludes">useEclipseDefaultExcludes</a></li>
181      * <li>configuration files for IDEA, see
182      * <a href="#useIdeaDefaultExcludes">useIdeaDefaultExcludes</a></li>
183      * </ul>
184      * @deprecated When set to true specifies that the STANDARD_PATTERNS are excluded, as are
185      * the STANDARD_SCMS patterns. Use the various InputExclude and InputInclude elements to
186      * explicitly specify what to include or exclude.
187      */
188     @Parameter(property = "rat.useDefaultExcludes", defaultValue = "true")
189     @Deprecated
190     private boolean useDefaultExcludes;
191 
192     /**
193      * Whether to use the Maven specific default excludes when scanning for files.
194      * Maven specific default excludes are given by the constant
195      * MAVEN_DEFAULT_EXCLUDES: The <code>target</code> directory, the
196      * <code>cobertura.ser</code> file, and so on.
197      * @deprecated When set to true specifies that the MAVEN patterns are excluded.
198      * Use "inputIncludeStd MAVEN" to override.
199      */
200     @Parameter(property = "rat.useMavenDefaultExcludes", defaultValue = "true")
201     @Deprecated
202     private boolean useMavenDefaultExcludes;
203 
204     /**
205      * Whether to parse source code management system (SCM) ignore files and use
206      * their contents as excludes. At the moment this works for the following SCMs:
207      *
208      * @see org.apache.rat.config.exclusion.StandardCollection
209      * @deprecated When set to true specifies that the STANDARD_SCMS exclusion file
210      * processors are used to exclude files and directories (e.g. ".gitignore" or ".hgignore").
211      * Use "inputIncludeStd STANDARD_SCMS" to override.
212      */
213     @Parameter(property = "rat.parseSCMIgnoresAsExcludes", defaultValue = "true")
214     @Deprecated
215     private boolean parseSCMIgnoresAsExcludes;
216 
217     /**
218      * Whether to use the Eclipse specific default excludes when scanning for files.
219      * Eclipse specific default excludes are given by the constant
220      * ECLIPSE_DEFAULT_EXCLUDES: The <code>.classpath</code> and
221      * <code>.project</code> files, the <code>.settings</code> directory, and so on.
222      * @deprecated When set to true specifies that the ECLIPSE patterns are excluded.
223      * Use "inputIncludeStd ECLIPSE" to override.
224      */
225     @Parameter(property = "rat.useEclipseDefaultExcludes", defaultValue = "true")
226     @Deprecated
227     private boolean useEclipseDefaultExcludes;
228 
229     /**
230      * Whether to use the IDEA specific default excludes when scanning for files.
231      * IDEA specific default excludes are given by the constant
232      * IDEA_DEFAULT_EXCLUDES: The <code>*.iml</code>, <code>*.ipr</code> and
233      * <code>*.iws</code> files and the <code>.idea</code> directory.
234      * @deprecated When set to true specifies that the IDEA patterns are excluded.
235      * Use "inputIncludeStd IDEA" to override.
236      */
237     @Deprecated
238     @Parameter(property = "rat.useIdeaDefaultExcludes", defaultValue = "true")
239     private boolean useIdeaDefaultExcludes;
240 
241     /**
242      * Whether to exclude subprojects. This is recommended, if you want a separate
243      * apache-rat-plugin report for each subproject.
244      */
245     @Parameter(property = "rat.excludeSubprojects", defaultValue = "true")
246     private boolean excludeSubProjects;
247 
248     /**
249      * Will skip the plugin execution, e.g. for technical builds that do not take
250      * license compliance into account.
251      *
252      * @since 0.11
253      */
254     @Parameter(property = "rat.skip", defaultValue = "false")
255     protected boolean skip;
256 
257     /**
258      * Holds the maven-internal project to allow resolution of artifact properties
259      * during mojo runs.
260      */
261     @Parameter(defaultValue = "${project}", required = true, readonly = true)
262     protected MavenProject project;
263 
264     protected AbstractRatMojo() {
265         DefaultLog.setInstance(makeLog());
266     }
267 
268     /**
269      * @return the Maven project.
270      */
271     protected MavenProject getProject() {
272         return project;
273     }
274 
275     protected Defaults.Builder getDefaultsBuilder() {
276         Defaults.Builder result = Defaults.builder().noDefault();
277         if (defaultLicenseFiles != null) {
278             for (String defaultLicenseFile : defaultLicenseFiles) {
279                 result.add(defaultLicenseFile);
280             }
281         }
282         return result;
283     }
284 
285     @Deprecated // remove this for version 1.0
286     private Stream<License> getLicenses() {
287         if (licenses == null) {
288             return Stream.empty();
289         }
290         return Arrays.stream(licenses).filter(s -> s instanceof License).map(License.class::cast);
291     }
292 
293     @Deprecated // remove this for version 1.0
294     private Stream<DeprecatedConfig> getDeprecatedConfigs() {
295         if (licenses == null) {
296             return Stream.empty();
297         }
298         return Arrays.stream(licenses).filter(s -> s instanceof DeprecatedConfig).map(DeprecatedConfig.class::cast);
299     }
300 
301     @Deprecated // remove this for version 1.0
302     private void reportDeprecatedProcessing() {
303         if (getDeprecatedConfigs().findAny().isPresent()) {
304             DefaultLog.getInstance().warn("Configuration uses deprecated configuration. You need to upgrade to v0.17 configuration options.");
305         }
306     }
307 
308     @Deprecated // remove this for version 1.0
309     private void processLicenseFamilies(final ReportConfiguration config) {
310         List<ILicenseFamily> families = getDeprecatedConfigs().map(DeprecatedConfig::getLicenseFamily).filter(Objects::nonNull).collect(Collectors.toList());
311         if (licenseFamilies != null) {
312             for (SimpleLicenseFamily slf : licenseFamilies) {
313                 if (StringUtils.isBlank(slf.getFamilyCategory())) {
314                     families.stream().filter(f -> f.getFamilyName().equalsIgnoreCase(slf.getFamilyName())).findFirst()
315                     .ifPresent(config::addApprovedLicenseCategory);
316                 } else {
317                     config.addApprovedLicenseCategory(ILicenseFamily.builder().setLicenseFamilyCategory(slf.getFamilyCategory())
318                     .setLicenseFamilyName(StringUtils.defaultIfBlank(slf.getFamilyName(), slf.getFamilyCategory()))
319                     .build());
320                 }
321             }
322         }
323     }
324 
325     /**
326      * Reads values for the Arg.
327      *
328      * @param arg The Arg to get the values for.
329      * @return The list of values or an empty list.
330      */
331     protected List<String> getValues(final Arg arg) {
332         List<String> result = new ArrayList<>();
333         for (Option option : arg.group().getOptions()) {
334             if (option.getLongOpt() != null) {
335                 List<String> args = getArg(option.getLongOpt());
336                 if (args != null) {
337                     result.addAll(args);
338                 }
339             }
340         }
341         return result;
342     }
343 
344     /**
345      * Removes all values for an Arg.
346      * @param arg The arg to remove values for.
347      */
348     protected void removeKey(final Arg arg) {
349         for (Option option : arg.group().getOptions()) {
350             if (option.getLongOpt() != null) {
351                 removeArg(option.getLongOpt());
352             }
353         }
354     }
355 
356     private org.apache.rat.utils.Log makeLog() {
357         return new org.apache.rat.utils.Log() {
358             @Override
359             public Level getLevel() {
360                 final org.apache.maven.plugin.logging.Log log = getLog();
361                 if (log.isDebugEnabled()) {
362                     return Level.DEBUG;
363                 }
364                 if (log.isInfoEnabled()) {
365                     return Level.INFO;
366                 }
367                 if (log.isWarnEnabled()) {
368                     return Level.WARN;
369                 }
370                 if (log.isErrorEnabled()) {
371                     return Level.ERROR;
372                 }
373                 return Level.OFF;
374             }
375 
376             @Override
377             public void log(final Level level, final String message, final Throwable throwable) {
378                 final org.apache.maven.plugin.logging.Log log = getLog();
379                 switch (level) {
380                     case DEBUG:
381                         if (throwable != null) {
382                             log.debug(message, throwable);
383                         } else {
384                             log.debug(message);
385                         }
386                         break;
387                     case INFO:
388                         if (throwable != null) {
389                             log.info(message, throwable);
390                         } else {
391                             log.info(message);
392                         }
393                         break;
394                     case WARN:
395                         if (throwable != null) {
396                             log.warn(message, throwable);
397                         } else {
398                             log.warn(message);
399                         }
400                         break;
401                     case ERROR:
402                         if (throwable != null) {
403                             log.error(message, throwable);
404                         } else {
405                             log.error(message);
406                         }
407                         break;
408                     case OFF:
409                         break;
410                 }
411             }
412 
413             @Override
414             public void log(final Level level, final String msg) {
415                 final org.apache.maven.plugin.logging.Log log = getLog();
416                 switch (level) {
417                     case DEBUG:
418                         log.debug(msg);
419                         break;
420                     case INFO:
421                         log.info(msg);
422                         break;
423                     case WARN:
424                         log.warn(msg);
425                         break;
426                     case ERROR:
427                         log.error(msg);
428                         break;
429                     case OFF:
430                         break;
431                 }
432             }
433         };
434     }
435 
436     private void setIncludeExclude() {
437 
438         if (excludeSubProjects && project != null && project.getModules() != null) {
439             List<String> subModules = new ArrayList<>();
440             project.getModules().forEach(s -> subModules.add(format("%s/**", s)));
441             setInputExcludes(subModules.toArray(new String[0]));
442         }
443 
444         List<String> values = getValues(Arg.EXCLUDE);
445         if (values.isEmpty() && useDefaultExcludes) {
446             DefaultLog.getInstance().debug("Adding plexus default exclusions...");
447             setInputExcludes(StandardCollection.STANDARD_PATTERNS.patterns().toArray(new String[0]));
448 
449             DefaultLog.getInstance().debug("Adding SCM default exclusions...");
450             setInputExcludes(StandardCollection.STANDARD_SCMS.patterns().toArray(new String[0]));
451         }
452 
453         if (useMavenDefaultExcludes) {
454             setInputExcludeStd(StandardCollection.MAVEN.name());
455         }
456         if (useEclipseDefaultExcludes) {
457             setInputExcludeStd(StandardCollection.ECLIPSE.name());
458         }
459         if (useIdeaDefaultExcludes) {
460             setInputExcludeStd(StandardCollection.IDEA.name());
461         }
462 
463         if (parseSCMIgnoresAsExcludes) {
464             setInputExcludeParsedScm(StandardCollection.STANDARD_SCMS.name());
465         }
466     }
467 
468     protected ReportConfiguration getConfiguration() throws MojoExecutionException {
469         Log log = DefaultLog.getInstance();
470         if (reportConfiguration == null) {
471             try {
472                 if (getLog().isDebugEnabled()) {
473                     log.debug("Start BaseRatMojo Configuration options");
474                     for (Map.Entry<String, List<String>> entry : args.entrySet()) {
475                         log.debug(format(" * %s %s", entry.getKey(), String.join(", ", entry.getValue())));
476                     }
477                     log.debug("End BaseRatMojo Configuration options");
478                 }
479 
480                 boolean helpLicenses = !getValues(Arg.HELP_LICENSES).isEmpty();
481                 removeKey(Arg.HELP_LICENSES);
482                 setIncludeExclude();
483 
484                 getLog().debug("Basedir is : " + basedir);
485                 ReportConfiguration config = OptionCollection.parseCommands(basedir, args().toArray(new String[0]),
486                         o -> getLog().warn("Help option not supported"),
487                         true);
488                 reportDeprecatedProcessing();
489 
490                 if (additionalLicenseFiles != null) {
491                     for (String licenseFile : additionalLicenseFiles) {
492                         URI uri = new File(licenseFile).toURI();
493                         Format fmt = Format.from(licenseFile);
494                         MatcherReader mReader = fmt.matcherReader();
495                         if (mReader != null) {
496                             mReader.addMatchers(uri);
497                         }
498                         LicenseReader lReader = fmt.licenseReader();
499                         if (lReader != null) {
500                             lReader.addLicenses(uri);
501                             config.addLicenses(lReader.readLicenses());
502                             config.addApprovedLicenseCategories(lReader.approvedLicenseId());
503                         }
504                     }
505                 }
506                 if (families != null || getDeprecatedConfigs().findAny().isPresent()) {
507                     if (log.isEnabled(Log.Level.DEBUG) && families != null) {
508                         log.debug(format("%s license families loaded from pom", families.length));
509                     }
510                     Consumer<ILicenseFamily> logger = super.getLog().isDebugEnabled() ? l -> log.debug(format("Family: %s", l))
511                             : l -> {
512                     };
513 
514                     Consumer<ILicenseFamily> process = logger.andThen(config::addFamily);
515                     getDeprecatedConfigs().map(DeprecatedConfig::getLicenseFamily).filter(Objects::nonNull).forEach(process);
516                     if (families != null) { // TODO remove if check in v1.0
517                         Arrays.stream(families).map(Family::build).forEach(process);
518                     }
519                 }
520 
521                 processLicenseFamilies(config);
522 
523                 if (approvedLicenses != null && approvedLicenses.length > 0) {
524                     Arrays.stream(approvedLicenses).forEach(config::addApprovedLicenseCategory);
525                 }
526 
527                 if (licenses != null) {
528                     if (log.isEnabled(Log.Level.DEBUG)) {
529                         log.debug(format("%s licenses loaded from pom", licenses.length));
530                     }
531                     Consumer<ILicense> logger = log.isEnabled(Log.Level.DEBUG) ? l -> log.debug(format("License: %s", l))
532                             : l -> {
533                     };
534                     Consumer<ILicense> addApproved = (approvedLicenses == null || approvedLicenses.length == 0)
535                             ? l -> config.addApprovedLicenseCategory(l.getLicenseFamily())
536                             : l -> {
537                     };
538 
539                     Consumer<ILicense> process = logger.andThen(config::addLicense).andThen(addApproved);
540                     SortedSet<ILicenseFamily> families = config.getLicenseFamilies(LicenseFilter.ALL);
541                     getDeprecatedConfigs().map(DeprecatedConfig::getLicense).filter(Objects::nonNull)
542                             .map(x -> x.setLicenseFamilies(families).build()).forEach(process);
543                     getLicenses().map(x -> x.build(families)).forEach(process);
544                 }
545                 DocumentName dirName = DocumentName.builder(basedir).build();
546                 config.addSource(new DirectoryWalker(new FileDocument(dirName, basedir, config.getDocumentExcluder(dirName))));
547 
548                 if (helpLicenses) {
549                     new org.apache.rat.help.Licenses(config, new PrintWriter(log.asWriter())).printHelp();
550                 }
551                 reportConfiguration = config;
552             } catch (IOException e) {
553                 throw new MojoExecutionException(e);
554             }
555         }
556         return reportConfiguration;
557     }
558 
559     protected void logLicenses(final Collection<ILicense> licenses) {
560         if (getLog().isDebugEnabled()) {
561             getLog().debug("The following " + licenses.size() + " licenses are activated:");
562             for (ILicense license : licenses) {
563                 getLog().debug("* " + license.toString());
564             }
565         }
566     }
567 }