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