blob: f1d0afd3934731e79bfbba9fd051a51b4154871d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.exception;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.IOException;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.model.building.ModelProblemUtils;
import org.apache.maven.plugin.AbstractMojoExecutionException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.PluginContainerException;
import org.apache.maven.plugin.PluginExecutionException;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingResult;
/*
- test projects for each of these
- how to categorize the problems so that the id of the problem can be match to a page with descriptive help and the test
project
- nice little sample projects that could be run in the core as well as integration tests
All Possible Errors
- invalid lifecycle phase (maybe same as bad CLI param, though you were talking about embedder too)
- <module> specified is not found
- malformed settings
- malformed POM
- local repository not writable
- remote repositories not available
- artifact metadata missing
- extension metadata missing
- extension artifact missing
- artifact metadata retrieval problem
- version range violation
- circular dependency
- artifact missing
- artifact retrieval exception
- md5 checksum doesn't match for local artifact, need to redownload this
- POM doesn't exist for a goal that requires one
- parent POM missing (in both the repository + relative path)
- component not found
Plugins:
- plugin metadata missing
- plugin metadata retrieval problem
- plugin artifact missing
- plugin artifact retrieval problem
- plugin dependency metadata missing
- plugin dependency metadata retrieval problem
- plugin configuration problem
- plugin execution failure due to something that is know to possibly go wrong (like compilation failure)
- plugin execution error due to something that is not expected to go wrong (the compiler executable missing)
- asking to use a plugin for which you do not have a version defined - tools to easily select versions
- goal not found in a plugin (probably could list the ones that are)
*/
// PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
// CycleDetectedInPluginGraphException;
/**
* Transform an exception into useful end-user message.
*/
@Named
@Singleton
public class DefaultExceptionHandler implements ExceptionHandler {
public ExceptionSummary handleException(Throwable exception) {
return handle("", exception);
}
private ExceptionSummary handle(String message, Throwable exception) {
String reference = getReference(exception);
List<ExceptionSummary> children = null;
if (exception instanceof ProjectBuildingException) {
List<ProjectBuildingResult> results = ((ProjectBuildingException) exception).getResults();
children = new ArrayList<>();
for (ProjectBuildingResult result : results) {
ExceptionSummary child = handle(result);
if (child != null) {
children.add(child);
}
}
message = "The build could not read " + children.size() + " project" + (children.size() == 1 ? "" : "s");
} else {
message = getMessage(message, exception);
}
return new ExceptionSummary(exception, message, reference, children);
}
private ExceptionSummary handle(ProjectBuildingResult result) {
List<ExceptionSummary> children = new ArrayList<>();
for (ModelProblem problem : result.getProblems()) {
ExceptionSummary child = handle(problem, result.getProjectId());
if (child != null) {
children.add(child);
}
}
if (children.isEmpty()) {
return null;
}
String message = System.lineSeparator()
+ "The project " + (result.getProjectId().isEmpty() ? "" : result.getProjectId() + " ")
+ "(" + result.getPomFile() + ") has "
+ children.size() + " error" + (children.size() == 1 ? "" : "s");
return new ExceptionSummary(null, message, null, children);
}
private ExceptionSummary handle(ModelProblem problem, String projectId) {
if (ModelProblem.Severity.ERROR.compareTo(problem.getSeverity()) >= 0) {
String message = problem.getMessage();
String location = ModelProblemUtils.formatLocation(problem, projectId);
if (!location.isEmpty()) {
message += " @ " + location;
}
return handle(message, problem.getException());
} else {
return null;
}
}
private String getReference(Throwable exception) {
String reference = "";
if (exception != null) {
if (exception instanceof MojoExecutionException) {
reference = MojoExecutionException.class.getSimpleName();
Throwable cause = exception.getCause();
if (cause instanceof IOException) {
cause = cause.getCause();
if (cause instanceof ConnectException) {
reference = ConnectException.class.getSimpleName();
}
}
} else if (exception instanceof MojoFailureException) {
reference = MojoFailureException.class.getSimpleName();
} else if (exception instanceof LinkageError) {
reference = LinkageError.class.getSimpleName();
} else if (exception instanceof PluginExecutionException) {
Throwable cause = exception.getCause();
if (cause instanceof PluginContainerException) {
Throwable cause2 = cause.getCause();
if (cause2 instanceof NoClassDefFoundError) {
String message = cause2.getMessage();
if (message != null && message.contains("org/sonatype/aether/")) {
reference = "AetherClassNotFound";
}
}
}
if (reference == null || reference.isEmpty()) {
reference = getReference(cause);
}
if (reference == null || reference.isEmpty()) {
reference = exception.getClass().getSimpleName();
}
} else if (exception instanceof LifecycleExecutionException) {
reference = getReference(exception.getCause());
} else if (isNoteworthyException(exception)) {
reference = exception.getClass().getSimpleName();
}
}
if ((reference != null && !reference.isEmpty()) && !reference.startsWith("http:")) {
reference = "http://cwiki.apache.org/confluence/display/MAVEN/" + reference;
}
return reference;
}
private boolean isNoteworthyException(Throwable exception) {
if (exception == null) {
return false;
} else if (exception instanceof Error) {
return true;
} else if (exception instanceof RuntimeException) {
return false;
} else {
return !exception.getClass().getName().startsWith("java");
}
}
private String getMessage(String message, Throwable exception) {
String fullMessage = (message != null) ? message : "";
// To break out of possible endless loop when getCause returns "this"
for (Throwable t = exception; t != null && t != t.getCause(); t = t.getCause()) {
String exceptionMessage = t.getMessage();
if (t instanceof AbstractMojoExecutionException) {
String longMessage = ((AbstractMojoExecutionException) t).getLongMessage();
if (longMessage != null && !longMessage.isEmpty()) {
if ((exceptionMessage == null || exceptionMessage.isEmpty())
|| longMessage.contains(exceptionMessage)) {
exceptionMessage = longMessage;
} else if (!exceptionMessage.contains(longMessage)) {
exceptionMessage = join(exceptionMessage, System.lineSeparator() + longMessage);
}
}
}
if (exceptionMessage == null || exceptionMessage.isEmpty()) {
exceptionMessage = t.getClass().getSimpleName();
}
if (t instanceof UnknownHostException && !fullMessage.contains("host")) {
fullMessage = join(fullMessage, "Unknown host " + exceptionMessage);
} else if (!fullMessage.contains(exceptionMessage)) {
fullMessage = join(fullMessage, exceptionMessage);
}
}
return fullMessage.trim();
}
private String join(String message1, String message2) {
String message = "";
if (message1 != null && !message1.isEmpty()) {
message = message1.trim();
}
if (message2 != null && !message2.isEmpty()) {
if (message != null && !message.isEmpty()) {
if (message.endsWith(".") || message.endsWith("!") || message.endsWith(":")) {
message += " ";
} else {
message += ": ";
}
}
message += message2;
}
return message;
}
}