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