Merge remote-tracking branch 'upstream/master' into MNG-8451

This commit is contained in:
Tamas Cservenak 2024-12-19 16:05:03 +01:00
commit 99f5cab86e
32 changed files with 652 additions and 555 deletions

View File

@ -18,10 +18,6 @@
*/ */
package org.apache.maven.api.services; package org.apache.maven.api.services;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Experimental;
/** /**
@ -32,14 +28,14 @@ import org.apache.maven.api.annotations.Experimental;
@Experimental @Experimental
public abstract class MavenBuilderException extends MavenException { public abstract class MavenBuilderException extends MavenException {
private final List<BuilderProblem> problems; private final ProblemCollector<BuilderProblem> problems;
public MavenBuilderException(String message, Throwable cause) { public MavenBuilderException(String message, Throwable cause) {
super(message, cause); super(message, cause);
problems = List.of(); problems = ProblemCollector.empty();
} }
public MavenBuilderException(String message, List<BuilderProblem> problems) { public MavenBuilderException(String message, ProblemCollector<BuilderProblem> problems) {
super(buildMessage(message, problems), null); super(buildMessage(message, problems), null);
this.problems = problems; this.problems = problems;
} }
@ -49,20 +45,16 @@ public abstract class MavenBuilderException extends MavenException {
* and then a list is built. These exceptions are usually thrown in "fatal" cases (and usually prevent Maven * and then a list is built. These exceptions are usually thrown in "fatal" cases (and usually prevent Maven
* from starting), and these exceptions may end up very early on output. * from starting), and these exceptions may end up very early on output.
*/ */
protected static String buildMessage(String message, List<BuilderProblem> problems) { protected static String buildMessage(String message, ProblemCollector<BuilderProblem> problems) {
StringBuilder msg = new StringBuilder(message); StringBuilder msg = new StringBuilder(message);
ArrayList<BuilderProblem> sorted = new ArrayList<>(problems); problems.problems().forEach(problem -> msg.append("\n * ")
sorted.sort(Comparator.comparing(BuilderProblem::getSeverity)); .append(problem.getSeverity().name())
for (BuilderProblem problem : sorted) { .append(": ")
msg.append("\n * ") .append(problem.getMessage()));
.append(problem.getSeverity().name())
.append(": ")
.append(problem.getMessage());
}
return msg.toString(); return msg.toString();
} }
public List<BuilderProblem> getProblems() { public ProblemCollector<BuilderProblem> getProblemCollector() {
return problems; return problems;
} }
} }

View File

@ -19,8 +19,6 @@
package org.apache.maven.api.services; package org.apache.maven.api.services;
import java.io.Serial; import java.io.Serial;
import java.util.Collections;
import java.util.List;
import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Experimental;
@ -82,10 +80,10 @@ public class ModelBuilderException extends MavenException {
* *
* @return The problems that caused this exception, never {@code null}. * @return The problems that caused this exception, never {@code null}.
*/ */
public List<ModelProblem> getProblems() { public ProblemCollector<ModelProblem> getProblemCollector() {
if (result == null) { if (result == null) {
return Collections.emptyList(); return ProblemCollector.empty();
} }
return Collections.unmodifiableList(result.getProblems()); return result.getProblemCollector();
} }
} }

View File

@ -91,12 +91,12 @@ public interface ModelBuilderResult {
List<Profile> getActiveExternalProfiles(); List<Profile> getActiveExternalProfiles();
/** /**
* Gets the problems that were encountered during the project building. * Gets the problem collector that collected problems encountered during the project building.
* *
* @return the problems that were encountered during the project building, can be empty but never {@code null} * @return the problem collector that collected problems encountered during the project building
*/ */
@Nonnull @Nonnull
List<ModelProblem> getProblems(); ProblemCollector<ModelProblem> getProblemCollector();
/** /**
* Gets the children of this result. * Gets the children of this result.

View File

@ -18,8 +18,6 @@
*/ */
package org.apache.maven.api.services; package org.apache.maven.api.services;
import java.util.List;
import org.apache.maven.api.model.InputLocation; import org.apache.maven.api.model.InputLocation;
import org.apache.maven.api.model.Model; import org.apache.maven.api.model.Model;
@ -33,15 +31,15 @@ import org.apache.maven.api.model.Model;
*/ */
public interface ModelProblemCollector { public interface ModelProblemCollector {
/** ProblemCollector<ModelProblem> getProblemCollector();
* The collected problems.
* @return a list of model problems encountered, never {@code null}
*/
List<ModelProblem> getProblems();
boolean hasErrors(); default boolean hasErrors() {
return getProblemCollector().hasErrorProblems();
}
boolean hasFatalErrors(); default boolean hasFatalErrors() {
return getProblemCollector().hasFatalProblems();
}
default void add(BuilderProblem.Severity severity, ModelProblem.Version version, String message) { default void add(BuilderProblem.Severity severity, ModelProblem.Version version, String message) {
add(severity, version, message, null, null); add(severity, version, message, null, null);
@ -64,7 +62,9 @@ public interface ModelProblemCollector {
InputLocation location, InputLocation location,
Exception exception); Exception exception);
void add(ModelProblem problem); default void add(ModelProblem problem) {
getProblemCollector().reportProblem(problem);
}
ModelBuilderException newModelBuilderException(); ModelBuilderException newModelBuilderException();

View File

@ -0,0 +1,261 @@
/*
* 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.api.services;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Stream;
import org.apache.maven.api.Constants;
import org.apache.maven.api.ProtoSession;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
import static java.util.Objects.requireNonNull;
/**
* Collects problems that were encountered during project building.
*
* @param <P> The type of the problem.
* @since 4.0.0
*/
@Experimental
public interface ProblemCollector<P extends BuilderProblem> {
/**
* Returns {@code true} if there is at least one problem collected with severity equal or more severe than
* {@link org.apache.maven.api.services.BuilderProblem.Severity#WARNING}. This check is logically equivalent
* to "is there any problem reported?", given warning is the lowest severity.
*/
default boolean hasWarningProblems() {
return hasProblemsFor(BuilderProblem.Severity.WARNING);
}
/**
* Returns {@code true} if there is at least one problem collected with severity equal or more severe than
* {@link org.apache.maven.api.services.BuilderProblem.Severity#ERROR}.
*/
default boolean hasErrorProblems() {
return hasProblemsFor(BuilderProblem.Severity.ERROR);
}
/**
* Returns {@code true} if there is at least one problem collected with severity equal or more severe than
* {@link org.apache.maven.api.services.BuilderProblem.Severity#FATAL}.
*/
default boolean hasFatalProblems() {
return hasProblemsFor(BuilderProblem.Severity.FATAL);
}
/**
* Returns {@code true} if there is at least one problem collected with severity equal or more severe than
* passed in severity.
*/
default boolean hasProblemsFor(BuilderProblem.Severity severity) {
requireNonNull(severity, "severity");
for (BuilderProblem.Severity s : BuilderProblem.Severity.values()) {
if (s.ordinal() <= severity.ordinal() && problemsReportedFor(s) > 0) {
return true;
}
}
return false;
}
/**
* Returns total count of problems reported.
*/
default int totalProblemsReported() {
return problemsReportedFor(BuilderProblem.Severity.values());
}
/**
* Returns count of problems reported for given severities.
*/
int problemsReportedFor(BuilderProblem.Severity... severities);
/**
* Returns {@code true} if reported problem count exceeded allowed count, and issues were lost. When this
* method returns {@code true}, it means that element count of stream returned by method {@link #problems()}
* and the counter returned by {@link #totalProblemsReported()} are not equal (latter is bigger than former).
*/
boolean problemsOverflow();
/**
* Reports a problem: always maintains the counters, but whether problem is preserved in memory, depends on
* implementation and its configuration.
*
* @return {@code true} if passed problem is preserved by this call.
*/
boolean reportProblem(P problem);
/**
* Returns all reported and preserved problems ordered by severity in decreasing order. Note: counters and
* element count in this stream does not have to be equal.
*/
@Nonnull
default Stream<P> problems() {
Stream<P> result = Stream.empty();
for (BuilderProblem.Severity severity : BuilderProblem.Severity.values()) {
result = Stream.concat(result, problems(severity));
}
return result;
}
/**
* Returns all reported and preserved problems for given severity. Note: counters and element count in this
* stream does not have to be equal.
*/
@Nonnull
Stream<P> problems(BuilderProblem.Severity severity);
/**
* Creates "empty" problem collector.
*/
@Nonnull
static <P extends BuilderProblem> ProblemCollector<P> empty() {
return new ProblemCollector<>() {
@Override
public boolean problemsOverflow() {
return false;
}
@Override
public int problemsReportedFor(BuilderProblem.Severity... severities) {
return 0;
}
@Override
public boolean reportProblem(P problem) {
throw new IllegalStateException("empty problem collector");
}
@Override
public Stream<P> problems(BuilderProblem.Severity severity) {
return Stream.empty();
}
};
}
/**
* Creates new instance of problem collector.
*/
@Nonnull
static <P extends BuilderProblem> ProblemCollector<P> create(@Nullable ProtoSession protoSession) {
if (protoSession != null
&& protoSession.getUserProperties().containsKey(Constants.MAVEN_BUILDER_MAX_PROBLEMS)) {
return new Impl<>(
Integer.parseInt(protoSession.getUserProperties().get(Constants.MAVEN_BUILDER_MAX_PROBLEMS)));
} else {
return create(100);
}
}
/**
* Creates new instance of problem collector. Visible for testing only.
*/
@Nonnull
static <P extends BuilderProblem> ProblemCollector<P> create(int maxCountLimit) {
return new Impl<>(maxCountLimit);
}
class Impl<P extends BuilderProblem> implements ProblemCollector<P> {
private final int maxCountLimit;
private final AtomicInteger totalCount;
private final ConcurrentMap<BuilderProblem.Severity, LongAdder> counters;
private final ConcurrentMap<BuilderProblem.Severity, List<P>> problems;
private static final List<BuilderProblem.Severity> REVERSED_ORDER = Arrays.stream(
BuilderProblem.Severity.values())
.sorted(Comparator.reverseOrder())
.toList();
private Impl(int maxCountLimit) {
if (maxCountLimit < 0) {
throw new IllegalArgumentException("maxCountLimit must be non-negative");
}
this.maxCountLimit = maxCountLimit;
this.totalCount = new AtomicInteger();
this.counters = new ConcurrentHashMap<>();
this.problems = new ConcurrentHashMap<>();
}
@Override
public int problemsReportedFor(BuilderProblem.Severity... severity) {
int result = 0;
for (BuilderProblem.Severity s : severity) {
result += getCounter(s).intValue();
}
return result;
}
@Override
public boolean problemsOverflow() {
return totalCount.get() > maxCountLimit;
}
@Override
public boolean reportProblem(P problem) {
requireNonNull(problem, "problem");
int currentCount = totalCount.incrementAndGet();
getCounter(problem.getSeverity()).increment();
if (currentCount <= maxCountLimit || dropProblemWithLowerSeverity(problem.getSeverity())) {
getProblems(problem.getSeverity()).add(problem);
return true;
}
return false;
}
@Override
public Stream<P> problems(BuilderProblem.Severity severity) {
requireNonNull(severity, "severity");
return getProblems(severity).stream();
}
private LongAdder getCounter(BuilderProblem.Severity severity) {
return counters.computeIfAbsent(severity, k -> new LongAdder());
}
private List<P> getProblems(BuilderProblem.Severity severity) {
return problems.computeIfAbsent(severity, k -> new CopyOnWriteArrayList<>());
}
private boolean dropProblemWithLowerSeverity(BuilderProblem.Severity severity) {
for (BuilderProblem.Severity s : REVERSED_ORDER) {
if (s.ordinal() > severity.ordinal()) {
List<P> problems = getProblems(s);
while (!problems.isEmpty()) {
try {
return problems.remove(0) != null;
} catch (IndexOutOfBoundsException e) {
// empty, continue
}
}
}
}
return false;
}
}
}

View File

@ -19,7 +19,6 @@
package org.apache.maven.api.services; package org.apache.maven.api.services;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List;
import org.apache.maven.api.Service; import org.apache.maven.api.Service;
import org.apache.maven.api.Session; import org.apache.maven.api.Session;
@ -108,7 +107,7 @@ public interface SettingsBuilder extends Service {
* @return The list of problems that were encountered, must not be {@code null}. * @return The list of problems that were encountered, must not be {@code null}.
*/ */
@Nonnull @Nonnull
default List<BuilderProblem> validate(@Nonnull Settings settings) { default ProblemCollector<BuilderProblem> validate(@Nonnull Settings settings) {
return validate(settings, false); return validate(settings, false);
} }
@ -120,7 +119,7 @@ public interface SettingsBuilder extends Service {
* @return The list of problems that were encountered, must not be {@code null}. * @return The list of problems that were encountered, must not be {@code null}.
*/ */
@Nonnull @Nonnull
List<BuilderProblem> validate(@Nonnull Settings settings, boolean isProjectSettings); ProblemCollector<BuilderProblem> validate(@Nonnull Settings settings, boolean isProjectSettings);
/** /**
* Convert a model profile to a settings profile. * Convert a model profile to a settings profile.

View File

@ -19,7 +19,6 @@
package org.apache.maven.api.services; package org.apache.maven.api.services;
import java.io.Serial; import java.io.Serial;
import java.util.List;
import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Experimental;
@ -42,7 +41,7 @@ public class SettingsBuilderException extends MavenBuilderException {
super(message, e); super(message, e);
} }
public SettingsBuilderException(String message, List<BuilderProblem> problems) { public SettingsBuilderException(String message, ProblemCollector<BuilderProblem> problems) {
super(message, problems); super(message, problems);
} }
} }

View File

@ -18,8 +18,6 @@
*/ */
package org.apache.maven.api.services; package org.apache.maven.api.services;
import java.util.List;
import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.settings.Settings; import org.apache.maven.api.settings.Settings;
@ -47,5 +45,5 @@ public interface SettingsBuilderResult {
* @return the problems that were encountered during the settings building, can be empty but never {@code null} * @return the problems that were encountered during the settings building, can be empty but never {@code null}
*/ */
@Nonnull @Nonnull
List<BuilderProblem> getProblems(); ProblemCollector<BuilderProblem> getProblems();
} }

View File

@ -19,7 +19,6 @@
package org.apache.maven.api.services; package org.apache.maven.api.services;
import java.io.Serial; import java.io.Serial;
import java.util.List;
import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Experimental;
@ -42,7 +41,7 @@ public class ToolchainsBuilderException extends MavenBuilderException {
super(message, e); super(message, e);
} }
public ToolchainsBuilderException(String message, List<BuilderProblem> problems) { public ToolchainsBuilderException(String message, ProblemCollector<BuilderProblem> problems) {
super(message, problems); super(message, problems);
} }
} }

View File

@ -18,8 +18,6 @@
*/ */
package org.apache.maven.api.services; package org.apache.maven.api.services;
import java.util.List;
import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.toolchain.PersistedToolchains; import org.apache.maven.api.toolchain.PersistedToolchains;
@ -46,5 +44,5 @@ public interface ToolchainsBuilderResult {
* @return the problems that were encountered during the settings building, can be empty but never {@code null} * @return the problems that were encountered during the settings building, can be empty but never {@code null}
*/ */
@Nonnull @Nonnull
List<BuilderProblem> getProblems(); ProblemCollector<BuilderProblem> getProblems();
} }

View File

@ -95,9 +95,11 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
.build()); .build());
return new DefaultSettingsBuildingResult( return new DefaultSettingsBuildingResult(
new Settings(result.getEffectiveSettings()), convert(result.getProblems())); new Settings(result.getEffectiveSettings()),
convert(result.getProblems().problems().toList()));
} catch (SettingsBuilderException e) { } catch (SettingsBuilderException e) {
throw new SettingsBuildingException(convert(e.getProblems())); throw new SettingsBuildingException(
convert(e.getProblemCollector().problems().toList()));
} }
} }

View File

@ -22,10 +22,10 @@ import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.ProblemCollector;
import org.apache.maven.api.services.SettingsBuilder; import org.apache.maven.api.services.SettingsBuilder;
import org.apache.maven.settings.Settings; import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.SettingsProblem.Severity; import org.apache.maven.settings.building.SettingsProblem.Severity;
@ -56,8 +56,8 @@ public class DefaultSettingsValidator implements SettingsValidator {
@Override @Override
public void validate(Settings settings, boolean isProjectSettings, SettingsProblemCollector problems) { public void validate(Settings settings, boolean isProjectSettings, SettingsProblemCollector problems) {
List<BuilderProblem> list = settingsBuilder.validate(settings.getDelegate(), isProjectSettings); ProblemCollector<BuilderProblem> list = settingsBuilder.validate(settings.getDelegate(), isProjectSettings);
for (BuilderProblem problem : list) { for (BuilderProblem problem : list.problems().toList()) {
addViolation(problems, Severity.valueOf(problem.getSeverity().name()), problem.getMessage()); addViolation(problems, Severity.valueOf(problem.getSeverity().name()), problem.getMessage());
} }
} }

View File

@ -99,9 +99,10 @@ class DefaultSettingsValidatorTest {
SimpleProblemCollector problems = new SimpleProblemCollector(); SimpleProblemCollector problems = new SimpleProblemCollector();
validator.validate(settings, problems); validator.validate(settings, problems);
assertEquals(4, problems.messages.size()); assertEquals(4, problems.messages.size());
assertContains(problems.messages.get(0), "'mirrors.mirror.id' must not be 'local'"); // errors are now by severity descending
assertContains(problems.messages.get(1), "'mirrors.mirror.url' for local is missing"); assertContains(problems.messages.get(0), "'mirrors.mirror.url' for local is missing");
assertContains(problems.messages.get(2), "'mirrors.mirror.mirrorOf' for local is missing"); assertContains(problems.messages.get(1), "'mirrors.mirror.mirrorOf' for local is missing");
assertContains(problems.messages.get(2), "'mirrors.mirror.id' must not be 'local'");
assertContains(problems.messages.get(3), "'mirrors.mirror.id' must not contain any of these characters"); assertContains(problems.messages.get(3), "'mirrors.mirror.id' must not contain any of these characters");
} }
@ -121,11 +122,12 @@ class DefaultSettingsValidatorTest {
SimpleProblemCollector problems = new SimpleProblemCollector(); SimpleProblemCollector problems = new SimpleProblemCollector();
validator.validate(settings, problems); validator.validate(settings, problems);
assertEquals(3, problems.messages.size()); assertEquals(3, problems.messages.size());
// errors are now by severity descending
assertContains( assertContains(
problems.messages.get(0), "'profiles.profile[default].repositories.repository.id' must not be 'local'"); problems.messages.get(0),
assertContains(
problems.messages.get(1),
"'profiles.profile[default].repositories.repository.url' for local is missing"); "'profiles.profile[default].repositories.repository.url' for local is missing");
assertContains(
problems.messages.get(1), "'profiles.profile[default].repositories.repository.id' must not be 'local'");
assertContains( assertContains(
problems.messages.get(2), problems.messages.get(2),
"'profiles.profile[default].repositories.repository.id' must not contain any of these characters"); "'profiles.profile[default].repositories.repository.id' must not contain any of these characters");

View File

@ -92,9 +92,11 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
.build()); .build());
return new DefaultToolchainsBuildingResult( return new DefaultToolchainsBuildingResult(
new PersistedToolchains(result.getEffectiveToolchains()), convert(result.getProblems())); new PersistedToolchains(result.getEffectiveToolchains()),
convert(result.getProblems().problems().toList()));
} catch (ToolchainsBuilderException e) { } catch (ToolchainsBuilderException e) {
throw new ToolchainsBuildingException(convert(e.getProblems())); throw new ToolchainsBuildingException(
convert(e.getProblemCollector().problems().toList()));
} }
} }

View File

@ -597,13 +597,16 @@ public abstract class LookupInvoker<C extends LookupContext> implements Invoker
context.interactive = mayDisableInteractiveMode(context, context.effectiveSettings.isInteractiveMode()); context.interactive = mayDisableInteractiveMode(context, context.effectiveSettings.isInteractiveMode());
context.localRepositoryPath = localRepositoryPath(context); context.localRepositoryPath = localRepositoryPath(context);
if (emitSettingsWarnings && !settingsResult.getProblems().isEmpty()) { if (emitSettingsWarnings && settingsResult.getProblems().hasWarningProblems()) {
int totalProblems = settingsResult.getProblems().totalProblemsReported();
context.logger.info(""); context.logger.info("");
context.logger.info( context.logger.info(String.format(
"Some problems were encountered while building the effective settings (use -X to see details)"); "%s %s encountered while building the effective settings (use -e to see details)",
totalProblems, (totalProblems == 1) ? "problem was" : "problems were"));
if (context.invokerRequest.options().verbose().orElse(false)) { if (context.invokerRequest.options().showErrors().orElse(false)) {
for (BuilderProblem problem : settingsResult.getProblems()) { for (BuilderProblem problem :
settingsResult.getProblems().problems().toList()) {
context.logger.warn(problem.getMessage() + " @ " + problem.getLocation()); context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
} }
} }

View File

@ -205,15 +205,21 @@ public class MavenInvoker extends LookupInvoker<MavenContext> {
new org.apache.maven.toolchain.model.PersistedToolchains( new org.apache.maven.toolchain.model.PersistedToolchains(
toolchainsResult.getEffectiveToolchains())); toolchainsResult.getEffectiveToolchains()));
if (!toolchainsResult.getProblems().isEmpty()) { if (toolchainsResult.getProblems().hasWarningProblems()) {
context.logger.warn(""); int totalProblems = toolchainsResult.getProblems().totalProblemsReported();
context.logger.warn("Some problems were encountered while building the effective toolchains"); context.logger.info("");
context.logger.info(String.format(
"%s %s encountered while building the effective toolchains (use -e to see details)",
totalProblems, (totalProblems == 1) ? "problem was" : "problems were"));
for (BuilderProblem problem : toolchainsResult.getProblems()) { if (context.invokerRequest.options().showErrors().orElse(false)) {
context.logger.warn(problem.getMessage() + " @ " + problem.getLocation()); for (BuilderProblem problem :
toolchainsResult.getProblems().problems().toList()) {
context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
}
} }
context.logger.warn(""); context.logger.info("");
} }
} }

View File

@ -66,6 +66,7 @@ import org.apache.maven.api.services.ModelProblem.Version;
import org.apache.maven.api.services.ModelProblemCollector; import org.apache.maven.api.services.ModelProblemCollector;
import org.apache.maven.api.services.ModelSource; import org.apache.maven.api.services.ModelSource;
import org.apache.maven.api.services.ModelTransformer; import org.apache.maven.api.services.ModelTransformer;
import org.apache.maven.api.services.ProblemCollector;
import org.apache.maven.api.services.Source; import org.apache.maven.api.services.Source;
import org.apache.maven.api.services.model.LifecycleBindingsInjector; import org.apache.maven.api.services.model.LifecycleBindingsInjector;
import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.Artifact;
@ -340,7 +341,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
try { try {
MavenProject project = request.getProject(); MavenProject project = request.getProject();
List<ModelProblem> modelProblems = null; ProblemCollector<ModelProblem> problemCollector = null;
Throwable error = null; Throwable error = null;
if (project == null) { if (project == null) {
@ -378,7 +379,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
error = e; error = e;
} }
modelProblems = result.getProblems(); problemCollector = result.getProblemCollector();
initProject(project, result); initProject(project, result);
} }
@ -391,7 +392,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
} }
ProjectBuildingResult result = ProjectBuildingResult result =
new DefaultProjectBuildingResult(project, convert(modelProblems), resolutionResult); new DefaultProjectBuildingResult(project, convert(problemCollector), resolutionResult);
if (error != null) { if (error != null) {
ProjectBuildingException e = new ProjectBuildingException(List.of(result)); ProjectBuildingException e = new ProjectBuildingException(List.of(result));
@ -498,18 +499,14 @@ public class DefaultProjectBuilder implements ProjectBuilder {
} catch (ModelBuilderException e) { } catch (ModelBuilderException e) {
result = e.getResult(); result = e.getResult();
if (result == null || result.getEffectiveModel() == null) { if (result == null || result.getEffectiveModel() == null) {
return List.of(new DefaultProjectBuildingResult(e.getModelId(), pomFile, convert(e.getProblems()))); return List.of(new DefaultProjectBuildingResult(
e.getModelId(), pomFile, convert(e.getProblemCollector())));
} }
} }
List<ProjectBuildingResult> results = new ArrayList<>(); List<ProjectBuildingResult> results = new ArrayList<>();
List<ModelBuilderResult> allModels = results(result).toList(); List<ModelBuilderResult> allModels = results(result).toList();
for (ModelBuilderResult r : allModels) { for (ModelBuilderResult r : allModels) {
List<ModelProblem> problems = new ArrayList<>(r.getProblems());
results(r)
.filter(c -> c != r)
.flatMap(c -> c.getProblems().stream())
.forEach(problems::remove);
if (r.getEffectiveModel() != null) { if (r.getEffectiveModel() != null) {
File pom = r.getSource().getPath().toFile(); File pom = r.getSource().getPath().toFile();
MavenProject project = MavenProject project =
@ -529,9 +526,10 @@ public class DefaultProjectBuilder implements ProjectBuilder {
if (request.isResolveDependencies()) { if (request.isResolveDependencies()) {
resolutionResult = resolveDependencies(project); resolutionResult = resolveDependencies(project);
} }
results.add(new DefaultProjectBuildingResult(project, convert(problems), resolutionResult)); results.add(new DefaultProjectBuildingResult(
project, convert(r.getProblemCollector()), resolutionResult));
} else { } else {
results.add(new DefaultProjectBuildingResult(null, convert(problems), null)); results.add(new DefaultProjectBuildingResult(null, convert(r.getProblemCollector()), null));
} }
} }
return results; return results;
@ -541,11 +539,34 @@ public class DefaultProjectBuilder implements ProjectBuilder {
return Stream.concat(result.getChildren().stream().flatMap(this::results), Stream.of(result)); return Stream.concat(result.getChildren().stream().flatMap(this::results), Stream.of(result));
} }
private List<org.apache.maven.model.building.ModelProblem> convert(List<ModelProblem> problems) { private List<org.apache.maven.model.building.ModelProblem> convert(
if (problems == null) { ProblemCollector<ModelProblem> problemCollector) {
if (problemCollector == null) {
return null; return null;
} }
return problems.stream().map(p -> convert(p)).toList(); ArrayList<org.apache.maven.model.building.ModelProblem> problems = new ArrayList<>();
problemCollector.problems().map(BuildSession::convert).forEach(problems::add);
if (problemCollector.problemsOverflow()) {
problems.add(
0,
new DefaultModelProblem(
"Too many model problems reported (listed problems are just a subset of reported problems)",
org.apache.maven.model.building.ModelProblem.Severity.WARNING,
null,
null,
-1,
-1,
null,
null));
return new ArrayList<>(problems) {
@Override
public int size() {
return problemCollector.totalProblemsReported();
}
};
} else {
return problems;
}
} }
private static org.apache.maven.model.building.ModelProblem convert(ModelProblem p) { private static org.apache.maven.model.building.ModelProblem convert(ModelProblem p) {

View File

@ -62,27 +62,33 @@ public class DefaultProjectsSelector implements ProjectsSelector {
List<MavenProject> projects = new ArrayList<>(results.size()); List<MavenProject> projects = new ArrayList<>(results.size());
boolean problems = false; long totalProblemsCount = 0;
for (ProjectBuildingResult result : results) { for (ProjectBuildingResult result : results) {
projects.add(result.getProject()); projects.add(result.getProject());
if (!result.getProblems().isEmpty() && LOGGER.isWarnEnabled()) { int problemsCount = result.getProblems().size();
totalProblemsCount += problemsCount;
if (problemsCount != 0 && LOGGER.isWarnEnabled()) {
LOGGER.warn(""); LOGGER.warn("");
LOGGER.warn( LOGGER.warn(
"Some problems were encountered while building the effective model for '{}'", "{} {} encountered while building the effective model for '{}' (use -e to see details)",
problemsCount,
(problemsCount == 1) ? "problem was" : "problems were",
result.getProject().getId()); result.getProject().getId());
for (ModelProblem problem : result.getProblems()) { if (request.isShowErrors()) { // this means -e or -X (as -X enables -e as well)
String loc = ModelProblemUtils.formatLocation(problem, result.getProjectId()); for (ModelProblem problem : result.getProblems()) {
LOGGER.warn("{}{}", problem.getMessage(), ((loc != null && !loc.isEmpty()) ? " @ " + loc : "")); String loc = ModelProblemUtils.formatLocation(problem, result.getProjectId());
LOGGER.warn("{}{}", problem.getMessage(), ((loc != null && !loc.isEmpty()) ? " @ " + loc : ""));
}
} }
problems = true;
} }
} }
if (problems) { if (totalProblemsCount > 0) {
LOGGER.warn("");
LOGGER.warn("Total model problems reported: {}", totalProblemsCount);
LOGGER.warn(""); LOGGER.warn("");
LOGGER.warn("It is highly recommended to fix these problems" LOGGER.warn("It is highly recommended to fix these problems"
+ " because they threaten the stability of your build."); + " because they threaten the stability of your build.");

View File

@ -26,7 +26,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -39,6 +38,7 @@ import org.apache.maven.api.di.Inject;
import org.apache.maven.api.di.Named; import org.apache.maven.api.di.Named;
import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.Interpolator; import org.apache.maven.api.services.Interpolator;
import org.apache.maven.api.services.ProblemCollector;
import org.apache.maven.api.services.SettingsBuilder; import org.apache.maven.api.services.SettingsBuilder;
import org.apache.maven.api.services.SettingsBuilderException; import org.apache.maven.api.services.SettingsBuilderException;
import org.apache.maven.api.services.SettingsBuilderRequest; import org.apache.maven.api.services.SettingsBuilderRequest;
@ -97,7 +97,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
@Override @Override
public SettingsBuilderResult build(SettingsBuilderRequest request) throws SettingsBuilderException { public SettingsBuilderResult build(SettingsBuilderRequest request) throws SettingsBuilderException {
List<BuilderProblem> problems = new ArrayList<>(); ProblemCollector<BuilderProblem> problems = ProblemCollector.create(request.getSession());
Source installationSource = request.getInstallationSettingsSource().orElse(null); Source installationSource = request.getInstallationSettingsSource().orElse(null);
Settings installation = readSettings(installationSource, false, request, problems); Settings installation = readSettings(installationSource, false, request, problems);
@ -139,30 +139,18 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
} }
} }
if (hasErrors(problems)) { if (problems.hasErrorProblems()) {
throw new SettingsBuilderException("Error building settings", problems); throw new SettingsBuilderException("Error building settings", problems);
} }
return new DefaultSettingsBuilderResult(effective, problems); return new DefaultSettingsBuilderResult(effective, problems);
} }
private boolean hasErrors(List<BuilderProblem> problems) {
if (problems != null) {
for (BuilderProblem problem : problems) {
if (BuilderProblem.Severity.ERROR.compareTo(problem.getSeverity()) >= 0) {
return true;
}
}
}
return false;
}
private Settings readSettings( private Settings readSettings(
Source settingsSource, Source settingsSource,
boolean isProjectSettings, boolean isProjectSettings,
SettingsBuilderRequest request, SettingsBuilderRequest request,
List<BuilderProblem> problems) { ProblemCollector<BuilderProblem> problems) {
if (settingsSource == null) { if (settingsSource == null) {
return Settings.newInstance(); return Settings.newInstance();
} }
@ -184,7 +172,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
.strict(false) .strict(false)
.build()); .build());
Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null; Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
problems.add(new DefaultBuilderProblem( problems.reportProblem(new DefaultBuilderProblem(
settingsSource.getLocation(), settingsSource.getLocation(),
loc != null ? loc.getLineNumber() : -1, loc != null ? loc.getLineNumber() : -1,
loc != null ? loc.getColumnNumber() : -1, loc != null ? loc.getColumnNumber() : -1,
@ -195,7 +183,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
} }
} catch (XmlReaderException e) { } catch (XmlReaderException e) {
Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null; Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
problems.add(new DefaultBuilderProblem( problems.reportProblem(new DefaultBuilderProblem(
settingsSource.getLocation(), settingsSource.getLocation(),
loc != null ? loc.getLineNumber() : -1, loc != null ? loc.getLineNumber() : -1,
loc != null ? loc.getColumnNumber() : -1, loc != null ? loc.getColumnNumber() : -1,
@ -204,7 +192,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
BuilderProblem.Severity.FATAL)); BuilderProblem.Severity.FATAL));
return Settings.newInstance(); return Settings.newInstance();
} catch (IOException e) { } catch (IOException e) {
problems.add(new DefaultBuilderProblem( problems.reportProblem(new DefaultBuilderProblem(
settingsSource.getLocation(), settingsSource.getLocation(),
-1, -1,
-1, -1,
@ -242,7 +230,8 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
return settings; return settings;
} }
private Settings interpolate(Settings settings, SettingsBuilderRequest request, List<BuilderProblem> problems) { private Settings interpolate(
Settings settings, SettingsBuilderRequest request, ProblemCollector<BuilderProblem> problems) {
Function<String, String> src; Function<String, String> src;
if (request.getInterpolationSource().isPresent()) { if (request.getInterpolationSource().isPresent()) {
src = request.getInterpolationSource().get(); src = request.getInterpolationSource().get();
@ -269,7 +258,10 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
} }
private Settings decrypt( private Settings decrypt(
Source settingsSource, Settings settings, SettingsBuilderRequest request, List<BuilderProblem> problems) { Source settingsSource,
Settings settings,
SettingsBuilderRequest request,
ProblemCollector<BuilderProblem> problems) {
if (dispatchers.isEmpty()) { if (dispatchers.isEmpty()) {
return settings; return settings;
} }
@ -284,7 +276,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
try { try {
return secDispatcher.decrypt(str); return secDispatcher.decrypt(str);
} catch (Exception e) { } catch (Exception e) {
problems.add(new DefaultBuilderProblem( problems.reportProblem(new DefaultBuilderProblem(
settingsSource.getLocation(), settingsSource.getLocation(),
-1, -1,
-1, -1,
@ -297,7 +289,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
}; };
Settings result = new SettingsTransformer(decryptFunction).visit(settings); Settings result = new SettingsTransformer(decryptFunction).visit(settings);
if (preMaven4Passwords.get() > 0) { if (preMaven4Passwords.get() > 0) {
problems.add(new DefaultBuilderProblem( problems.reportProblem(new DefaultBuilderProblem(
settingsSource.getLocation(), settingsSource.getLocation(),
-1, -1,
-1, -1,
@ -323,8 +315,9 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
} }
@Override @Override
public List<BuilderProblem> validate(Settings settings, boolean isProjectSettings) { public ProblemCollector<BuilderProblem> validate(Settings settings, boolean isProjectSettings) {
ArrayList<BuilderProblem> problems = new ArrayList<>(); // TODO: any way to get ProtoSession here?
ProblemCollector<BuilderProblem> problems = ProblemCollector.create(null);
settingsValidator.validate(settings, isProjectSettings, problems); settingsValidator.validate(settings, isProjectSettings, problems);
return problems; return problems;
} }
@ -347,11 +340,11 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
private final Settings effectiveSettings; private final Settings effectiveSettings;
private final List<BuilderProblem> problems; private final ProblemCollector<BuilderProblem> problems;
DefaultSettingsBuilderResult(Settings effectiveSettings, List<BuilderProblem> problems) { DefaultSettingsBuilderResult(Settings effectiveSettings, ProblemCollector<BuilderProblem> problems) {
this.effectiveSettings = effectiveSettings; this.effectiveSettings = effectiveSettings;
this.problems = (problems != null) ? problems : new ArrayList<>(); this.problems = (problems != null) ? problems : ProblemCollector.empty();
} }
@Override @Override
@ -360,7 +353,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
} }
@Override @Override
public List<BuilderProblem> getProblems() { public ProblemCollector<BuilderProblem> getProblems() {
return problems; return problems;
} }
} }

View File

@ -24,6 +24,7 @@ import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.ProblemCollector;
import org.apache.maven.api.settings.Mirror; import org.apache.maven.api.settings.Mirror;
import org.apache.maven.api.settings.Profile; import org.apache.maven.api.settings.Profile;
import org.apache.maven.api.settings.Proxy; import org.apache.maven.api.settings.Proxy;
@ -41,7 +42,7 @@ public class DefaultSettingsValidator {
private static final String ILLEGAL_REPO_ID_CHARS = "\\/:\"<>|?*"; // ILLEGAL_FS_CHARS private static final String ILLEGAL_REPO_ID_CHARS = "\\/:\"<>|?*"; // ILLEGAL_FS_CHARS
@SuppressWarnings("checkstyle:MethodLength") @SuppressWarnings("checkstyle:MethodLength")
public void validate(Settings settings, boolean isProjectSettings, List<BuilderProblem> problems) { public void validate(Settings settings, boolean isProjectSettings, ProblemCollector<BuilderProblem> problems) {
if (isProjectSettings) { if (isProjectSettings) {
String msgS = "is not supported on project settings."; String msgS = "is not supported on project settings.";
String msgP = "are not supported on project settings."; String msgP = "are not supported on project settings.";
@ -208,7 +209,8 @@ public class DefaultSettingsValidator {
} }
} }
private void validateRepositories(List<BuilderProblem> problems, List<Repository> repositories, String prefix) { private void validateRepositories(
ProblemCollector<BuilderProblem> problems, List<Repository> repositories, String prefix) {
Set<String> repoIds = new HashSet<>(); Set<String> repoIds = new HashSet<>();
for (Repository repository : repositories) { for (Repository repository : repositories) {
@ -268,7 +270,7 @@ public class DefaultSettingsValidator {
* </ul> * </ul>
*/ */
private static boolean validateStringEmpty( private static boolean validateStringEmpty(
List<BuilderProblem> problems, String fieldName, String string, String message) { ProblemCollector<BuilderProblem> problems, String fieldName, String string, String message) {
if (string == null || string.length() == 0) { if (string == null || string.length() == 0) {
return true; return true;
} }
@ -287,7 +289,7 @@ public class DefaultSettingsValidator {
* </ul> * </ul>
*/ */
private static boolean validateStringNotEmpty( private static boolean validateStringNotEmpty(
List<BuilderProblem> problems, String fieldName, String string, String sourceHint) { ProblemCollector<BuilderProblem> problems, String fieldName, String string, String sourceHint) {
if (!validateNotNull(problems, fieldName, string, sourceHint)) { if (!validateNotNull(problems, fieldName, string, sourceHint)) {
return false; return false;
} }
@ -309,7 +311,7 @@ public class DefaultSettingsValidator {
* </ul> * </ul>
*/ */
private static boolean validateNotNull( private static boolean validateNotNull(
List<BuilderProblem> problems, String fieldName, Object object, String sourceHint) { ProblemCollector<BuilderProblem> problems, String fieldName, Object object, String sourceHint) {
if (object != null) { if (object != null) {
return true; return true;
} }
@ -320,7 +322,7 @@ public class DefaultSettingsValidator {
} }
private static boolean validateBannedCharacters( private static boolean validateBannedCharacters(
List<BuilderProblem> problems, ProblemCollector<BuilderProblem> problems,
String fieldName, String fieldName,
BuilderProblem.Severity severity, BuilderProblem.Severity severity,
String string, String string,
@ -344,7 +346,7 @@ public class DefaultSettingsValidator {
} }
private static void addViolation( private static void addViolation(
List<BuilderProblem> problems, ProblemCollector<BuilderProblem> problems,
BuilderProblem.Severity severity, BuilderProblem.Severity severity,
String fieldName, String fieldName,
String sourceHint, String sourceHint,
@ -358,6 +360,6 @@ public class DefaultSettingsValidator {
buffer.append(' ').append(message); buffer.append(' ').append(message);
problems.add(new DefaultBuilderProblem(null, -1, -1, null, buffer.toString(), severity)); problems.reportProblem(new DefaultBuilderProblem(null, -1, -1, null, buffer.toString(), severity));
} }
} }

View File

@ -23,8 +23,6 @@ import javax.xml.stream.XMLStreamException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
@ -32,6 +30,7 @@ import org.apache.maven.api.di.Inject;
import org.apache.maven.api.di.Named; import org.apache.maven.api.di.Named;
import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.Interpolator; import org.apache.maven.api.services.Interpolator;
import org.apache.maven.api.services.ProblemCollector;
import org.apache.maven.api.services.Source; import org.apache.maven.api.services.Source;
import org.apache.maven.api.services.ToolchainsBuilder; import org.apache.maven.api.services.ToolchainsBuilder;
import org.apache.maven.api.services.ToolchainsBuilderException; import org.apache.maven.api.services.ToolchainsBuilderException;
@ -70,7 +69,7 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
@Override @Override
public ToolchainsBuilderResult build(ToolchainsBuilderRequest request) throws ToolchainsBuilderException { public ToolchainsBuilderResult build(ToolchainsBuilderRequest request) throws ToolchainsBuilderException {
List<BuilderProblem> problems = new ArrayList<>(); ProblemCollector<BuilderProblem> problems = ProblemCollector.create(request.getSession());
Source installationSource = request.getInstallationToolchainsSource().orElse(null); Source installationSource = request.getInstallationToolchainsSource().orElse(null);
PersistedToolchains installation = readToolchains(installationSource, request, problems); PersistedToolchains installation = readToolchains(installationSource, request, problems);
@ -80,27 +79,15 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
PersistedToolchains effective = toolchainsMerger.merge(user, installation, false, null); PersistedToolchains effective = toolchainsMerger.merge(user, installation, false, null);
if (hasErrors(problems)) { if (problems.hasErrorProblems()) {
throw new ToolchainsBuilderException("Error building toolchains", problems); throw new ToolchainsBuilderException("Error building toolchains", problems);
} }
return new DefaultToolchainsBuilderResult(effective, problems); return new DefaultToolchainsBuilderResult(effective, problems);
} }
private boolean hasErrors(List<BuilderProblem> problems) {
if (problems != null) {
for (BuilderProblem problem : problems) {
if (BuilderProblem.Severity.ERROR.compareTo(problem.getSeverity()) >= 0) {
return true;
}
}
}
return false;
}
private PersistedToolchains readToolchains( private PersistedToolchains readToolchains(
Source toolchainsSource, ToolchainsBuilderRequest request, List<BuilderProblem> problems) { Source toolchainsSource, ToolchainsBuilderRequest request, ProblemCollector<BuilderProblem> problems) {
if (toolchainsSource == null) { if (toolchainsSource == null) {
return PersistedToolchains.newInstance(); return PersistedToolchains.newInstance();
} }
@ -129,7 +116,7 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
.strict(false) .strict(false)
.build()); .build());
Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null; Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
problems.add(new DefaultBuilderProblem( problems.reportProblem(new DefaultBuilderProblem(
toolchainsSource.getLocation(), toolchainsSource.getLocation(),
loc != null ? loc.getLineNumber() : -1, loc != null ? loc.getLineNumber() : -1,
loc != null ? loc.getColumnNumber() : -1, loc != null ? loc.getColumnNumber() : -1,
@ -139,7 +126,7 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
} }
} catch (XmlReaderException e) { } catch (XmlReaderException e) {
Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null; Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
problems.add(new DefaultBuilderProblem( problems.reportProblem(new DefaultBuilderProblem(
toolchainsSource.getLocation(), toolchainsSource.getLocation(),
loc != null ? loc.getLineNumber() : -1, loc != null ? loc.getLineNumber() : -1,
loc != null ? loc.getColumnNumber() : -1, loc != null ? loc.getColumnNumber() : -1,
@ -148,7 +135,7 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
BuilderProblem.Severity.FATAL)); BuilderProblem.Severity.FATAL));
return PersistedToolchains.newInstance(); return PersistedToolchains.newInstance();
} catch (IOException e) { } catch (IOException e) {
problems.add(new DefaultBuilderProblem( problems.reportProblem(new DefaultBuilderProblem(
toolchainsSource.getLocation(), toolchainsSource.getLocation(),
-1, -1,
-1, -1,
@ -164,7 +151,9 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
} }
private PersistedToolchains interpolate( private PersistedToolchains interpolate(
PersistedToolchains toolchains, ToolchainsBuilderRequest request, List<BuilderProblem> problems) { PersistedToolchains toolchains,
ToolchainsBuilderRequest request,
ProblemCollector<BuilderProblem> problems) {
Map<String, String> userProperties = request.getSession().getUserProperties(); Map<String, String> userProperties = request.getSession().getUserProperties();
Map<String, String> systemProperties = request.getSession().getSystemProperties(); Map<String, String> systemProperties = request.getSession().getSystemProperties();
Function<String, String> src = Interpolator.chain(userProperties::get, systemProperties::get); Function<String, String> src = Interpolator.chain(userProperties::get, systemProperties::get);
@ -180,11 +169,12 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
private final PersistedToolchains effectiveToolchains; private final PersistedToolchains effectiveToolchains;
private final List<BuilderProblem> problems; private final ProblemCollector<BuilderProblem> problems;
DefaultToolchainsBuilderResult(PersistedToolchains effectiveToolchains, List<BuilderProblem> problems) { DefaultToolchainsBuilderResult(
PersistedToolchains effectiveToolchains, ProblemCollector<BuilderProblem> problems) {
this.effectiveToolchains = effectiveToolchains; this.effectiveToolchains = effectiveToolchains;
this.problems = (problems != null) ? problems : new ArrayList<>(); this.problems = (problems != null) ? problems : ProblemCollector.empty();
} }
@Override @Override
@ -193,7 +183,7 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
} }
@Override @Override
public List<BuilderProblem> getProblems() { public ProblemCollector<BuilderProblem> getProblems() {
return problems; return problems;
} }
} }

View File

@ -81,6 +81,7 @@ import org.apache.maven.api.services.ModelProblem;
import org.apache.maven.api.services.ModelProblem.Version; import org.apache.maven.api.services.ModelProblem.Version;
import org.apache.maven.api.services.ModelProblemCollector; import org.apache.maven.api.services.ModelProblemCollector;
import org.apache.maven.api.services.ModelSource; import org.apache.maven.api.services.ModelSource;
import org.apache.maven.api.services.ProblemCollector;
import org.apache.maven.api.services.RepositoryFactory; import org.apache.maven.api.services.RepositoryFactory;
import org.apache.maven.api.services.Source; import org.apache.maven.api.services.Source;
import org.apache.maven.api.services.SuperPomProvider; import org.apache.maven.api.services.SuperPomProvider;
@ -205,10 +206,6 @@ public class DefaultModelBuilder implements ModelBuilder {
return new ModelBuilderSessionImpl(); return new ModelBuilderSessionImpl();
} }
static int getMaxProblems(Session session) {
return Integer.parseInt(session.getUserProperties().getOrDefault(Constants.MAVEN_BUILDER_MAX_PROBLEMS, "100"));
}
protected class ModelBuilderSessionImpl implements ModelBuilderSession { protected class ModelBuilderSessionImpl implements ModelBuilderSession {
ModelBuilderSessionState mainSession; ModelBuilderSessionState mainSession;
@ -227,8 +224,8 @@ public class DefaultModelBuilder implements ModelBuilder {
mainSession = new ModelBuilderSessionState(request); mainSession = new ModelBuilderSessionState(request);
session = mainSession; session = mainSession;
} else { } else {
session = session = mainSession.derive(
mainSession.derive(request, new DefaultModelBuilderResult(getMaxProblems(mainSession.session))); request, new DefaultModelBuilderResult(ProblemCollector.create(mainSession.session)));
} }
// Build the request // Build the request
if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) { if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) {
@ -264,7 +261,7 @@ public class DefaultModelBuilder implements ModelBuilder {
this( this(
request.getSession(), request.getSession(),
request, request,
new DefaultModelBuilderResult(DefaultModelBuilder.getMaxProblems(request.getSession())), new DefaultModelBuilderResult(ProblemCollector.create(request.getSession())),
request.getSession() request.getSession()
.getData() .getData()
.computeIfAbsent(SessionData.key(ModelCache.class), modelCacheFactory::newInstance), .computeIfAbsent(SessionData.key(ModelCache.class), modelCacheFactory::newInstance),
@ -305,12 +302,8 @@ public class DefaultModelBuilder implements ModelBuilder {
this.result.setSource(this.request.getSource()); this.result.setSource(this.request.getSource());
} }
int getMaxProblems() {
return DefaultModelBuilder.getMaxProblems(session);
}
ModelBuilderSessionState derive(ModelSource source) { ModelBuilderSessionState derive(ModelSource source) {
return derive(source, new DefaultModelBuilderResult(result, getMaxProblems())); return derive(source, new DefaultModelBuilderResult(ProblemCollector.create(session)));
} }
ModelBuilderSessionState derive(ModelSource source, DefaultModelBuilderResult result) { ModelBuilderSessionState derive(ModelSource source, DefaultModelBuilderResult result) {
@ -321,7 +314,7 @@ public class DefaultModelBuilder implements ModelBuilder {
* Creates a new session, sharing cached datas and propagating errors. * Creates a new session, sharing cached datas and propagating errors.
*/ */
ModelBuilderSessionState derive(ModelBuilderRequest request) { ModelBuilderSessionState derive(ModelBuilderRequest request) {
return derive(request, new DefaultModelBuilderResult(result, getMaxProblems())); return derive(request, new DefaultModelBuilderResult(ProblemCollector.create(session)));
} }
ModelBuilderSessionState derive(ModelBuilderRequest request, DefaultModelBuilderResult result) { ModelBuilderSessionState derive(ModelBuilderRequest request, DefaultModelBuilderResult result) {
@ -433,18 +426,9 @@ public class DefaultModelBuilder implements ModelBuilder {
} }
} }
public boolean hasFatalErrors() {
return result.getProblems().stream().anyMatch(p -> p.getSeverity() == Severity.FATAL);
}
public boolean hasErrors() {
return result.getProblems().stream()
.anyMatch(p -> p.getSeverity() == Severity.FATAL || p.getSeverity() == Severity.ERROR);
}
@Override @Override
public List<ModelProblem> getProblems() { public ProblemCollector<ModelProblem> getProblemCollector() {
return result.getProblems(); return result.getProblemCollector();
} }
public void setSource(String source) { public void setSource(String source) {
@ -481,30 +465,6 @@ public class DefaultModelBuilder implements ModelBuilder {
} }
@Override @Override
public void add(ModelProblem problem) {
result.addProblem(problem);
}
@Override
public void add(BuilderProblem.Severity severity, ModelProblem.Version version, String message) {
add(severity, version, message, null, null);
}
@Override
public void add(
BuilderProblem.Severity severity,
ModelProblem.Version version,
String message,
InputLocation location) {
add(severity, version, message, location, null);
}
@Override
public void add(
BuilderProblem.Severity severity, ModelProblem.Version version, String message, Exception exception) {
add(severity, version, message, null, exception);
}
public void add( public void add(
BuilderProblem.Severity severity, BuilderProblem.Severity severity,
ModelProblem.Version version, ModelProblem.Version version,
@ -720,8 +680,9 @@ public class DefaultModelBuilder implements ModelBuilder {
private void loadFromRoot(Path root, Path top) { private void loadFromRoot(Path root, Path top) {
try (PhasingExecutor executor = createExecutor()) { try (PhasingExecutor executor = createExecutor()) {
DefaultModelBuilderResult r = DefaultModelBuilderResult r = Objects.equals(top, root)
Objects.equals(top, root) ? result : new DefaultModelBuilderResult(getMaxProblems()); ? result
: new DefaultModelBuilderResult(ProblemCollector.create(session));
loadFilePom(executor, top, root, Set.of(), r); loadFilePom(executor, top, root, Set.of(), r);
} }
if (result.getFileModel() == null && !Objects.equals(top, root)) { if (result.getFileModel() == null && !Objects.equals(top, root)) {
@ -767,7 +728,7 @@ public class DefaultModelBuilder implements ModelBuilder {
-1, -1,
-1, -1,
null); null);
r.addProblem(problem); r.getProblemCollector().reportProblem(problem);
continue; continue;
} }
@ -789,13 +750,13 @@ public class DefaultModelBuilder implements ModelBuilder {
-1, -1,
-1, -1,
null); null);
r.addProblem(problem); r.getProblemCollector().reportProblem(problem);
continue; continue;
} }
DefaultModelBuilderResult cr = Objects.equals(top, subprojectFile) DefaultModelBuilderResult cr = Objects.equals(top, subprojectFile)
? result ? result
: new DefaultModelBuilderResult(r, getMaxProblems()); : new DefaultModelBuilderResult(ProblemCollector.create(session));
if (request.isRecursive()) { if (request.isRecursive()) {
r.getChildren().add(cr); r.getChildren().add(cr);
} }
@ -806,9 +767,6 @@ public class DefaultModelBuilder implements ModelBuilder {
// gathered with problem collector // gathered with problem collector
add(Severity.ERROR, Version.V40, "Failed to load project " + pom, e); add(Severity.ERROR, Version.V40, "Failed to load project " + pom, e);
} }
if (r != result) {
r.getProblems().forEach(result::addProblem);
}
} }
static <T> Set<T> concat(Set<T> a, T b) { static <T> Set<T> concat(Set<T> a, T b) {
@ -1731,12 +1689,9 @@ public class DefaultModelBuilder implements ModelBuilder {
modelBuilderSession.buildEffectiveModel(importIds); modelBuilderSession.buildEffectiveModel(importIds);
importResult = modelBuilderSession.result; importResult = modelBuilderSession.result;
} catch (ModelBuilderException e) { } catch (ModelBuilderException e) {
e.getResult().getProblems().forEach(this::add);
return null; return null;
} }
importResult.getProblems().forEach(this::add);
importModel = importResult.getEffectiveModel(); importModel = importResult.getEffectiveModel();
return importModel; return importModel;
@ -1858,6 +1813,7 @@ public class DefaultModelBuilder implements ModelBuilder {
return subprojects; return subprojects;
} }
@Override
public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException { public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException {
ModelBuilderSessionState build = new ModelBuilderSessionState(request); ModelBuilderSessionState build = new ModelBuilderSessionState(request);
Model model = build.readRawModel(); Model model = build.readRawModel();

View File

@ -20,20 +20,15 @@ package org.apache.maven.internal.impl.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.maven.api.model.Model; import org.apache.maven.api.model.Model;
import org.apache.maven.api.model.Profile; import org.apache.maven.api.model.Profile;
import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.ModelBuilderResult; import org.apache.maven.api.services.ModelBuilderResult;
import org.apache.maven.api.services.ModelProblem; import org.apache.maven.api.services.ModelProblem;
import org.apache.maven.api.services.ModelSource; import org.apache.maven.api.services.ModelSource;
import org.apache.maven.api.services.ProblemCollector;
/** /**
* Collects the output of the model builder. * Collects the output of the model builder.
@ -46,23 +41,14 @@ class DefaultModelBuilderResult implements ModelBuilderResult {
private Model effectiveModel; private Model effectiveModel;
private List<Profile> activePomProfiles; private List<Profile> activePomProfiles;
private List<Profile> activeExternalProfiles; private List<Profile> activeExternalProfiles;
private final Queue<ModelProblem> problems = new ConcurrentLinkedQueue<>(); private final ProblemCollector<ModelProblem> problemCollector;
private final DefaultModelBuilderResult problemHolder;
private final List<DefaultModelBuilderResult> children = new ArrayList<>(); private final List<DefaultModelBuilderResult> children = new ArrayList<>();
private int maxProblems; DefaultModelBuilderResult(ProblemCollector<ModelProblem> problemCollector) {
private Map<BuilderProblem.Severity, AtomicInteger> problemCount = new ConcurrentHashMap<>(); this.problemCollector = problemCollector;
DefaultModelBuilderResult(int maxProblems) {
this(null, maxProblems);
}
DefaultModelBuilderResult(DefaultModelBuilderResult problemHolder, int maxProblems) {
this.problemHolder = problemHolder;
this.maxProblems = maxProblems;
} }
@Override
public ModelSource getSource() { public ModelSource getSource() {
return source; return source;
} }
@ -132,39 +118,8 @@ class DefaultModelBuilderResult implements ModelBuilderResult {
* guaranteed to be non-null but possibly empty. * guaranteed to be non-null but possibly empty.
*/ */
@Override @Override
public List<ModelProblem> getProblems() { public ProblemCollector<ModelProblem> getProblemCollector() {
List<ModelProblem> additionalProblems = new ArrayList<>(); return problemCollector;
problemCount.forEach((s, i) -> {
if (i.get() > maxProblems) {
additionalProblems.add(new DefaultModelProblem(
String.format("Too many problems %d of severity %s", i.get(), s.name()),
s,
ModelProblem.Version.BASE,
null,
-1,
-1,
null,
null));
}
});
return Stream.concat(problems.stream(), additionalProblems.stream()).toList();
}
/**
* Adds a given problem to the list of problems and propagates it to the parent result if present.
*
* @param problem The problem to be added. It must be an instance of ModelProblem.
*/
public void addProblem(ModelProblem problem) {
int problemCount = this.problemCount
.computeIfAbsent(problem.getSeverity(), s -> new AtomicInteger())
.incrementAndGet();
if (problemCount < maxProblems) {
problems.add(problem);
}
if (problemHolder != null) {
problemHolder.addProblem(problem);
}
} }
@Override @Override
@ -183,18 +138,19 @@ class DefaultModelBuilderResult implements ModelBuilderResult {
} else { } else {
modelId = null; modelId = null;
} }
if (!problems.isEmpty()) { if (problemCollector.hasWarningProblems()) {
int totalProblems = problemCollector.totalProblemsReported();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(problems.size()) sb.append(totalProblems)
.append( .append(
(problems.size() == 1) (totalProblems == 1)
? " problem was " ? " problem was "
: " problems were encountered while building the effective model"); : " problems were encountered while building the effective model");
if (modelId != null && !modelId.isEmpty()) { if (modelId != null && !modelId.isEmpty()) {
sb.append(" for "); sb.append(" for ");
sb.append(modelId); sb.append(modelId);
} }
for (ModelProblem problem : problems) { for (ModelProblem problem : problemCollector.problems().toList()) {
sb.append(System.lineSeparator()); sb.append(System.lineSeparator());
sb.append(" - ["); sb.append(" - [");
sb.append(problem.getSeverity()); sb.append(problem.getSeverity());

View File

@ -1,230 +0,0 @@
/*
* 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.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.maven.api.services.BuilderProblem.Severity;
import org.apache.maven.api.services.ModelProblem;
/**
* There are various forms of results that are represented by this class:
* <ol>
* <li>success - in which case only the model field is set
* <li>success with warnings - model field + non-error model problems
* <li>error - no model, but diagnostics
* <li>error - (partial) model and diagnostics
* </ol>
* Could encode these variants as subclasses, but kept in one for now
*
* @param <T> the model type
*/
public class Result<T> {
/**
* Success without warnings
*
* @param model
*/
public static <T> Result<T> success(T model) {
return success(model, Collections.emptyList());
}
/**
* Success with warnings
*
* @param model
* @param problems
*/
public static <T> Result<T> success(T model, Iterable<? extends ModelProblem> problems) {
assert !hasErrors(problems);
return new Result<>(false, model, problems);
}
/**
* Success with warnings
*
* @param model
* @param results
*/
public static <T> Result<T> success(T model, Result<?>... results) {
final List<ModelProblem> problemsList = new ArrayList<>();
for (Result<?> result1 : results) {
for (ModelProblem modelProblem : result1.getProblems()) {
problemsList.add(modelProblem);
}
}
return success(model, problemsList);
}
/**
* Error with problems describing the cause
*
* @param problems
*/
public static <T> Result<T> error(Iterable<? extends ModelProblem> problems) {
return error(null, problems);
}
public static <T> Result<T> error(T model) {
return error(model, Collections.emptyList());
}
public static <T> Result<T> error(Result<?> result) {
return error(result.getProblems());
}
public static <T> Result<T> error(Result<?>... results) {
final List<ModelProblem> problemsList = new ArrayList<>();
for (Result<?> result1 : results) {
for (ModelProblem modelProblem : result1.getProblems()) {
problemsList.add(modelProblem);
}
}
return error(problemsList);
}
/**
* Error with partial result and problems describing the cause
*
* @param model
* @param problems
*/
public static <T> Result<T> error(T model, Iterable<? extends ModelProblem> problems) {
return new Result<>(true, model, problems);
}
/**
* New result - determine whether error or success by checking problems for errors
*
* @param model
* @param problems
*/
public static <T> Result<T> newResult(T model, Iterable<? extends ModelProblem> problems) {
return new Result<>(hasErrors(problems), model, problems);
}
/**
* New result consisting of given result and new problem. Convenience for newResult(result.get(),
* concat(result.getProblems(),problems)).
*
* @param result
* @param problem
*/
public static <T> Result<T> addProblem(Result<T> result, ModelProblem problem) {
return addProblems(result, Collections.singleton(problem));
}
/**
* New result that includes the given
*
* @param result
* @param problems
*/
public static <T> Result<T> addProblems(Result<T> result, Iterable<? extends ModelProblem> problems) {
Collection<ModelProblem> list = new ArrayList<>();
for (ModelProblem item : problems) {
list.add(item);
}
for (ModelProblem item : result.getProblems()) {
list.add(item);
}
return new Result<>(result.hasErrors() || hasErrors(problems), result.get(), list);
}
public static <T> Result<T> addProblems(Result<T> result, Result<?>... results) {
final List<ModelProblem> problemsList = new ArrayList<>();
for (Result<?> result1 : results) {
for (ModelProblem modelProblem : result1.getProblems()) {
problemsList.add(modelProblem);
}
}
return addProblems(result, problemsList);
}
/**
* Turns the given results into a single result by combining problems and models into single collection.
*
* @param results
*/
public static <T> Result<Iterable<T>> newResultSet(Iterable<? extends Result<? extends T>> results) {
boolean hasErrors = false;
List<T> modelsList = new ArrayList<>();
List<ModelProblem> problemsList = new ArrayList<>();
for (Result<? extends T> result : results) {
modelsList.add(result.get());
for (ModelProblem modelProblem : result.getProblems()) {
problemsList.add(modelProblem);
}
if (result.hasErrors()) {
hasErrors = true;
}
}
return new Result<>(hasErrors, (Iterable<T>) modelsList, problemsList);
}
// helper to determine if problems contain error
private static boolean hasErrors(Iterable<? extends ModelProblem> problems) {
for (ModelProblem input : problems) {
if (input.getSeverity().equals(Severity.ERROR)
|| input.getSeverity().equals(Severity.FATAL)) {
return true;
}
}
return false;
}
/**
* Class definition
*/
private final boolean errors;
private final T value;
private final Iterable<? extends ModelProblem> problems;
private Result(boolean errors, T model, Iterable<? extends ModelProblem> problems) {
this.errors = errors;
this.value = model;
this.problems = problems;
}
public Iterable<? extends ModelProblem> getProblems() {
return problems;
}
public T get() {
return value;
}
public boolean hasErrors() {
return errors;
}
}

View File

@ -35,6 +35,7 @@ import org.apache.maven.api.services.ModelBuilderRequest;
import org.apache.maven.api.services.ModelBuilderResult; import org.apache.maven.api.services.ModelBuilderResult;
import org.apache.maven.api.services.ModelProblem; import org.apache.maven.api.services.ModelProblem;
import org.apache.maven.api.services.ModelSource; import org.apache.maven.api.services.ModelSource;
import org.apache.maven.api.services.ProblemCollector;
import org.apache.maven.api.services.model.ModelResolverException; import org.apache.maven.api.services.model.ModelResolverException;
import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.internal.impl.InternalSession;
import org.apache.maven.internal.impl.model.ModelProblemUtils; import org.apache.maven.internal.impl.model.ModelProblemUtils;
@ -207,20 +208,22 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader
ModelBuilderResult modelResult = modelBuilder.newSession().build(modelRequest); ModelBuilderResult modelResult = modelBuilder.newSession().build(modelRequest);
// ModelBuildingEx is thrown only on FATAL and ERROR severities, but we still can have WARNs // ModelBuildingEx is thrown only on FATAL and ERROR severities, but we still can have WARNs
// that may lead to unexpected build failure, log them // that may lead to unexpected build failure, log them
if (!modelResult.getProblems().isEmpty()) { if (modelResult.getProblemCollector().hasWarningProblems()) {
List<ModelProblem> problems = modelResult.getProblems(); ProblemCollector<ModelProblem> problemCollector = modelResult.getProblemCollector();
int totalProblems = problemCollector.totalProblemsReported();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(problems.size()) sb.append(totalProblems)
.append(" ") .append(" ")
.append((problems.size() == 1) ? "problem was" : "problems were") .append((totalProblems == 1) ? "problem was" : "problems were")
.append(" encountered while building the effective model for ") .append(" encountered while building the effective model for '")
.append(request.getArtifact()) .append(request.getArtifact())
.append(" during ") .append("' during ")
.append(RequestTraceHelper.interpretTrace(true, request.getTrace())) .append(RequestTraceHelper.interpretTrace(true, request.getTrace()))
.append("\n") .append("\n")
.append((problems.size() == 1) ? "Problem" : "Problems"); .append((totalProblems == 1) ? "Problem" : "Problems");
for (ModelProblem modelProblem : problems) { for (ModelProblem modelProblem :
problemCollector.problems().toList()) {
sb.append("\n* ") sb.append("\n* ")
.append(modelProblem.getMessage()) .append(modelProblem.getMessage())
.append(" @ ") .append(" @ ")
@ -229,16 +232,17 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader
logger.warn(sb.toString()); logger.warn(sb.toString());
} else { } else {
logger.warn( logger.warn(
"{} {} encountered while building the effective model for {} during {} (use -X to see details)", "{} {} encountered while building the effective model for '{}' during {} (use -X to see details)",
problems.size(), totalProblems,
(problems.size() == 1) ? "problem was" : "problems were", (totalProblems == 1) ? "problem was" : "problems were",
request.getArtifact(), request.getArtifact(),
RequestTraceHelper.interpretTrace(false, request.getTrace())); RequestTraceHelper.interpretTrace(false, request.getTrace()));
} }
} }
model = modelResult.getEffectiveModel(); model = modelResult.getEffectiveModel();
} catch (ModelBuilderException e) { } catch (ModelBuilderException e) {
for (ModelProblem problem : e.getResult().getProblems()) { for (ModelProblem problem :
e.getResult().getProblemCollector().problems().toList()) {
if (problem.getException() instanceof ModelResolverException) { if (problem.getException() instanceof ModelResolverException) {
result.addException(problem.getException()); result.addException(problem.getException());
throw new ArtifactDescriptorException(result); throw new ArtifactDescriptorException(result);

View File

@ -109,8 +109,7 @@ public class DefaultVersionResolver implements VersionResolver {
cacheKey = new Key(session, request); cacheKey = new Key(session, request);
Object obj = cache.get(session, cacheKey); Object obj = cache.get(session, cacheKey);
if (obj instanceof Record) { if (obj instanceof Record record) {
Record record = (Record) obj;
result.setVersion(record.version); result.setVersion(record.version);
result.setRepository( result.setRepository(
getRepository(session, request.getRepositories(), record.repoClass, record.repoId)); getRepository(session, request.getRepositories(), record.repoClass, record.repoId));
@ -189,8 +188,7 @@ public class DefaultVersionResolver implements VersionResolver {
if (result.getVersion() != null && result.getVersion().endsWith(SNAPSHOT)) { if (result.getVersion() != null && result.getVersion().endsWith(SNAPSHOT)) {
VersionRequest subRequest = new VersionRequest(); VersionRequest subRequest = new VersionRequest();
subRequest.setArtifact(artifact.setVersion(result.getVersion())); subRequest.setArtifact(artifact.setVersion(result.getVersion()));
if (result.getRepository() instanceof RemoteRepository) { if (result.getRepository() instanceof RemoteRepository r) {
RemoteRepository r = (RemoteRepository) result.getRepository();
subRequest.setRepositories(Collections.singletonList(r)); subRequest.setRepositories(Collections.singletonList(r));
} else { } else {
subRequest.setRepositories(request.getRepositories()); subRequest.setRepositories(request.getRepositories());

View File

@ -0,0 +1,162 @@
/*
* 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;
import java.util.stream.IntStream;
import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.ProblemCollector;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* This UT is for {@link ProblemCollector} but here we have implementations for problems.
*/
class DefaultProblemCollectorTest {
@Test
void severityFatalDetection() {
ProblemCollector<BuilderProblem> collector = ProblemCollector.create(5);
assertFalse(collector.hasProblemsFor(BuilderProblem.Severity.WARNING));
assertFalse(collector.hasErrorProblems());
assertFalse(collector.hasFatalProblems());
collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.FATAL));
// fatal triggers all
assertTrue(collector.hasProblemsFor(BuilderProblem.Severity.WARNING));
assertTrue(collector.hasErrorProblems());
assertTrue(collector.hasFatalProblems());
}
@Test
void severityErrorDetection() {
ProblemCollector<BuilderProblem> collector = ProblemCollector.create(5);
assertFalse(collector.hasProblemsFor(BuilderProblem.Severity.WARNING));
assertFalse(collector.hasErrorProblems());
assertFalse(collector.hasFatalProblems());
collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.ERROR));
// error triggers error + warning
assertTrue(collector.hasProblemsFor(BuilderProblem.Severity.WARNING));
assertTrue(collector.hasErrorProblems());
assertFalse(collector.hasFatalProblems());
}
@Test
void severityWarningDetection() {
ProblemCollector<BuilderProblem> collector = ProblemCollector.create(5);
assertFalse(collector.hasProblemsFor(BuilderProblem.Severity.WARNING));
assertFalse(collector.hasErrorProblems());
assertFalse(collector.hasFatalProblems());
collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.WARNING));
// warning triggers warning only
assertTrue(collector.hasProblemsFor(BuilderProblem.Severity.WARNING));
assertFalse(collector.hasErrorProblems());
assertFalse(collector.hasFatalProblems());
}
@Test
void lossy() {
ProblemCollector<BuilderProblem> collector = ProblemCollector.create(5);
IntStream.range(0, 5)
.forEach(i -> collector.reportProblem(new DefaultBuilderProblem(
"source", 0, 0, null, "message " + i, BuilderProblem.Severity.WARNING)));
// collector is "full" of warnings
assertFalse(collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.WARNING)));
// but collector will drop warning for more severe issues
assertTrue(collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.ERROR)));
assertTrue(collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.FATAL)));
// collector is full of warnings, errors and fatal (mixed)
assertFalse(collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.WARNING)));
// fill it up with fatal ones
IntStream.range(0, 5)
.forEach(i -> collector.reportProblem(new DefaultBuilderProblem(
"source", 0, 0, null, "message " + i, BuilderProblem.Severity.FATAL)));
// from now on, only counters work, problems are lost
assertFalse(collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.WARNING)));
assertFalse(collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.ERROR)));
assertFalse(collector.reportProblem(
new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.FATAL)));
assertEquals(17, collector.totalProblemsReported());
assertEquals(8, collector.problemsReportedFor(BuilderProblem.Severity.WARNING));
assertEquals(2, collector.problemsReportedFor(BuilderProblem.Severity.ERROR));
assertEquals(7, collector.problemsReportedFor(BuilderProblem.Severity.FATAL));
// but preserved problems count == capacity
assertEquals(5, collector.problems().count());
}
@Test
void moreSeverePushOutLeastSevere() {
ProblemCollector<BuilderProblem> collector = ProblemCollector.create(5);
assertEquals(0, collector.totalProblemsReported());
assertEquals(0, collector.problems().count());
IntStream.range(0, 5)
.forEach(i -> collector.reportProblem(new DefaultBuilderProblem(
"source", 0, 0, null, "message " + i, BuilderProblem.Severity.WARNING)));
assertEquals(5, collector.totalProblemsReported());
assertEquals(5, collector.problems().count());
IntStream.range(0, 5)
.forEach(i -> collector.reportProblem(new DefaultBuilderProblem(
"source", 0, 0, null, "message " + i, BuilderProblem.Severity.ERROR)));
assertEquals(10, collector.totalProblemsReported());
assertEquals(5, collector.problems().count());
IntStream.range(0, 4)
.forEach(i -> collector.reportProblem(new DefaultBuilderProblem(
"source", 0, 0, null, "message " + i, BuilderProblem.Severity.FATAL)));
assertEquals(14, collector.totalProblemsReported());
assertEquals(5, collector.problems().count());
assertEquals(5, collector.problemsReportedFor(BuilderProblem.Severity.WARNING));
assertEquals(5, collector.problemsReportedFor(BuilderProblem.Severity.ERROR));
assertEquals(4, collector.problemsReportedFor(BuilderProblem.Severity.FATAL));
assertEquals(0, collector.problems(BuilderProblem.Severity.WARNING).count());
assertEquals(1, collector.problems(BuilderProblem.Severity.ERROR).count());
assertEquals(4, collector.problems(BuilderProblem.Severity.FATAL).count());
}
}

View File

@ -21,6 +21,7 @@ package org.apache.maven.internal.impl;
import java.util.List; import java.util.List;
import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.ProblemCollector;
import org.apache.maven.api.services.SettingsBuilder; import org.apache.maven.api.services.SettingsBuilder;
import org.apache.maven.api.settings.Profile; import org.apache.maven.api.settings.Profile;
import org.apache.maven.api.settings.Repository; import org.apache.maven.api.settings.Repository;
@ -56,32 +57,29 @@ class DefaultSettingsValidatorTest {
void testValidate() { void testValidate() {
Profile prof = Profile.newBuilder().id("xxx").build(); Profile prof = Profile.newBuilder().id("xxx").build();
Settings model = Settings.newBuilder().profiles(List.of(prof)).build(); Settings model = Settings.newBuilder().profiles(List.of(prof)).build();
List<BuilderProblem> problems = validator.validate(model); ProblemCollector<BuilderProblem> problems = validator.validate(model);
assertEquals(0, problems.size()); assertEquals(0, problems.totalProblemsReported());
Repository repo = org.apache.maven.api.settings.Repository.newInstance(false); Repository repo = org.apache.maven.api.settings.Repository.newInstance(false);
Settings model2 = Settings.newBuilder() Settings model2 = Settings.newBuilder()
.profiles(List.of(prof.withRepositories(List.of(repo)))) .profiles(List.of(prof.withRepositories(List.of(repo))))
.build(); .build();
problems.clear();
problems = validator.validate(model2); problems = validator.validate(model2);
assertEquals(2, problems.size()); assertEquals(2, problems.totalProblemsReported());
repo = repo.withUrl("http://xxx.xxx.com"); repo = repo.withUrl("http://xxx.xxx.com");
model2 = Settings.newBuilder() model2 = Settings.newBuilder()
.profiles(List.of(prof.withRepositories(List.of(repo)))) .profiles(List.of(prof.withRepositories(List.of(repo))))
.build(); .build();
problems.clear();
problems = validator.validate(model2); problems = validator.validate(model2);
assertEquals(1, problems.size()); assertEquals(1, problems.totalProblemsReported());
repo = repo.withId("xxx"); repo = repo.withId("xxx");
model2 = Settings.newBuilder() model2 = Settings.newBuilder()
.profiles(List.of(prof.withRepositories(List.of(repo)))) .profiles(List.of(prof.withRepositories(List.of(repo))))
.build(); .build();
problems.clear();
problems = validator.validate(model2); problems = validator.validate(model2);
assertEquals(0, problems.size()); assertEquals(0, problems.totalProblemsReported());
} }
/* /*

View File

@ -76,7 +76,8 @@ class ComplexActivationTest {
.build(); .build();
ModelBuilderResult result = builder.newSession().build(request); ModelBuilderResult result = builder.newSession().build(request);
assertNotNull(result); assertNotNull(result);
assertTrue(result.getProblems().stream() assertTrue(result.getProblemCollector()
.problems()
.anyMatch(p -> p.getSeverity() == BuilderProblem.Severity.WARNING .anyMatch(p -> p.getSeverity() == BuilderProblem.Severity.WARNING
&& p.getMessage().contains("The 'missing' assertion will be ignored."))); && p.getMessage().contains("The 'missing' assertion will be ignored.")));
} }

View File

@ -18,7 +18,6 @@
*/ */
package org.apache.maven.internal.impl.model.profile; package org.apache.maven.internal.impl.model.profile;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.maven.api.model.InputLocation; import org.apache.maven.api.model.InputLocation;
@ -27,6 +26,7 @@ import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.ModelBuilderException; import org.apache.maven.api.services.ModelBuilderException;
import org.apache.maven.api.services.ModelProblem; import org.apache.maven.api.services.ModelProblem;
import org.apache.maven.api.services.ModelProblemCollector; import org.apache.maven.api.services.ModelProblemCollector;
import org.apache.maven.api.services.ProblemCollector;
import org.apache.maven.internal.impl.model.DefaultModelProblem; import org.apache.maven.internal.impl.model.DefaultModelProblem;
/** /**
@ -34,23 +34,11 @@ import org.apache.maven.internal.impl.model.DefaultModelProblem;
*/ */
public class SimpleProblemCollector implements ModelProblemCollector { public class SimpleProblemCollector implements ModelProblemCollector {
final List<ModelProblem> problems = new ArrayList<>(); final ProblemCollector<ModelProblem> problemCollector = ProblemCollector.create(100);
@Override @Override
public List<ModelProblem> getProblems() { public ProblemCollector<ModelProblem> getProblemCollector() {
return problems; return problemCollector;
}
@Override
public boolean hasErrors() {
return problems.stream()
.anyMatch(p -> p.getSeverity() == ModelProblem.Severity.FATAL
|| p.getSeverity() == ModelProblem.Severity.ERROR);
}
@Override
public boolean hasFatalErrors() {
return problems.stream().anyMatch(p -> p.getSeverity() == ModelProblem.Severity.FATAL);
} }
@Override @Override
@ -70,11 +58,6 @@ public class SimpleProblemCollector implements ModelProblemCollector {
exception)); exception));
} }
@Override
public void add(ModelProblem problem) {
this.problems.add(problem);
}
@Override @Override
public ModelBuilderException newModelBuilderException() { public ModelBuilderException newModelBuilderException() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@ -118,8 +101,8 @@ public class SimpleProblemCollector implements ModelProblemCollector {
} }
private List<String> getForLevel(BuilderProblem.Severity severity) { private List<String> getForLevel(BuilderProblem.Severity severity) {
return problems.stream() return problemCollector
.filter(p -> p.getSeverity() == severity) .problems(severity)
.map(BuilderProblem::getMessage) .map(BuilderProblem::getMessage)
.toList(); .toList();
} }

View File

@ -47,8 +47,7 @@ class MavenITmng8379SettingsDecryptTest extends AbstractMavenIntegrationTestCase
verifier.verifyErrorFreeLog(); verifier.verifyErrorFreeLog();
// there is a warning and all fields decrypted // there is a warning and all fields decrypted
verifier.verifyTextInLog( verifier.verifyTextInLog(" encountered while building the effective settings (use -e to see details)");
"[INFO] Some problems were encountered while building the effective settings (use -X to see details)");
verifier.verifyTextInLog("<password>testtest</password>"); verifier.verifyTextInLog("<password>testtest</password>");
verifier.verifyTextInLog("<value>testtest</value>"); verifier.verifyTextInLog("<value>testtest</value>");
} }
@ -71,8 +70,7 @@ class MavenITmng8379SettingsDecryptTest extends AbstractMavenIntegrationTestCase
// there is no warning and all fields decrypted // there is no warning and all fields decrypted
verifier.verifyTextNotInLog("[WARNING]"); verifier.verifyTextNotInLog("[WARNING]");
verifier.verifyTextNotInLog( verifier.verifyTextNotInLog(" encountered while building the effective settings (use -e to see details)");
"[INFO] Some problems were encountered while building the effective settings (use -X to see details)");
verifier.verifyTextInLog("<password>testtest</password>"); verifier.verifyTextInLog("<password>testtest</password>");
verifier.verifyTextInLog("<value>secretHeader</value>"); verifier.verifyTextInLog("<value>secretHeader</value>");
} }

View File

@ -156,7 +156,7 @@ under the License.
<jlineVersion>3.28.0</jlineVersion> <jlineVersion>3.28.0</jlineVersion>
<junitVersion>5.11.4</junitVersion> <junitVersion>5.11.4</junitVersion>
<jxpathVersion>1.3</jxpathVersion> <jxpathVersion>1.3</jxpathVersion>
<logbackClassicVersion>1.5.12</logbackClassicVersion> <logbackClassicVersion>1.5.13</logbackClassicVersion>
<mockitoVersion>5.14.2</mockitoVersion> <mockitoVersion>5.14.2</mockitoVersion>
<plexusInteractivityVersion>1.3</plexusInteractivityVersion> <plexusInteractivityVersion>1.3</plexusInteractivityVersion>
<plexusInterpolationVersion>1.27</plexusInterpolationVersion> <plexusInterpolationVersion>1.27</plexusInterpolationVersion>