org.commonjava.maven.ext.common.model.Project Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pom-manipulation-common Show documentation
Show all versions of pom-manipulation-common Show documentation
Common code shared amongst IO and Core
/*
* Copyright (C) 2012 Red Hat, Inc. ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.commonjava.maven.ext.common.model;
import org.apache.maven.model.Activation;
import org.apache.maven.model.ActivationProperty;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.model.Profile;
import org.commonjava.maven.atlas.ident.ref.ArtifactRef;
import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef;
import org.commonjava.maven.atlas.ident.ref.SimpleArtifactRef;
import org.commonjava.maven.atlas.ident.ref.SimpleProjectVersionRef;
import org.commonjava.maven.atlas.ident.util.VersionUtils;
import org.commonjava.maven.ext.common.ManipulationException;
import org.commonjava.maven.ext.common.session.MavenSessionHandler;
import org.commonjava.maven.ext.common.util.ProfileUtils;
import org.commonjava.maven.ext.common.util.PropertyResolver;
import org.commonjava.maven.galley.maven.internal.defaults.StandardMaven350PluginDefaults;
import org.commonjava.maven.galley.maven.spi.defaults.MavenPluginDefaults;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import static org.apache.commons.lang.StringUtils.isEmpty;
import static org.apache.commons.lang.StringUtils.isNotEmpty;
/**
* Provides a convenient way of passing around related information about a Maven
* project without passing multiple parameters. The model in this class
* represents the model that is being modified by the extension. Also stored is
* the original POM file related to these models.
*
* @author jdcasey
*/
public class Project
{
private static final MavenPluginDefaults PLUGIN_DEFAULTS = new StandardMaven350PluginDefaults();
private final Logger logger = LoggerFactory.getLogger( getClass() );
/**
* Original POM file from which this model information was loaded.
*/
private final File pom;
/**
* Model undergoing modification during execution. This model is what
* will eventually be written back to disk.
*/
private final Model model;
/**
* Denotes if this Project represents the top level POM of a build.
*/
private boolean inheritanceRoot;
/**
* Denotes if this Project is the execution root.
*/
private boolean executionRoot;
private boolean incrementalPME;
/**
* Tracking inheritance across the project.
*/
private Project projectParent;
public Project( final File pom, final Model model ) throws ManipulationException
{
this.pom = pom;
this.model = model;
// Validate the model.
if ( model == null )
{
throw new ManipulationException( "Invalid null model." );
}
else if ( model.getVersion() == null && model.getParent() == null )
{
throw new ManipulationException( "Invalid model: " + model + " Cannot find version!" );
}
}
/**
* Create a project with only a Model. Only used by tests currently.
* @param model the Model to use.
* @throws ManipulationException if an error occurs.
*/
public Project( final Model model )
throws ManipulationException
{
this( model.getPomFile(), model );
}
/**
* Create a project by copying another.
* @param original the Project to use.
*/
public Project( final Project original )
{
this.pom = original.pom;
this.model = original.model.clone();
this.inheritanceRoot = original.inheritanceRoot;
this.executionRoot = original.executionRoot;
this.incrementalPME = original.incrementalPME;
if ( original.projectParent != null )
{
this.projectParent = new Project( original.projectParent );
}
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + getArtifactId().hashCode();
result = prime * result + getGroupId().hashCode();
result = prime * result + getVersion().hashCode();
return result;
}
@Override
public boolean equals( final Object obj )
{
if ( this == obj )
{
return true;
}
if ( obj == null )
{
return false;
}
if ( getClass() != obj.getClass() )
{
return false;
}
final Project other = (Project) obj;
// Simply inlined ProjectVersionRef comparison here as ProjectVersionRef are created now
// on demand to ensure they have the current values. However we are using VersionSpec.equals
// in order to maintain the same semantics as ProjectVersionRef.equals.
return getGroupId().equals( other.getGroupId() )
&& getArtifactId().equals( other.getArtifactId() )
&& VersionUtils.createFromSpec( getVersion() ).equals( VersionUtils.createFromSpec( other.getVersion() ) );
}
@Override
public String toString()
{
return getKey() + " [pom=" + pom + "]";
}
public File getPom()
{
return pom;
}
/**
* Retrieve the model undergoing modification.
* @return the Model being modified.
*/
public Model getModel()
{
return model;
}
public ProjectVersionRef getKey()
{
return new SimpleProjectVersionRef( getGroupId(), getArtifactId(), getVersion() );
}
public Parent getModelParent()
{
return model.getParent();
}
/**
* Returns the Project groupId. Also used by Interpolator.
* @return the groupId
*/
public String getGroupId()
{
String g = model.getGroupId();
if ( g == null )
{
// Note: reliant upon model validation that the parent is not null.
g = model.getParent().getGroupId();
}
return g;
}
/**
* Returns the Project artifactId. Also used by Interpolator.
* @return the artifactId
*/
public String getArtifactId()
{
return getModel().getArtifactId();
}
/**
* Returns the Project version. Also used by Interpolator.
* @return the version
*/
public String getVersion()
{
String v = model.getVersion();
if ( v == null )
{
// Note: reliant upon model validation that the parent is not null.
v = model.getParent().getVersion();
}
return v;
}
/**
* This method will scan the dependencies in the potentially active Profiles in this project and
* return a fully resolved list. Note that this will only return full dependencies not managed
* i.e. those with a group, artifact and version.
*
* Note that while updating the {@link Dependency} reference returned will be reflected in the
* Model as it is the same object, if you wish to remove or add items to the Model then you
* must use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency}
* @throws ManipulationException if an error occurs
*/
public Map> getResolvedProfileDependencies( MavenSessionHandler session) throws ManipulationException
{
Map> resolvedProfileDependencies = new HashMap<>();
for ( final Profile profile : ProfileUtils.getProfiles( session, model ) )
{
Map profileDeps = new HashMap<>();
resolveDeps( session, profile.getDependencies(), false, profileDeps );
resolvedProfileDependencies.put( profile, profileDeps );
}
return resolvedProfileDependencies;
}
/**
* This method will scan the dependencies in the potentially active Profiles in this project and
* return a fully resolved list. Note that this will return all dependencies including managed
* i.e. those with a group, artifact and potentially empty version.
*
* Note that while updating the {@link Dependency} reference returned will be reflected in the
* Model as it is the same object, if you wish to remove or add items to the Model then you
* must use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency}
* @throws ManipulationException if an error occurs
*/
public Map> getAllResolvedProfileDependencies( MavenSessionHandler session) throws ManipulationException
{
Map> allResolvedProfileDependencies = new HashMap<>();
for ( final Profile profile : ProfileUtils.getProfiles( session, model ) )
{
HashMap profileDeps = new HashMap<>();
resolveDeps( session, profile.getDependencies(), true, profileDeps );
allResolvedProfileDependencies.put( profile, profileDeps );
}
return allResolvedProfileDependencies;
}
/**
* This method will scan the dependencies in the dependencyManagement section of the potentially active Profiles in
* this project and return a fully resolved list. Note that while updating the {@link Dependency}
* reference returned will be reflected in the Model as it is the same object, if you wish to remove or add items
* to the Model then you must use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency} (that were within DependencyManagement)
* @throws ManipulationException if an error occurs
*/
public Map> getResolvedProfileManagedDependencies( MavenSessionHandler session) throws ManipulationException
{
Map> resolvedProfileManagedDependencies = new HashMap<>();
for ( final Profile profile : ProfileUtils.getProfiles( session, model ) )
{
Map profileDeps = new HashMap<>();
final DependencyManagement dm = profile.getDependencyManagement();
if ( dm != null )
{
resolveDeps( session, dm.getDependencies(), false, profileDeps );
}
resolvedProfileManagedDependencies.put( profile, profileDeps );
}
return resolvedProfileManagedDependencies;
}
/**
* This method will scan the plugins in this project and return a fully resolved list. Note that
* while updating the {@link Plugin} reference returned will be reflected in the Model as it is the
* same object, if you wish to remove or add items to the Model then you must use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ProjectVersionRef} to the original {@link Plugin}
* @throws ManipulationException if an error occurs
*/
public Map getResolvedPlugins ( MavenSessionHandler session) throws ManipulationException
{
Map resolvedPlugins = new HashMap<>();
if ( getModel().getBuild() != null )
{
resolvePlugins( session, getModel().getBuild().getPlugins(), resolvedPlugins );
}
return resolvedPlugins;
}
/**
* This method will scan the plugins in the pluginManagement section of this project and return a fully
* resolved list. Note that while updating the {@link Plugin} reference returned will be reflected in
* the Model as it is the same object, if you wish to remove or add items to the Model then you must
* use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ProjectVersionRef} to the original {@link Plugin}
* @throws ManipulationException if an error occurs
*/
public Map getResolvedManagedPlugins ( MavenSessionHandler session) throws ManipulationException
{
Map resolvedManagedPlugins = new HashMap<>();
if ( getModel().getBuild() != null )
{
final PluginManagement pm = getModel().getBuild().getPluginManagement();
if ( !( pm == null || pm.getPlugins() == null ) )
{
resolvePlugins( session, pm.getPlugins(), resolvedManagedPlugins );
}
}
return resolvedManagedPlugins;
}
/**
* This method will scan the plugins in the potentially active Profiles in this project and
* return a fully resolved list. Note that while updating the {@link Plugin} reference
* returned will be reflected in the Model as it is the same object, if you wish to
* remove or add items to the Model then you must use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ProjectVersionRef} to the original {@link Plugin}
* @throws ManipulationException if an error occurs
*/
public Map> getResolvedProfilePlugins( MavenSessionHandler session )
throws ManipulationException
{
Map> resolvedProfilePlugins = new HashMap<>();
for ( final Profile profile : ProfileUtils.getProfiles( session, model ) )
{
HashMap profileDeps = new HashMap<>();
if ( profile.getBuild() != null )
{
resolvePlugins( session, profile.getBuild().getPlugins(), profileDeps );
}
resolvedProfilePlugins.put( profile, profileDeps );
}
return resolvedProfilePlugins;
}
/**
* This method will scan the plugins in the pluginManagement section in the potentially active Profiles
* in this project and return a fully resolved list. Note that while updating the {@link Plugin}
* reference returned will be reflected in the Model as it is the same object, if you wish to remove
* or add items to the Model then you must use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ProjectVersionRef} to the original {@link Plugin}
* @throws ManipulationException if an error occurs
*/
public Map> getResolvedProfileManagedPlugins( MavenSessionHandler session )
throws ManipulationException
{
Map> resolvedProfileManagedPlugins = new HashMap<>();
for ( final Profile profile : ProfileUtils.getProfiles( session, model ) )
{
Map profileDeps = new HashMap<>();
if ( profile.getBuild() != null )
{
final PluginManagement pm = profile.getBuild().getPluginManagement();
if ( pm != null )
{
resolvePlugins( session, pm.getPlugins(), profileDeps );
}
}
resolvedProfileManagedPlugins.put( profile, profileDeps );
}
return resolvedProfileManagedPlugins;
}
/**
* This method will scan the dependencies in this project and return a fully resolved list. Note that this
* will only return full dependencies not managed i.e. those with a group, artifact and version.
*
* Note that while updating the {@link Dependency} reference returned will be reflected in the Model
* as it is the same object, if you wish to remove or add items to the Model then you must use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency}
* @throws ManipulationException if an error occurs
*/
public Map getResolvedDependencies( MavenSessionHandler session) throws ManipulationException
{
Map resolvedDependencies = new HashMap<>();
resolveDeps( session, getModel().getDependencies(), false, resolvedDependencies );
return resolvedDependencies;
}
/**
* This method will scan the dependencies in this project and return a fully resolved list. Note that this
* will return all dependencies including managed i.e. those with a group, artifact and potentially empty
* version.
*
* Note that while updating the {@link Dependency} reference returned will be reflected in the Model
* as it is the same object, if you wish to remove or add items to the Model then you must use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency}
* @throws ManipulationException if an error occurs
*/
public Map getAllResolvedDependencies( MavenSessionHandler session ) throws ManipulationException
{
Map allResolvedDependencies = new HashMap<>();
resolveDeps( session, getModel().getDependencies(), true, allResolvedDependencies );
return allResolvedDependencies;
}
/**
* This method will scan the dependencies in the dependencyManagement section of this project and return a
* fully resolved list. Note that while updating the {@link Dependency} reference returned will be reflected
* in the Model as it is the same object, if you wish to remove or add items to the Model then you must use {@link #getModel()}
*
* @param session MavenSessionHandler, used by {@link PropertyResolver}
* @return a list of fully resolved {@link ArtifactRef} to the original {@link Dependency} (that were within DependencyManagement)
* @throws ManipulationException if an error occurs
*/
public Map getResolvedManagedDependencies( MavenSessionHandler session ) throws ManipulationException
{
Map resolvedManagedDependencies = new HashMap<>();
final DependencyManagement dm = getModel().getDependencyManagement();
if ( !( dm == null || dm.getDependencies() == null ) )
{
resolveDeps( session, dm.getDependencies(), false, resolvedManagedDependencies );
}
return resolvedManagedDependencies;
}
private void resolveDeps( MavenSessionHandler session, List deps, boolean includeManagedDependencies,
Map resolvedDependencies )
throws ManipulationException
{
ListIterator iterator = deps.listIterator( deps.size() );
// Iterate in reverse order so later deps take precedence
while ( iterator.hasPrevious() )
{
Dependency d = iterator.previous();
if ( session.getExcludedScopes().contains( d.getScope() ) )
{
logger.debug( "Ignoring dependency {} as scope matched {}", d, session.getExcludedScopes());
continue;
}
String g = PropertyResolver.resolveInheritedProperties( session, this, "${project.groupId}".equals( d.getGroupId() ) ?
getGroupId() :
d.getGroupId() );
String a = PropertyResolver.resolveInheritedProperties( session, this, "${project.artifactId}".equals( d.getArtifactId() ) ?
getArtifactId() :
d.getArtifactId() );
String v = PropertyResolver.resolveInheritedProperties( session, this, d.getVersion() );
if ( includeManagedDependencies && isEmpty( v ) )
{
v = "*";
}
if ( isNotEmpty( g ) && isNotEmpty( a ) && isNotEmpty( v ) )
{
SimpleArtifactRef sar = new SimpleArtifactRef( g, a, v, d.getType(), d.getClassifier() );
// If the GAVTC already exists within the map it means we have a duplicate entry. While Maven
// technically allows this it does warn that this leads to unstable models. In PME case this breaks
// the indexing as we don't have duplicate entries. Given they are exact matches, remove older duplicate.
if ( resolvedDependencies.containsKey( sar ) )
{
logger.error( "Found duplicate entry within dependency list. Key of {} and dependency {}", sar, d );
iterator.remove();
}
else
{
Dependency old = resolvedDependencies.put( sar, d );
if ( old != null )
{
logger.error( "Internal project dependency resolution failure ; replaced {} in store by {}:{}:{}.",
old, g, a, v );
throw new ManipulationException(
"Internal project dependency resolution failure ; replaced " + old + " by " + d );
}
}
}
}
}
private void resolvePlugins ( MavenSessionHandler session, List plugins, Map resolvedPlugins)
throws ManipulationException
{
ListIterator iterator = plugins.listIterator( plugins.size() );
// Iterate in reverse order so later plugins take precedence
while ( iterator.hasPrevious() )
{
Plugin p = iterator.previous();
String g = PropertyResolver.resolveInheritedProperties( session, this, "${project.groupId}".equals( p.getGroupId() ) ?
getGroupId() :
p.getGroupId() );
String a = PropertyResolver.resolveInheritedProperties( session, this, "${project.artifactId}".equals( p.getArtifactId() ) ?
getArtifactId() :
p.getArtifactId() );
String v = PropertyResolver.resolveInheritedProperties( session, this, p.getVersion() );
// Its possible the internal plugin list is either abbreviated or empty. Attempt to fill in default values for
// comparison purposes.
if ( isEmpty( g ) )
{
g = PLUGIN_DEFAULTS.getDefaultGroupId( a );
}
// Theoretically we could default an empty v via PLUGIN_DEFAULTS.getDefaultVersion( g, a ) but
// this means managed plugins would be included which confuses things.
if ( isNotEmpty( g ) && isNotEmpty( a ) && isNotEmpty( v ) )
{
SimpleProjectVersionRef spv = new SimpleProjectVersionRef( g, a, v );
// If the GAV already exists within the map it means we have a duplicate entry. While Maven
// technically allows this it does warn that this leads to unstable models. In PME case this breaks
// the indexing as we don't have duplicate entries. Given they are exact matches, remove older duplicate.
if ( resolvedPlugins.containsKey( spv ) )
{
logger.error( "Found duplicate entry within plugin list. Key of {} and plugin {}", spv, p );
iterator.remove();
}
else
{
Plugin old = resolvedPlugins.put( spv, p );
if ( old != null )
{
logger.error( "Internal project plugin resolution failure ; replaced {} in store by {}.", old,
spv );
throw new ManipulationException(
"Internal project plugin resolution failure ; replaced " + old + " by " + spv );
}
}
}
}
}
public void setInheritanceRoot( final boolean inheritanceRoot )
{
this.inheritanceRoot = inheritanceRoot;
}
/**
* @return true if this Project represents the top level POM of a build.
*/
public boolean isInheritanceRoot()
{
return inheritanceRoot;
}
public void setExecutionRoot()
{
executionRoot = true;
}
/**
* Returns whether this project is the execution root.
* @return true if this Project is the execution root.
*/
public boolean isExecutionRoot()
{
return executionRoot;
}
public void setIncrementalPME( boolean incrementalPME )
{
this.incrementalPME = incrementalPME;
}
public boolean isIncrementalPME( )
{
return incrementalPME;
}
public void setProjectParent( Project parent )
{
this.projectParent = parent;
}
public Project getProjectParent()
{
return projectParent;
}
/**
* @return inherited projects. Returned with order of root project first, down to this project.
*/
public List getInheritedList()
{
final List found = new ArrayList<>( );
found.add( this );
Project loop = this;
while ( loop.getProjectParent() != null)
{
// Place inherited first so latter down tree take precedence.
found.add( 0, loop.getProjectParent() );
loop = loop.getProjectParent();
}
return found;
}
/**
* @return inherited projects. Returned with order of this project first, up to root project.
*/
public List getReverseInheritedList()
{
final List found = new ArrayList<>( );
found.add( this );
Project loop = this;
while ( loop.getProjectParent() != null)
{
// Place inherited last for iteration purposes
found.add( loop.getProjectParent() );
loop = loop.getProjectParent();
}
return found;
}
public void updateProfiles (List remoteProfiles)
{
final List profiles = model.getProfiles();
if ( !remoteProfiles.isEmpty() )
{
for ( Profile profile : remoteProfiles )
{
final Iterator i = profiles.iterator();
while ( i.hasNext() )
{
final Profile p = i.next();
if ( profile.getId().equals( p.getId() ) )
{
logger.debug( "Removing local profile {} ", p );
i.remove();
// Don't break out of the loop so we can check for active profiles
}
// If we have injected profiles and one of the current profiles is using
// activeByDefault it will get mistakenly deactivated due to the semantics
// of activeByDefault. Therefore replace the activation.
if ( p.getActivation() != null && p.getActivation().isActiveByDefault() )
{
logger.warn( "Profile {} is activeByDefault", p );
final Activation replacement = new Activation();
final ActivationProperty replacementProp = new ActivationProperty();
replacementProp.setName( "!disableProfileActivation" );
replacement.setProperty( replacementProp );
p.setActivation( replacement );
}
}
logger.debug( "Adding profile {}", profile );
profiles.add( profile );
}
}
}
}