http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactProperties.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactProperties.java b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactProperties.java new file mode 100644 index 0000000..1108086 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactProperties.java @@ -0,0 +1,74 @@ +package org.eclipse.aether.artifact; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/** + * The keys for common properties of artifacts. + * + * @see Artifact#getProperties() + */ +public final class ArtifactProperties +{ + + /** + * A high-level characterization of the artifact, e.g. "maven-plugin" or "test-jar". + * + * @see ArtifactType#getId() + */ + public static final String TYPE = "type"; + + /** + * The programming language this artifact is relevant for, e.g. "java" or "none". + */ + public static final String LANGUAGE = "language"; + + /** + * The (expected) path to the artifact on the local filesystem. An artifact which has this property set is assumed + * to be not present in any regular repository and likewise has no artifact descriptor. Artifact resolution will + * verify the path and resolve the artifact if the path actually denotes an existing file. If the path isn't valid, + * resolution will fail and no attempts to search local/remote repositories are made. + */ + public static final String LOCAL_PATH = "localPath"; + + /** + * A boolean flag indicating whether the artifact presents some kind of bundle that physically includes its + * dependencies, e.g. a fat WAR. + */ + public static final String INCLUDES_DEPENDENCIES = "includesDependencies"; + + /** + * A boolean flag indicating whether the artifact is meant to be used for the compile/runtime/test build path of a + * consumer project. + */ + public static final String CONSTITUTES_BUILD_PATH = "constitutesBuildPath"; + + /** + * The URL to a web page from which the artifact can be manually downloaded. This URL is not contacted by the + * repository system but serves as a pointer for the end user to assist in getting artifacts that are not published + * in a proper repository. + */ + public static final String DOWNLOAD_URL = "downloadUrl"; + + private ArtifactProperties() + { + // hide constructor + } + +}
http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactType.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactType.java b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactType.java new file mode 100644 index 0000000..5f87217 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactType.java @@ -0,0 +1,67 @@ +package org.eclipse.aether.artifact; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.util.Map; + +/** + * An artifact type describing artifact characteristics/properties that are common for certain artifacts. Artifact types + * are a means to simplify the description of an artifact by referring to an artifact type instead of specifying the + * various properties individually. + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * @see ArtifactTypeRegistry + * @see DefaultArtifact#DefaultArtifact(String, String, String, String, String, ArtifactType) + */ +public interface ArtifactType +{ + + /** + * Gets the identifier of this type, e.g. "maven-plugin" or "test-jar". + * + * @return The identifier of this type, never {@code null}. + * @see ArtifactProperties#TYPE + */ + String getId(); + + /** + * Gets the file extension to use for artifacts of this type (unless explicitly overridden by the artifact). + * + * @return The usual file extension, never {@code null}. + */ + String getExtension(); + + /** + * Gets the classifier to use for artifacts of this type (unless explicitly overridden by the artifact). + * + * @return The usual classifier or an empty string if none, never {@code null}. + */ + String getClassifier(); + + /** + * Gets the properties to use for artifacts of this type (unless explicitly overridden by the artifact). + * + * @return The (read-only) properties, never {@code null}. + * @see ArtifactProperties + */ + Map<String, String> getProperties(); + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactTypeRegistry.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactTypeRegistry.java b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactTypeRegistry.java new file mode 100644 index 0000000..f379173 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/ArtifactTypeRegistry.java @@ -0,0 +1,38 @@ +package org.eclipse.aether.artifact; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/** + * A registry of known artifact types. + * + * @see org.eclipse.aether.RepositorySystemSession#getArtifactTypeRegistry() + */ +public interface ArtifactTypeRegistry +{ + + /** + * Gets the artifact type with the specified identifier. + * + * @param typeId The identifier of the type, must not be {@code null}. + * @return The artifact type or {@code null} if no type with the requested identifier exists. + */ + ArtifactType get( String typeId ); + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java new file mode 100644 index 0000000..786af74 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifact.java @@ -0,0 +1,285 @@ +package org.eclipse.aether.artifact; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A simple artifact. <em>Note:</em> Instances of this class are immutable and the exposed mutators return new objects + * rather than changing the current instance. + */ +public final class DefaultArtifact + extends AbstractArtifact +{ + + private final String groupId; + + private final String artifactId; + + private final String version; + + private final String classifier; + + private final String extension; + + private final File file; + + private final Map<String, String> properties; + + /** + * Creates a new artifact with the specified coordinates. If not specified in the artifact coordinates, the + * artifact's extension defaults to {@code jar} and classifier to an empty string. + * + * @param coords The artifact coordinates in the format + * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}. + */ + public DefaultArtifact( String coords ) + { + this( coords, Collections.<String, String>emptyMap() ); + } + + /** + * Creates a new artifact with the specified coordinates and properties. If not specified in the artifact + * coordinates, the artifact's extension defaults to {@code jar} and classifier to an empty string. + * + * @param coords The artifact coordinates in the format + * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}. + * @param properties The artifact properties, may be {@code null}. + */ + public DefaultArtifact( String coords, Map<String, String> properties ) + { + Pattern p = Pattern.compile( "([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)" ); + Matcher m = p.matcher( coords ); + if ( !m.matches() ) + { + throw new IllegalArgumentException( "Bad artifact coordinates " + coords + + ", expected format is <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>" ); + } + groupId = m.group( 1 ); + artifactId = m.group( 2 ); + extension = get( m.group( 4 ), "jar" ); + classifier = get( m.group( 6 ), "" ); + version = m.group( 7 ); + file = null; + this.properties = copyProperties( properties ); + } + + private static String get( String value, String defaultValue ) + { + return ( value == null || value.length() <= 0 ) ? defaultValue : value; + } + + /** + * Creates a new artifact with the specified coordinates and no classifier. Passing {@code null} for any of the + * coordinates is equivalent to specifying an empty string. + * + * @param groupId The group identifier of the artifact, may be {@code null}. + * @param artifactId The artifact identifier of the artifact, may be {@code null}. + * @param extension The file extension of the artifact, may be {@code null}. + * @param version The version of the artifact, may be {@code null}. + */ + public DefaultArtifact( String groupId, String artifactId, String extension, String version ) + { + this( groupId, artifactId, "", extension, version ); + } + + /** + * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is + * equivalent to specifying an empty string. + * + * @param groupId The group identifier of the artifact, may be {@code null}. + * @param artifactId The artifact identifier of the artifact, may be {@code null}. + * @param classifier The classifier of the artifact, may be {@code null}. + * @param extension The file extension of the artifact, may be {@code null}. + * @param version The version of the artifact, may be {@code null}. + */ + public DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version ) + { + this( groupId, artifactId, classifier, extension, version, null, (File) null ); + } + + /** + * Creates a new artifact with the specified coordinates. Passing {@code null} for any of the coordinates is + * equivalent to specifying an empty string. The optional artifact type provided to this constructor will be used to + * determine the artifact's classifier and file extension if the corresponding arguments for this constructor are + * {@code null}. + * + * @param groupId The group identifier of the artifact, may be {@code null}. + * @param artifactId The artifact identifier of the artifact, may be {@code null}. + * @param classifier The classifier of the artifact, may be {@code null}. + * @param extension The file extension of the artifact, may be {@code null}. + * @param version The version of the artifact, may be {@code null}. + * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}. + */ + public DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version, + ArtifactType type ) + { + this( groupId, artifactId, classifier, extension, version, null, type ); + } + + /** + * Creates a new artifact with the specified coordinates and properties. Passing {@code null} for any of the + * coordinates is equivalent to specifying an empty string. The optional artifact type provided to this constructor + * will be used to determine the artifact's classifier and file extension if the corresponding arguments for this + * constructor are {@code null}. If the artifact type specifies properties, those will get merged with the + * properties passed directly into the constructor, with the latter properties taking precedence. + * + * @param groupId The group identifier of the artifact, may be {@code null}. + * @param artifactId The artifact identifier of the artifact, may be {@code null}. + * @param classifier The classifier of the artifact, may be {@code null}. + * @param extension The file extension of the artifact, may be {@code null}. + * @param version The version of the artifact, may be {@code null}. + * @param properties The properties of the artifact, may be {@code null} if none. + * @param type The artifact type from which to query classifier, file extension and properties, may be {@code null}. + */ + public DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version, + Map<String, String> properties, ArtifactType type ) + { + this.groupId = emptify( groupId ); + this.artifactId = emptify( artifactId ); + if ( classifier != null || type == null ) + { + this.classifier = emptify( classifier ); + } + else + { + this.classifier = emptify( type.getClassifier() ); + } + if ( extension != null || type == null ) + { + this.extension = emptify( extension ); + } + else + { + this.extension = emptify( type.getExtension() ); + } + this.version = emptify( version ); + this.file = null; + this.properties = merge( properties, ( type != null ) ? type.getProperties() : null ); + } + + private static Map<String, String> merge( Map<String, String> dominant, Map<String, String> recessive ) + { + Map<String, String> properties; + + if ( ( dominant == null || dominant.isEmpty() ) && ( recessive == null || recessive.isEmpty() ) ) + { + properties = Collections.emptyMap(); + } + else + { + properties = new HashMap<String, String>(); + if ( recessive != null ) + { + properties.putAll( recessive ); + } + if ( dominant != null ) + { + properties.putAll( dominant ); + } + properties = Collections.unmodifiableMap( properties ); + } + + return properties; + } + + /** + * Creates a new artifact with the specified coordinates, properties and file. Passing {@code null} for any of the + * coordinates is equivalent to specifying an empty string. + * + * @param groupId The group identifier of the artifact, may be {@code null}. + * @param artifactId The artifact identifier of the artifact, may be {@code null}. + * @param classifier The classifier of the artifact, may be {@code null}. + * @param extension The file extension of the artifact, may be {@code null}. + * @param version The version of the artifact, may be {@code null}. + * @param properties The properties of the artifact, may be {@code null} if none. + * @param file The resolved file of the artifact, may be {@code null}. + */ + public DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version, + Map<String, String> properties, File file ) + { + this.groupId = emptify( groupId ); + this.artifactId = emptify( artifactId ); + this.classifier = emptify( classifier ); + this.extension = emptify( extension ); + this.version = emptify( version ); + this.file = file; + this.properties = copyProperties( properties ); + } + + DefaultArtifact( String groupId, String artifactId, String classifier, String extension, String version, File file, + Map<String, String> properties ) + { + // NOTE: This constructor assumes immutability of the provided properties, for internal use only + this.groupId = emptify( groupId ); + this.artifactId = emptify( artifactId ); + this.classifier = emptify( classifier ); + this.extension = emptify( extension ); + this.version = emptify( version ); + this.file = file; + this.properties = properties; + } + + private static String emptify( String str ) + { + return ( str == null ) ? "" : str; + } + + public String getGroupId() + { + return groupId; + } + + public String getArtifactId() + { + return artifactId; + } + + public String getVersion() + { + return version; + } + + public String getClassifier() + { + return classifier; + } + + public String getExtension() + { + return extension; + } + + public File getFile() + { + return file; + } + + public Map<String, String> getProperties() + { + return properties; + } + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifactType.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifactType.java b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifactType.java new file mode 100644 index 0000000..c0d2ad8 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/DefaultArtifactType.java @@ -0,0 +1,146 @@ +package org.eclipse.aether.artifact; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * A simple artifact type. + */ +public final class DefaultArtifactType + implements ArtifactType +{ + + private final String id; + + private final String extension; + + private final String classifier; + + private final Map<String, String> properties; + + /** + * Creates a new artifact type with the specified identifier. This constructor assumes the usual file extension + * equals the given type id and that the usual classifier is empty. Additionally, the properties + * {@link ArtifactProperties#LANGUAGE}, {@link ArtifactProperties#CONSTITUTES_BUILD_PATH} and + * {@link ArtifactProperties#INCLUDES_DEPENDENCIES} will be set to {@code "none"}, {@code true} and {@code false}, + * respectively. + * + * @param id The identifier of the type which will also be used as the value for the {@link ArtifactProperties#TYPE} + * property, must not be {@code null} or empty. + */ + public DefaultArtifactType( String id ) + { + this( id, id, "", "none", false, false ); + } + + /** + * Creates a new artifact type with the specified properties. Additionally, the properties + * {@link ArtifactProperties#CONSTITUTES_BUILD_PATH} and {@link ArtifactProperties#INCLUDES_DEPENDENCIES} will be + * set to {@code true} and {@code false}, respectively. + * + * @param id The identifier of the type which will also be used as the value for the {@link ArtifactProperties#TYPE} + * property, must not be {@code null} or empty. + * @param extension The usual file extension for artifacts of this type, may be {@code null}. + * @param classifier The usual classifier for artifacts of this type, may be {@code null}. + * @param language The value for the {@link ArtifactProperties#LANGUAGE} property, may be {@code null}. + */ + public DefaultArtifactType( String id, String extension, String classifier, String language ) + { + this( id, extension, classifier, language, true, false ); + } + + /** + * Creates a new artifact type with the specified properties. + * + * @param id The identifier of the type which will also be used as the value for the {@link ArtifactProperties#TYPE} + * property, must not be {@code null} or empty. + * @param extension The usual file extension for artifacts of this type, may be {@code null}. + * @param classifier The usual classifier for artifacts of this type, may be {@code null}. + * @param language The value for the {@link ArtifactProperties#LANGUAGE} property, may be {@code null}. + * @param constitutesBuildPath The value for the {@link ArtifactProperties#CONSTITUTES_BUILD_PATH} property. + * @param includesDependencies The value for the {@link ArtifactProperties#INCLUDES_DEPENDENCIES} property. + */ + public DefaultArtifactType( String id, String extension, String classifier, String language, + boolean constitutesBuildPath, boolean includesDependencies ) + { + if ( id == null || id.length() < 0 ) + { + throw new IllegalArgumentException( "no type id specified" ); + } + this.id = id; + this.extension = emptify( extension ); + this.classifier = emptify( classifier ); + Map<String, String> props = new HashMap<String, String>(); + props.put( ArtifactProperties.TYPE, id ); + props.put( ArtifactProperties.LANGUAGE, ( language != null && language.length() > 0 ) ? language : "none" ); + props.put( ArtifactProperties.INCLUDES_DEPENDENCIES, Boolean.toString( includesDependencies ) ); + props.put( ArtifactProperties.CONSTITUTES_BUILD_PATH, Boolean.toString( constitutesBuildPath ) ); + properties = Collections.unmodifiableMap( props ); + } + + /** + * Creates a new artifact type with the specified properties. + * + * @param id The identifier of the type, must not be {@code null} or empty. + * @param extension The usual file extension for artifacts of this type, may be {@code null}. + * @param classifier The usual classifier for artifacts of this type, may be {@code null}. + * @param properties The properties for artifacts of this type, may be {@code null}. + */ + public DefaultArtifactType( String id, String extension, String classifier, Map<String, String> properties ) + { + if ( id == null || id.length() < 0 ) + { + throw new IllegalArgumentException( "no type id specified" ); + } + this.id = id; + this.extension = emptify( extension ); + this.classifier = emptify( classifier ); + this.properties = AbstractArtifact.copyProperties( properties ); + } + + private static String emptify( String str ) + { + return ( str == null ) ? "" : str; + } + + public String getId() + { + return id; + } + + public String getExtension() + { + return extension; + } + + public String getClassifier() + { + return classifier; + } + + public Map<String, String> getProperties() + { + return properties; + } + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/package-info.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/package-info.java b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/package-info.java new file mode 100644 index 0000000..9a4cc79 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/artifact/package-info.java @@ -0,0 +1,24 @@ +// CHECKSTYLE_OFF: RegexpHeader +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/** + * The definition of an artifact, that is the primary entity managed by the repository system. + */ +package org.eclipse.aether.artifact; + http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectRequest.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectRequest.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectRequest.java new file mode 100644 index 0000000..d9c2527 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectRequest.java @@ -0,0 +1,356 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.repository.RemoteRepository; + +/** + * A request to collect the transitive dependencies and to build a dependency graph from them. There are three ways to + * create a dependency graph. First, only the root dependency can be given. Second, a root dependency and direct + * dependencies can be specified in which case the specified direct dependencies are merged with the direct dependencies + * retrieved from the artifact descriptor of the root dependency. And last, only direct dependencies can be specified in + * which case the root node of the resulting graph has no associated dependency. + * + * @see RepositorySystem#collectDependencies(RepositorySystemSession, CollectRequest) + */ +public final class CollectRequest +{ + + private Artifact rootArtifact; + + private Dependency root; + + private List<Dependency> dependencies = Collections.emptyList(); + + private List<Dependency> managedDependencies = Collections.emptyList(); + + private List<RemoteRepository> repositories = Collections.emptyList(); + + private String context = ""; + + private RequestTrace trace; + + /** + * Creates an uninitialized request. + */ + public CollectRequest() + { + // enables default constructor + } + + /** + * Creates a request with the specified properties. + * + * @param root The root dependency whose transitive dependencies should be collected, may be {@code null}. + * @param repositories The repositories to use for the collection, may be {@code null}. + */ + public CollectRequest( Dependency root, List<RemoteRepository> repositories ) + { + setRoot( root ); + setRepositories( repositories ); + } + + /** + * Creates a new request with the specified properties. + * + * @param root The root dependency whose transitive dependencies should be collected, may be {@code null}. + * @param dependencies The direct dependencies to merge with the direct dependencies from the root dependency's + * artifact descriptor. + * @param repositories The repositories to use for the collection, may be {@code null}. + */ + public CollectRequest( Dependency root, List<Dependency> dependencies, List<RemoteRepository> repositories ) + { + setRoot( root ); + setDependencies( dependencies ); + setRepositories( repositories ); + } + + /** + * Creates a new request with the specified properties. + * + * @param dependencies The direct dependencies of some imaginary root, may be {@code null}. + * @param managedDependencies The dependency management information to apply to the transitive dependencies, may be + * {@code null}. + * @param repositories The repositories to use for the collection, may be {@code null}. + */ + public CollectRequest( List<Dependency> dependencies, List<Dependency> managedDependencies, + List<RemoteRepository> repositories ) + { + setDependencies( dependencies ); + setManagedDependencies( managedDependencies ); + setRepositories( repositories ); + } + + /** + * Gets the root artifact for the dependency graph. + * + * @return The root artifact for the dependency graph or {@code null} if none. + */ + public Artifact getRootArtifact() + { + return rootArtifact; + } + + /** + * Sets the root artifact for the dependency graph. This must not be confused with {@link #setRoot(Dependency)}: The + * root <em>dependency</em>, like any other specified dependency, will be subject to dependency + * collection/resolution, i.e. should have an artifact descriptor and a corresponding artifact file. The root + * <em>artifact</em> on the other hand is only used as a label for the root node of the graph in case no root + * dependency was specified. As such, the configured root artifact is ignored if {@link #getRoot()} does not return + * {@code null}. + * + * @param rootArtifact The root artifact for the dependency graph, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest setRootArtifact( Artifact rootArtifact ) + { + this.rootArtifact = rootArtifact; + return this; + } + + /** + * Gets the root dependency of the graph. + * + * @return The root dependency of the graph or {@code null} if none. + */ + public Dependency getRoot() + { + return root; + } + + /** + * Sets the root dependency of the graph. + * + * @param root The root dependency of the graph, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest setRoot( Dependency root ) + { + this.root = root; + return this; + } + + /** + * Gets the direct dependencies. + * + * @return The direct dependencies, never {@code null}. + */ + public List<Dependency> getDependencies() + { + return dependencies; + } + + /** + * Sets the direct dependencies. If both a root dependency and direct dependencies are given in the request, the + * direct dependencies from the request will be merged with the direct dependencies from the root dependency's + * artifact descriptor, giving higher priority to the dependencies from the request. + * + * @param dependencies The direct dependencies, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest setDependencies( List<Dependency> dependencies ) + { + if ( dependencies == null ) + { + this.dependencies = Collections.emptyList(); + } + else + { + this.dependencies = dependencies; + } + return this; + } + + /** + * Adds the specified direct dependency. + * + * @param dependency The dependency to add, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest addDependency( Dependency dependency ) + { + if ( dependency != null ) + { + if ( this.dependencies.isEmpty() ) + { + this.dependencies = new ArrayList<Dependency>(); + } + this.dependencies.add( dependency ); + } + return this; + } + + /** + * Gets the dependency management to apply to transitive dependencies. + * + * @return The dependency management to apply to transitive dependencies, never {@code null}. + */ + public List<Dependency> getManagedDependencies() + { + return managedDependencies; + } + + /** + * Sets the dependency management to apply to transitive dependencies. To clarify, this management does not apply to + * the direct dependencies of the root node. + * + * @param managedDependencies The dependency management, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest setManagedDependencies( List<Dependency> managedDependencies ) + { + if ( managedDependencies == null ) + { + this.managedDependencies = Collections.emptyList(); + } + else + { + this.managedDependencies = managedDependencies; + } + return this; + } + + /** + * Adds the specified managed dependency. + * + * @param managedDependency The managed dependency to add, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest addManagedDependency( Dependency managedDependency ) + { + if ( managedDependency != null ) + { + if ( this.managedDependencies.isEmpty() ) + { + this.managedDependencies = new ArrayList<Dependency>(); + } + this.managedDependencies.add( managedDependency ); + } + return this; + } + + /** + * Gets the repositories to use for the collection. + * + * @return The repositories to use for the collection, never {@code null}. + */ + public List<RemoteRepository> getRepositories() + { + return repositories; + } + + /** + * Sets the repositories to use for the collection. + * + * @param repositories The repositories to use for the collection, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest setRepositories( List<RemoteRepository> repositories ) + { + if ( repositories == null ) + { + this.repositories = Collections.emptyList(); + } + else + { + this.repositories = repositories; + } + return this; + } + + /** + * Adds the specified repository for collection. + * + * @param repository The repository to collect dependency information from, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest addRepository( RemoteRepository repository ) + { + if ( repository != null ) + { + if ( this.repositories.isEmpty() ) + { + this.repositories = new ArrayList<RemoteRepository>(); + } + this.repositories.add( repository ); + } + return this; + } + + /** + * Gets the context in which this request is made. + * + * @return The context, never {@code null}. + */ + public String getRequestContext() + { + return context; + } + + /** + * Sets the context in which this request is made. + * + * @param context The context, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest setRequestContext( String context ) + { + this.context = ( context != null ) ? context : ""; + return this; + } + + /** + * Gets the trace information that describes the higher level request/operation in which this request is issued. + * + * @return The trace information about the higher level operation or {@code null} if none. + */ + public RequestTrace getTrace() + { + return trace; + } + + /** + * Sets the trace information that describes the higher level request/operation in which this request is issued. + * + * @param trace The trace information about the higher level operation, may be {@code null}. + * @return This request for chaining, never {@code null}. + */ + public CollectRequest setTrace( RequestTrace trace ) + { + this.trace = trace; + return this; + } + + @Override + public String toString() + { + return getRoot() + " -> " + getDependencies() + " < " + getRepositories(); + } + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectResult.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectResult.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectResult.java new file mode 100644 index 0000000..dd9f02e --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectResult.java @@ -0,0 +1,159 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.graph.DependencyCycle; +import org.eclipse.aether.graph.DependencyNode; + +/** + * The result of a dependency collection request. + * + * @see RepositorySystem#collectDependencies(RepositorySystemSession, CollectRequest) + */ +public final class CollectResult +{ + + private final CollectRequest request; + + private List<Exception> exceptions; + + private List<DependencyCycle> cycles; + + private DependencyNode root; + + /** + * Creates a new result for the specified request. + * + * @param request The resolution request, must not be {@code null}. + */ + public CollectResult( CollectRequest request ) + { + if ( request == null ) + { + throw new IllegalArgumentException( "dependency collection request has not been specified" ); + } + this.request = request; + exceptions = Collections.emptyList(); + cycles = Collections.emptyList(); + } + + /** + * Gets the collection request that was made. + * + * @return The collection request, never {@code null}. + */ + public CollectRequest getRequest() + { + return request; + } + + /** + * Gets the exceptions that occurred while building the dependency graph. + * + * @return The exceptions that occurred, never {@code null}. + */ + public List<Exception> getExceptions() + { + return exceptions; + } + + /** + * Records the specified exception while building the dependency graph. + * + * @param exception The exception to record, may be {@code null}. + * @return This result for chaining, never {@code null}. + */ + public CollectResult addException( Exception exception ) + { + if ( exception != null ) + { + if ( exceptions.isEmpty() ) + { + exceptions = new ArrayList<Exception>(); + } + exceptions.add( exception ); + } + return this; + } + + /** + * Gets the dependency cycles that were encountered while building the dependency graph. + * + * @return The dependency cycles in the (raw) graph, never {@code null}. + */ + public List<DependencyCycle> getCycles() + { + return cycles; + } + + /** + * Records the specified dependency cycle. + * + * @param cycle The dependency cycle to record, may be {@code null}. + * @return This result for chaining, never {@code null}. + */ + public CollectResult addCycle( DependencyCycle cycle ) + { + if ( cycle != null ) + { + if ( cycles.isEmpty() ) + { + cycles = new ArrayList<DependencyCycle>(); + } + cycles.add( cycle ); + } + return this; + } + + /** + * Gets the root node of the dependency graph. + * + * @return The root node of the dependency graph or {@code null} if none. + */ + public DependencyNode getRoot() + { + return root; + } + + /** + * Sets the root node of the dependency graph. + * + * @param root The root node of the dependency graph, may be {@code null}. + * @return This result for chaining, never {@code null}. + */ + public CollectResult setRoot( DependencyNode root ) + { + this.root = root; + return this; + } + + @Override + public String toString() + { + return String.valueOf( getRoot() ); + } + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionContext.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionContext.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionContext.java new file mode 100644 index 0000000..671bd2a --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionContext.java @@ -0,0 +1,75 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.util.List; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.graph.Dependency; + +/** + * A context used during dependency collection to update the dependency manager, selector and traverser. + * + * @see DependencyManager#deriveChildManager(DependencyCollectionContext) + * @see DependencyTraverser#deriveChildTraverser(DependencyCollectionContext) + * @see DependencySelector#deriveChildSelector(DependencyCollectionContext) + * @see VersionFilter#deriveChildFilter(DependencyCollectionContext) + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface DependencyCollectionContext +{ + + /** + * Gets the repository system session during which the dependency collection happens. + * + * @return The repository system session, never {@code null}. + */ + RepositorySystemSession getSession(); + + /** + * Gets the artifact whose children are to be processed next during dependency collection. For all nodes but the + * root, this is simply shorthand for {@code getDependency().getArtifact()}. In case of the root node however, + * {@link #getDependency()} might be {@code null} while the node still has an artifact which serves as its label and + * is not to be resolved. + * + * @return The artifact whose children are going to be processed or {@code null} in case of the root node without + * dependency and label. + */ + Artifact getArtifact(); + + /** + * Gets the dependency whose children are to be processed next during dependency collection. + * + * @return The dependency whose children are going to be processed or {@code null} in case of the root node without + * dependency. + */ + Dependency getDependency(); + + /** + * Gets the dependency management information that was contributed by the artifact descriptor of the current + * dependency. + * + * @return The dependency management information, never {@code null}. + */ + List<Dependency> getManagedDependencies(); + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionException.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionException.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionException.java new file mode 100644 index 0000000..8a04d79 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyCollectionException.java @@ -0,0 +1,111 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.eclipse.aether.RepositoryException; + +/** + * Thrown in case of bad artifact descriptors, version ranges or other issues encountered during calculation of the + * dependency graph. + */ +public class DependencyCollectionException + extends RepositoryException +{ + + private final transient CollectResult result; + + /** + * Creates a new exception with the specified result. + * + * @param result The collection result at the point the exception occurred, may be {@code null}. + */ + public DependencyCollectionException( CollectResult result ) + { + super( "Failed to collect dependencies for " + getSource( result ), getCause( result ) ); + this.result = result; + } + + /** + * Creates a new exception with the specified result and detail message. + * + * @param result The collection result at the point the exception occurred, may be {@code null}. + * @param message The detail message, may be {@code null}. + */ + public DependencyCollectionException( CollectResult result, String message ) + { + super( message, getCause( result ) ); + this.result = result; + } + + /** + * Creates a new exception with the specified result, detail message and cause. + * + * @param result The collection result at the point the exception occurred, may be {@code null}. + * @param message The detail message, may be {@code null}. + * @param cause The exception that caused this one, may be {@code null}. + */ + public DependencyCollectionException( CollectResult result, String message, Throwable cause ) + { + super( message, cause ); + this.result = result; + } + + /** + * Gets the collection result at the point the exception occurred. Despite being incomplete, callers might want to + * use this result to fail gracefully and continue their operation with whatever interim data has been gathered. + * + * @return The collection result or {@code null} if unknown. + */ + public CollectResult getResult() + { + return result; + } + + private static String getSource( CollectResult result ) + { + if ( result == null ) + { + return ""; + } + + CollectRequest request = result.getRequest(); + if ( request.getRoot() != null ) + { + return request.getRoot().toString(); + } + if ( request.getRootArtifact() != null ) + { + return request.getRootArtifact().toString(); + } + + return request.getDependencies().toString(); + } + + private static Throwable getCause( CollectResult result ) + { + Throwable cause = null; + if ( result != null && !result.getExceptions().isEmpty() ) + { + cause = result.getExceptions().get( 0 ); + } + return cause; + } + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformationContext.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformationContext.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformationContext.java new file mode 100644 index 0000000..ba66474 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformationContext.java @@ -0,0 +1,58 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.eclipse.aether.RepositorySystemSession; + +/** + * A context used during dependency collection to exchange information within a chain of dependency graph transformers. + * + * @see DependencyGraphTransformer + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface DependencyGraphTransformationContext +{ + + /** + * Gets the repository system session during which the graph transformation happens. + * + * @return The repository system session, never {@code null}. + */ + RepositorySystemSession getSession(); + + /** + * Gets a keyed value from the context. + * + * @param key The key used to query the value, must not be {@code null}. + * @return The queried value or {@code null} if none. + */ + Object get( Object key ); + + /** + * Puts a keyed value into the context. + * + * @param key The key used to store the value, must not be {@code null}. + * @param value The value to store, may be {@code null} to remove the mapping. + * @return The previous value associated with the key or {@code null} if none. + */ + Object put( Object key, Object value ); + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformer.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformer.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformer.java new file mode 100644 index 0000000..c472500 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyGraphTransformer.java @@ -0,0 +1,51 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.eclipse.aether.RepositoryException; +import org.eclipse.aether.graph.DependencyNode; + +/** + * Transforms a given dependency graph. + * <p> + * <strong>Note:</strong> Implementations must be stateless. + * <p> + * <em>Warning:</em> Dependency graphs may generally contain cycles. As such a graph transformer that cannot assume for + * sure that cycles have already been eliminated must gracefully handle cyclic graphs, e.g. guard against infinite + * recursion. + * + * @see org.eclipse.aether.RepositorySystemSession#getDependencyGraphTransformer() + */ +public interface DependencyGraphTransformer +{ + + /** + * Transforms the dependency graph denoted by the specified root node. The transformer may directly change the + * provided input graph or create a new graph, the former is recommended for performance reasons. + * + * @param node The root node of the (possibly cyclic!) graph to transform, must not be {@code null}. + * @param context The graph transformation context, must not be {@code null}. + * @return The result graph of the transformation, never {@code null}. + * @throws RepositoryException If the transformation failed. + */ + DependencyNode transformGraph( DependencyNode node, DependencyGraphTransformationContext context ) + throws RepositoryException; + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java new file mode 100644 index 0000000..054bfe0 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java @@ -0,0 +1,177 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.util.Collection; +import java.util.Map; + +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.Exclusion; + +/** + * The management updates to apply to a dependency. + * + * @see DependencyManager#manageDependency(Dependency) + */ +public final class DependencyManagement +{ + + private String version; + + private String scope; + + private Boolean optional; + + private Collection<Exclusion> exclusions; + + private Map<String, String> properties; + + /** + * Creates an empty management update. + */ + public DependencyManagement() + { + // enables default constructor + } + + /** + * Gets the new version to apply to the dependency. + * + * @return The new version or {@code null} if the version is not managed and the existing dependency version should + * remain unchanged. + */ + public String getVersion() + { + return version; + } + + /** + * Sets the new version to apply to the dependency. + * + * @param version The new version, may be {@code null} if the version is not managed. + * @return This management update for chaining, never {@code null}. + */ + public DependencyManagement setVersion( String version ) + { + this.version = version; + return this; + } + + /** + * Gets the new scope to apply to the dependency. + * + * @return The new scope or {@code null} if the scope is not managed and the existing dependency scope should remain + * unchanged. + */ + public String getScope() + { + return scope; + } + + /** + * Sets the new scope to apply to the dependency. + * + * @param scope The new scope, may be {@code null} if the scope is not managed. + * @return This management update for chaining, never {@code null}. + */ + public DependencyManagement setScope( String scope ) + { + this.scope = scope; + return this; + } + + /** + * Gets the new optional flag to apply to the dependency. + * + * @return The new optional flag or {@code null} if the flag is not managed and the existing optional flag of the + * dependency should remain unchanged. + */ + public Boolean getOptional() + { + return optional; + } + + /** + * Sets the new optional flag to apply to the dependency. + * + * @param optional The optional flag, may be {@code null} if the flag is not managed. + * @return This management update for chaining, never {@code null}. + */ + public DependencyManagement setOptional( Boolean optional ) + { + this.optional = optional; + return this; + } + + /** + * Gets the new exclusions to apply to the dependency. Note that this collection denotes the complete set of + * exclusions for the dependency, i.e. the dependency manager controls whether any existing exclusions get merged + * with information from dependency management or overridden by it. + * + * @return The new exclusions or {@code null} if the exclusions are not managed and the existing dependency + * exclusions should remain unchanged. + */ + public Collection<Exclusion> getExclusions() + { + return exclusions; + } + + /** + * Sets the new exclusions to apply to the dependency. Note that this collection denotes the complete set of + * exclusions for the dependency, i.e. the dependency manager controls whether any existing exclusions get merged + * with information from dependency management or overridden by it. + * + * @param exclusions The new exclusions, may be {@code null} if the exclusions are not managed. + * @return This management update for chaining, never {@code null}. + */ + public DependencyManagement setExclusions( Collection<Exclusion> exclusions ) + { + this.exclusions = exclusions; + return this; + } + + /** + * Gets the new properties to apply to the dependency. Note that this map denotes the complete set of properties, + * i.e. the dependency manager controls whether any existing properties get merged with the information from + * dependency management or overridden by it. + * + * @return The new artifact properties or {@code null} if the properties are not managed and the existing properties + * should remain unchanged. + */ + public Map<String, String> getProperties() + { + return properties; + } + + /** + * Sets the new properties to apply to the dependency. Note that this map denotes the complete set of properties, + * i.e. the dependency manager controls whether any existing properties get merged with the information from + * dependency management or overridden by it. + * + * @param properties The new artifact properties, may be {@code null} if the properties are not managed. + * @return This management update for chaining, never {@code null}. + */ + public DependencyManagement setProperties( Map<String, String> properties ) + { + this.properties = properties; + return this; + } + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManager.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManager.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManager.java new file mode 100644 index 0000000..993e388 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManager.java @@ -0,0 +1,57 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.eclipse.aether.graph.Dependency; + +/** + * Applies dependency management to the dependencies of a dependency node. + * <p> + * <strong>Note:</strong> Implementations must be stateless. + * <p> + * <em>Warning:</em> This hook is called from a hot spot and therefore implementations should pay attention to + * performance. Among others, implementations should provide a semantic {@link Object#equals(Object) equals()} method. + * + * @see org.eclipse.aether.RepositorySystemSession#getDependencyManager() + * @see org.eclipse.aether.RepositorySystem#collectDependencies(org.eclipse.aether.RepositorySystemSession, + * CollectRequest) + */ +public interface DependencyManager +{ + + /** + * Applies dependency management to the specified dependency. + * + * @param dependency The dependency to manage, must not be {@code null}. + * @return The management update to apply to the dependency or {@code null} if the dependency is not managed at all. + */ + DependencyManagement manageDependency( Dependency dependency ); + + /** + * Derives a dependency manager for the specified collection context. When calculating the child manager, + * implementors are strongly advised to simply return the current instance if nothing changed to help save memory. + * + * @param context The dependency collection context, must not be {@code null}. + * @return The dependency manager for the dependencies of the target node or {@code null} if dependency management + * should no longer be applied. + */ + DependencyManager deriveChildManager( DependencyCollectionContext context ); + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencySelector.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencySelector.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencySelector.java new file mode 100644 index 0000000..b257ffa --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencySelector.java @@ -0,0 +1,58 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.eclipse.aether.graph.Dependency; + +/** + * Decides what dependencies to include in the dependency graph. + * <p> + * <strong>Note:</strong> Implementations must be stateless. + * <p> + * <em>Warning:</em> This hook is called from a hot spot and therefore implementations should pay attention to + * performance. Among others, implementations should provide a semantic {@link Object#equals(Object) equals()} method. + * + * @see org.eclipse.aether.RepositorySystemSession#getDependencySelector() + * @see org.eclipse.aether.RepositorySystem#collectDependencies(org.eclipse.aether.RepositorySystemSession, + * CollectRequest) + */ +public interface DependencySelector +{ + + /** + * Decides whether the specified dependency should be included in the dependency graph. + * + * @param dependency The dependency to check, must not be {@code null}. + * @return {@code false} if the dependency should be excluded from the children of the current node, {@code true} + * otherwise. + */ + boolean selectDependency( Dependency dependency ); + + /** + * Derives a dependency selector for the specified collection context. When calculating the child selector, + * implementors are strongly advised to simply return the current instance if nothing changed to help save memory. + * + * @param context The dependency collection context, must not be {@code null}. + * @return The dependency selector for the target node or {@code null} if dependencies should be unconditionally + * included in the sub graph. + */ + DependencySelector deriveChildSelector( DependencyCollectionContext context ); + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyTraverser.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyTraverser.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyTraverser.java new file mode 100644 index 0000000..be1887b --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyTraverser.java @@ -0,0 +1,59 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.eclipse.aether.graph.Dependency; + +/** + * Decides whether the dependencies of a dependency node should be traversed as well. + * <p> + * <strong>Note:</strong> Implementations must be stateless. + * <p> + * <em>Warning:</em> This hook is called from a hot spot and therefore implementations should pay attention to + * performance. Among others, implementations should provide a semantic {@link Object#equals(Object) equals()} method. + * + * @see org.eclipse.aether.RepositorySystemSession#getDependencyTraverser() + * @see org.eclipse.aether.RepositorySystem#collectDependencies(org.eclipse.aether.RepositorySystemSession, + * CollectRequest) + */ +public interface DependencyTraverser +{ + + /** + * Decides whether the dependencies of the specified dependency should be traversed. + * + * @param dependency The dependency to check, must not be {@code null}. + * @return {@code true} if the dependency graph builder should recurse into the specified dependency and process its + * dependencies, {@code false} otherwise. + */ + boolean traverseDependency( Dependency dependency ); + + /** + * Derives a dependency traverser that will be used to decide whether the transitive dependencies of the dependency + * given in the collection context shall be traversed. When calculating the child traverser, implementors are + * strongly advised to simply return the current instance if nothing changed to help save memory. + * + * @param context The dependency collection context, must not be {@code null}. + * @return The dependency traverser for the target node or {@code null} if dependencies should be unconditionally + * traversed in the sub graph. + */ + DependencyTraverser deriveChildTraverser( DependencyCollectionContext context ); + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/UnsolvableVersionConflictException.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/UnsolvableVersionConflictException.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/UnsolvableVersionConflictException.java new file mode 100644 index 0000000..54a7004 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/UnsolvableVersionConflictException.java @@ -0,0 +1,142 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; + +import org.eclipse.aether.RepositoryException; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.version.VersionConstraint; + +/** + * Thrown in case of an unsolvable conflict between different version constraints for a dependency. + */ +public class UnsolvableVersionConflictException + extends RepositoryException +{ + + private final transient Collection<String> versions; + + private final transient Collection<? extends List<? extends DependencyNode>> paths; + + /** + * Creates a new exception with the specified paths to conflicting nodes in the dependency graph. + * + * @param paths The paths to the dependency nodes that participate in the version conflict, may be {@code null}. + */ + public UnsolvableVersionConflictException( Collection<? extends List<? extends DependencyNode>> paths ) + { + super( "Could not resolve version conflict among " + toPaths( paths ) ); + if ( paths == null ) + { + this.paths = Collections.emptyList(); + this.versions = Collections.emptyList(); + } + else + { + this.paths = paths; + this.versions = new LinkedHashSet<String>(); + for ( List<? extends DependencyNode> path : paths ) + { + VersionConstraint constraint = path.get( path.size() - 1 ).getVersionConstraint(); + if ( constraint != null && constraint.getRange() != null ) + { + versions.add( constraint.toString() ); + } + } + } + } + + private static String toPaths( Collection<? extends List<? extends DependencyNode>> paths ) + { + String result = ""; + + if ( paths != null ) + { + Collection<String> strings = new LinkedHashSet<String>(); + + for ( List<? extends DependencyNode> path : paths ) + { + strings.add( toPath( path ) ); + } + + result = strings.toString(); + } + + return result; + } + + private static String toPath( List<? extends DependencyNode> path ) + { + StringBuilder buffer = new StringBuilder( 256 ); + + for ( Iterator<? extends DependencyNode> it = path.iterator(); it.hasNext(); ) + { + DependencyNode node = it.next(); + if ( node.getDependency() == null ) + { + continue; + } + + Artifact artifact = node.getDependency().getArtifact(); + buffer.append( artifact.getGroupId() ); + buffer.append( ':' ).append( artifact.getArtifactId() ); + buffer.append( ':' ).append( artifact.getExtension() ); + if ( artifact.getClassifier().length() > 0 ) + { + buffer.append( ':' ).append( artifact.getClassifier() ); + } + buffer.append( ':' ).append( node.getVersionConstraint() ); + + if ( it.hasNext() ) + { + buffer.append( " -> " ); + } + } + + return buffer.toString(); + } + + /** + * Gets the paths leading to the conflicting dependencies. + * + * @return The (read-only) paths leading to the conflicting dependencies, never {@code null}. + */ + public Collection<? extends List<? extends DependencyNode>> getPaths() + { + return paths; + } + + /** + * Gets the conflicting version constraints of the dependency. + * + * @return The (read-only) conflicting version constraints, never {@code null}. + */ + public Collection<String> getVersions() + { + return versions; + } + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/3a1b8ae0/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilter.java ---------------------------------------------------------------------- diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilter.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilter.java new file mode 100644 index 0000000..fb36747 --- /dev/null +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilter.java @@ -0,0 +1,135 @@ +package org.eclipse.aether.collection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 java.util.Iterator; +import java.util.List; + +import org.eclipse.aether.RepositoryException; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.repository.ArtifactRepository; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.version.Version; +import org.eclipse.aether.version.VersionConstraint; + +/** + * Decides which versions matching a version range should actually be considered for the dependency graph. The version + * filter is not invoked for dependencies that do not declare a version range but a single version. + * <p> + * <strong>Note:</strong> Implementations must be stateless. + * <p> + * <em>Warning:</em> This hook is called from a hot spot and therefore implementations should pay attention to + * performance. Among others, implementations should provide a semantic {@link Object#equals(Object) equals()} method. + * + * @see org.eclipse.aether.RepositorySystemSession#getVersionFilter() + * @see org.eclipse.aether.RepositorySystem#collectDependencies(org.eclipse.aether.RepositorySystemSession, + * CollectRequest) + */ +public interface VersionFilter +{ + + /** + * A context used during version filtering to hold relevant data. + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ + interface VersionFilterContext + extends Iterable<Version> + { + + /** + * Gets the repository system session during which the version filtering happens. + * + * @return The repository system session, never {@code null}. + */ + RepositorySystemSession getSession(); + + /** + * Gets the dependency whose version range is being filtered. + * + * @return The dependency, never {@code null}. + */ + Dependency getDependency(); + + /** + * Gets the total number of available versions. This count reflects any removals made during version filtering. + * + * @return The total number of available versions. + */ + int getCount(); + + /** + * Gets an iterator over the available versions of the dependency. The iterator returns versions in ascending + * order. Use {@link Iterator#remove()} to exclude a version from further consideration in the dependency graph. + * + * @return The iterator of available versions, never {@code null}. + */ + Iterator<Version> iterator(); + + /** + * Gets the version constraint that was parsed from the dependency's version string. + * + * @return The parsed version constraint, never {@code null}. + */ + VersionConstraint getVersionConstraint(); + + /** + * Gets the repository from which the specified version was resolved. + * + * @param version The version whose source repository should be retrieved, must not be {@code null}. + * @return The repository from which the version was resolved or {@code null} if unknown. + */ + ArtifactRepository getRepository( Version version ); + + /** + * Gets the remote repositories from which the versions were resolved. + * + * @return The (read-only) list of repositories, never {@code null}. + */ + List<RemoteRepository> getRepositories(); + + } + + /** + * Filters the available versions for a given dependency. Implementations will usually call + * {@link VersionFilterContext#iterator() context.iterator()} to inspect the available versions and use + * {@link java.util.Iterator#remove()} to delete unacceptable versions. If no versions remain after all filtering + * has been performed, the dependency collection process will automatically fail, i.e. implementations need not + * handle this situation on their own. + * + * @param context The version filter context, must not be {@code null}. + * @throws RepositoryException If the filtering could not be performed. + */ + void filterVersions( VersionFilterContext context ) + throws RepositoryException; + + /** + * Derives a version filter for the specified collection context. The derived filter will be used to handle version + * ranges encountered in child dependencies of the current node. When calculating the child filter, implementors are + * strongly advised to simply return the current instance if nothing changed to help save memory. + * + * @param context The dependency collection context, must not be {@code null}. + * @return The version filter for the target node or {@code null} if versions should not be filtered any more. + */ + VersionFilter deriveChildFilter( DependencyCollectionContext context ); + +}