diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java index 3cd34c4e09..1c5839e094 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java @@ -40,6 +40,7 @@ import org.apache.maven.api.services.ModelResolverException; import org.apache.maven.api.services.ModelSource; import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.internal.impl.model.ModelProblemUtils; +import org.codehaus.plexus.interpolation.util.StringUtils; import org.eclipse.aether.RepositoryEvent; import org.eclipse.aether.RepositoryEvent.EventType; import org.eclipse.aether.RepositoryException; @@ -127,6 +128,7 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader return result; } + @SuppressWarnings("MethodLength") private Model loadPom( RepositorySystemSession session, ArtifactDescriptorRequest request, ArtifactDescriptorResult result) throws ArtifactDescriptorException { @@ -227,16 +229,29 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader // that may lead to unexpected build failure, log them if (!modelResult.getProblems().isEmpty()) { List problems = modelResult.getProblems(); - logger.warn( - "{} {} encountered while building the effective model for {}", - problems.size(), - (problems.size() == 1) ? "problem was" : "problems were", - request.getArtifact()); if (logger.isDebugEnabled()) { - for (ModelProblem problem : problems) { - logger.warn( - "{} @ {}", problem.getMessage(), ModelProblemUtils.formatLocation(problem, null)); + String problem = (problems.size() == 1) ? "problem" : "problems"; + String problemPredicate = problem + ((problems.size() == 1) ? " was" : " were"); + String message = String.format( + "%s %s encountered while building the effective model for %s during %s\n", + problems.size(), + problemPredicate, + request.getArtifact(), + RequestTraceHelper.interpretTrace(true, request.getTrace())); + message += StringUtils.capitalizeFirstLetter(problem); + for (ModelProblem modelProblem : problems) { + message += String.format( + "\n* %s @ %s", + modelProblem.getMessage(), ModelProblemUtils.formatLocation(modelProblem, null)); } + logger.warn(message); + } else { + logger.warn( + "{} {} encountered while building the effective model for {} during {} (use -X to see details)", + problems.size(), + (problems.size() == 1) ? "problem was" : "problems were", + request.getArtifact(), + RequestTraceHelper.interpretTrace(false, request.getTrace())); } } model = modelResult.getEffectiveModel(); diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/RequestTraceHelper.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/RequestTraceHelper.java new file mode 100644 index 0000000000..25223a9c3a --- /dev/null +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/RequestTraceHelper.java @@ -0,0 +1,69 @@ +/* + * 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.internal.impl.resolver; + +import java.util.stream.Collectors; + +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.collection.CollectRequest; +import org.eclipse.aether.collection.CollectStepData; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.DependencyRequest; + +/** + * Helper class to manage {@link RequestTrace} for better error logging. + */ +public final class RequestTraceHelper { + + /** + * Method that creates some informational string based on passed in {@link RequestTrace}. The contents of request + * trace can literally be anything, but this class tries to cover "most common" cases that are happening in Maven. + */ + public static String interpretTrace(boolean detailed, RequestTrace requestTrace) { + while (requestTrace != null) { + Object data = requestTrace.getData(); + if (data instanceof DependencyRequest request) { + return "dependency resolution for " + request; + } else if (data instanceof CollectRequest request) { + return "dependency collection for " + request; + } else if (data instanceof CollectStepData stepData) { + String msg = "dependency collection step for " + stepData.getContext(); + if (detailed) { + msg += ". Path to offending node from root:\n"; + msg += stepData.getPath().stream() + .map(n -> " -> " + n.toString()) + .collect(Collectors.joining("\n")); + msg += "\n => " + stepData.getNode(); + } + return msg; + } else if (data instanceof ArtifactDescriptorRequest request) { + return "artifact descriptor request for " + request.getArtifact(); + } else if (data instanceof ArtifactRequest request) { + return "artifact request for " + request.getArtifact(); + // TODO: this class is not reachable here! + // } else if (data instanceof org.apache.maven.model.Plugin plugin) { + // return "plugin " + plugin.getId(); + } + requestTrace = requestTrace.getParent(); + } + + return "n/a"; + } +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java index ac8478b465..2f32cacc85 100644 --- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java @@ -23,6 +23,7 @@ import javax.inject.Named; import javax.inject.Singleton; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; @@ -33,8 +34,11 @@ import org.apache.maven.model.building.DefaultModelBuildingRequest; import org.apache.maven.model.building.ModelBuilder; import org.apache.maven.model.building.ModelBuildingException; import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.building.ModelBuildingResult; import org.apache.maven.model.building.ModelProblem; +import org.apache.maven.model.building.ModelProblemUtils; import org.apache.maven.model.resolution.UnresolvableModelException; +import org.codehaus.plexus.util.StringUtils; import org.eclipse.aether.RepositoryEvent; import org.eclipse.aether.RepositoryEvent.EventType; import org.eclipse.aether.RepositoryException; @@ -61,6 +65,8 @@ import org.eclipse.aether.resolution.VersionRequest; import org.eclipse.aether.resolution.VersionResolutionException; import org.eclipse.aether.resolution.VersionResult; import org.eclipse.aether.transfer.ArtifactNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Default artifact descriptor reader. @@ -77,6 +83,7 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader private final ModelCacheFactory modelCacheFactory; private final Map artifactRelocationSources; private final ArtifactDescriptorReaderDelegate delegate; + private final Logger logger = LoggerFactory.getLogger(getClass()); @Inject @SuppressWarnings("checkstyle:ParameterNumber") @@ -124,6 +131,7 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader return result; } + @SuppressWarnings("MethodLength") private Model loadPom( RepositorySystemSession session, ArtifactDescriptorRequest request, ArtifactDescriptorResult result) throws ArtifactDescriptorException { @@ -221,7 +229,37 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader pomArtifact.getVersion())); } - model = modelBuilder.build(modelRequest).getEffectiveModel(); + ModelBuildingResult modelResult = modelBuilder.build(modelRequest); + // ModelBuildingEx is thrown only on FATAL and ERROR severities, but we still can have WARNs + // that may lead to unexpected build failure, log them + if (!modelResult.getProblems().isEmpty()) { + List problems = modelResult.getProblems(); + if (logger.isDebugEnabled()) { + String problem = (problems.size() == 1) ? "problem" : "problems"; + String problemPredicate = problem + ((problems.size() == 1) ? " was" : " were"); + String message = String.format( + "%s %s encountered while building the effective model for %s during %s\n", + problems.size(), + problemPredicate, + request.getArtifact(), + RequestTraceHelper.interpretTrace(true, request.getTrace())); + message += StringUtils.capitalizeFirstLetter(problem); + for (ModelProblem modelProblem : problems) { + message += String.format( + "\n* %s @ %s", + modelProblem.getMessage(), ModelProblemUtils.formatLocation(modelProblem, null)); + } + logger.warn(message); + } else { + logger.warn( + "{} {} encountered while building the effective model for {} during {} (use -X to see details)", + problems.size(), + (problems.size() == 1) ? "problem was" : "problems were", + request.getArtifact(), + RequestTraceHelper.interpretTrace(false, request.getTrace())); + } + } + model = modelResult.getEffectiveModel(); } catch (ModelBuildingException e) { for (ModelProblem problem : e.getProblems()) { if (problem.getException() instanceof UnresolvableModelException) { diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RequestTraceHelper.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RequestTraceHelper.java new file mode 100644 index 0000000000..99435d8788 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RequestTraceHelper.java @@ -0,0 +1,71 @@ +/* + * 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.repository.internal; + +import java.util.stream.Collectors; + +import org.apache.maven.model.Plugin; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.collection.CollectRequest; +import org.eclipse.aether.collection.CollectStepData; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.DependencyRequest; + +/** + * Helper class to manage {@link RequestTrace} for better error logging. + * + * @since 3.9.9 + */ +public final class RequestTraceHelper { + + /** + * Method that creates some informational string based on passed in {@link RequestTrace}. The contents of request + * trace can literally be anything, but this class tries to cover "most common" cases that are happening in Maven. + */ + public static String interpretTrace(boolean detailed, RequestTrace requestTrace) { + while (requestTrace != null) { + Object data = requestTrace.getData(); + if (data instanceof DependencyRequest request) { + return "dependency resolution for " + request; + } else if (data instanceof CollectRequest request) { + return "dependency collection for " + request; + } else if (data instanceof CollectStepData stepData) { + String msg = "dependency collection step for " + stepData.getContext(); + if (detailed) { + msg += ". Path to offending node from root:\n"; + msg += stepData.getPath().stream() + .map(n -> " -> " + n.toString()) + .collect(Collectors.joining("\n")); + msg += "\n => " + stepData.getNode(); + } + return msg; + } else if (data instanceof ArtifactDescriptorRequest request) { + return "artifact descriptor request for " + request.getArtifact(); + } else if (data instanceof ArtifactRequest request) { + return "artifact request for " + request.getArtifact(); + } else if (data instanceof Plugin plugin) { + return "plugin request " + plugin.getId(); + } + requestTrace = requestTrace.getParent(); + } + + return "n/a"; + } +}