View Javadoc
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.Collections;
22  import java.util.List;
23  
24  import org.eclipse.aether.RepositoryException;
25  import org.eclipse.aether.repository.LocalArtifactResult;
26  import org.eclipse.aether.transfer.ArtifactFilteredOutException;
27  import org.eclipse.aether.transfer.ArtifactNotFoundException;
28  import org.eclipse.aether.transfer.RepositoryOfflineException;
29  
30  /**
31   * Thrown in case of a unresolvable artifacts.
32   */
33  public class ArtifactResolutionException extends RepositoryException {
34      private final transient List<ArtifactResult> results;
35  
36      /**
37       * Creates a new exception with the specified results.
38       *
39       * @param results The resolution results at the point the exception occurred, may be {@code null}.
40       */
41      public ArtifactResolutionException(List<ArtifactResult> results) {
42          super(getMessage(results), getCause(results));
43          this.results = results != null ? results : Collections.emptyList();
44      }
45  
46      /**
47       * Creates a new exception with the specified results and detail message.
48       *
49       * @param results The resolution results at the point the exception occurred, may be {@code null}.
50       * @param message The detail message, may be {@code null}.
51       */
52      public ArtifactResolutionException(List<ArtifactResult> results, String message) {
53          super(message, getCause(results));
54          this.results = results != null ? results : Collections.emptyList();
55      }
56  
57      /**
58       * Creates a new exception with the specified results, detail message and cause.
59       *
60       * @param results The resolution results at the point the exception occurred, may be {@code null}.
61       * @param message The detail message, may be {@code null}.
62       * @param cause The exception that caused this one, may be {@code null}.
63       */
64      public ArtifactResolutionException(List<ArtifactResult> results, String message, Throwable cause) {
65          super(message, cause);
66          this.results = results != null ? results : Collections.emptyList();
67      }
68  
69      /**
70       * Gets the resolution results at the point the exception occurred. Despite being incomplete, callers might want to
71       * use these results to fail gracefully and continue their operation with whatever interim data has been gathered.
72       *
73       * @return The resolution results, never {@code null} (empty if unknown).
74       */
75      public List<ArtifactResult> getResults() {
76          return results;
77      }
78  
79      /**
80       * Gets the first result from {@link #getResults()}. This is a convenience method for cases where callers know only
81       * a single result/request is involved.
82       *
83       * @return The (first) resolution result or {@code null} if none.
84       */
85      public ArtifactResult getResult() {
86          return (results != null && !results.isEmpty()) ? results.get(0) : null;
87      }
88  
89      private static String getMessage(List<? extends ArtifactResult> results) {
90          if (results == null) {
91              return null;
92          }
93          StringBuilder buffer = new StringBuilder(256);
94  
95          buffer.append("The following artifacts could not be resolved: ");
96  
97          String sep = "";
98          for (ArtifactResult result : results) {
99              if (!result.isResolved()) {
100                 buffer.append(sep);
101                 buffer.append(result.getRequest().getArtifact());
102                 LocalArtifactResult localResult = result.getLocalArtifactResult();
103                 if (localResult != null) {
104                     buffer.append(" (");
105                     if (localResult.getFile() != null) {
106                         buffer.append("present");
107                         if (!localResult.isAvailable()) {
108                             buffer.append(", but unavailable");
109                         }
110                     } else {
111                         buffer.append("absent");
112                     }
113                     buffer.append(")");
114                 }
115                 sep = ", ";
116             }
117         }
118 
119         Throwable cause = getCause(results);
120         if (cause != null) {
121             buffer.append(": ").append(cause.getMessage());
122         }
123 
124         return buffer.toString();
125     }
126 
127     /**
128      * This method tries to be smart and figure out "cause", but it results in somewhat incomplete result. Maven Core
129      * and probably many other code relies on it, so is left in place, but client code should use {@link #getResults()}
130      * and {@link ArtifactResult#getMappedExceptions()} methods to build more appropriate error messages.
131      */
132     private static Throwable getCause(List<? extends ArtifactResult> results) {
133         if (results == null) {
134             return null;
135         }
136         for (ArtifactResult result : results) {
137             if (!result.isResolved()) {
138                 Throwable notFound = null, offline = null;
139                 for (Throwable t : result.getExceptions()) {
140                     if (t instanceof ArtifactNotFoundException) {
141                         if (notFound == null || notFound instanceof ArtifactFilteredOutException) {
142                             notFound = t;
143                         }
144                         if (offline == null && t.getCause() instanceof RepositoryOfflineException) {
145                             offline = t;
146                         }
147                     } else {
148                         return t;
149                     }
150                 }
151                 if (offline != null) {
152                     return offline;
153                 }
154                 if (notFound != null) {
155                     return notFound;
156                 }
157             }
158         }
159         return null;
160     }
161 }