001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.resolution;
020
021import java.util.ArrayList;
022import java.util.List;
023import java.util.Map;
024import java.util.concurrent.ConcurrentHashMap;
025import java.util.concurrent.CopyOnWriteArrayList;
026
027import org.eclipse.aether.RepositorySystem;
028import org.eclipse.aether.artifact.Artifact;
029import org.eclipse.aether.repository.ArtifactRepository;
030import org.eclipse.aether.repository.LocalArtifactResult;
031import org.eclipse.aether.transfer.ArtifactNotFoundException;
032
033import static java.util.Objects.requireNonNull;
034
035/**
036 * The result of an artifact resolution request.
037 *
038 * @see RepositorySystem#resolveArtifacts(org.eclipse.aether.RepositorySystemSession, java.util.Collection)
039 * @see Artifact#getPath()
040 */
041public final class ArtifactResult {
042
043    /**
044     * A sentinel object, that is used as key for exceptions that had no related repository during resolution.
045     *
046     * @since 2.0.0
047     */
048    public static final ArtifactRepository NO_REPOSITORY = new NoRepository();
049
050    private static final class NoRepository implements ArtifactRepository {
051
052        private NoRepository() {}
053
054        public String getContentType() {
055            return "unknown";
056        }
057
058        public String getId() {
059            return "unknown";
060        }
061
062        @Override
063        public String toString() {
064            return getId();
065        }
066    }
067
068    private final ArtifactRequest request;
069
070    private final Map<ArtifactRepository, List<Exception>> exceptions;
071
072    private Artifact artifact;
073
074    private ArtifactRepository repository;
075
076    private LocalArtifactResult localArtifactResult;
077
078    /**
079     * Creates a new result for the specified request.
080     *
081     * @param request The resolution request, must not be {@code null}.
082     */
083    public ArtifactResult(ArtifactRequest request) {
084        this.request = requireNonNull(request, "artifact request cannot be null");
085        this.exceptions = new ConcurrentHashMap<>();
086    }
087
088    /**
089     * Gets the resolution request that was made.
090     *
091     * @return The resolution request, never {@code null}.
092     */
093    public ArtifactRequest getRequest() {
094        return request;
095    }
096
097    /**
098     * Gets the resolved artifact (if any). Use {@link #getExceptions()} to query the errors that occurred while trying
099     * to resolve the artifact.
100     *
101     * @return The resolved artifact or {@code null} if the resolution failed.
102     */
103    public Artifact getArtifact() {
104        return artifact;
105    }
106
107    /**
108     * Sets the resolved artifact.
109     *
110     * @param artifact The resolved artifact, may be {@code null} if the resolution failed.
111     * @return This result for chaining, never {@code null}.
112     */
113    public ArtifactResult setArtifact(Artifact artifact) {
114        this.artifact = artifact;
115        return this;
116    }
117
118    /**
119     * Gets the exceptions that occurred while resolving the artifact. Note that this list can be non-empty even if the
120     * artifact was successfully resolved, e.g. when one of the contacted remote repositories didn't contain the
121     * artifact but a later repository eventually contained it.
122     *
123     * @return The exceptions that occurred, never {@code null}.
124     * @see #isResolved()
125     * @see #isMissing()
126     */
127    public List<Exception> getExceptions() {
128        ArrayList<Exception> result = new ArrayList<>();
129        exceptions.values().forEach(result::addAll);
130        return result;
131    }
132
133    /**
134     * Gets the exceptions that occurred while resolving the artifact. Note that this map can be non-empty even if the
135     * artifact was successfully resolved, e.g. when one of the contacted remote repositories didn't contain the
136     * artifact but a later repository eventually contained it.
137     *
138     * @return Map of exceptions per repository, that occurred during resolution, never {@code null}.
139     * @see #isResolved()
140     * @see #isMissing()
141     * @since 2.0.0
142     */
143    public Map<ArtifactRepository, List<Exception>> getMappedExceptions() {
144        return exceptions;
145    }
146
147    /**
148     * Records the specified exception while resolving the artifact.
149     *
150     * @param exception The exception to record, may be {@code null}.
151     * @return This result for chaining, never {@code null}.
152     * @deprecated Use {@link #addException(ArtifactRepository, Exception)} method instead.
153     */
154    @Deprecated
155    public ArtifactResult addException(Exception exception) {
156        return addException(NO_REPOSITORY, exception);
157    }
158
159    /**
160     * Records the specified exception while resolving the artifact.
161     *
162     * @param exception The exception to record, may be {@code null}.
163     * @return This result for chaining, never {@code null}.
164     * @since 2.0.0
165     */
166    public ArtifactResult addException(ArtifactRepository repository, Exception exception) {
167        if (repository != null && exception != null) {
168            exceptions
169                    .computeIfAbsent(repository, k -> new CopyOnWriteArrayList<>())
170                    .add(exception);
171        }
172        return this;
173    }
174
175    /**
176     * Gets the repository from which the artifact was eventually resolved. Note that successive resolutions of the same
177     * artifact might yield different results if the employed local repository does not track the origin of an artifact.
178     *
179     * @return The repository from which the artifact was resolved or {@code null} if unknown.
180     */
181    public ArtifactRepository getRepository() {
182        return repository;
183    }
184
185    /**
186     * Sets the repository from which the artifact was resolved.
187     *
188     * @param repository The repository from which the artifact was resolved, may be {@code null}.
189     * @return This result for chaining, never {@code null}.
190     */
191    public ArtifactResult setRepository(ArtifactRepository repository) {
192        this.repository = repository;
193        return this;
194    }
195
196    /**
197     * Gets the {@link LocalArtifactResult} received during artifact resolution.
198     *
199     * @return The {@link LocalArtifactResult} or {@code null}.
200     * @since 1.9.6
201     */
202    public LocalArtifactResult getLocalArtifactResult() {
203        return localArtifactResult;
204    }
205
206    /**
207     * Sets the {@link LocalArtifactResult} that is received during artifact resolution.
208     *
209     * @param localArtifactResult The local artifact result.
210     * @since 1.9.6
211     */
212    public void setLocalArtifactResult(LocalArtifactResult localArtifactResult) {
213        this.localArtifactResult = localArtifactResult;
214    }
215
216    /**
217     * Indicates whether the requested artifact was resolved. Note that the artifact might have been successfully
218     * resolved despite {@link #getExceptions()} indicating transfer errors while trying to fetch the artifact from some
219     * of the specified remote repositories.
220     *
221     * @return {@code true} if the artifact was resolved, {@code false} otherwise.
222     * @see Artifact#getPath()
223     */
224    public boolean isResolved() {
225        return getArtifact() != null && getArtifact().getPath() != null;
226    }
227
228    /**
229     * Indicates whether the requested artifact is not present in any of the specified repositories.
230     *
231     * @return {@code true} if the artifact is not present in any repository, {@code false} otherwise.
232     */
233    public boolean isMissing() {
234        for (Exception e : getExceptions()) {
235            if (!(e instanceof ArtifactNotFoundException)) {
236                return false;
237            }
238        }
239        return !isResolved();
240    }
241
242    @Override
243    public String toString() {
244        return getArtifact() + " < " + getRepository();
245    }
246}