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.ByteArrayOutputStream;
22  import java.io.File;
23  import java.io.FileFilter;
24  import java.io.IOException;
25  import java.io.Writer;
26  import java.nio.charset.StandardCharsets;
27  import java.util.List;
28  
29  import org.apache.maven.plugin.MojoExecutionException;
30  import org.apache.maven.plugin.MojoFailureException;
31  import org.apache.maven.plugins.annotations.LifecyclePhase;
32  import org.apache.maven.plugins.annotations.Mojo;
33  import org.apache.maven.plugins.annotations.Parameter;
34  import org.apache.rat.ReportConfiguration;
35  import org.apache.rat.Reporter;
36  import org.apache.rat.commandline.Arg;
37  import org.apache.rat.commandline.StyleSheets;
38  import org.apache.rat.config.exclusion.StandardCollection;
39  import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
40  import org.apache.rat.report.claim.ClaimStatistic;
41  import org.apache.rat.utils.DefaultLog;
42  import org.apache.rat.utils.Log;
43  
44  import static java.lang.String.format;
45  
46  /**
47   * Run RAT to perform a violation check.
48   * <p>
49   *     This documentation mentions data types for some arguments.
50   * </p>
51   */
52  @Mojo(name = "check", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true)
53  public class RatCheckMojo extends AbstractRatMojo {
54  
55      /** The default output file if no other is specified.
56       * @deprecated Use &lt;outputFile&gt; instead.
57       */
58      @Deprecated
59      @Parameter(defaultValue = "${project.build.directory}/rat.txt")
60      private File defaultReportFile;
61  
62      /**
63       * The defined build directory
64       */
65      @Parameter(defaultValue = "${project.build.directory}", readonly = true)
66      private String buildDirectory;
67  
68      /**
69       * Where to store the report.
70       * @deprecated Use 'out' property instead.
71       */
72      @Deprecated
73      @Parameter
74      public void setReportFile(final File reportFile) {
75          if (!reportFile.getParentFile().exists() && !reportFile.getParentFile().mkdirs()) {
76              getLog().error("Unable to create directory " + reportFile.getParentFile());
77          }
78          setOutputFile(reportFile.getAbsolutePath());
79      }
80  
81      /**
82       * Output style of the report. Use "plain" (the default) for a plain text report
83       * or "xml" for the raw XML report. Alternatively you can give the path of an
84       * XSL transformation that will be applied on the raw XML to produce the report
85       * written to the output file.
86       * @deprecated Use setStyleSheet or xml instead.
87       */
88      @Deprecated
89      @Parameter(property = "rat.outputStyle")
90      public void setReportStyle(final String value) {
91          if (value.equalsIgnoreCase("xml")) {
92              setXml(true);
93          } else if (value.equalsIgnoreCase("plain")) {
94              setStylesheet("plain-rat");
95          } else {
96              setStylesheet(value);
97          }
98      }
99  
100     /**
101      * Maximum number of files with unapproved licenses.
102      * @deprecated Use &lt;counterMax&gt;Unapproved:value&lt;/counterMax&gt;.
103      */
104     @Deprecated
105     @Parameter(property = "rat.numUnapprovedLicenses", defaultValue = "0")
106     private int numUnapprovedLicenses;
107 
108     /**
109      * Whether to add license headers; possible values are {@code forced},
110      * {@code true}, and {@code false} (default).
111      * @deprecated Use &lt;editLicense&gt; and &lt;editOverwrite&gt;.
112      */
113     @Deprecated
114     @Parameter(property = "rat.addLicenseHeaders")
115     public void setAddLicenseHeaders(final String addLicenseHeaders) {
116         switch (addLicenseHeaders.trim().toUpperCase()) {
117             case "FALSE":
118                 // do nothing;
119                 break;
120             case "TRUE":
121                 setAddLicense(true);
122                 break;
123             case "FORCED":
124                 setAddLicense(true);
125                 setForce(true);
126                 break;
127             default:
128                 throw new IllegalArgumentException("Unknown addlicense header: " + addLicenseHeaders);
129         }
130     }
131 
132     /**
133      * Copyright message to add to license headers.
134      * @deprecated Deprecated for removal since 0.17: Use &lt;editCopyright&gt; instead.
135      */
136     @Deprecated
137     @Parameter(property = "rat.copyrightMessage")
138     public void setCopyrightMessage(final String copyrightMessage) {
139         setCopyright(copyrightMessage);
140     }
141 
142     /**
143      * Will ignore RAT errors and display a log message if any. Its use is NOT
144      * RECOMMENDED, but quite convenient on occasion.
145      *
146      * @since 0.9
147      */
148     @Parameter(property = "rat.ignoreErrors", defaultValue = "false")
149     private boolean ignoreErrors;
150 
151     /**
152      * Whether to output the names of files that have unapproved licenses to the
153      * console. Defaults to {@code true} to ease builds in containers where you are
154      * unable to access rat.txt easily.
155      *
156      * @since 0.12
157      */
158     @Parameter(property = "rat.consoleOutput", defaultValue = "true")
159     private boolean consoleOutput;
160 
161     /** The reporter that this mojo uses */
162     private Reporter reporter;
163 
164     @Override
165     protected ReportConfiguration getConfiguration() throws MojoExecutionException {
166         ReportConfiguration result = super.getConfiguration();
167         if (numUnapprovedLicenses > 0) {
168             result.getClaimValidator().setMax(ClaimStatistic.Counter.UNAPPROVED, numUnapprovedLicenses);
169         }
170         result.addExcludedCollection(StandardCollection.MAVEN);
171         if (StandardCollection.MAVEN.fileProcessorBuilder().hasNext()) {
172             result.addExcludedFileProcessor(StandardCollection.MAVEN);
173         }
174         if (StandardCollection.MAVEN.hasStaticDocumentNameMatcher()) {
175             StandardCollection.MAVEN.staticDocumentNameMatcher();
176         }
177 
178         String buildDirAbsolutePath = new File(buildDirectory).getAbsolutePath();
179         FileFilter buildFilter = f -> f.getAbsolutePath().startsWith(buildDirAbsolutePath);
180         result.addExcludedFilter(buildFilter);
181 
182         return result;
183     }
184 
185         /**
186          * Invoked by Maven to execute the Mojo.
187          *
188          * @throws MojoFailureException if an error in the plugin configuration was
189          * detected.
190          * @throws MojoExecutionException if another error occurred while executing the
191          * plugin.
192          */
193     @Override
194     public void execute() throws MojoExecutionException, MojoFailureException {
195         if (skip) {
196             getLog().info("RAT will not execute since it is configured to be skipped via system property 'rat.skip'.");
197             return;
198         }
199 
200         if (getValues(Arg.OUTPUT_FILE).isEmpty()) {
201             setArg(Arg.OUTPUT_FILE.option().getLongOpt(), defaultReportFile.getAbsolutePath());
202         }
203 
204         try (Writer logWriter = DefaultLog.getInstance().asWriter()) {
205             ReportConfiguration config = getConfiguration();
206             logLicenses(config.getLicenses(LicenseFilter.ALL));
207             if (verbose) {
208                 config.reportExclusions(logWriter);
209             }
210             try {
211                 this.reporter = new Reporter(config);
212                 reporter.output();
213                 if (verbose) {
214                     reporter.writeSummary(logWriter);
215                 }
216                 check(config);
217             } catch (MojoFailureException e) {
218                 throw e;
219             } catch (Exception e) {
220                 throw new MojoExecutionException(e.getMessage(), e);
221             }
222         } catch (IOException e) {
223             DefaultLog.getInstance().warn("Unable to close writable log.", e);
224         }
225     }
226 
227     protected void check(final ReportConfiguration config) throws MojoFailureException {
228         ClaimStatistic statistics = reporter.getClaimsStatistic();
229         try {
230            reporter.writeSummary(DefaultLog.getInstance().asWriter(Log.Level.DEBUG));
231            if (config.getClaimValidator().hasErrors()) {
232                config.getClaimValidator().logIssues(statistics);
233                if (consoleOutput &&
234                        !config.getClaimValidator().isValid(ClaimStatistic.Counter.UNAPPROVED, statistics.getCounter(ClaimStatistic.Counter.UNAPPROVED))) {
235                    try {
236                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
237                        reporter.output(StyleSheets.UNAPPROVED_LICENSES.getStyleSheet(), () -> baos);
238                        getLog().warn(baos.toString(StandardCharsets.UTF_8));
239                    } catch (RuntimeException rte) {
240                        throw rte;
241                    } catch (Exception e) {
242                        getLog().warn("Unable to print the files with unapproved licenses to the console.");
243                    }
244                }
245 
246                String msg = format("Counter(s) %s exceeded minimum or maximum values. See RAT report in: '%s'.",
247                        String.join(", ", config.getClaimValidator().listIssues(statistics)),
248                        getRatTxtFile());
249 
250                if (!ignoreErrors) {
251                    throw new RatCheckException(msg);
252                } else {
253                    getLog().info(msg);
254                }
255            } else {
256                DefaultLog.getInstance().info("No issues found.");
257            }
258         } catch (IOException e) {
259            throw new MojoFailureException(e);
260        }
261     }
262 
263     /**
264      * Reads the location of the RAT text file from the Mojo.
265      *
266      * @return Value of the "reportFile" property.
267      * @throws MojoFailureException If no output file was specified.
268      */
269     public File getRatTxtFile() throws MojoFailureException {
270         List<String> args = getValues(Arg.OUTPUT_FILE);
271         if (args != null) {
272             return new File(args.get(0));
273         }
274         throw new MojoFailureException("No output file specified");
275     }
276 }