PR: MNG-505

enable version ranges in resolution (only default conflict resolution - nearest suggested version, fail if over-constrained)

git-svn-id: https://svn.apache.org/repos/asf/maven/components/trunk@219844 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Brett Leslie Porter 2005-07-20 05:53:57 +00:00
parent 9e661cadb3
commit 289aa0f6a5
7 changed files with 303 additions and 37 deletions

View File

@ -20,6 +20,7 @@ import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.metadata.ArtifactMetadata;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.versioning.VersionRange;
import java.io.File;
import java.util.List;
@ -107,4 +108,10 @@ public interface Artifact
void setDependencyTrail( List dependencyTrail );
void setScope( String scope );
VersionRange getVersionRange();
void setVersionRange( VersionRange newRange );
void selectVersion( String version );
}

View File

@ -78,9 +78,7 @@ public class DefaultArtifact
this.artifactId = artifactId;
this.versionRange = versionRange;
this.version = versionRange == null ? null : versionRange.getRecommendedVersion().toString();
setVersionRange( versionRange );
this.artifactHandler = artifactHandler;
@ -113,7 +111,7 @@ public class DefaultArtifact
"The type cannot be empty." );
}
if ( getVersion() == null )
if ( version == null && versionRange == null )
{
throw new InvalidArtifactRTException( groupId, artifactId, getVersion(), type,
"The version cannot be empty." );
@ -229,7 +227,10 @@ public class DefaultArtifact
result = 37 * result + groupId.hashCode();
result = 37 * result + artifactId.hashCode();
result = 37 * result + type.hashCode();
result = 37 * result + version.hashCode();
if ( version != null )
{
result = 37 * result + version.hashCode();
}
result = 37 * result + ( classifier != null ? classifier.hashCode() : 0 );
return result;
}
@ -380,4 +381,29 @@ public class DefaultArtifact
{
this.scope = scope;
}
public VersionRange getVersionRange()
{
return versionRange;
}
public final void setVersionRange( VersionRange versionRange )
{
this.versionRange = versionRange;
if ( versionRange != null && versionRange.getRecommendedVersion() != null )
{
this.version = versionRange.getRecommendedVersion().toString();
}
else
{
this.version = null;
}
}
public void selectVersion( String version )
{
this.version = version;
}
}

View File

@ -23,6 +23,8 @@ import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.metadata.ResolutionGroup;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
import org.apache.maven.artifact.versioning.VersionRange;
import java.util.Collections;
import java.util.HashMap;
@ -60,41 +62,47 @@ public class DefaultArtifactCollector
Map resolvedArtifacts = new HashMap();
ResolutionNode root = new ResolutionNode( originatingArtifact, remoteRepositories );
root.addDependencies( artifacts, remoteRepositories, filter );
recurse( root, resolvedArtifacts, managedVersions, localRepository, remoteRepositories, source, filter,
artifactFactory, listeners );
Set set = new HashSet();
for ( Iterator i = resolvedArtifacts.values().iterator(); i.hasNext(); )
try
{
ResolutionNode node = (ResolutionNode) i.next();
if ( !node.equals( root ) )
root.addDependencies( artifacts, remoteRepositories, filter );
recurse( root, resolvedArtifacts, managedVersions, localRepository, remoteRepositories, source, filter,
artifactFactory, listeners );
Set set = new HashSet();
for ( Iterator i = resolvedArtifacts.values().iterator(); i.hasNext(); )
{
Artifact artifact = node.getArtifact();
ResolutionNode node = (ResolutionNode) i.next();
if ( !node.equals( root ) )
{
Artifact artifact = node.getArtifact();
artifact.setDependencyTrail( node.getDependencyTrail() );
artifact.setDependencyTrail( node.getDependencyTrail() );
set.add( node );
set.add( node );
}
}
ArtifactResolutionResult result = new ArtifactResolutionResult();
result.setArtifactResolutionNodes( set );
return result;
}
catch ( OverConstrainedVersionException e )
{
throw new ArtifactResolutionException( "Unable to mediate dependency", e );
}
ArtifactResolutionResult result = new ArtifactResolutionResult();
result.setArtifactResolutionNodes( set );
return result;
}
private void recurse( ResolutionNode node, Map resolvedArtifacts, Map managedVersions,
ArtifactRepository localRepository, List remoteRepositories, ArtifactMetadataSource source,
ArtifactFilter filter, ArtifactFactory artifactFactory, List listeners )
throws CyclicDependencyException, TransitiveArtifactResolutionException
throws CyclicDependencyException, TransitiveArtifactResolutionException, OverConstrainedVersionException
{
fireEvent( ResolutionListener.TEST_ARTIFACT, listeners, node );
// TODO: conflict resolvers, shouldn't be munging original artifact perhaps?
// TODO: use as a conflict resolver
Object key = node.getKey();
if ( managedVersions.containsKey( key ) )
{
@ -116,6 +124,25 @@ public class DefaultArtifactCollector
if ( previous != null )
{
// TODO: use as conflict resolver(s), chain and introduce version mediation
VersionRange previousRange = previous.getArtifact().getVersionRange();
VersionRange currentRange = node.getArtifact().getVersionRange();
if ( previousRange == null )
{
// version was already resolved
node.getArtifact().setVersion( previous.getArtifact().getVersion() );
}
else if ( currentRange == null )
{
// version was already resolved
previous.getArtifact().setVersion( node.getArtifact().getVersion() );
}
else
{
VersionRange newRange = previousRange.restrict( currentRange );
previous.getArtifact().setVersionRange( newRange );
node.getArtifact().setVersionRange( newRange );
}
// previous one is more dominant
if ( previous.getDepth() <= node.getDepth() )
@ -145,10 +172,18 @@ public class DefaultArtifactCollector
ResolutionNode child = (ResolutionNode) i.next();
if ( !child.isResolved() )
{
Artifact artifact = child.getArtifact();
try
{
ResolutionGroup rGroup = source.retrieve( child.getArtifact(), localRepository,
remoteRepositories );
if ( artifact.getVersion() == null )
{
// set the recommended version
VersionRange versionRange = artifact.getVersionRange();
String version = versionRange.getSelectedVersion().toString();
artifact.selectVersion( version );
}
ResolutionGroup rGroup = source.retrieve( artifact, localRepository, remoteRepositories );
child.addDependencies( rGroup.getArtifacts(), rGroup.getResolutionRepositories(), filter );
}
catch ( CyclicDependencyException e )
@ -162,9 +197,8 @@ public class DefaultArtifactCollector
}
catch ( ArtifactMetadataRetrievalException e )
{
child.getArtifact().setDependencyTrail( node.getDependencyTrail() );
throw new TransitiveArtifactResolutionException( e.getMessage(), child.getArtifact(),
remoteRepositories, e );
artifact.setDependencyTrail( node.getDependencyTrail() );
throw new TransitiveArtifactResolutionException( e.getMessage(), artifact, remoteRepositories, e );
}
recurse( child, resolvedArtifacts, managedVersions, localRepository, remoteRepositories, source, filter,

View File

@ -1,7 +1,25 @@
package org.apache.maven.artifact.resolver;
/*
* Copyright 2001-2005 The Apache Software Foundation.
*
* 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.
*/
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
import org.apache.maven.artifact.versioning.VersionRange;
import java.util.ArrayList;
import java.util.Collections;
@ -14,7 +32,7 @@ public class ResolutionNode
{
private Artifact artifact;
private List children = null;
private List children;
private final List parents;
@ -55,7 +73,7 @@ public class ResolutionNode
}
public void addDependencies( Set artifacts, List remoteRepositories, ArtifactFilter filter )
throws CyclicDependencyException
throws CyclicDependencyException, OverConstrainedVersionException
{
children = new ArrayList( artifacts.size() );
@ -78,12 +96,22 @@ public class ResolutionNode
}
public List getDependencyTrail()
throws OverConstrainedVersionException
{
List path = new LinkedList();
ResolutionNode node = this;
while ( node != null )
{
path.add( 0, node.getArtifact().getId() );
Artifact artifact = node.getArtifact();
if ( artifact.getVersion() == null )
{
// set the recommended version
VersionRange versionRange = artifact.getVersionRange();
String version = versionRange.getSelectedVersion().toString();
artifact.selectVersion( version );
}
path.add( 0, artifact.getId() );
node = node.parent;
}
return path;
@ -108,7 +136,7 @@ public class ResolutionNode
{
this.artifact = artifact;
}
public List getRemoteRepositories()
{
return remoteRepositories;

View File

@ -0,0 +1,32 @@
package org.apache.maven.artifact.versioning;
/*
* Copyright 2001-2005 The Apache Software Foundation.
*
* 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.
*/
/**
* Occurs when ranges exclude each other and no valid value remains.
*
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
* @version $Id$
*/
public class OverConstrainedVersionException
extends Exception
{
public OverConstrainedVersionException( String msg )
{
super( msg );
}
}

View File

@ -29,6 +29,8 @@ import java.util.List;
*/
public class VersionRange
{
private final ArtifactVersion RELEASE = new DefaultArtifactVersion( "RELEASE" );
private final ArtifactVersion recommendedVersion;
private final List restrictions;
@ -382,4 +384,66 @@ public class VersionRange
return v2;
}
}
public ArtifactVersion getSelectedVersion()
throws OverConstrainedVersionException
{
ArtifactVersion version;
if ( recommendedVersion != null )
{
version = recommendedVersion;
}
else
{
if ( restrictions.size() == 0 )
{
throw new OverConstrainedVersionException( "The artifact has no valid ranges" );
}
else
{
Restriction restriction = (Restriction) restrictions.get( restrictions.size() - 1 );
// TODO: how can we find the latest release before something to facilitate ) at the end?
version = restriction.getUpperBound();
if ( version == null )
{
version = RELEASE;
}
}
}
return version;
}
public String toString()
{
if ( recommendedVersion != null )
{
return recommendedVersion.toString();
}
else
{
StringBuffer buf = new StringBuffer();
for ( Iterator i = restrictions.iterator(); i.hasNext(); )
{
Restriction r = (Restriction) i.next();
buf.append( r.isLowerBoundInclusive() ? "[" : "(" );
if ( r.getLowerBound() != null )
{
buf.append( r.getLowerBound().toString() );
}
buf.append( "," );
if ( r.getUpperBound() != null )
{
buf.append( r.getUpperBound().toString() );
}
buf.append( r.isUpperBoundInclusive() ? "]" : ")" );
if ( i.hasNext() )
{
buf.append( "," );
}
}
return buf.toString();
}
}
}

View File

@ -135,6 +135,73 @@ public class DefaultArtifactCollectorTest
ArtifactResolutionResult res = collect( a );
assertEquals( "Check artifact list", createSet( new Object[]{a.artifact, b.artifact, c.artifact} ),
res.getArtifacts() );
assertEquals( "Check version", "3.0", getArtifact( "c", res.getArtifacts() ).getVersion() );
}
public void testResolveNearestWithRanges()
throws ArtifactResolutionException
{
ArtifactSpec a = createArtifact( "a", "1.0" );
ArtifactSpec b = a.addDependency( "b", "1.0" );
ArtifactSpec c = a.addDependency( "c", "2.0" );
b.addDependency( "c", "[1.0,3.0]" );
ArtifactResolutionResult res = collect( a );
assertEquals( "Check artifact list", createSet( new Object[]{a.artifact, b.artifact, c.artifact} ),
res.getArtifacts() );
assertEquals( "Check version", "2.0", getArtifact( "c", res.getArtifacts() ).getVersion() );
}
public void testCompatibleRanges()
throws ArtifactResolutionException
{
ArtifactSpec a = createArtifact( "a", "1.0" );
ArtifactSpec b = a.addDependency( "b", "1.0" );
a.addDependency( "c", "[2.0,2.5]" );
b.addDependency( "c", "[1.0,3.0]" );
ArtifactResolutionResult res = collect( a );
ArtifactSpec c = createArtifact( "c", "2.5" );
assertEquals( "Check artifact list", createSet( new Object[]{a.artifact, b.artifact, c.artifact} ),
res.getArtifacts() );
assertEquals( "Check version", "2.5", getArtifact( "c", res.getArtifacts() ).getVersion() );
}
public void testIncompatibleRanges()
throws ArtifactResolutionException
{
ArtifactSpec a = createArtifact( "a", "1.0" );
ArtifactSpec b = a.addDependency( "b", "1.0" );
a.addDependency( "c", "[2.4,3.0]" );
b.addDependency( "c", "[1.0,2.0]" );
try
{
ArtifactResolutionResult res = collect( a );
fail( "Should not succeed collecting, got: " + res.getArtifacts() );
}
catch ( ArtifactResolutionException expected )
{
}
}
public void testUnboundedRange()
throws ArtifactResolutionException
{
ArtifactSpec a = createArtifact( "a", "1.0" );
ArtifactSpec b = a.addDependency( "b", "1.0" );
a.addDependency( "c", "[2.0,]" );
b.addDependency( "c", "[1.0,]" );
ArtifactResolutionResult res = collect( a );
ArtifactSpec c = createArtifact( "c", "RELEASE" );
assertEquals( "Check artifact list", createSet( new Object[]{a.artifact, b.artifact, c.artifact} ),
res.getArtifacts() );
assertEquals( "Check version", "RELEASE", getArtifact( "c", res.getArtifacts() ).getVersion() );
}
public void testResolveManagedVersion()
@ -303,8 +370,9 @@ public class DefaultArtifactCollectorTest
private ArtifactSpec createArtifact( String id, String version, String scope )
{
ArtifactSpec spec = new ArtifactSpec();
spec.artifact = artifactFactory.createArtifact( GROUP_ID, id, version, scope, "jar" );
source.artifacts.put( spec.artifact.getId(), spec );
Artifact artifact = artifactFactory.createArtifact( GROUP_ID, id, version, scope, "jar" );
spec.artifact = artifact;
source.artifacts.put( source.getKey( artifact ), spec );
return spec;
}
@ -346,7 +414,9 @@ public class DefaultArtifactCollectorTest
List remoteRepositories )
throws ArtifactMetadataRetrievalException
{
ArtifactSpec a = (ArtifactSpec) artifacts.get( artifact.getId() );
String key = getKey( artifact );
ArtifactSpec a = (ArtifactSpec) artifacts.get( key );
try
{
return new ResolutionGroup( createArtifacts( artifactFactory, a.dependencies, artifact.getScope(),
@ -358,6 +428,11 @@ public class DefaultArtifactCollectorTest
}
}
private String getKey( Artifact artifact )
{
return artifact.getDependencyConflictId() + ":" + artifact.getVersionRange();
}
private Set createArtifacts( ArtifactFactory artifactFactory, Set dependencies, String inheritedScope,
ArtifactFilter dependencyFilter )
throws InvalidVersionSpecificationException