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.Collections; 022import java.util.List; 023 024import org.eclipse.aether.RepositoryException; 025import org.eclipse.aether.repository.LocalArtifactResult; 026import org.eclipse.aether.transfer.ArtifactFilteredOutException; 027import org.eclipse.aether.transfer.ArtifactNotFoundException; 028import org.eclipse.aether.transfer.RepositoryOfflineException; 029 030/** 031 * Thrown in case of a unresolvable artifacts. 032 */ 033public class ArtifactResolutionException extends RepositoryException { 034 private final transient List<ArtifactResult> results; 035 036 /** 037 * Creates a new exception with the specified results. 038 * 039 * @param results The resolution results at the point the exception occurred, may be {@code null}. 040 */ 041 public ArtifactResolutionException(List<ArtifactResult> results) { 042 super(getMessage(results), getCause(results)); 043 this.results = results != null ? results : Collections.emptyList(); 044 } 045 046 /** 047 * Creates a new exception with the specified results and detail message. 048 * 049 * @param results The resolution results at the point the exception occurred, may be {@code null}. 050 * @param message The detail message, may be {@code null}. 051 */ 052 public ArtifactResolutionException(List<ArtifactResult> results, String message) { 053 super(message, getCause(results)); 054 this.results = results != null ? results : Collections.emptyList(); 055 } 056 057 /** 058 * Creates a new exception with the specified results, detail message and cause. 059 * 060 * @param results The resolution results at the point the exception occurred, may be {@code null}. 061 * @param message The detail message, may be {@code null}. 062 * @param cause The exception that caused this one, may be {@code null}. 063 */ 064 public ArtifactResolutionException(List<ArtifactResult> results, String message, Throwable cause) { 065 super(message, cause); 066 this.results = results != null ? results : Collections.emptyList(); 067 } 068 069 /** 070 * Gets the resolution results at the point the exception occurred. Despite being incomplete, callers might want to 071 * use these results to fail gracefully and continue their operation with whatever interim data has been gathered. 072 * 073 * @return The resolution results, never {@code null} (empty if unknown). 074 */ 075 public List<ArtifactResult> getResults() { 076 return results; 077 } 078 079 /** 080 * Gets the first result from {@link #getResults()}. This is a convenience method for cases where callers know only 081 * a single result/request is involved. 082 * 083 * @return The (first) resolution result or {@code null} if none. 084 */ 085 public ArtifactResult getResult() { 086 return (results != null && !results.isEmpty()) ? results.get(0) : null; 087 } 088 089 private static String getMessage(List<? extends ArtifactResult> results) { 090 if (results == null) { 091 return null; 092 } 093 StringBuilder buffer = new StringBuilder(256); 094 095 buffer.append("The following artifacts could not be resolved: "); 096 097 String sep = ""; 098 for (ArtifactResult result : results) { 099 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.getPath() != 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}