diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenBuilderException.java index 1af8ab33556e..27292d4f08ac 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenBuilderException.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenBuilderException.java @@ -18,10 +18,6 @@ */ 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; /** @@ -32,14 +28,14 @@ @Experimental public abstract class MavenBuilderException extends MavenException { - private final List problems; + private final ProblemCollector problems; public MavenBuilderException(String message, Throwable cause) { super(message, cause); - problems = List.of(); + problems = ProblemCollector.empty(); } - public MavenBuilderException(String message, List problems) { + public MavenBuilderException(String message, ProblemCollector problems) { super(buildMessage(message, problems), null); this.problems = problems; } @@ -49,20 +45,16 @@ public MavenBuilderException(String message, List problems) { * 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. */ - protected static String buildMessage(String message, List problems) { + protected static String buildMessage(String message, ProblemCollector problems) { StringBuilder msg = new StringBuilder(message); - ArrayList sorted = new ArrayList<>(problems); - sorted.sort(Comparator.comparing(BuilderProblem::getSeverity)); - for (BuilderProblem problem : sorted) { - msg.append("\n * ") - .append(problem.getSeverity().name()) - .append(": ") - .append(problem.getMessage()); - } + problems.problems().forEach(problem -> msg.append("\n * ") + .append(problem.getSeverity().name()) + .append(": ") + .append(problem.getMessage())); return msg.toString(); } - public List getProblems() { + public ProblemCollector getProblemCollector() { return problems; } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java index 19921e5624f3..c01fa138ab9d 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java @@ -19,8 +19,6 @@ package org.apache.maven.api.services; import java.io.Serial; -import java.util.Collections; -import java.util.List; import org.apache.maven.api.annotations.Experimental; @@ -82,10 +80,10 @@ public String getModelId() { * * @return The problems that caused this exception, never {@code null}. */ - public List getProblems() { + public ProblemCollector getProblemCollector() { if (result == null) { - return Collections.emptyList(); + return ProblemCollector.empty(); } - return Collections.unmodifiableList(result.getProblems()); + return result.getProblemCollector(); } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java index bf98e933911b..df38a5a62dc7 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java @@ -91,12 +91,12 @@ public interface ModelBuilderResult { List 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 - List getProblems(); + ProblemCollector getProblemCollector(); /** * Gets the children of this result. diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java index 9ef9aa861ed3..66c1fcc31ead 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java @@ -18,8 +18,6 @@ */ package org.apache.maven.api.services; -import java.util.List; - import org.apache.maven.api.model.InputLocation; import org.apache.maven.api.model.Model; @@ -33,15 +31,15 @@ */ public interface ModelProblemCollector { - /** - * The collected problems. - * @return a list of model problems encountered, never {@code null} - */ - List getProblems(); + ProblemCollector getProblemCollector(); - 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) { add(severity, version, message, null, null); @@ -64,7 +62,9 @@ void add( InputLocation location, Exception exception); - void add(ModelProblem problem); + default void add(ModelProblem problem) { + getProblemCollector().reportProblem(problem); + } ModelBuilderException newModelBuilderException(); diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java new file mode 100644 index 000000000000..13d8c3552049 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java @@ -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

The type of the problem. + * @since 4.0.0 + */ +@Experimental +public interface ProblemCollector

{ + /** + * 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

problems() { + Stream

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

problems(BuilderProblem.Severity severity); + + /** + * Creates "empty" problem collector. + */ + @Nonnull + static

ProblemCollector

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

problems(BuilderProblem.Severity severity) { + return Stream.empty(); + } + }; + } + + /** + * Creates new instance of problem collector. + */ + @Nonnull + static

ProblemCollector

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

ProblemCollector

create(int maxCountLimit) { + return new Impl<>(maxCountLimit); + } + + class Impl

implements ProblemCollector

{ + + private final int maxCountLimit; + private final AtomicInteger totalCount; + private final ConcurrentMap counters; + private final ConcurrentMap> problems; + + private static final List 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

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

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

problems = getProblems(s); + while (!problems.isEmpty()) { + try { + return problems.remove(0) != null; + } catch (IndexOutOfBoundsException e) { + // empty, continue + } + } + } + } + return false; + } + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilder.java index c0e66d8ac53a..198e5c5fd930 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilder.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilder.java @@ -19,7 +19,6 @@ package org.apache.maven.api.services; import java.nio.file.Path; -import java.util.List; import org.apache.maven.api.Service; import org.apache.maven.api.Session; @@ -108,7 +107,7 @@ default SettingsBuilderResult build( * @return The list of problems that were encountered, must not be {@code null}. */ @Nonnull - default List validate(@Nonnull Settings settings) { + default ProblemCollector validate(@Nonnull Settings settings) { return validate(settings, false); } @@ -120,7 +119,7 @@ default List validate(@Nonnull Settings settings) { * @return The list of problems that were encountered, must not be {@code null}. */ @Nonnull - List validate(@Nonnull Settings settings, boolean isProjectSettings); + ProblemCollector validate(@Nonnull Settings settings, boolean isProjectSettings); /** * Convert a model profile to a settings profile. diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java index c324a9250a9b..a8ccd0f47f53 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java @@ -19,7 +19,6 @@ package org.apache.maven.api.services; import java.io.Serial; -import java.util.List; import org.apache.maven.api.annotations.Experimental; @@ -42,7 +41,7 @@ public SettingsBuilderException(String message, Exception e) { super(message, e); } - public SettingsBuilderException(String message, List problems) { + public SettingsBuilderException(String message, ProblemCollector problems) { super(message, problems); } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java index 7286fba9c17b..c05e244767fa 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java @@ -18,8 +18,6 @@ */ package org.apache.maven.api.services; -import java.util.List; - import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Nonnull; 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} */ @Nonnull - List getProblems(); + ProblemCollector getProblems(); } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java index 89a2c8ffef09..10bd3b5dc5bc 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java @@ -19,7 +19,6 @@ package org.apache.maven.api.services; import java.io.Serial; -import java.util.List; import org.apache.maven.api.annotations.Experimental; @@ -42,7 +41,7 @@ public ToolchainsBuilderException(String message, Exception e) { super(message, e); } - public ToolchainsBuilderException(String message, List problems) { + public ToolchainsBuilderException(String message, ProblemCollector problems) { super(message, problems); } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java index dbf9d91d3f65..d48d45bd799e 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java @@ -18,8 +18,6 @@ */ package org.apache.maven.api.services; -import java.util.List; - import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Nonnull; 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} */ @Nonnull - List getProblems(); + ProblemCollector getProblems(); } diff --git a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java index 7f1e97f9bc4c..29c93ee48384 100644 --- a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java +++ b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java @@ -95,9 +95,11 @@ public SettingsBuildingResult build(SettingsBuildingRequest request) throws Sett .build()); return new DefaultSettingsBuildingResult( - new Settings(result.getEffectiveSettings()), convert(result.getProblems())); + new Settings(result.getEffectiveSettings()), + convert(result.getProblems().problems().toList())); } catch (SettingsBuilderException e) { - throw new SettingsBuildingException(convert(e.getProblems())); + throw new SettingsBuildingException( + convert(e.getProblemCollector().problems().toList())); } } diff --git a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java index 59806240121d..fa323a6763d8 100644 --- a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java +++ b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java @@ -22,10 +22,10 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.util.List; import java.util.regex.Pattern; 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.settings.Settings; import org.apache.maven.settings.building.SettingsProblem.Severity; @@ -56,8 +56,8 @@ public void validate(Settings settings, SettingsProblemCollector problems) { @Override public void validate(Settings settings, boolean isProjectSettings, SettingsProblemCollector problems) { - List list = settingsBuilder.validate(settings.getDelegate(), isProjectSettings); - for (BuilderProblem problem : list) { + ProblemCollector list = settingsBuilder.validate(settings.getDelegate(), isProjectSettings); + for (BuilderProblem problem : list.problems().toList()) { addViolation(problems, Severity.valueOf(problem.getSeverity().name()), problem.getMessage()); } } diff --git a/compat/maven-settings-builder/src/test/java/org/apache/maven/settings/validation/DefaultSettingsValidatorTest.java b/compat/maven-settings-builder/src/test/java/org/apache/maven/settings/validation/DefaultSettingsValidatorTest.java index 69cb2ca8e9ca..31219e345bcf 100644 --- a/compat/maven-settings-builder/src/test/java/org/apache/maven/settings/validation/DefaultSettingsValidatorTest.java +++ b/compat/maven-settings-builder/src/test/java/org/apache/maven/settings/validation/DefaultSettingsValidatorTest.java @@ -99,9 +99,10 @@ void testValidateMirror() throws Exception { SimpleProblemCollector problems = new SimpleProblemCollector(); validator.validate(settings, problems); assertEquals(4, problems.messages.size()); - assertContains(problems.messages.get(0), "'mirrors.mirror.id' must not be 'local'"); - assertContains(problems.messages.get(1), "'mirrors.mirror.url' for local is missing"); - assertContains(problems.messages.get(2), "'mirrors.mirror.mirrorOf' for local is missing"); + // errors are now by severity descending + assertContains(problems.messages.get(0), "'mirrors.mirror.url' 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"); } @@ -121,11 +122,12 @@ void testValidateRepository() throws Exception { SimpleProblemCollector problems = new SimpleProblemCollector(); validator.validate(settings, problems); assertEquals(3, problems.messages.size()); + // errors are now by severity descending assertContains( - problems.messages.get(0), "'profiles.profile[default].repositories.repository.id' must not be 'local'"); - assertContains( - problems.messages.get(1), + problems.messages.get(0), "'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( problems.messages.get(2), "'profiles.profile[default].repositories.repository.id' must not contain any of these characters"); diff --git a/compat/maven-toolchain-builder/src/main/java/org/apache/maven/toolchain/building/DefaultToolchainsBuilder.java b/compat/maven-toolchain-builder/src/main/java/org/apache/maven/toolchain/building/DefaultToolchainsBuilder.java index 55a72ad94dbf..52471e96ee77 100644 --- a/compat/maven-toolchain-builder/src/main/java/org/apache/maven/toolchain/building/DefaultToolchainsBuilder.java +++ b/compat/maven-toolchain-builder/src/main/java/org/apache/maven/toolchain/building/DefaultToolchainsBuilder.java @@ -92,9 +92,11 @@ public ToolchainsBuildingResult build(ToolchainsBuildingRequest request) throws .build()); return new DefaultToolchainsBuildingResult( - new PersistedToolchains(result.getEffectiveToolchains()), convert(result.getProblems())); + new PersistedToolchains(result.getEffectiveToolchains()), + convert(result.getProblems().problems().toList())); } catch (ToolchainsBuilderException e) { - throw new ToolchainsBuildingException(convert(e.getProblems())); + throw new ToolchainsBuildingException( + convert(e.getProblemCollector().problems().toList())); } } diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java index 95d1ce9cc457..00bd9c658eec 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java @@ -597,13 +597,16 @@ protected void settings(C context, boolean emitSettingsWarnings, SettingsBuilder context.interactive = mayDisableInteractiveMode(context, context.effectiveSettings.isInteractiveMode()); 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( - "Some problems were encountered while building the effective settings (use -X to see details)"); + context.logger.info(String.format( + "%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)) { - for (BuilderProblem problem : settingsResult.getProblems()) { + if (context.invokerRequest.options().showErrors().orElse(false)) { + for (BuilderProblem problem : + settingsResult.getProblems().problems().toList()) { context.logger.warn(problem.getMessage() + " @ " + problem.getLocation()); } } diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java index 50291321709c..f185ad85f66e 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java @@ -205,15 +205,21 @@ protected void toolchains(MavenContext context, MavenExecutionRequest request) t new org.apache.maven.toolchain.model.PersistedToolchains( toolchainsResult.getEffectiveToolchains())); - if (!toolchainsResult.getProblems().isEmpty()) { - context.logger.warn(""); - context.logger.warn("Some problems were encountered while building the effective toolchains"); - - for (BuilderProblem problem : toolchainsResult.getProblems()) { - context.logger.warn(problem.getMessage() + " @ " + problem.getLocation()); + if (toolchainsResult.getProblems().hasWarningProblems()) { + int totalProblems = toolchainsResult.getProblems().totalProblemsReported(); + 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")); + + if (context.invokerRequest.options().showErrors().orElse(false)) { + for (BuilderProblem problem : + toolchainsResult.getProblems().problems().toList()) { + context.logger.warn(problem.getMessage() + " @ " + problem.getLocation()); + } } - context.logger.warn(""); + context.logger.info(""); } } diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java index 428f0aa199bf..583a2294be2f 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java @@ -66,6 +66,7 @@ import org.apache.maven.api.services.ModelProblemCollector; import org.apache.maven.api.services.ModelSource; 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.model.LifecycleBindingsInjector; import org.apache.maven.artifact.Artifact; @@ -340,7 +341,7 @@ ProjectBuildingResult build(Path pomFile, ModelSource modelSource) throws Projec try { MavenProject project = request.getProject(); - List modelProblems = null; + ProblemCollector problemCollector = null; Throwable error = null; if (project == null) { @@ -378,7 +379,7 @@ ProjectBuildingResult build(Path pomFile, ModelSource modelSource) throws Projec error = e; } - modelProblems = result.getProblems(); + problemCollector = result.getProblemCollector(); initProject(project, result); } @@ -391,7 +392,7 @@ ProjectBuildingResult build(Path pomFile, ModelSource modelSource) throws Projec } ProjectBuildingResult result = - new DefaultProjectBuildingResult(project, convert(modelProblems), resolutionResult); + new DefaultProjectBuildingResult(project, convert(problemCollector), resolutionResult); if (error != null) { ProjectBuildingException e = new ProjectBuildingException(List.of(result)); @@ -498,18 +499,14 @@ private List build(File pomFile, boolean recursive) { } catch (ModelBuilderException e) { result = e.getResult(); 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 results = new ArrayList<>(); List allModels = results(result).toList(); for (ModelBuilderResult r : allModels) { - List problems = new ArrayList<>(r.getProblems()); - results(r) - .filter(c -> c != r) - .flatMap(c -> c.getProblems().stream()) - .forEach(problems::remove); if (r.getEffectiveModel() != null) { File pom = r.getSource().getPath().toFile(); MavenProject project = @@ -529,9 +526,10 @@ private List build(File pomFile, boolean recursive) { if (request.isResolveDependencies()) { resolutionResult = resolveDependencies(project); } - results.add(new DefaultProjectBuildingResult(project, convert(problems), resolutionResult)); + results.add(new DefaultProjectBuildingResult( + project, convert(r.getProblemCollector()), resolutionResult)); } else { - results.add(new DefaultProjectBuildingResult(null, convert(problems), null)); + results.add(new DefaultProjectBuildingResult(null, convert(r.getProblemCollector()), null)); } } return results; @@ -541,11 +539,34 @@ private Stream results(ModelBuilderResult result) { return Stream.concat(result.getChildren().stream().flatMap(this::results), Stream.of(result)); } - private List convert(List problems) { - if (problems == null) { + private List convert( + ProblemCollector problemCollector) { + if (problemCollector == null) { return null; } - return problems.stream().map(p -> convert(p)).toList(); + ArrayList 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) { diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/collector/DefaultProjectsSelector.java b/impl/maven-core/src/main/java/org/apache/maven/project/collector/DefaultProjectsSelector.java index 69cca1453b3f..828e546c1cbb 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/project/collector/DefaultProjectsSelector.java +++ b/impl/maven-core/src/main/java/org/apache/maven/project/collector/DefaultProjectsSelector.java @@ -62,27 +62,33 @@ public List selectProjects(List files, MavenExecutionRequest List projects = new ArrayList<>(results.size()); - boolean problems = false; + long totalProblemsCount = 0; for (ProjectBuildingResult result : results) { 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( - "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()); - for (ModelProblem problem : result.getProblems()) { - String loc = ModelProblemUtils.formatLocation(problem, result.getProjectId()); - LOGGER.warn("{}{}", problem.getMessage(), ((loc != null && !loc.isEmpty()) ? " @ " + loc : "")); + if (request.isShowErrors()) { // this means -e or -X (as -X enables -e as well) + for (ModelProblem problem : result.getProblems()) { + 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("It is highly recommended to fix these problems" + " because they threaten the stability of your build."); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsBuilder.java index e542689f612b..397ce985312e 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsBuilder.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsBuilder.java @@ -26,7 +26,6 @@ import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -39,6 +38,7 @@ import org.apache.maven.api.di.Named; import org.apache.maven.api.services.BuilderProblem; 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.SettingsBuilderException; import org.apache.maven.api.services.SettingsBuilderRequest; @@ -97,7 +97,7 @@ public DefaultSettingsBuilder( @Override public SettingsBuilderResult build(SettingsBuilderRequest request) throws SettingsBuilderException { - List problems = new ArrayList<>(); + ProblemCollector problems = ProblemCollector.create(request.getSession()); Source installationSource = request.getInstallationSettingsSource().orElse(null); Settings installation = readSettings(installationSource, false, request, problems); @@ -139,30 +139,18 @@ public SettingsBuilderResult build(SettingsBuilderRequest request) throws Settin } } - if (hasErrors(problems)) { + if (problems.hasErrorProblems()) { throw new SettingsBuilderException("Error building settings", problems); } return new DefaultSettingsBuilderResult(effective, problems); } - private boolean hasErrors(List problems) { - if (problems != null) { - for (BuilderProblem problem : problems) { - if (BuilderProblem.Severity.ERROR.compareTo(problem.getSeverity()) >= 0) { - return true; - } - } - } - - return false; - } - private Settings readSettings( Source settingsSource, boolean isProjectSettings, SettingsBuilderRequest request, - List problems) { + ProblemCollector problems) { if (settingsSource == null) { return Settings.newInstance(); } @@ -184,7 +172,7 @@ private Settings readSettings( .strict(false) .build()); Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null; - problems.add(new DefaultBuilderProblem( + problems.reportProblem(new DefaultBuilderProblem( settingsSource.getLocation(), loc != null ? loc.getLineNumber() : -1, loc != null ? loc.getColumnNumber() : -1, @@ -195,7 +183,7 @@ private Settings readSettings( } } catch (XmlReaderException e) { Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null; - problems.add(new DefaultBuilderProblem( + problems.reportProblem(new DefaultBuilderProblem( settingsSource.getLocation(), loc != null ? loc.getLineNumber() : -1, loc != null ? loc.getColumnNumber() : -1, @@ -204,7 +192,7 @@ private Settings readSettings( BuilderProblem.Severity.FATAL)); return Settings.newInstance(); } catch (IOException e) { - problems.add(new DefaultBuilderProblem( + problems.reportProblem(new DefaultBuilderProblem( settingsSource.getLocation(), -1, -1, @@ -242,7 +230,8 @@ private Settings readSettings( return settings; } - private Settings interpolate(Settings settings, SettingsBuilderRequest request, List problems) { + private Settings interpolate( + Settings settings, SettingsBuilderRequest request, ProblemCollector problems) { Function src; if (request.getInterpolationSource().isPresent()) { src = request.getInterpolationSource().get(); @@ -269,7 +258,10 @@ protected Activation.Builder transformActivation_Condition( } private Settings decrypt( - Source settingsSource, Settings settings, SettingsBuilderRequest request, List problems) { + Source settingsSource, + Settings settings, + SettingsBuilderRequest request, + ProblemCollector problems) { if (dispatchers.isEmpty()) { return settings; } @@ -284,7 +276,7 @@ private Settings decrypt( try { return secDispatcher.decrypt(str); } catch (Exception e) { - problems.add(new DefaultBuilderProblem( + problems.reportProblem(new DefaultBuilderProblem( settingsSource.getLocation(), -1, -1, @@ -297,7 +289,7 @@ private Settings decrypt( }; Settings result = new SettingsTransformer(decryptFunction).visit(settings); if (preMaven4Passwords.get() > 0) { - problems.add(new DefaultBuilderProblem( + problems.reportProblem(new DefaultBuilderProblem( settingsSource.getLocation(), -1, -1, @@ -323,8 +315,9 @@ private Path getSecuritySettings(ProtoSession session) { } @Override - public List validate(Settings settings, boolean isProjectSettings) { - ArrayList problems = new ArrayList<>(); + public ProblemCollector validate(Settings settings, boolean isProjectSettings) { + // TODO: any way to get ProtoSession here? + ProblemCollector problems = ProblemCollector.create(null); settingsValidator.validate(settings, isProjectSettings, problems); return problems; } @@ -347,11 +340,11 @@ static class DefaultSettingsBuilderResult implements SettingsBuilderResult { private final Settings effectiveSettings; - private final List problems; + private final ProblemCollector problems; - DefaultSettingsBuilderResult(Settings effectiveSettings, List problems) { + DefaultSettingsBuilderResult(Settings effectiveSettings, ProblemCollector problems) { this.effectiveSettings = effectiveSettings; - this.problems = (problems != null) ? problems : new ArrayList<>(); + this.problems = (problems != null) ? problems : ProblemCollector.empty(); } @Override @@ -360,7 +353,7 @@ public Settings getEffectiveSettings() { } @Override - public List getProblems() { + public ProblemCollector getProblems() { return problems; } } diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsValidator.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsValidator.java index e3cc996c586e..31e68864cf72 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsValidator.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsValidator.java @@ -24,6 +24,7 @@ import java.util.regex.Pattern; 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.Profile; 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 @SuppressWarnings("checkstyle:MethodLength") - public void validate(Settings settings, boolean isProjectSettings, List problems) { + public void validate(Settings settings, boolean isProjectSettings, ProblemCollector problems) { if (isProjectSettings) { String msgS = "is not supported on project settings."; String msgP = "are not supported on project settings."; @@ -208,7 +209,8 @@ public void validate(Settings settings, boolean isProjectSettings, List problems, List repositories, String prefix) { + private void validateRepositories( + ProblemCollector problems, List repositories, String prefix) { Set repoIds = new HashSet<>(); for (Repository repository : repositories) { @@ -268,7 +270,7 @@ private void validateRepositories(List problems, List */ private static boolean validateStringEmpty( - List problems, String fieldName, String string, String message) { + ProblemCollector problems, String fieldName, String string, String message) { if (string == null || string.length() == 0) { return true; } @@ -287,7 +289,7 @@ private static boolean validateStringEmpty( * */ private static boolean validateStringNotEmpty( - List problems, String fieldName, String string, String sourceHint) { + ProblemCollector problems, String fieldName, String string, String sourceHint) { if (!validateNotNull(problems, fieldName, string, sourceHint)) { return false; } @@ -309,7 +311,7 @@ private static boolean validateStringNotEmpty( * */ private static boolean validateNotNull( - List problems, String fieldName, Object object, String sourceHint) { + ProblemCollector problems, String fieldName, Object object, String sourceHint) { if (object != null) { return true; } @@ -320,7 +322,7 @@ private static boolean validateNotNull( } private static boolean validateBannedCharacters( - List problems, + ProblemCollector problems, String fieldName, BuilderProblem.Severity severity, String string, @@ -344,7 +346,7 @@ private static boolean validateBannedCharacters( } private static void addViolation( - List problems, + ProblemCollector problems, BuilderProblem.Severity severity, String fieldName, String sourceHint, @@ -358,6 +360,6 @@ private static void addViolation( 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)); } } diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultToolchainsBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultToolchainsBuilder.java index 4cdbdfc7c619..0bcf493cfd51 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultToolchainsBuilder.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultToolchainsBuilder.java @@ -23,8 +23,6 @@ import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.function.Function; @@ -32,6 +30,7 @@ import org.apache.maven.api.di.Named; import org.apache.maven.api.services.BuilderProblem; 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.ToolchainsBuilder; import org.apache.maven.api.services.ToolchainsBuilderException; @@ -70,7 +69,7 @@ public DefaultToolchainsBuilder(Interpolator interpolator, ToolchainsXmlFactory @Override public ToolchainsBuilderResult build(ToolchainsBuilderRequest request) throws ToolchainsBuilderException { - List problems = new ArrayList<>(); + ProblemCollector problems = ProblemCollector.create(request.getSession()); Source installationSource = request.getInstallationToolchainsSource().orElse(null); PersistedToolchains installation = readToolchains(installationSource, request, problems); @@ -80,27 +79,15 @@ public ToolchainsBuilderResult build(ToolchainsBuilderRequest request) throws To PersistedToolchains effective = toolchainsMerger.merge(user, installation, false, null); - if (hasErrors(problems)) { + if (problems.hasErrorProblems()) { throw new ToolchainsBuilderException("Error building toolchains", problems); } return new DefaultToolchainsBuilderResult(effective, problems); } - private boolean hasErrors(List problems) { - if (problems != null) { - for (BuilderProblem problem : problems) { - if (BuilderProblem.Severity.ERROR.compareTo(problem.getSeverity()) >= 0) { - return true; - } - } - } - - return false; - } - private PersistedToolchains readToolchains( - Source toolchainsSource, ToolchainsBuilderRequest request, List problems) { + Source toolchainsSource, ToolchainsBuilderRequest request, ProblemCollector problems) { if (toolchainsSource == null) { return PersistedToolchains.newInstance(); } @@ -129,7 +116,7 @@ private PersistedToolchains readToolchains( .strict(false) .build()); Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null; - problems.add(new DefaultBuilderProblem( + problems.reportProblem(new DefaultBuilderProblem( toolchainsSource.getLocation(), loc != null ? loc.getLineNumber() : -1, loc != null ? loc.getColumnNumber() : -1, @@ -139,7 +126,7 @@ private PersistedToolchains readToolchains( } } catch (XmlReaderException e) { Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null; - problems.add(new DefaultBuilderProblem( + problems.reportProblem(new DefaultBuilderProblem( toolchainsSource.getLocation(), loc != null ? loc.getLineNumber() : -1, loc != null ? loc.getColumnNumber() : -1, @@ -148,7 +135,7 @@ private PersistedToolchains readToolchains( BuilderProblem.Severity.FATAL)); return PersistedToolchains.newInstance(); } catch (IOException e) { - problems.add(new DefaultBuilderProblem( + problems.reportProblem(new DefaultBuilderProblem( toolchainsSource.getLocation(), -1, -1, @@ -164,7 +151,9 @@ private PersistedToolchains readToolchains( } private PersistedToolchains interpolate( - PersistedToolchains toolchains, ToolchainsBuilderRequest request, List problems) { + PersistedToolchains toolchains, + ToolchainsBuilderRequest request, + ProblemCollector problems) { Map userProperties = request.getSession().getUserProperties(); Map systemProperties = request.getSession().getSystemProperties(); Function src = Interpolator.chain(userProperties::get, systemProperties::get); @@ -180,11 +169,12 @@ static class DefaultToolchainsBuilderResult implements ToolchainsBuilderResult { private final PersistedToolchains effectiveToolchains; - private final List problems; + private final ProblemCollector problems; - DefaultToolchainsBuilderResult(PersistedToolchains effectiveToolchains, List problems) { + DefaultToolchainsBuilderResult( + PersistedToolchains effectiveToolchains, ProblemCollector problems) { this.effectiveToolchains = effectiveToolchains; - this.problems = (problems != null) ? problems : new ArrayList<>(); + this.problems = (problems != null) ? problems : ProblemCollector.empty(); } @Override @@ -193,7 +183,7 @@ public PersistedToolchains getEffectiveToolchains() { } @Override - public List getProblems() { + public ProblemCollector getProblems() { return problems; } } diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java index 147bc10e068a..0367d7fd378a 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java @@ -81,6 +81,7 @@ import org.apache.maven.api.services.ModelProblem.Version; import org.apache.maven.api.services.ModelProblemCollector; 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.Source; import org.apache.maven.api.services.SuperPomProvider; @@ -205,10 +206,6 @@ public ModelBuilderSession newSession() { 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 { ModelBuilderSessionState mainSession; @@ -227,8 +224,8 @@ public ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilder mainSession = new ModelBuilderSessionState(request); session = mainSession; } else { - session = - mainSession.derive(request, new DefaultModelBuilderResult(getMaxProblems(mainSession.session))); + session = mainSession.derive( + request, new DefaultModelBuilderResult(ProblemCollector.create(mainSession.session))); } // Build the request if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) { @@ -264,7 +261,7 @@ protected class ModelBuilderSessionState implements ModelProblemCollector { this( request.getSession(), request, - new DefaultModelBuilderResult(DefaultModelBuilder.getMaxProblems(request.getSession())), + new DefaultModelBuilderResult(ProblemCollector.create(request.getSession())), request.getSession() .getData() .computeIfAbsent(SessionData.key(ModelCache.class), modelCacheFactory::newInstance), @@ -305,12 +302,8 @@ private ModelBuilderSessionState( this.result.setSource(this.request.getSource()); } - int getMaxProblems() { - return DefaultModelBuilder.getMaxProblems(session); - } - 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) { @@ -321,7 +314,7 @@ ModelBuilderSessionState derive(ModelSource source, DefaultModelBuilderResult re * Creates a new session, sharing cached datas and propagating errors. */ 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) { @@ -433,18 +426,9 @@ public void putSource(String groupId, String artifactId, ModelSource source) { } } - 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 - public List getProblems() { - return result.getProblems(); + public ProblemCollector getProblemCollector() { + return result.getProblemCollector(); } public void setSource(String source) { @@ -480,31 +464,7 @@ public Model getRootModel() { return rootModel; } - @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( BuilderProblem.Severity severity, ModelProblem.Version version, @@ -720,8 +680,9 @@ Stream results(DefaultModelBuilderResult r) { private void loadFromRoot(Path root, Path top) { try (PhasingExecutor executor = createExecutor()) { - DefaultModelBuilderResult r = - Objects.equals(top, root) ? result : new DefaultModelBuilderResult(getMaxProblems()); + DefaultModelBuilderResult r = Objects.equals(top, root) + ? result + : new DefaultModelBuilderResult(ProblemCollector.create(session)); loadFilePom(executor, top, root, Set.of(), r); } if (result.getFileModel() == null && !Objects.equals(top, root)) { @@ -767,7 +728,7 @@ private void loadFilePom( -1, -1, null); - r.addProblem(problem); + r.getProblemCollector().reportProblem(problem); continue; } @@ -789,13 +750,13 @@ private void loadFilePom( -1, -1, null); - r.addProblem(problem); + r.getProblemCollector().reportProblem(problem); continue; } DefaultModelBuilderResult cr = Objects.equals(top, subprojectFile) ? result - : new DefaultModelBuilderResult(r, getMaxProblems()); + : new DefaultModelBuilderResult(ProblemCollector.create(session)); if (request.isRecursive()) { r.getChildren().add(cr); } @@ -806,9 +767,6 @@ private void loadFilePom( // gathered with problem collector add(Severity.ERROR, Version.V40, "Failed to load project " + pom, e); } - if (r != result) { - r.getProblems().forEach(result::addProblem); - } } static Set concat(Set a, T b) { @@ -1731,12 +1689,9 @@ private Model doLoadDependencyManagement( modelBuilderSession.buildEffectiveModel(importIds); importResult = modelBuilderSession.result; } catch (ModelBuilderException e) { - e.getResult().getProblems().forEach(this::add); return null; } - importResult.getProblems().forEach(this::add); - importModel = importResult.getEffectiveModel(); return importModel; @@ -1858,6 +1813,7 @@ private static List getSubprojects(Model activated) { return subprojects; } + @Override public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException { ModelBuilderSessionState build = new ModelBuilderSessionState(request); Model model = build.readRawModel(); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilderResult.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilderResult.java index be4e0b074a21..dd477ad1dd15 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilderResult.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilderResult.java @@ -20,20 +20,15 @@ import java.util.ArrayList; 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.Stream; import org.apache.maven.api.model.Model; 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.ModelProblem; import org.apache.maven.api.services.ModelSource; +import org.apache.maven.api.services.ProblemCollector; /** * Collects the output of the model builder. @@ -46,23 +41,14 @@ class DefaultModelBuilderResult implements ModelBuilderResult { private Model effectiveModel; private List activePomProfiles; private List activeExternalProfiles; - private final Queue problems = new ConcurrentLinkedQueue<>(); - private final DefaultModelBuilderResult problemHolder; - + private final ProblemCollector problemCollector; private final List children = new ArrayList<>(); - private int maxProblems; - private Map problemCount = new ConcurrentHashMap<>(); - - DefaultModelBuilderResult(int maxProblems) { - this(null, maxProblems); - } - - DefaultModelBuilderResult(DefaultModelBuilderResult problemHolder, int maxProblems) { - this.problemHolder = problemHolder; - this.maxProblems = maxProblems; + DefaultModelBuilderResult(ProblemCollector problemCollector) { + this.problemCollector = problemCollector; } + @Override public ModelSource getSource() { return source; } @@ -132,39 +118,8 @@ public void setActiveExternalProfiles(List activeProfiles) { * guaranteed to be non-null but possibly empty. */ @Override - public List getProblems() { - List additionalProblems = new ArrayList<>(); - 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); - } + public ProblemCollector getProblemCollector() { + return problemCollector; } @Override @@ -183,18 +138,19 @@ public String toString() { } else { modelId = null; } - if (!problems.isEmpty()) { + if (problemCollector.hasWarningProblems()) { + int totalProblems = problemCollector.totalProblemsReported(); StringBuilder sb = new StringBuilder(); - sb.append(problems.size()) + sb.append(totalProblems) .append( - (problems.size() == 1) + (totalProblems == 1) ? " problem was " : " problems were encountered while building the effective model"); if (modelId != null && !modelId.isEmpty()) { sb.append(" for "); sb.append(modelId); } - for (ModelProblem problem : problems) { + for (ModelProblem problem : problemCollector.problems().toList()) { sb.append(System.lineSeparator()); sb.append(" - ["); sb.append(problem.getSeverity()); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/Result.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/Result.java deleted file mode 100644 index e0be89c1c918..000000000000 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/Result.java +++ /dev/null @@ -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: - *

    - *
  1. success - in which case only the model field is set - *
  2. success with warnings - model field + non-error model problems - *
  3. error - no model, but diagnostics - *
  4. error - (partial) model and diagnostics - *
- * Could encode these variants as subclasses, but kept in one for now - * - * @param the model type - */ -public class Result { - - /** - * Success without warnings - * - * @param model - */ - public static Result success(T model) { - return success(model, Collections.emptyList()); - } - - /** - * Success with warnings - * - * @param model - * @param problems - */ - public static Result success(T model, Iterable problems) { - assert !hasErrors(problems); - return new Result<>(false, model, problems); - } - - /** - * Success with warnings - * - * @param model - * @param results - */ - public static Result success(T model, Result... results) { - final List 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 Result error(Iterable problems) { - return error(null, problems); - } - - public static Result error(T model) { - return error(model, Collections.emptyList()); - } - - public static Result error(Result result) { - return error(result.getProblems()); - } - - public static Result error(Result... results) { - final List 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 Result error(T model, Iterable 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 Result newResult(T model, Iterable 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 Result addProblem(Result result, ModelProblem problem) { - return addProblems(result, Collections.singleton(problem)); - } - - /** - * New result that includes the given - * - * @param result - * @param problems - */ - public static Result addProblems(Result result, Iterable problems) { - Collection 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 Result addProblems(Result result, Result... results) { - final List 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 Result> newResultSet(Iterable> results) { - boolean hasErrors = false; - List modelsList = new ArrayList<>(); - List problemsList = new ArrayList<>(); - - for (Result result : results) { - modelsList.add(result.get()); - - for (ModelProblem modelProblem : result.getProblems()) { - problemsList.add(modelProblem); - } - - if (result.hasErrors()) { - hasErrors = true; - } - } - return new Result<>(hasErrors, (Iterable) modelsList, problemsList); - } - - // helper to determine if problems contain error - private static boolean hasErrors(Iterable 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 problems; - - private Result(boolean errors, T model, Iterable problems) { - this.errors = errors; - this.value = model; - this.problems = problems; - } - - public Iterable getProblems() { - return problems; - } - - public T get() { - return value; - } - - public boolean hasErrors() { - return errors; - } -} diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java index 29d7b5439d1c..60b3880c061b 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java @@ -35,6 +35,7 @@ import org.apache.maven.api.services.ModelBuilderResult; import org.apache.maven.api.services.ModelProblem; 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.internal.impl.InternalSession; import org.apache.maven.internal.impl.model.ModelProblemUtils; @@ -207,20 +208,22 @@ private Model loadPom( ModelBuilderResult modelResult = modelBuilder.newSession().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 (modelResult.getProblemCollector().hasWarningProblems()) { + ProblemCollector problemCollector = modelResult.getProblemCollector(); + int totalProblems = problemCollector.totalProblemsReported(); if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); - sb.append(problems.size()) + sb.append(totalProblems) .append(" ") - .append((problems.size() == 1) ? "problem was" : "problems were") - .append(" encountered while building the effective model for ") + .append((totalProblems == 1) ? "problem was" : "problems were") + .append(" encountered while building the effective model for '") .append(request.getArtifact()) - .append(" during ") + .append("' during ") .append(RequestTraceHelper.interpretTrace(true, request.getTrace())) .append("\n") - .append((problems.size() == 1) ? "Problem" : "Problems"); - for (ModelProblem modelProblem : problems) { + .append((totalProblems == 1) ? "Problem" : "Problems"); + for (ModelProblem modelProblem : + problemCollector.problems().toList()) { sb.append("\n* ") .append(modelProblem.getMessage()) .append(" @ ") @@ -229,16 +232,17 @@ private Model loadPom( logger.warn(sb.toString()); } 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", + "{} {} encountered while building the effective model for '{}' during {} (use -X to see details)", + totalProblems, + (totalProblems == 1) ? "problem was" : "problems were", request.getArtifact(), RequestTraceHelper.interpretTrace(false, request.getTrace())); } } model = modelResult.getEffectiveModel(); } catch (ModelBuilderException e) { - for (ModelProblem problem : e.getResult().getProblems()) { + for (ModelProblem problem : + e.getResult().getProblemCollector().problems().toList()) { if (problem.getException() instanceof ModelResolverException) { result.addException(problem.getException()); throw new ArtifactDescriptorException(result); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultVersionResolver.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultVersionResolver.java index f06245d1a8c4..0694b9edbfe8 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultVersionResolver.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultVersionResolver.java @@ -109,8 +109,7 @@ public VersionResult resolveVersion(RepositorySystemSession session, VersionRequ cacheKey = new Key(session, request); Object obj = cache.get(session, cacheKey); - if (obj instanceof Record) { - Record record = (Record) obj; + if (obj instanceof Record record) { result.setVersion(record.version); result.setRepository( getRepository(session, request.getRepositories(), record.repoClass, record.repoId)); @@ -189,8 +188,7 @@ public VersionResult resolveVersion(RepositorySystemSession session, VersionRequ if (result.getVersion() != null && result.getVersion().endsWith(SNAPSHOT)) { VersionRequest subRequest = new VersionRequest(); subRequest.setArtifact(artifact.setVersion(result.getVersion())); - if (result.getRepository() instanceof RemoteRepository) { - RemoteRepository r = (RemoteRepository) result.getRepository(); + if (result.getRepository() instanceof RemoteRepository r) { subRequest.setRepositories(Collections.singletonList(r)); } else { subRequest.setRepositories(request.getRepositories()); diff --git a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/DefaultProblemCollectorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/DefaultProblemCollectorTest.java new file mode 100644 index 000000000000..2c5a7ac999b1 --- /dev/null +++ b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/DefaultProblemCollectorTest.java @@ -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 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 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 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 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 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()); + } +} diff --git a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/DefaultSettingsValidatorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/DefaultSettingsValidatorTest.java index 7efbe7e618a7..bb28c0bccfb2 100644 --- a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/DefaultSettingsValidatorTest.java +++ b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/DefaultSettingsValidatorTest.java @@ -21,6 +21,7 @@ import java.util.List; 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.settings.Profile; import org.apache.maven.api.settings.Repository; @@ -56,32 +57,29 @@ private void assertContains(String msg, String substring) { void testValidate() { Profile prof = Profile.newBuilder().id("xxx").build(); Settings model = Settings.newBuilder().profiles(List.of(prof)).build(); - List problems = validator.validate(model); - assertEquals(0, problems.size()); + ProblemCollector problems = validator.validate(model); + assertEquals(0, problems.totalProblemsReported()); Repository repo = org.apache.maven.api.settings.Repository.newInstance(false); Settings model2 = Settings.newBuilder() .profiles(List.of(prof.withRepositories(List.of(repo)))) .build(); - problems.clear(); problems = validator.validate(model2); - assertEquals(2, problems.size()); + assertEquals(2, problems.totalProblemsReported()); repo = repo.withUrl("http://xxx.xxx.com"); model2 = Settings.newBuilder() .profiles(List.of(prof.withRepositories(List.of(repo)))) .build(); - problems.clear(); problems = validator.validate(model2); - assertEquals(1, problems.size()); + assertEquals(1, problems.totalProblemsReported()); repo = repo.withId("xxx"); model2 = Settings.newBuilder() .profiles(List.of(prof.withRepositories(List.of(repo)))) .build(); - problems.clear(); problems = validator.validate(model2); - assertEquals(0, problems.size()); + assertEquals(0, problems.totalProblemsReported()); } /* diff --git a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/ComplexActivationTest.java b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/ComplexActivationTest.java index df86fcc95679..e2bf87b89d72 100644 --- a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/ComplexActivationTest.java +++ b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/ComplexActivationTest.java @@ -76,7 +76,8 @@ public void testConditionExistingAndMissingInActivation() throws Exception { .build(); ModelBuilderResult result = builder.newSession().build(request); assertNotNull(result); - assertTrue(result.getProblems().stream() + assertTrue(result.getProblemCollector() + .problems() .anyMatch(p -> p.getSeverity() == BuilderProblem.Severity.WARNING && p.getMessage().contains("The 'missing' assertion will be ignored."))); } diff --git a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/SimpleProblemCollector.java b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/SimpleProblemCollector.java index fb9b1977cff1..5e92303a52e0 100644 --- a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/SimpleProblemCollector.java +++ b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/profile/SimpleProblemCollector.java @@ -18,7 +18,6 @@ */ package org.apache.maven.internal.impl.model.profile; -import java.util.ArrayList; import java.util.List; import org.apache.maven.api.model.InputLocation; @@ -27,6 +26,7 @@ import org.apache.maven.api.services.ModelBuilderException; import org.apache.maven.api.services.ModelProblem; import org.apache.maven.api.services.ModelProblemCollector; +import org.apache.maven.api.services.ProblemCollector; import org.apache.maven.internal.impl.model.DefaultModelProblem; /** @@ -34,23 +34,11 @@ */ public class SimpleProblemCollector implements ModelProblemCollector { - final List problems = new ArrayList<>(); + final ProblemCollector problemCollector = ProblemCollector.create(100); @Override - public List getProblems() { - return problems; - } - - @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); + public ProblemCollector getProblemCollector() { + return problemCollector; } @Override @@ -70,11 +58,6 @@ public void add( exception)); } - @Override - public void add(ModelProblem problem) { - this.problems.add(problem); - } - @Override public ModelBuilderException newModelBuilderException() { throw new UnsupportedOperationException(); @@ -118,8 +101,8 @@ public List getWarnings() { } private List getForLevel(BuilderProblem.Severity severity) { - return problems.stream() - .filter(p -> p.getSeverity() == severity) + return problemCollector + .problems(severity) .map(BuilderProblem::getMessage) .toList(); } diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8379SettingsDecryptTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8379SettingsDecryptTest.java index fbc80d4d925b..c00c2b382c03 100644 --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8379SettingsDecryptTest.java +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8379SettingsDecryptTest.java @@ -47,8 +47,7 @@ void testLegacy() throws Exception { verifier.verifyErrorFreeLog(); // there is a warning and all fields decrypted - verifier.verifyTextInLog( - "[INFO] Some problems were encountered while building the effective settings (use -X to see details)"); + verifier.verifyTextInLog(" encountered while building the effective settings (use -e to see details)"); verifier.verifyTextInLog("testtest"); verifier.verifyTextInLog("testtest"); } @@ -71,8 +70,7 @@ void testModern() throws Exception { // there is no warning and all fields decrypted verifier.verifyTextNotInLog("[WARNING]"); - verifier.verifyTextNotInLog( - "[INFO] Some problems were encountered while building the effective settings (use -X to see details)"); + verifier.verifyTextNotInLog(" encountered while building the effective settings (use -e to see details)"); verifier.verifyTextInLog("testtest"); verifier.verifyTextInLog("secretHeader"); }