/* * #%L * License Maven Plugin * * $Id$ * $HeadURL$ * %% * Copyright (C) 2008 - 2011 CodeLutin, Codehaus, 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 * <http://www.gnu.org/licenses/lgpl-3.0.html>. * #L% */ package org.codehaus.mojo.license; import org.apache.commons.collections.CollectionUtils; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.apache.maven.project.ProjectBuildingException; import java.io.File; import java.io.IOException; import java.util.*; /** * Abstract mojo for all third-party mojos. * * @author tchemit <chemit@codelutin.com> * @since 1.0 */ public abstract class AbstractAddThirdPartyMojo extends AbstractLicenseMojo { /** * Directory where to generate files. * * @parameter expression="${license.outputDirectory}" default-value="${project.build.directory}/generated-sources/license" * @required * @since 1.0 */ protected File outputDirectory; /** * File where to wirte the third-party file. * * @parameter expression="${license.thirdPartyFilename}" default-value="THIRD-PARTY.txt" * @required * @since 1.0 */ protected String thirdPartyFilename; /** * A flag to use the missing licenses file to consolidate the THID-PARTY file. * * @parameter expression="${license.useMissingFile}" default-value="false" * @since 1.0 */ protected boolean useMissingFile; /** * The file where to fill the license for dependencies with unknwon license. * * @parameter expression="${license.missingFile}" default-value="src/license/THIRD-PARTY.properties" * @since 1.0 */ protected File missingFile; /** * To merge licenses in final file. * <p/> * Each entry represents a merge (first license is main license to keep), licenses are separated by {@code |}. * <p/> * Example : * <p/> * <pre> * <licenseMerges> * <licenseMerge>The Apache Software License|Version 2.0,Apache License, Version 2.0</licenseMerge> * </licenseMerges> * </pre> * * @parameter * @since 1.0 */ protected List<String> licenseMerges; /** * The path of the bundled third party file to produce when * {@link #generateBundle} is on. * <p/> * <b>Note:</b> This option is not available for {@code pom} module types. * * @parameter expression="${license.bundleThirdPartyPath}" default-value="META-INF/${project.artifactId}-THIRD-PARTY.txt" * @since 1.0 */ protected String bundleThirdPartyPath; /** * A flag to copy a bundled version of the third-party file. This is usefull * to avoid for a final application collision name of third party file. * <p/> * The file will be copied at the {@link #bundleThirdPartyPath} location. * * @parameter expression="${license.generateBundle}" default-value="false" * @since 1.0 */ protected boolean generateBundle; /** * To force generation of the third-party file even if every thing is up to date. * * @parameter expression="${license.force}" default-value="false" * @since 1.0 */ protected boolean force; /** * A flag to fail the build if at least one dependency was detected without a license. * * @parameter expression="${license.failIfWarning}" default-value="false" * @since 1.0 */ protected boolean failIfWarning; /** * A flag to change the grouping of the generated THIRD-PARTY file. * <p/> * By default, group by dependecies. * <p/> * If sets to {@code true}, the it will group by license type. * * @parameter expression="${license.groupByLicense}" default-value="false" * @since 1.0 */ protected boolean groupByLicense; /** * A filter to exclude some GroupIds * * @parameter expression="${license.excludedGroups}" default-value="" * @since 1.0 */ protected String excludedGroups; /** * A filter to include only some GroupIds * * @parameter expression="${license.includedGroups}" default-value="" * @since 1.0 */ protected String includedGroups; /** * A filter to exclude some ArtifactsIds * * @parameter expression="${license.excludedArtifacts}" default-value="" * @since 1.0 */ protected String excludedArtifacts; /** * A filter to include only some ArtifactsIds * * @parameter expression="${license.includedArtifacts}" default-value="" * @since 1.0 */ protected String includedArtifacts; /** * Include transitive dependencies when downloading license files. * * @parameter default-value="true" * @since 1.0 */ protected boolean includeTransitiveDependencies; private LicenseMap licenseMap; private SortedSet<MavenProject> unsafeDependencies; private File thirdPartyFile; private SortedProperties unsafeMappings; private boolean doGenerate; private boolean doGenerateBundle; public static final String NO_DEPENDENCIES_MESSAGE = "the project has no dependencies."; private static SortedMap<String, MavenProject> artifactCache; public static SortedMap<String, MavenProject> getArtifactCache() { if ( artifactCache == null ) { artifactCache = new TreeMap<String, MavenProject>(); } return artifactCache; } protected abstract SortedMap<String, MavenProject> loadDependencies(); protected abstract SortedProperties createUnsafeMapping() throws ProjectBuildingException, IOException; @Override protected void init() throws Exception { Log log = getLog(); if ( log.isDebugEnabled() ) { // always be verbose in debug mode setVerbose( true ); } File file = new File( getOutputDirectory(), getThirdPartyFilename() ); setThirdPartyFile( file ); long buildTimestamp = getBuildTimestamp(); if ( isVerbose() ) { log.info( "Build start at : " + buildTimestamp ); log.info( "third-party file : " + file.lastModified() ); } setDoGenerate( isForce() || !file.exists() || buildTimestamp > file.lastModified() ); if ( isGenerateBundle() ) { File bundleFile = FileUtil.getFile( getOutputDirectory(), getBundleThirdPartyPath() ); if ( isVerbose() ) { log.info( "bundle third-party file : " + bundleFile.lastModified() ); } setDoGenerateBundle( isForce() || !bundleFile.exists() || buildTimestamp > bundleFile.lastModified() ); } else { // not generating bundled file setDoGenerateBundle( false ); } SortedMap<String, MavenProject> projectDependencies = loadDependencies(); licenseMap = createLicenseMap( projectDependencies ); unsafeDependencies = licenseMap.getUnsafeDependencies(); if ( !CollectionUtils.isEmpty( unsafeDependencies ) && isUseMissingFile() && isDoGenerate() ) { // load unsafeMapping unsafeMappings = createUnsafeMapping(); } if ( !CollectionUtils.isEmpty( licenseMerges ) ) { // merge licenses in license map for ( String merge : licenseMerges ) { merge = merge.trim(); if ( isVerbose() ) { getLog().info( "Will merge [" + merge + "]" ); } String[] split = merge.split( "\\|" ); licenseMap.mergeLicenses( split ); } } } protected LicenseMap createLicenseMap( SortedMap<String, MavenProject> dependencies ) { LicenseMap licenseMap = new LicenseMap(); licenseMap.setLog( getLog() ); for ( MavenProject project : dependencies.values() ) { licenseMap.addLicense( project, project.getLicenses() ); } return licenseMap; } protected boolean checkUnsafeDependencies() { SortedSet<MavenProject> unsafeDependencies = getUnsafeDependencies(); boolean unsafe = !CollectionUtils.isEmpty( unsafeDependencies ); if ( unsafe ) { Log log = getLog(); log.warn( "There is " + unsafeDependencies.size() + " dependencies with no license :" ); for ( MavenProject dep : unsafeDependencies ) { // no license found for the dependency log.warn( " - " + ArtifactHelper.getArtifactId( dep.getArtifact() ) ); } } return unsafe; } protected void writeThirdPartyFile() throws IOException { Log log = getLog(); LicenseMap licenseMap = getLicenseMap(); File target = getThirdPartyFile(); if ( isDoGenerate() ) { StringBuilder sb = new StringBuilder(); if ( licenseMap.isEmpty() ) { sb.append( NO_DEPENDENCIES_MESSAGE ); } else { if ( isGroupByLicense() ) { // group by license sb.append( "List of third-party dependencies grouped by " + "their license type." ); for ( String licenseName : licenseMap.keySet() ) { SortedSet<MavenProject> projects = licenseMap.get( licenseName ); sb.append( "\n\n" ).append( licenseName ).append( " : " ); for ( MavenProject mavenProject : projects ) { String s = ArtifactHelper.getArtifactName( mavenProject ); sb.append( "\n * " ).append( s ); } } } else { // group by dependencies SortedMap<MavenProject, String[]> map = licenseMap.toDependencyMap(); sb.append( "List of " ).append( map.size() ).append( " third-party dependencies.\n" ); List<String> lines = new ArrayList<String>(); for ( Map.Entry<MavenProject, String[]> entry : map.entrySet() ) { String artifact = ArtifactHelper.getArtifactName( entry.getKey() ); StringBuilder buffer = new StringBuilder(); for ( String license : entry.getValue() ) { buffer.append( " (" ).append( license ).append( ")" ); } String licenses = buffer.toString(); String line = licenses + " " + artifact; lines.add( line ); } Collections.sort( lines ); for ( String line : lines ) { sb.append( '\n' ).append( line ); } lines.clear(); } } String content = sb.toString(); log.info( "Writing third-party file to " + target ); if ( isVerbose() ) { log.info( content ); } FileUtil.writeString( target, content, getEncoding() ); } if ( isDoGenerateBundle() ) { // creates the bundled license file File bundleTarget = FileUtil.getFile( getOutputDirectory(), getBundleThirdPartyPath() ); log.info( "Writing bundled third-party file to " + bundleTarget ); FileUtil.copyFile( target, bundleTarget ); } } public boolean isGroupByLicense() { return groupByLicense; } public void setGroupByLicense( boolean groupByLicense ) { this.groupByLicense = groupByLicense; } public File getOutputDirectory() { return outputDirectory; } public String getThirdPartyFilename() { return thirdPartyFilename; } public String getBundleThirdPartyPath() { return bundleThirdPartyPath; } public boolean isGenerateBundle() { return generateBundle; } public boolean isFailIfWarning() { return failIfWarning; } public SortedSet<MavenProject> getUnsafeDependencies() { return unsafeDependencies; } public File getThirdPartyFile() { return thirdPartyFile; } public LicenseMap getLicenseMap() { return licenseMap; } public void setOutputDirectory( File outputDirectory ) { this.outputDirectory = outputDirectory; } public void setThirdPartyFilename( String thirdPartyFilename ) { this.thirdPartyFilename = thirdPartyFilename; } public void setBundleThirdPartyPath( String bundleThirdPartyPath ) { this.bundleThirdPartyPath = bundleThirdPartyPath; } public void setGenerateBundle( boolean generateBundle ) { this.generateBundle = generateBundle; } public void setThirdPartyFile( File thirdPartyFile ) { this.thirdPartyFile = thirdPartyFile; } public boolean isUseMissingFile() { return useMissingFile; } public File getMissingFile() { return missingFile; } public void setUseMissingFile( boolean useMissingFile ) { this.useMissingFile = useMissingFile; } public void setMissingFile( File missingFile ) { this.missingFile = missingFile; } public void setFailIfWarning( boolean failIfWarning ) { this.failIfWarning = failIfWarning; } public SortedProperties getUnsafeMappings() { return unsafeMappings; } public boolean isForce() { return force; } public boolean isDoGenerate() { return doGenerate; } public void setForce( boolean force ) { this.force = force; } public void setDoGenerate( boolean doGenerate ) { this.doGenerate = doGenerate; } public boolean isDoGenerateBundle() { return doGenerateBundle; } public void setDoGenerateBundle( boolean doGenerateBundle ) { this.doGenerateBundle = doGenerateBundle; } public String getExcludedGroups() { return excludedGroups; } public void setExcludedGroups( String excludedGroups ) { this.excludedGroups = excludedGroups; } public String getIncludedGroups() { return includedGroups; } public void setIncludedGroups( String includedGroups ) { this.includedGroups = includedGroups; } public String getExcludedArtifacts() { return excludedArtifacts; } public void setExcludedArtifacts( String excludedArtifacts ) { this.excludedArtifacts = excludedArtifacts; } public String getIncludedArtifacts() { return includedArtifacts; } public void setIncludedArtifacts( String includedArtifacts ) { this.includedArtifacts = includedArtifacts; } }