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.transfer.ArtifactNotFoundException;
026import org.eclipse.aether.transfer.RepositoryOfflineException;
027
028/**
029 * Thrown in case of a unresolvable artifacts.
030 */
031public class ArtifactResolutionException extends RepositoryException {
032
033    private final transient List<ArtifactResult> results;
034
035    /**
036     * Creates a new exception with the specified results.
037     *
038     * @param results The resolution results at the point the exception occurred, may be {@code null}.
039     */
040    public ArtifactResolutionException(List<ArtifactResult> results) {
041        super(getMessage(results), getCause(results));
042        this.results = (results != null) ? results : Collections.<ArtifactResult>emptyList();
043    }
044
045    /**
046     * Creates a new exception with the specified results and detail message.
047     *
048     * @param results The resolution results at the point the exception occurred, may be {@code null}.
049     * @param message The detail message, may be {@code null}.
050     */
051    public ArtifactResolutionException(List<ArtifactResult> results, String message) {
052        super(message, getCause(results));
053        this.results = (results != null) ? results : Collections.<ArtifactResult>emptyList();
054    }
055
056    /**
057     * Creates a new exception with the specified results, detail message and cause.
058     *
059     * @param results The resolution results at the point the exception occurred, may be {@code null}.
060     * @param message The detail message, may be {@code null}.
061     * @param cause The exception that caused this one, may be {@code null}.
062     */
063    public ArtifactResolutionException(List<ArtifactResult> results, String message, Throwable cause) {
064        super(message, cause);
065        this.results = (results != null) ? results : Collections.<ArtifactResult>emptyList();
066    }
067
068    /**
069     * Gets the resolution results at the point the exception occurred. Despite being incomplete, callers might want to
070     * use these results to fail gracefully and continue their operation with whatever interim data has been gathered.
071     *
072     * @return The resolution results or {@code null} if unknown.
073     */
074    public List<ArtifactResult> getResults() {
075        return results;
076    }
077
078    /**
079     * Gets the first result from {@link #getResults()}. This is a convenience method for cases where callers know only
080     * a single result/request is involved.
081     *
082     * @return The (first) resolution result or {@code null} if none.
083     */
084    public ArtifactResult getResult() {
085        return (results != null && !results.isEmpty()) ? results.get(0) : null;
086    }
087
088    private static String getMessage(List<? extends ArtifactResult> results) {
089        StringBuilder buffer = new StringBuilder(256);
090
091        buffer.append("The following artifacts could not be resolved: ");
092
093        int unresolved = 0;
094
095        String sep = "";
096        for (ArtifactResult result : results) {
097            if (!result.isResolved()) {
098                unresolved++;
099
100                buffer.append(sep);
101                buffer.append(result.getRequest().getArtifact());
102                sep = ", ";
103            }
104        }
105
106        Throwable cause = getCause(results);
107        if (cause != null) {
108            if (unresolved == 1) {
109                buffer.setLength(0);
110                buffer.append(cause.getMessage());
111            } else {
112                buffer.append(": ").append(cause.getMessage());
113            }
114        }
115
116        return buffer.toString();
117    }
118
119    private static Throwable getCause(List<? extends ArtifactResult> results) {
120        for (ArtifactResult result : results) {
121            if (!result.isResolved()) {
122                Throwable notFound = null, offline = null;
123                for (Throwable t : result.getExceptions()) {
124                    if (t instanceof ArtifactNotFoundException) {
125                        if (notFound == null) {
126                            notFound = t;
127                        }
128                        if (offline == null && t.getCause() instanceof RepositoryOfflineException) {
129                            offline = t;
130                        }
131                    } else {
132                        return t;
133                    }
134                }
135                if (offline != null) {
136                    return offline;
137                }
138                if (notFound != null) {
139                    return notFound;
140                }
141            }
142        }
143        return null;
144    }
145}