org.codehaus.mojo.license.api.DefaultThirdPartyTool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of license-maven-plugin Show documentation
Show all versions of license-maven-plugin Show documentation
Maven plugin to download and collect license files from project dependencies.
package org.codehaus.mojo.license.api;
/*
* #%L
* License Maven Plugin
* %%
* Copyright (C) 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
* .
* #L%
*/
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.model.License;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.mojo.license.model.LicenseMap;
import org.codehaus.mojo.license.utils.FileUtil;
import org.codehaus.mojo.license.utils.MojoHelper;
import org.codehaus.mojo.license.utils.SortedProperties;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Default implementation of the third party tool.
*
* @author Tony Chemit
* @version $Id: DefaultThirdPartyTool.java 19147 2013-12-29 14:08:39Z tchemit $
*/
@Component( role = ThirdPartyTool.class, hint = "default" )
public class DefaultThirdPartyTool
extends AbstractLogEnabled
implements ThirdPartyTool
{
/**
* Classifier of the third-parties descriptor attached to a maven module.
*/
public static final String DESCRIPTOR_CLASSIFIER = "third-party";
/**
* Type of the the third-parties descriptor attached to a maven module.
*/
public static final String DESCRIPTOR_TYPE = "properties";
/**
* Pattern of a GAV plus a type.
*/
private static final Pattern GAV_PLUS_TYPE_PATTERN = Pattern.compile( "(.+)--(.+)--(.+)--(.+)" );
/**
* Pattern of a GAV plus a type plus a classifier.
*/
private static final Pattern GAV_PLUS_TYPE_AND_CLASSIFIER_PATTERN =
Pattern.compile( "(.+)--(.+)--(.+)--(.+)--(.+)" );
static final String LICENSE_DB_TYPE = "license.properties";
// ----------------------------------------------------------------------
// Components
// ----------------------------------------------------------------------
/**
* The component that is used to resolve additional artifacts required.
*/
@Requirement
private ArtifactResolver artifactResolver;
/**
* The component used for creating artifact instances.
*/
@Requirement
private ArtifactFactory artifactFactory;
/**
* Maven ProjectHelper.
*/
@Requirement
private MavenProjectHelper projectHelper;
/**
* freeMarker helper.
*/
private FreeMarkerHelper freeMarkerHelper = FreeMarkerHelper.newDefaultHelper();
/**
* Maven project comparator.
*/
private final Comparator projectComparator = MojoHelper.newMavenProjectComparator();
private boolean verbose;
/**
* {@inheritDoc}
*/
public boolean isVerbose()
{
return verbose;
}
/**
* {@inheritDoc}
*/
public void setVerbose( boolean verbose )
{
this.verbose = verbose;
}
/**
* {@inheritDoc}
*/
public void attachThirdPartyDescriptor( MavenProject project, File file )
{
projectHelper.attachArtifact( project, DESCRIPTOR_TYPE, DESCRIPTOR_CLASSIFIER, file );
}
/**
* {@inheritDoc}
*/
public SortedSet getProjectsWithNoLicense( LicenseMap licenseMap, boolean doLog )
{
Logger log = getLogger();
// get unsafe dependencies (says with no license)
SortedSet unsafeDependencies = licenseMap.get( LicenseMap.UNKNOWN_LICENSE_MESSAGE );
if ( doLog )
{
if ( CollectionUtils.isEmpty( unsafeDependencies ) )
{
log.debug( "There is no dependency with no license from poms." );
}
else
{
log.debug( "There is " + unsafeDependencies.size() + " dependencies with no license from poms : " );
for ( MavenProject dep : unsafeDependencies )
{
// no license found for the dependency
log.debug( " - " + MojoHelper.getArtifactId( dep.getArtifact() ) );
}
}
}
return unsafeDependencies;
}
/**
* {@inheritDoc}
*/
public SortedProperties loadThirdPartyDescriptorsForUnsafeMapping( Set topLevelDependencies,
String encoding,
Collection projects,
SortedSet unsafeDependencies,
LicenseMap licenseMap,
ArtifactRepository localRepository,
List remoteRepositories )
throws ThirdPartyToolException, IOException
{
SortedProperties result = new SortedProperties( encoding );
Map unsafeProjects = new HashMap();
for ( MavenProject unsafeDependency : unsafeDependencies )
{
String id = MojoHelper.getArtifactId( unsafeDependency.getArtifact() );
unsafeProjects.put( id, unsafeDependency );
}
for ( MavenProject mavenProject : projects )
{
if ( CollectionUtils.isEmpty( unsafeDependencies ) )
{
// no more unsafe dependencies to find
break;
}
File thirdPartyDescriptor = resolvThirdPartyDescriptor( mavenProject, localRepository, remoteRepositories );
if ( thirdPartyDescriptor != null && thirdPartyDescriptor.exists() && thirdPartyDescriptor.length() > 0 )
{
if ( getLogger().isInfoEnabled() )
{
getLogger().info( "Detects third party descriptor " + thirdPartyDescriptor );
}
// there is a third party file detected form the given dependency
SortedProperties unsafeMappings = new SortedProperties( encoding );
if ( thirdPartyDescriptor.exists() )
{
getLogger().info( "Load missing file " + thirdPartyDescriptor );
// load the missing file
unsafeMappings.load( thirdPartyDescriptor );
}
resolveUnsafe( unsafeDependencies, licenseMap, unsafeProjects, unsafeMappings, result );
}
}
try
{
loadGlobalLicenses( topLevelDependencies, localRepository, remoteRepositories, unsafeDependencies,
licenseMap, unsafeProjects, result );
}
catch ( ArtifactNotFoundException e )
{
throw new ThirdPartyToolException( "Failed to load global licenses", e );
}
catch ( ArtifactResolutionException e )
{
throw new ThirdPartyToolException( "Failed to load global licenses", e );
}
return result;
}
private void resolveUnsafe( SortedSet unsafeDependencies, LicenseMap licenseMap,
Map unsafeProjects, SortedProperties unsafeMappings,
SortedProperties result )
{
for ( String id : unsafeProjects.keySet() )
{
if ( unsafeMappings.containsKey( id ) )
{
String license = (String) unsafeMappings.get( id );
if ( StringUtils.isEmpty( license ) )
{
// empty license means not fill, skip it
continue;
}
// found a resolved unsafe dependency in the missing third party file
MavenProject resolvedProject = unsafeProjects.get( id );
unsafeDependencies.remove( resolvedProject );
// push back to
result.put( id, license.trim() );
addLicense( licenseMap, resolvedProject, license );
}
}
}
/**
* {@inheritDoc}
*/
public File resolvThirdPartyDescriptor( MavenProject project, ArtifactRepository localRepository,
List repositories )
throws ThirdPartyToolException
{
if ( project == null )
{
throw new IllegalArgumentException( "The parameter 'project' can not be null" );
}
if ( localRepository == null )
{
throw new IllegalArgumentException( "The parameter 'localRepository' can not be null" );
}
if ( repositories == null )
{
throw new IllegalArgumentException( "The parameter 'remoteArtifactRepositories' can not be null" );
}
try
{
return resolveThirdPartyDescriptor( project, localRepository, repositories );
}
catch ( ArtifactNotFoundException e )
{
getLogger().debug( "ArtifactNotFoundException: Unable to locate third party descriptor: " + e );
return null;
}
catch ( ArtifactResolutionException e )
{
throw new ThirdPartyToolException(
"ArtifactResolutionException: Unable to locate third party descriptor: " + e.getMessage(), e );
}
catch ( IOException e )
{
throw new ThirdPartyToolException(
"IOException: Unable to locate third party descriptor: " + e.getMessage(), e );
}
}
/**
* {@inheritDoc}
*/
public void addLicense( LicenseMap licenseMap, MavenProject project, String licenseName )
{
License license = new License();
license.setName( licenseName.trim() );
license.setUrl( licenseName.trim() );
addLicense( licenseMap, project, license );
}
/**
* {@inheritDoc}
*/
public void addLicense( LicenseMap licenseMap, MavenProject project, License license )
{
addLicense( licenseMap, project, Arrays.asList( license ) );
}
/**
* {@inheritDoc}
*/
public void addLicense( LicenseMap licenseMap, MavenProject project, List> licenses )
{
if ( Artifact.SCOPE_SYSTEM.equals( project.getArtifact().getScope() ) )
{
// do NOT treat system dependency
return;
}
if ( CollectionUtils.isEmpty( licenses ) )
{
// no license found for the dependency
licenseMap.put( LicenseMap.UNKNOWN_LICENSE_MESSAGE, project );
return;
}
for ( Object o : licenses )
{
String id = MojoHelper.getArtifactId( project.getArtifact() );
if ( o == null )
{
getLogger().warn( "could not acquire the license for " + id );
continue;
}
License license = (License) o;
String licenseKey = license.getName();
// tchemit 2010-08-29 Ano #816 Check if the License object is well formed
if ( StringUtils.isEmpty( license.getName() ) )
{
getLogger().warn( "No license name defined for " + id );
licenseKey = license.getUrl();
}
if ( StringUtils.isEmpty( licenseKey ) )
{
getLogger().warn( "No license url defined for " + id );
licenseKey = LicenseMap.UNKNOWN_LICENSE_MESSAGE;
}
licenseMap.put( licenseKey, project );
}
}
/**
* {@inheritDoc}
*/
public void mergeLicenses( LicenseMap licenseMap, String mainLicense, Set licenses )
{
if ( licenses.isEmpty() )
{
// nothing to merge, is this can really happen ?
return;
}
SortedSet mainSet = licenseMap.get( mainLicense );
if ( mainSet == null )
{
if ( isVerbose() )
{
getLogger().warn( "No license [" + mainLicense + "] found, will create it." );
}
mainSet = new TreeSet( projectComparator );
licenseMap.put( mainLicense, mainSet );
}
for ( String license : licenses )
{
SortedSet set = licenseMap.get( license );
if ( set == null )
{
if ( isVerbose() )
{
getLogger().warn( "No license [" + license + "] found, skip the merge to [" + mainLicense + "]" );
}
continue;
}
if ( isVerbose() )
{
getLogger().info(
"Merge license [" + license + "] to [" + mainLicense + "] (" + set.size() + " dependencies)." );
}
mainSet.addAll( set );
set.clear();
licenseMap.remove( license );
}
}
/**
* {@inheritDoc}
*/
public SortedProperties loadUnsafeMapping( LicenseMap licenseMap, SortedMap artifactCache,
String encoding, File missingFile )
throws IOException
{
SortedSet unsafeDependencies = getProjectsWithNoLicense( licenseMap, false );
SortedProperties unsafeMappings = new SortedProperties( encoding );
if ( missingFile.exists() )
{
// there is some unsafe dependencies
getLogger().info( "Load missing file " + missingFile );
// load the missing file
unsafeMappings.load( missingFile );
}
// get from the missing file, all unknown dependencies
List unknownDependenciesId = new ArrayList();
// coming from maven-license-plugin, we used the full g/a/v/c/t. Now we remove classifier and type
// since GAV is good enough to qualify a license of any artifact of it...
Map migrateKeys = migrateMissingFileKeys( unsafeMappings.keySet() );
for ( Object o : migrateKeys.keySet() )
{
String id = (String) o;
String migratedId = migrateKeys.get( id );
MavenProject project = artifactCache.get( migratedId );
if ( project == null )
{
// now we are sure this is a unknown dependency
unknownDependenciesId.add( id );
}
else
{
if ( !id.equals( migratedId ) )
{
// migrates id to migratedId
getLogger().info( "Migrates [" + id + "] to [" + migratedId + "] in the missing file." );
Object value = unsafeMappings.get( id );
unsafeMappings.remove( id );
unsafeMappings.put( migratedId, value );
}
}
}
if ( !unknownDependenciesId.isEmpty() )
{
// there is some unknown dependencies in the missing file, remove them
for ( String id : unknownDependenciesId )
{
getLogger().warn(
"dependency [" + id + "] does not exist in project, remove it from the missing file." );
unsafeMappings.remove( id );
}
unknownDependenciesId.clear();
}
// push back loaded dependencies
for ( Object o : unsafeMappings.keySet() )
{
String id = (String) o;
MavenProject project = artifactCache.get( id );
if ( project == null )
{
getLogger().warn( "dependency [" + id + "] does not exist in project." );
continue;
}
String license = (String) unsafeMappings.get( id );
if ( StringUtils.isEmpty( license ) )
{
// empty license means not fill, skip it
continue;
}
// add license in map
addLicense( licenseMap, project, license );
// remove unknown license
unsafeDependencies.remove( project );
}
if ( unsafeDependencies.isEmpty() )
{
// no more unknown license in map
licenseMap.remove( LicenseMap.UNKNOWN_LICENSE_MESSAGE );
}
else
{
// add a "with no value license" for missing dependencies
for ( MavenProject project : unsafeDependencies )
{
String id = MojoHelper.getArtifactId( project.getArtifact() );
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "dependency [" + id + "] has no license, add it in the missing file." );
}
unsafeMappings.setProperty( id, "" );
}
}
return unsafeMappings;
}
/**
* {@inheritDoc}
*/
public void writeThirdPartyFile( LicenseMap licenseMap, File thirdPartyFile, boolean verbose, String encoding,
String lineFormat )
throws IOException
{
Logger log = getLogger();
Map properties = new HashMap();
properties.put( "licenseMap", licenseMap.entrySet() );
properties.put( "dependencyMap", licenseMap.toDependencyMap().entrySet() );
String content = freeMarkerHelper.renderTemplate( lineFormat, properties );
log.info( "Writing third-party file to " + thirdPartyFile );
if ( verbose )
{
log.info( content );
}
FileUtil.writeString( thirdPartyFile, content, encoding );
}
/**
* {@inheritDoc}
*/
public void writeBundleThirdPartyFile( File thirdPartyFile, File outputDirectory, String bundleThirdPartyPath )
throws IOException
{
// creates the bundled license file
File bundleTarget = FileUtil.getFile( outputDirectory, bundleThirdPartyPath );
getLogger().info( "Writing bundled third-party file to " + bundleTarget );
FileUtil.copyFile( thirdPartyFile, bundleTarget );
}
public void loadGlobalLicenses( Set dependencies, ArtifactRepository localRepository,
List repositories, SortedSet unsafeDependencies,
LicenseMap licenseMap, Map unsafeProjects,
SortedProperties result )
throws IOException, ArtifactNotFoundException, ArtifactResolutionException
{
for ( Artifact dep : dependencies )
{
if ( LICENSE_DB_TYPE.equals( dep.getType() ) )
{
loadOneGlobalSet( unsafeDependencies, licenseMap, unsafeProjects, dep, localRepository, repositories,
result );
}
}
}
private void loadOneGlobalSet( SortedSet unsafeDependencies, LicenseMap licenseMap,
Map unsafeProjects, Artifact dep,
ArtifactRepository localRepository, List repositories,
SortedProperties result )
throws IOException, ArtifactNotFoundException, ArtifactResolutionException
{
artifactResolver.resolve( dep, repositories, localRepository );
File propFile = dep.getFile();
getLogger().info(
String.format( "Loading global license map from %s: %s", dep.toString(), propFile.getAbsolutePath() ) );
SortedProperties props = new SortedProperties( "utf-8" );
InputStream propStream = null;
try
{
propStream = new FileInputStream( propFile );
props.load( propStream );
}
finally
{
IOUtils.closeQuietly( propStream );
}
for ( Object keyObj : props.keySet() )
{
String key = (String) keyObj;
String val = (String) props.get( key );
result.put( key, val );
}
resolveUnsafe( unsafeDependencies, licenseMap, unsafeProjects, props, result );
}
// ----------------------------------------------------------------------
// Private methods
// ----------------------------------------------------------------------
/**
* @param project not null
* @param localRepository not null
* @param repositories not null
* @return the resolved site descriptor
* @throws IOException if any
* @throws ArtifactResolutionException if any
* @throws ArtifactNotFoundException if any
*/
private File resolveThirdPartyDescriptor( MavenProject project, ArtifactRepository localRepository,
List repositories )
throws IOException, ArtifactResolutionException, ArtifactNotFoundException
{
File result;
// TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1?
Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(),
project.getVersion(), DESCRIPTOR_TYPE,
DESCRIPTOR_CLASSIFIER );
try
{
artifactResolver.resolve( artifact, repositories, localRepository );
result = artifact.getFile();
// we use zero length files to avoid re-resolution (see below)
if ( result.length() == 0 )
{
getLogger().debug( "Skipped third party descriptor" );
}
}
catch ( ArtifactNotFoundException e )
{
getLogger().debug( "Unable to locate third party files descriptor : " + e );
// we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote
// repository, because the parent was already released (and snapshots are updated automatically if changed)
result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) );
FileUtil.createNewFile( result );
}
return result;
}
private Map migrateMissingFileKeys( Set
© 2015 - 2024 Weber Informatics LLC | Privacy Policy