diff --git a/src/it/download-licenses-failFast/invoker.properties b/src/it/download-licenses-failFast/invoker.properties new file mode 100644 index 000000000..d1b5ad3fd --- /dev/null +++ b/src/it/download-licenses-failFast/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = clean license:download-licenses -Dlicense.errorRemedy=failFast +invoker.buildResult=failure diff --git a/src/it/download-licenses-failFast/pom.xml b/src/it/download-licenses-failFast/pom.xml new file mode 100644 index 000000000..43cf72e70 --- /dev/null +++ b/src/it/download-licenses-failFast/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + org.codehaus.mojo.license + download-licenses-failFast + 1.0-SNAPSHOT + Integration Test + http://maven.apache.org + + Check -Dlicense.errorRemedy=failFast + + + + + commons-logging + commons-logging + 1.0 + + + groovy + groovy-all + 1.0-jsr-04 + + + + + + + org.codehaus.mojo + license-maven-plugin + @pom.version@ + + + + download-licenses + + + + + + + diff --git a/src/it/download-licenses-failFast/postbuild.groovy b/src/it/download-licenses-failFast/postbuild.groovy new file mode 100644 index 000000000..e69a6940f --- /dev/null +++ b/src/it/download-licenses-failFast/postbuild.groovy @@ -0,0 +1,30 @@ +/* + * #%L + * License Maven Plugin + * %% + * Copyright (C) 2008 - 2011 CodeLutin, Codehaus, Tony Chemit, Tony chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.nio.file.Path; +import java.nio.file.Files; + +final Path basePath = basedir.toPath() + +final Path log = basePath.resolve('build.log') +assert Files.exists(log) +assert log.text.contains('No license information available for: groovy:groovy-all') diff --git a/src/it/download-licenses-failFast/src/license/licenses.xml b/src/it/download-licenses-failFast/src/license/licenses.xml new file mode 100644 index 000000000..0c3d63fa2 --- /dev/null +++ b/src/it/download-licenses-failFast/src/license/licenses.xml @@ -0,0 +1,14 @@ + + + + commons-logging + commons-logging + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + \ No newline at end of file diff --git a/src/it/download-licenses-failFast/src/main/java/org/codehaus/mojo/HelloWorld.java b/src/it/download-licenses-failFast/src/main/java/org/codehaus/mojo/HelloWorld.java new file mode 100644 index 000000000..16aaa7fed --- /dev/null +++ b/src/it/download-licenses-failFast/src/main/java/org/codehaus/mojo/HelloWorld.java @@ -0,0 +1,15 @@ +package org.codehaus.mojo; + +public class HelloWorld +{ + + /** + * @param args + */ + public static void main( String[] args ) + { + // TODO Auto-generated method stub + + } + +} diff --git a/src/it/download-licenses-force/invoker.properties b/src/it/download-licenses-force/invoker.properties new file mode 100644 index 000000000..2d950418f --- /dev/null +++ b/src/it/download-licenses-force/invoker.properties @@ -0,0 +1 @@ +invoker.goals = clean license:download-licenses -Dlicense.forceDownload=true -Dlicense.errorRemedy=xmlOutput diff --git a/src/it/download-licenses-force/licenses-errors.expected.xml b/src/it/download-licenses-force/licenses-errors.expected.xml new file mode 100644 index 000000000..eeae85b50 --- /dev/null +++ b/src/it/download-licenses-force/licenses-errors.expected.xml @@ -0,0 +1,16 @@ + + + + + groovy + groovy-all + 1.0-jsr-04 + + + + + No license information available for: groovy:groovy-all + + + + diff --git a/src/it/download-licenses-force/licenses.expected.xml b/src/it/download-licenses-force/licenses.expected.xml new file mode 100644 index 000000000..fc69a29a5 --- /dev/null +++ b/src/it/download-licenses-force/licenses.expected.xml @@ -0,0 +1,17 @@ + + + + + commons-logging + commons-logging + 1.0 + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + apache license 2.0 - license-2.0.txt + + + + + diff --git a/src/it/download-licenses-force/pom.xml b/src/it/download-licenses-force/pom.xml new file mode 100644 index 000000000..4422e9c19 --- /dev/null +++ b/src/it/download-licenses-force/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + org.codehaus.mojo.license + download-licenses-force + 1.0-SNAPSHOT + Integration Test + http://maven.apache.org + + Check -Dlicense.forceDownload=true -Dlicense.errorRemedy=xmlOutput + + + + + commons-logging + commons-logging + 1.0 + + + groovy + groovy-all + 1.0-jsr-04 + + + + + + + org.codehaus.mojo + license-maven-plugin + @pom.version@ + + + + download-licenses + + + + + + + diff --git a/src/it/download-licenses-force/postbuild.groovy b/src/it/download-licenses-force/postbuild.groovy new file mode 100644 index 000000000..4b79c1207 --- /dev/null +++ b/src/it/download-licenses-force/postbuild.groovy @@ -0,0 +1,39 @@ +/* + * #%L + * License Maven Plugin + * %% + * Copyright (C) 2008 - 2011 CodeLutin, Codehaus, Tony Chemit, Tony chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.nio.file.Path; +import java.nio.file.Files; + +final Path basePath = basedir.toPath() + +final Path asl2 = basePath.resolve('target/generated-resources/licenses/apache license 2.0 - license-2.0.txt') +assert Files.exists(asl2) +assert !asl2.text.contains('This content is fake.') +assert asl2.text.contains('Version 2.0, January 2004') + +final Path expectedLicensesXml = basePath.resolve('licenses.expected.xml') +final Path licensesXml = basePath.resolve('target/generated-resources/licenses.xml') +assert expectedLicensesXml.text.equals(licensesXml.text) + +final Path expectedLicensesErrorsXml = basePath.resolve('licenses-errors.expected.xml') +final Path licensesErrorsXml = basePath.resolve('target/generated-resources/licenses-errors.xml') +assert expectedLicensesErrorsXml.text.equals(licensesErrorsXml.text) diff --git a/src/it/download-licenses-force/prebuild.groovy b/src/it/download-licenses-force/prebuild.groovy new file mode 100644 index 000000000..21ea1a3c4 --- /dev/null +++ b/src/it/download-licenses-force/prebuild.groovy @@ -0,0 +1,32 @@ +/* + * #%L + * License Maven Plugin + * %% + * Copyright (C) 2008 - 2011 CodeLutin, Codehaus, Tony Chemit, Tony chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.nio.file.Path; +import java.nio.file.Files; + +Path basePath = basedir.toPath() + +Files.move(basePath.resolve('target-initial'), basePath.resolve('target')) + +Path asl2 = basePath.resolve('target/generated-resources/licenses/apache license 2.0 - license-2.0.txt') +assert Files.exists(asl2) +assert asl2.text.contains('This content is fake.') \ No newline at end of file diff --git a/src/it/download-licenses-force/src/license/licenses.xml b/src/it/download-licenses-force/src/license/licenses.xml new file mode 100644 index 000000000..0c3d63fa2 --- /dev/null +++ b/src/it/download-licenses-force/src/license/licenses.xml @@ -0,0 +1,14 @@ + + + + commons-logging + commons-logging + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + \ No newline at end of file diff --git a/src/it/download-licenses-force/src/main/java/org/codehaus/mojo/HelloWorld.java b/src/it/download-licenses-force/src/main/java/org/codehaus/mojo/HelloWorld.java new file mode 100644 index 000000000..16aaa7fed --- /dev/null +++ b/src/it/download-licenses-force/src/main/java/org/codehaus/mojo/HelloWorld.java @@ -0,0 +1,15 @@ +package org.codehaus.mojo; + +public class HelloWorld +{ + + /** + * @param args + */ + public static void main( String[] args ) + { + // TODO Auto-generated method stub + + } + +} diff --git a/src/it/download-licenses-force/target-initial/generated-resources/licenses.xml b/src/it/download-licenses-force/target-initial/generated-resources/licenses.xml new file mode 100644 index 000000000..fc69a29a5 --- /dev/null +++ b/src/it/download-licenses-force/target-initial/generated-resources/licenses.xml @@ -0,0 +1,17 @@ + + + + + commons-logging + commons-logging + 1.0 + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + apache license 2.0 - license-2.0.txt + + + + + diff --git a/src/it/download-licenses-force/target-initial/generated-resources/licenses/apache license 2.0 - license-2.0.txt b/src/it/download-licenses-force/target-initial/generated-resources/licenses/apache license 2.0 - license-2.0.txt new file mode 100644 index 000000000..5289398cd --- /dev/null +++ b/src/it/download-licenses-force/target-initial/generated-resources/licenses/apache license 2.0 - license-2.0.txt @@ -0,0 +1,2 @@ +This content is fake. +-Dlicense.forceDownload should cause the plugin to overwrite this file. \ No newline at end of file diff --git a/src/main/java/org/codehaus/mojo/license/AbstractDownloadLicensesMojo.java b/src/main/java/org/codehaus/mojo/license/AbstractDownloadLicensesMojo.java index 890e4ca2c..b0139b6b9 100644 --- a/src/main/java/org/codehaus/mojo/license/AbstractDownloadLicensesMojo.java +++ b/src/main/java/org/codehaus/mojo/license/AbstractDownloadLicensesMojo.java @@ -27,6 +27,7 @@ import org.apache.maven.model.License; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; @@ -54,6 +55,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -142,6 +144,17 @@ public abstract class AbstractDownloadLicensesMojo @Parameter( property = "licensesOutputFileEncoding", defaultValue = "${project.build.sourceEncoding}" ) private String licensesOutputFileEncoding; + /** + * A file containing dependencies whose licenses could not be downloaded for some reason. The format is similar to + * {@link #licensesOutputFile} but the entries in {@link #licensesErrorsFile} have {@code } + * elements attached to them. Those should explain what kind of error happened during the processing of the given + * dependency. + * + * @since 1.18 + */ + @Parameter( property = "license.licensesErrorsFile", + defaultValue = "${project.build.directory}/generated-resources/licenses-errors.xml" ) + private File licensesErrorsFile; /** * A filter to exclude some scopes. @@ -184,13 +197,50 @@ public abstract class AbstractDownloadLicensesMojo private boolean offline; /** - * Don't show warnings about bad or missing license files. + * Before 1.18, {@link #quiet} having value {@code false} suppressed any license download related warnings in the + * log. After 1.18 (incl.), the behavior depends on the value of {@link #errorRemedy}: + * + * + * + * + * + * + * + *
quieterrorRemedyeffective errorRemedy
truewarnignore
falsewarnwarn
true or falseignoreignore
true or falsefailFastfailFast
true or falsexmlOutputxmlOutput
* * @since 1.0 + * @deprecated Use {@link #errorRemedy} instead */ @Parameter( defaultValue = "false" ) private boolean quiet; + /** + * What to do on any license download related error. The possible values are: + *
  • + *
      {@link ErrorRemedy#ignore}: all errors are ignored
    + *
      {@link ErrorRemedy#warn}: all errors are output to the log as warnings
    + *
      {@link ErrorRemedy#failFast}: a {@link MojoFailureException} is thrown on the first download related + * error
    + *
      {@link ErrorRemedy#xmlOutput}: error messages are added as {@code } to + * {@link AbstractDownloadLicensesMojo#licensesErrorsFile}
    + *
  • + * @since 1.18 + */ + @Parameter( property = "license.errorRemedy", defaultValue = "warn" ) + private ErrorRemedy errorRemedy; + + /** + * If {@code true}, all encountered dependency license URLs are downloaded, no matter what is there in + * {@link #licensesConfigFile} and {@link #licensesOutputFile}; otherwise {@link #licensesConfigFile}, + * {@link #licensesOutputFile} (eventually persisted from a previous build) and the content of + * {@link #licensesOutputDirectory} are considered sources of valid information - i.e. only URLs that do not appear + * to have been downloaded in the past will be downloaded. + * + * @since 1.18 + */ + @Parameter( property = "license.forceDownload", defaultValue = "false" ) + private boolean forceDownload; + /** * Include transitive dependencies when downloading license files. * @@ -369,9 +419,10 @@ protected void restoreProperties() /** * {@inheritDoc} + * @throws MojoFailureException */ public void execute() - throws MojoExecutionException + throws MojoExecutionException, MojoFailureException { if ( isSkip() ) @@ -380,6 +431,8 @@ public void execute() return; } + this.errorRemedy = getEffectiveErrorRemedy( this.quiet, this.errorRemedy ); + initDirectories(); try @@ -391,7 +444,7 @@ public void execute() // License info from previous build if ( licensesOutputFile.exists() ) { - loadLicenseInfo( configuredDepLicensesMap, licensesOutputFile, true ); + loadLicenseInfo( configuredDepLicensesMap, licensesOutputFile, !forceDownload ); } // Manually configured license info, loaded second to override previously loaded info @@ -449,8 +502,14 @@ public void execute() licensesOutputFileEol = Eol.autodetect( autodetectFromFile, charset ); } - LicenseSummaryWriter.writeLicenseSummary( depProjectLicenses, licensesOutputFile, - charset, licensesOutputFileEol ); + List depProjectLicensesWithErrors = filterErrors( depProjectLicenses ); + LicenseSummaryWriter.writeLicenseSummary( depProjectLicenses, licensesOutputFile, charset, + licensesOutputFileEol ); + if ( depProjectLicensesWithErrors != null && !depProjectLicensesWithErrors.isEmpty() ) + { + LicenseSummaryWriter.writeLicenseSummary( depProjectLicensesWithErrors, licensesErrorsFile, charset, + licensesOutputFileEol ); + } } catch ( Exception e ) { @@ -464,6 +523,42 @@ public void execute() } } + /** + * Removes from the given {@code depProjectLicenses} those elements which have non-empty + * {@link ProjectLicenseInfo#getDownloaderMessages()} and adds those to the resulting {@link List}. + * + * @param depProjectLicenses the list of {@link ProjectLicenseInfo}s to filter + * @return a new {@link List} of {@link ProjectLicenseInfo}s containing only elements with non-empty + * {@link ProjectLicenseInfo#getDownloaderMessages()} + */ + private List filterErrors( List depProjectLicenses ) + { + final List result = new ArrayList<>(); + final Iterator it = depProjectLicenses.iterator(); + while ( it.hasNext() ) + { + final ProjectLicenseInfo dep = it.next(); + final List messages = dep.getDownloaderMessages(); + if ( messages != null && !messages.isEmpty() ) + { + it.remove(); + result.add( dep ); + } + } + return result; + } + + private static ErrorRemedy getEffectiveErrorRemedy( boolean quiet, ErrorRemedy errorRemedy ) + { + switch ( errorRemedy ) + { + case warn: + return quiet ? ErrorRemedy.ignore : ErrorRemedy.warn; + default: + return errorRemedy; + } + } + private List sortByGroupIdAndArtifactId( List depProjectLicenses ) { List sorted = new ArrayList<>( depProjectLicenses ); @@ -768,8 +863,9 @@ private String getLicenseFileName( ProjectLicenseInfo depProject, final URL lice * Download the licenses associated with this project * * @param depProject The project which generated the dependency + * @throws MojoFailureException */ - private void downloadLicenses( ProjectLicenseInfo depProject ) + private void downloadLicenses( ProjectLicenseInfo depProject ) throws MojoFailureException { getLog().debug( "Downloading license(s) for project " + depProject ); @@ -777,10 +873,7 @@ private void downloadLicenses( ProjectLicenseInfo depProject ) if ( depProject.getLicenses() == null || depProject.getLicenses().isEmpty() ) { - if ( !quiet ) - { - getLog().warn( "No license information available for: " + depProject ); - } + handleError( depProject, "No license information available for: " + depProject ); return; } @@ -807,7 +900,7 @@ private void downloadLicenses( ProjectLicenseInfo depProject ) licenseOutputFile = new File( licensesOutputDirectory, licenseFileName ); } - if ( !licenseOutputFile.exists() ) + if ( !licenseOutputFile.exists() || forceDownload ) { if ( !downloadedLicenseURLs.containsKey( licenseUrl ) || organizeLicensesByDependencies ) { @@ -825,31 +918,45 @@ private void downloadLicenses( ProjectLicenseInfo depProject ) } catch ( MalformedURLException e ) { - if ( !quiet ) - { - getLog().warn( "POM for dependency " + depProject.toString() + " has an invalid license URL: " + handleError( depProject, "POM for dependency " + depProject.toString() + " has an invalid license URL: " + licenseUrl ); - } } catch ( FileNotFoundException e ) { - if ( !quiet ) - { - getLog().warn( "POM for dependency " + depProject.toString() + handleError( depProject, "POM for dependency " + depProject.toString() + " has a license URL that returns file not found: " + licenseUrl ); - } } catch ( IOException e ) { - getLog().warn( "Unable to retrieve license for dependency: " + depProject.toString() ); - getLog().warn( licenseUrl ); - getLog().warn( e.getMessage() ); + handleError( depProject, "Unable to retrieve license from URL '" + licenseUrl + "' for dependency '" + + depProject.toString() + "': " + e.getMessage() ); } } } + private void handleError( ProjectLicenseInfo depProject, String msg ) throws MojoFailureException + { + switch ( errorRemedy ) + { + case ignore: + /* do nothing */ + break; + case warn: + getLog().warn( msg ); + break; + case failFast: + throw new MojoFailureException( msg ); + case xmlOutput: + depProject.addDownloaderMessage( msg ); + break; + default: + throw new IllegalStateException( "Unexpected value of " + ErrorRemedy.class.getName() + ": " + + errorRemedy ); + } + } + private String rewriteLicenseUrlIfNecessary( final String originalLicenseUrl ) { String resultUrl = originalLicenseUrl; @@ -872,4 +979,23 @@ private String rewriteLicenseUrlIfNecessary( final String originalLicenseUrl ) } return resultUrl; } + + /** + * What to do in case of a license download error. + * + * @since 1.18 + */ + public static enum ErrorRemedy + { + /** All errors are ignored */ + ignore, + /** All errors are output to the log as warnings */ + warn, + /** The first encountered error is logged and a {@link MojoFailureException} is + * thrown */ + failFast, + /** Error messages are added as {@code } to + * {@link AbstractDownloadLicensesMojo#licensesErrorsFile} */ + xmlOutput + } } diff --git a/src/main/java/org/codehaus/mojo/license/model/ProjectLicenseInfo.java b/src/main/java/org/codehaus/mojo/license/model/ProjectLicenseInfo.java index 7dc649584..56b66b442 100644 --- a/src/main/java/org/codehaus/mojo/license/model/ProjectLicenseInfo.java +++ b/src/main/java/org/codehaus/mojo/license/model/ProjectLicenseInfo.java @@ -43,17 +43,7 @@ public class ProjectLicenseInfo private List licenses = new ArrayList<>(); - private String licenseResolutionResult; - - public String getLicenseResolutionResult() - { - return licenseResolutionResult; - } - - public void setLicenseResolutionResult( String licenseResolutionResult ) - { - this.licenseResolutionResult = licenseResolutionResult; - } + private List downloaderMessages = new ArrayList<>(); /** * Default constructor. @@ -125,6 +115,16 @@ public String getId() return groupId + ":" + artifactId; } + public List getDownloaderMessages() + { + return downloaderMessages; + } + + public void addDownloaderMessage( String message ) + { + downloaderMessages.add( message ); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/org/codehaus/mojo/license/utils/LicenseSummaryWriter.java b/src/main/java/org/codehaus/mojo/license/utils/LicenseSummaryWriter.java index 28260c873..d3704ad7d 100644 --- a/src/main/java/org/codehaus/mojo/license/utils/LicenseSummaryWriter.java +++ b/src/main/java/org/codehaus/mojo/license/utils/LicenseSummaryWriter.java @@ -116,6 +116,20 @@ public static Node createDependencyNode( Document doc, ProjectLicenseInfo dep ) } } depNode.appendChild( licensesNode ); + + final List messages = dep.getDownloaderMessages(); + if ( messages != null && !messages.isEmpty() ) + { + final Node downloaderMessagesNode = doc.createElement( "downloaderMessages" ); + for ( String msg : messages ) + { + final Node downloaderMessageNode = doc.createElement( "downloaderMessage" ); + downloaderMessageNode.appendChild( doc.createTextNode( msg ) ); + downloaderMessagesNode.appendChild( downloaderMessageNode ); + } + depNode.appendChild( downloaderMessagesNode ); + } + return depNode; }