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