View Javadoc

1   /**
2    * The Maven License Verifier Plugin
3    *
4    * Copyright (c) 2009, 2010, 2011 by SoftwareEntwicklung Beratung Schulung (SoEBeS)
5    * Copyright (c) 2009, 2010, 2011 by Karl Heinz Marbaise
6    *
7    * Licensed to the Apache Software Foundation (ASF) under one or more
8    * contributor license agreements.  See the NOTICE file distributed with
9    * this work for additional information regarding copyright ownership.
10   * The ASF licenses this file to You under the Apache License, Version 2.0
11   * (the "License"); you may not use this file except in compliance with
12   * the License.  You may obtain a copy of the License at
13   *
14   *    http://www.apache.org/licenses/LICENSE-2.0
15   *
16   * Unless required by applicable law or agreed to in writing, software
17   * distributed under the License is distributed on an "AS IS" BASIS,
18   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   * See the License for the specific language governing permissions and
20   * limitations under the License.
21   */
22  package com.soebes.maven.plugins.mlv;
23  
24  import java.io.File;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.ResourceBundle;
29  import java.util.Set;
30  
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.doxia.module.xhtml.decoration.render.RenderingContext;
33  import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
34  import org.apache.maven.model.License;
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.plugin.MojoFailureException;
37  import org.apache.maven.project.MavenProject;
38  import org.apache.maven.reporting.MavenReport;
39  import org.apache.maven.reporting.MavenReportException;
40  import org.codehaus.doxia.sink.Sink;
41  
42  import com.soebes.maven.plugins.mlv.model.LicenseItem;
43  
44  /**
45   * Generate a report about the license verifier report.
46   *
47   * @goal report
48   * @author <a href="mailto:kama@soebes.de">Karl Heinz Marbaise</a>
49   */
50  public class LicenseVerifierReport
51          extends AbstractLicenseVerifierPlugIn
52          implements MavenReport {
53  
54      /**
55       * The directory where the report will be generated
56       *
57       * @parameter expression="${project.reporting.outputDirectory}" default-value="${project.reporting.outputDirectory}/lvp"
58       * @required
59       * @readonly
60       */
61      private File outputDirectory;
62      /**
63       * The directory where the report will be generated
64       *
65       * @parameter expression="${project.reporting.outputDirectory}" default-value="${project.reporting.outputDirectory}/lvp"
66       * @required
67       * @readonly
68       */
69      private File reportOutputDirectory;
70  
71      /**
72       * @see org.apache.maven.reporting.AbstractMavenReport#executeReport(java.util.Locale)
73       */
74      protected void executeReport(Locale locale) throws MavenReportException {
75          try {
76              getLog().info("LicenseVerifierReport:executeReport()");
77              RenderingContext context = new RenderingContext(getOutputDirectory(), getOutputName() + ".html");
78              SiteRendererSink sink = new SiteRendererSink(context);
79              generate(sink, locale);
80          } catch (MavenReportException e) {
81              getLog().error("An error has occurred in " + getName(Locale.ENGLISH)
82                      + " report generation:" + e.getMessage(), e);
83          } catch (RuntimeException e) {
84              getLog().error(e.getMessage(), e);
85          }
86      }
87  
88      protected File getOutputDirectory() {
89          if (!outputDirectory.isAbsolute()) {
90              outputDirectory = new File(project.getBasedir(), outputDirectory.getPath());
91          }
92          //Create the folder structure.
93          if (!outputDirectory.exists()) {
94              outputDirectory.mkdirs();
95          }
96  
97          return outputDirectory.getAbsoluteFile();
98      }
99  
100     protected MavenProject getProject() {
101         return project;
102     }
103 
104     public String getDescription(Locale locale) {
105         return "Generated License Verifier Report";
106     }
107 
108     public String getName(Locale locale) {
109         return "License Verifier Report";
110     }
111 
112     public String getOutputName() {
113         return "licenseverifierreport";
114     }
115 
116     /**
117      * @see org.apache.maven.reporting.MavenReport#getOutputName()
118      */
119     protected ResourceBundle getBundle(Locale locale) {
120         return ResourceBundle.getBundle(
121                 "mlvp",
122                 locale,
123                 this.getClass().getClassLoader());
124     }
125 
126     /**
127      * generates an empty report in case there are no sources to generate a report with
128      *
129      * @param bundle the resource bundle to retrieve report phrases from
130      * @param sink   the report formatting tool
131      */
132     private void doGenerateEmptyReport(ResourceBundle bundle, Sink sink) {
133         sink.head();
134         sink.title();
135 
136         sink.text(bundle.getString("report.mlvp.header"));
137         sink.title_();
138         sink.head_();
139 
140         sink.body();
141         sink.section1();
142 
143         sink.sectionTitle1();
144         sink.text(bundle.getString("report.mlvp.mainTitle"));
145         sink.sectionTitle1_();
146 
147         sink.paragraph();
148         sink.text("No artifacts found to create and licesen verifier report.");
149         sink.paragraph_();
150 
151         sink.section1_();
152 
153         sink.body_();
154         sink.flush();
155         sink.close();
156     }
157 
158     private void doGenerateReport(ResourceBundle bundle, Sink sink) {
159 
160         sink.head();
161         sink.title();
162 
163         sink.text(bundle.getString("report.mlvp.header"));
164         sink.title_();
165         sink.head_();
166 
167         sink.body();
168 
169         sink.section1();
170         doGenerateLicenseCategories(bundle, sink);
171         sink.section1_();
172 
173         sink.section1();
174         doGenerateItemReport(bundle, sink);
175         sink.section1_();
176 
177 //The different scopes should be seen only if we have artifacts with an appropriately scope otherwise no headlines etc.
178         if (hasScope(Artifact.SCOPE_COMPILE)) {
179             sink.section1();
180             doGenerateArtifactCategories(bundle, sink, Artifact.SCOPE_COMPILE);
181             sink.section1_();
182         }
183 
184         if (hasScope(Artifact.SCOPE_TEST)) {
185             sink.section1();
186             doGenerateArtifactCategories(bundle, sink, Artifact.SCOPE_TEST);
187             sink.section1_();
188         }
189 
190         if (hasScope(Artifact.SCOPE_PROVIDED)) {
191             sink.section1();
192             doGenerateArtifactCategories(bundle, sink, Artifact.SCOPE_PROVIDED);
193             sink.section1_();
194         }
195 
196         if (hasScope(Artifact.SCOPE_RUNTIME)) {
197             sink.section1();
198             doGenerateArtifactCategories(bundle, sink, Artifact.SCOPE_RUNTIME);
199             sink.section1_();
200         }
201 
202         sink.body_();
203         sink.flush();
204         sink.close();
205     }
206 
207     private boolean hasScope(String scope) {
208         boolean result = false;
209         for (LicenseInformation item : getLicenseInformations()) {
210             if (scope.equals(item.getArtifact().getScope())) {
211                 result = true;
212             }
213         }
214         return result;
215     }
216 
217 //The following four methods are copy&past except the isXXXX() Think hard about that!!!
218     private boolean hasValidEntries (String scope) {
219         boolean hasEntries = false;
220         for (LicenseInformation item : getLicenseInformations()) {
221             if (scope.equals(item.getArtifact().getScope())) {
222                 if (licenseValidator.isValid(item.getLicenses())) {
223                     hasEntries = true;
224                 }
225             }
226         }
227         return hasEntries;
228     }
229     private boolean hasInvalidEntries (String scope) {
230         boolean hasEntries = false;
231         for (LicenseInformation item : getLicenseInformations()) {
232             if (scope.equals(item.getArtifact().getScope())) {
233                 if (licenseValidator.isInvalid(item.getLicenses())) {
234                     hasEntries = true;
235                 }
236             }
237         }
238         return hasEntries;
239     }
240     private boolean hasWarningEntries (String scope) {
241         boolean hasEntries = false;
242         for (LicenseInformation item : getLicenseInformations()) {
243             if (scope.equals(item.getArtifact().getScope())) {
244                 if (licenseValidator.isWarning(item.getLicenses())) {
245                     hasEntries = true;
246                 }
247             }
248         }
249         return hasEntries;
250     }
251     private boolean hasUnknownEntries (String scope) {
252         boolean hasEntries = false;
253         for (LicenseInformation item : getLicenseInformations()) {
254             if (scope.equals(item.getArtifact().getScope())) {
255                 if (licenseValidator.isUnknown(item.getLicenses())) {
256                     hasEntries = true;
257                 }
258             }
259         }
260         return hasEntries;
261     }
262 
263 
264     private void doGenerateLicenseCategories(ResourceBundle bundle, Sink sink) {
265 //TODO: Use bundles instead of hard coded strings.
266         sink.sectionTitle1();
267         sink.text("License Categories (Configured)");
268         sink.sectionTitle1_();
269 
270 //TODO: Show only areas which contain items!
271         doGenerateReportLicensesConfiguration(bundle, sink, "Valid", licenseValidator.getValid());
272         doGenerateReportLicensesConfiguration(bundle, sink, "Warning", licenseValidator.getWarning());
273         doGenerateReportLicensesConfiguration(bundle, sink, "Invalid", licenseValidator.getInvalid());
274 
275     }
276 
277     private void doGenerateArtifactCategories(ResourceBundle bundle, Sink sink, String scope) {
278         sink.sectionTitle1();
279         sink.text("Artifact Categories (" + scope + ")");
280         sink.sectionTitle1_();
281 
282         if (hasValidEntries(scope)) {
283             doGenerateValidReport(bundle, sink, scope);
284         }
285         if (hasWarningEntries(scope)) {
286             doGenerateWarningReport(bundle, sink, scope);
287         }
288         if (hasInvalidEntries(scope)) {
289             doGenerateInvalidReport(bundle, sink, scope);
290         }
291         if (hasUnknownEntries(scope)) {
292             doGenerateUnknownReport(bundle, sink, scope);
293         }
294     }
295 
296     private void doGenerateReportLicensesConfiguration(ResourceBundle bundle, Sink sink, String header, List<LicenseItem> licenseItems) {
297         sink.sectionTitle2();
298         sink.text(header);
299         sink.sectionTitle2_();
300 
301         sink.table();
302 
303         sink.tableRow();
304         headerCell(sink, "Id");
305         headerCell(sink, "Description");
306         headerCell(sink, "Names");
307         headerCell(sink, "URL's");
308         sink.tableRow_();
309 
310         for (LicenseItem item : licenseItems) {
311             sink.tableRow();
312             cell(sink, item.getId());
313             cell(sink, item.getDescription());
314 
315             sink.tableCell();
316             //List all names
317             sink.list();
318             for (String itemName : item.getNames()) {
319                 sink.listItem();
320                 sink.text(itemName);
321                 sink.listItem_();
322             }
323             sink.list_();
324             sink.tableCell_();
325 
326             sink.tableCell();
327             sink.list();
328             for (String itemURL : item.getUrls()) {
329                 sink.listItem();
330                 sink.link(itemURL);
331                 sink.text(itemURL);
332                 sink.link_();
333                 sink.listItem_();
334             }
335             sink.list_();
336             sink.tableCell_();
337 
338             sink.tableRow_();
339         }
340         sink.table_();
341     }
342 
343     private void createEmptyCell(Sink sink) {
344         sink.tableCell();
345         sink.tableCell_();
346     }
347 
348     private void createFigure(Sink sink, boolean success) {
349         sink.tableCell();
350         sink.figure();
351         if (success) {
352             sink.figureGraphics("images/icon_success_sml.gif");
353         } else {
354             sink.figureGraphics("images/icon_warning_sml.gif");
355         }
356         sink.figure_();
357         sink.tableCell_();
358     }
359 
360     private void doGenerateItemReport(ResourceBundle bundle, Sink sink) {
361         sink.sectionTitle1();
362         sink.text("Artifact License Categories");
363         sink.sectionTitle1_();
364 
365         sink.sectionTitle2();
366         sink.text("Overview");
367         sink.sectionTitle2_();
368 
369         sink.table();
370 
371         sink.tableRow();
372         headerCell(sink, "Id");
373         headerCell(sink, "Scope");
374         headerCell(sink, "Valid");
375         headerCell(sink, "Warning");
376         headerCell(sink, "Invalid");
377         headerCell(sink, "Unknown");
378         sink.tableRow_();
379 
380         for (LicenseInformation item : getLicenseInformations()) {
381             sink.tableRow();
382 
383             cell(sink, item.getArtifact().getId()); // 1st Row item artifactId
384 
385             boolean isValid = licenseValidator.isValid(item.getLicenses());
386             boolean isWarning = licenseValidator.isWarning(item.getLicenses());
387             boolean isInvalid = licenseValidator.isInvalid(item.getLicenses());
388             boolean isUnknown = licenseValidator.isUnknown(item.getLicenses());
389 
390             sink.tableCell();
391             sink.text(item.getArtifact().getScope());
392             sink.tableCell_();
393 
394             createFigure(sink, isValid);
395 
396             if (isWarning) {
397                 createFigure(sink, false);
398             } else {
399                 createEmptyCell(sink);
400             }
401 
402             if (isInvalid) {
403                 createFigure(sink, false);
404             } else {
405                 createEmptyCell(sink);
406             }
407 
408             if (isUnknown) {
409                 createFigure(sink, false);
410             } else {
411                 createEmptyCell(sink);
412             }
413 
414             sink.tableRow_();
415         }
416 
417         sink.table_();
418     }
419 
420 //FIXME: The following four methods are copy&past except for licenseValidate.isXXX() Think hard about this.!
421     private void doGenerateValidReport(ResourceBundle bundle, Sink sink, String scope) {
422         sink.sectionTitle2();
423         sink.text("Artifacts Catagorized as Valid");
424         sink.sectionTitle2_();
425 
426         sink.table();
427 
428         sink.tableRow();
429         headerCell(sink, "Id");
430         headerCell(sink, "Scope");
431         headerCell(sink, "Name");
432         headerCell(sink, "URL");
433         sink.tableRow_();
434 
435         for (LicenseInformation item : getLicenseInformations()) {
436             if (scope.equals(item.getArtifact().getScope())) {
437                 if (licenseValidator.isValid(item.getLicenses())) {
438                     sink.tableRow();
439 
440                     cell(sink, item.getArtifact().getId()); // 1st Row item artifactId
441 
442                     sink.tableCell();
443                     sink.text(item.getArtifact().getScope());
444                     sink.tableCell_();
445 
446                     if (item.getLicenses().isEmpty()) {
447                         cell(sink, "");
448                         cell(sink, "");
449                     } else {
450                         for (License license : item.getLicenses()) {
451                             cell(sink, license.getName());
452                             cell(sink, license.getUrl());
453                         }
454                     }
455                     sink.tableRow_();
456                 }
457             }
458         }
459 
460         sink.table_();
461     }
462 
463     private void doGenerateWarningReport(ResourceBundle bundle, Sink sink, String scope) {
464         sink.sectionTitle2();
465         sink.text("Artifacts Catagorized as Warning");
466         sink.sectionTitle2_();
467 
468         sink.table();
469 
470         sink.tableRow();
471         headerCell(sink, "Id");
472         headerCell(sink, "Scope");
473         headerCell(sink, "Name");
474         headerCell(sink, "URL");
475         sink.tableRow_();
476 
477         for (LicenseInformation item : getLicenseInformations()) {
478             if (scope.equals(item.getArtifact().getScope())) {
479                 if (licenseValidator.isWarning(item.getLicenses())) {
480                     sink.tableRow();
481 
482                     cell(sink, item.getArtifact().getId()); // 1st Row item artifactId
483                     sink.tableCell();
484                     sink.text(item.getArtifact().getScope());
485                     sink.tableCell_();
486 
487                     if (item.getLicenses().isEmpty()) {
488                         cell(sink, "");
489                         cell(sink, "");
490                     } else {
491                         for (License license : item.getLicenses()) {
492                             cell(sink, license.getName());
493                             cell(sink, license.getUrl());
494                         }
495                     }
496                     sink.tableRow_();
497                 }
498             }
499         }
500 
501         sink.table_();
502     }
503 
504     private void doGenerateInvalidReport(ResourceBundle bundle, Sink sink, String scope) {
505         sink.sectionTitle2();
506         sink.text("Artifacts Catagorized as Invalid");
507         sink.sectionTitle2_();
508 
509         sink.table();
510 
511         sink.tableRow();
512         headerCell(sink, "Id");
513         headerCell(sink, "Scope");
514         headerCell(sink, "Name");
515         headerCell(sink, "URL");
516         sink.tableRow_();
517 
518         for (LicenseInformation item : getLicenseInformations()) {
519             //Should be made better...
520             if (scope.equals(item.getArtifact().getScope())) {
521                 if (licenseValidator.isInvalid(item.getLicenses())) {
522                     sink.tableRow();
523 
524                     cell(sink, item.getArtifact().getId()); // 1st Row item artifactId
525                     sink.tableCell();
526                     sink.text(item.getArtifact().getScope());
527                     sink.tableCell_();
528 
529                     if (item.getLicenses().isEmpty()) {
530                         cell(sink, "");
531                         cell(sink, "");
532                     } else {
533                         for (License license : item.getLicenses()) {
534                             cell(sink, license.getName());
535                             cell(sink, license.getUrl());
536                         }
537                     }
538                     sink.tableRow_();
539                 }
540             }
541         }
542 
543         sink.table_();
544     }
545 
546     private void doGenerateUnknownReport(ResourceBundle bundle, Sink sink, String scope) {
547         sink.sectionTitle2();
548         sink.text("Artifacts Catagorized as Unknown");
549         sink.sectionTitle2_();
550 
551         sink.table();
552 
553         sink.tableRow();
554         headerCell(sink, "Id");
555         headerCell(sink, "Scope");
556         headerCell(sink, "Name");
557         headerCell(sink, "URL");
558         sink.tableRow_();
559 
560         for (LicenseInformation item : getLicenseInformations()) {
561             if (scope.equals(item.getArtifact().getScope())) {
562                 if (licenseValidator.isUnknown(item.getLicenses())) {
563                     sink.tableRow();
564 
565                     cell(sink, item.getArtifact().getId()); // 1st Row item artifactId
566                     sink.tableCell();
567                     sink.text(item.getArtifact().getScope());
568                     sink.tableCell_();
569 
570                     if (item.getLicenses().isEmpty()) {
571                         cell(sink, "");
572                         cell(sink, "");
573                     } else {
574                         for (License license : item.getLicenses()) {
575                             cell(sink, license.getName());
576                             cell(sink, license.getUrl());
577                         }
578                     }
579                     sink.tableRow_();
580                 }
581             }
582         }
583 
584         sink.table_();
585     }
586 
587     private void headerCell(Sink sink, String text) {
588         sink.tableHeaderCell();
589         sink.text(text);
590         sink.tableHeaderCell_();
591     }
592 
593     private void cell(Sink sink, String text) {
594         sink.tableCell();
595         sink.text(text);
596         sink.tableCell_();
597     }
598 
599     public void setProject(MavenProject project) {
600         this.project = project;
601     }
602 
603     public void setOutputDirectory(File outputDirectory) {
604         this.outputDirectory = outputDirectory;
605     }
606 
607     public boolean canGenerateReport() {
608         return true;
609     }
610 
611     public void generate(Sink sink, Locale locale) throws MavenReportException {
612         getLog().info("LicenseVerifierReport:generate()");
613         if (project.getArtifacts().isEmpty()) {
614             //If we have no artifacts at all.
615             doGenerateEmptyReport(getBundle(locale), sink);
616         } else {
617             try {
618                 loadLicensesFile();
619             } catch (MojoExecutionException e1) {
620                 throw new MavenReportException("Failure during load of the license.xml file", e1);
621             }
622 
623             //Get a set with all dependent artifacts incl.
624             //the transitive dependencies.
625             Set depArtifacts = project.getArtifacts();
626 
627             //Get all the informations about the licenses of the artifacts.
628             try {
629                 getDependArtifacts(depArtifacts);
630 
631                 //Sorting the artifacts...
632                 Collections.sort(getLicenseInformations(), new ArtifactComperator());
633                 doGenerateReport(getBundle(locale), sink);
634 
635             } catch (MojoExecutionException e) {
636                 getLog().error("Something wrong (BETTER MESSAGE): ", e);
637             }
638         }
639     }
640 
641     public String getCategoryName() {
642         return MavenReport.CATEGORY_PROJECT_REPORTS;
643     }
644 
645     public File getReportOutputDirectory() {
646         return reportOutputDirectory;
647     }
648 
649     public boolean isExternalReport() {
650         return false;
651     }
652 
653     public void setReportOutputDirectory(File outputDirectory) {
654         this.reportOutputDirectory = outputDirectory;
655 
656     }
657 
658     public void execute() throws MojoExecutionException, MojoFailureException {
659         try {
660             getLog().info("LicenseVerifierReport:execute()");
661             RenderingContext context = new RenderingContext(getOutputDirectory(), getOutputName() + ".html");
662             SiteRendererSink sink = new SiteRendererSink(context);
663             Locale locale = Locale.getDefault();
664             generate(sink, locale);
665         } catch (MavenReportException e) {
666             getLog().error("An error has occurred in " + getName(Locale.ENGLISH)
667                     + " report generation:" + e.getMessage(), e);
668         } catch (RuntimeException e) {
669             getLog().error(e.getMessage(), e);
670         }
671     }
672 }