1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.eclipse.aether.resolution;
20
21 import java.util.*;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.CopyOnWriteArrayList;
24
25 import org.eclipse.aether.RepositorySystem;
26 import org.eclipse.aether.artifact.Artifact;
27 import org.eclipse.aether.repository.ArtifactRepository;
28 import org.eclipse.aether.repository.LocalArtifactResult;
29 import org.eclipse.aether.transfer.ArtifactNotFoundException;
30
31 import static java.util.Objects.requireNonNull;
32
33 /**
34 * The result of an artifact resolution request.
35 *
36 * @see RepositorySystem#resolveArtifacts(org.eclipse.aether.RepositorySystemSession, java.util.Collection)
37 * @see Artifact#getPath()
38 */
39 public final class ArtifactResult {
40
41 /**
42 * A sentinel object, that is used as key for exceptions that had no related repository during resolution.
43 *
44 * @since 2.0.0
45 */
46 public static final ArtifactRepository NO_REPOSITORY = new NoRepository();
47
48 private static final class NoRepository implements ArtifactRepository {
49
50 private NoRepository() {}
51
52 public String getContentType() {
53 return "unknown";
54 }
55
56 public String getId() {
57 return "unknown";
58 }
59
60 @Override
61 public String toString() {
62 return getId();
63 }
64 }
65
66 private final ArtifactRequest request;
67
68 private final Map<ArtifactRepository, List<Exception>> exceptions;
69
70 private Artifact artifact;
71
72 private ArtifactRepository repository;
73
74 private LocalArtifactResult localArtifactResult;
75
76 /**
77 * Creates a new result for the specified request.
78 *
79 * @param request The resolution request, must not be {@code null}.
80 */
81 public ArtifactResult(ArtifactRequest request) {
82 this.request = requireNonNull(request, "artifact request cannot be null");
83 this.exceptions = new ConcurrentHashMap<>();
84 }
85
86 /**
87 * Gets the resolution request that was made.
88 *
89 * @return The resolution request, never {@code null}.
90 */
91 public ArtifactRequest getRequest() {
92 return request;
93 }
94
95 /**
96 * Gets the resolved artifact (if any). Use {@link #getExceptions()} to query the errors that occurred while trying
97 * to resolve the artifact.
98 *
99 * @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 }