mirror of
https://github.com/apache/maven.git
synced 2025-03-08 09:44:43 +00:00
[MNG-8447] Lossy ProblemCollector (#1994)
Historically (from Maven3) "problem collection" (in various scenarios, like building effective settings and toolchains, to building models) were done by passing around `List<Problem>` structure. This proved quite ineffective, as in case of (really) huge projects with quite big count of problems reported choked Maven (but also produced unusable output). We are aware of projects producing 3 million warnings! Dumping all of them onto console/log makes really no sense. This PR changes Maven that by default it reports "problems" (settings building, toolchains building, or model building) as one liner warnings: how much and where were problems collected. This produces much more less overwhelming output than happens on master. User can control "build errors" using `-e`, so `mvn -e` will dump errors on screen/log. The new `org.apache.maven.api.services.ProblemCollector<P>` class, that is somewhat "drop in" replacement for `List`, but with huge difference: it is "lossy", in a way, it maintains counters precisely, but actual problem instances are simply dropped after threshold `maven.builder.maxProblems` is surpassed (default 100). See `org.apache.maven.api.Constants#MAVEN_BUILDER_MAX_PROBLEMS`. Rules: * on problem addition counters are always updated * if below threshold, store the problem (and will be reported later to the user) * if above threshold, try to drop one problem with severity lower than currently reported problem, if succeeded, store the current problem, otherwise drop it --- https://issues.apache.org/jira/browse/MNG-8447
This commit is contained in:
parent
5599934016
commit
0222aff6c9
@ -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<BuilderProblem> problems;
|
||||
private final ProblemCollector<BuilderProblem> problems;
|
||||
|
||||
public MavenBuilderException(String message, Throwable 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);
|
||||
this.problems = problems;
|
||||
}
|
||||
@ -49,20 +45,16 @@ public MavenBuilderException(String message, List<BuilderProblem> 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<BuilderProblem> problems) {
|
||||
protected static String buildMessage(String message, ProblemCollector<BuilderProblem> problems) {
|
||||
StringBuilder msg = new StringBuilder(message);
|
||||
ArrayList<BuilderProblem> 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<BuilderProblem> getProblems() {
|
||||
public ProblemCollector<BuilderProblem> getProblemCollector() {
|
||||
return problems;
|
||||
}
|
||||
}
|
||||
|
@ -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<ModelProblem> getProblems() {
|
||||
public ProblemCollector<ModelProblem> getProblemCollector() {
|
||||
if (result == null) {
|
||||
return Collections.emptyList();
|
||||
return ProblemCollector.empty();
|
||||
}
|
||||
return Collections.unmodifiableList(result.getProblems());
|
||||
return result.getProblemCollector();
|
||||
}
|
||||
}
|
||||
|
@ -91,12 +91,12 @@ public interface ModelBuilderResult {
|
||||
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
|
||||
List<ModelProblem> getProblems();
|
||||
ProblemCollector<ModelProblem> getProblemCollector();
|
||||
|
||||
/**
|
||||
* Gets the children of this result.
|
||||
|
@ -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<ModelProblem> getProblems();
|
||||
ProblemCollector<ModelProblem> 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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<BuilderProblem> validate(@Nonnull Settings settings) {
|
||||
default ProblemCollector<BuilderProblem> validate(@Nonnull Settings settings) {
|
||||
return validate(settings, false);
|
||||
}
|
||||
|
||||
@ -120,7 +119,7 @@ default List<BuilderProblem> validate(@Nonnull Settings settings) {
|
||||
* @return The list of problems that were encountered, must not be {@code null}.
|
||||
*/
|
||||
@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.
|
||||
|
@ -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<BuilderProblem> problems) {
|
||||
public SettingsBuilderException(String message, ProblemCollector<BuilderProblem> problems) {
|
||||
super(message, problems);
|
||||
}
|
||||
}
|
||||
|
@ -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<BuilderProblem> getProblems();
|
||||
ProblemCollector<BuilderProblem> getProblems();
|
||||
}
|
||||
|
@ -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<BuilderProblem> problems) {
|
||||
public ToolchainsBuilderException(String message, ProblemCollector<BuilderProblem> problems) {
|
||||
super(message, problems);
|
||||
}
|
||||
}
|
||||
|
@ -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<BuilderProblem> getProblems();
|
||||
ProblemCollector<BuilderProblem> getProblems();
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<BuilderProblem> list = settingsBuilder.validate(settings.getDelegate(), isProjectSettings);
|
||||
for (BuilderProblem problem : list) {
|
||||
ProblemCollector<BuilderProblem> list = settingsBuilder.validate(settings.getDelegate(), isProjectSettings);
|
||||
for (BuilderProblem problem : list.problems().toList()) {
|
||||
addViolation(problems, Severity.valueOf(problem.getSeverity().name()), problem.getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
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"));
|
||||
|
||||
for (BuilderProblem problem : toolchainsResult.getProblems()) {
|
||||
context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
|
||||
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("");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<ModelProblem> modelProblems = null;
|
||||
ProblemCollector<ModelProblem> 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<ProjectBuildingResult> 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<ProjectBuildingResult> results = new ArrayList<>();
|
||||
List<ModelBuilderResult> allModels = results(result).toList();
|
||||
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) {
|
||||
File pom = r.getSource().getPath().toFile();
|
||||
MavenProject project =
|
||||
@ -529,9 +526,10 @@ private List<ProjectBuildingResult> 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<ModelBuilderResult> results(ModelBuilderResult 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) {
|
||||
if (problems == null) {
|
||||
private List<org.apache.maven.model.building.ModelProblem> convert(
|
||||
ProblemCollector<ModelProblem> problemCollector) {
|
||||
if (problemCollector == 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) {
|
||||
|
@ -62,27 +62,33 @@ public List<MavenProject> selectProjects(List<File> files, MavenExecutionRequest
|
||||
|
||||
List<MavenProject> 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.");
|
||||
|
@ -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<BuilderProblem> problems = new ArrayList<>();
|
||||
ProblemCollector<BuilderProblem> 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<BuilderProblem> 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<BuilderProblem> problems) {
|
||||
ProblemCollector<BuilderProblem> 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<BuilderProblem> problems) {
|
||||
private Settings interpolate(
|
||||
Settings settings, SettingsBuilderRequest request, ProblemCollector<BuilderProblem> problems) {
|
||||
Function<String, String> 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<BuilderProblem> problems) {
|
||||
Source settingsSource,
|
||||
Settings settings,
|
||||
SettingsBuilderRequest request,
|
||||
ProblemCollector<BuilderProblem> 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<BuilderProblem> validate(Settings settings, boolean isProjectSettings) {
|
||||
ArrayList<BuilderProblem> problems = new ArrayList<>();
|
||||
public ProblemCollector<BuilderProblem> validate(Settings settings, boolean isProjectSettings) {
|
||||
// TODO: any way to get ProtoSession here?
|
||||
ProblemCollector<BuilderProblem> 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<BuilderProblem> problems;
|
||||
private final ProblemCollector<BuilderProblem> problems;
|
||||
|
||||
DefaultSettingsBuilderResult(Settings effectiveSettings, List<BuilderProblem> problems) {
|
||||
DefaultSettingsBuilderResult(Settings effectiveSettings, ProblemCollector<BuilderProblem> 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<BuilderProblem> getProblems() {
|
||||
public ProblemCollector<BuilderProblem> getProblems() {
|
||||
return problems;
|
||||
}
|
||||
}
|
||||
|
@ -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<BuilderProblem> problems) {
|
||||
public void validate(Settings settings, boolean isProjectSettings, ProblemCollector<BuilderProblem> 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<BuilderP
|
||||
}
|
||||
}
|
||||
|
||||
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<>();
|
||||
|
||||
for (Repository repository : repositories) {
|
||||
@ -268,7 +270,7 @@ private void validateRepositories(List<BuilderProblem> problems, List<Repository
|
||||
* </ul>
|
||||
*/
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
@ -287,7 +289,7 @@ private static boolean validateStringEmpty(
|
||||
* </ul>
|
||||
*/
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
@ -309,7 +311,7 @@ private static boolean validateStringNotEmpty(
|
||||
* </ul>
|
||||
*/
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
@ -320,7 +322,7 @@ private static boolean validateNotNull(
|
||||
}
|
||||
|
||||
private static boolean validateBannedCharacters(
|
||||
List<BuilderProblem> problems,
|
||||
ProblemCollector<BuilderProblem> problems,
|
||||
String fieldName,
|
||||
BuilderProblem.Severity severity,
|
||||
String string,
|
||||
@ -344,7 +346,7 @@ private static boolean validateBannedCharacters(
|
||||
}
|
||||
|
||||
private static void addViolation(
|
||||
List<BuilderProblem> problems,
|
||||
ProblemCollector<BuilderProblem> 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));
|
||||
}
|
||||
}
|
||||
|
@ -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<BuilderProblem> problems = new ArrayList<>();
|
||||
ProblemCollector<BuilderProblem> 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<BuilderProblem> 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<BuilderProblem> problems) {
|
||||
Source toolchainsSource, ToolchainsBuilderRequest request, ProblemCollector<BuilderProblem> 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<BuilderProblem> problems) {
|
||||
PersistedToolchains toolchains,
|
||||
ToolchainsBuilderRequest request,
|
||||
ProblemCollector<BuilderProblem> problems) {
|
||||
Map<String, String> userProperties = request.getSession().getUserProperties();
|
||||
Map<String, String> systemProperties = request.getSession().getSystemProperties();
|
||||
Function<String, String> src = Interpolator.chain(userProperties::get, systemProperties::get);
|
||||
@ -180,11 +169,12 @@ static class DefaultToolchainsBuilderResult implements ToolchainsBuilderResult {
|
||||
|
||||
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.problems = (problems != null) ? problems : new ArrayList<>();
|
||||
this.problems = (problems != null) ? problems : ProblemCollector.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -193,7 +183,7 @@ public PersistedToolchains getEffectiveToolchains() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BuilderProblem> getProblems() {
|
||||
public ProblemCollector<BuilderProblem> getProblems() {
|
||||
return problems;
|
||||
}
|
||||
}
|
||||
|
@ -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<ModelProblem> getProblems() {
|
||||
return result.getProblems();
|
||||
public ProblemCollector<ModelProblem> getProblemCollector() {
|
||||
return result.getProblemCollector();
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
@ -481,30 +465,6 @@ public Model getRootModel() {
|
||||
}
|
||||
|
||||
@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<DefaultModelBuilderResult> 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 <T> Set<T> concat(Set<T> 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<String> getSubprojects(Model activated) {
|
||||
return subprojects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException {
|
||||
ModelBuilderSessionState build = new ModelBuilderSessionState(request);
|
||||
Model model = build.readRawModel();
|
||||
|
@ -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<Profile> activePomProfiles;
|
||||
private List<Profile> activeExternalProfiles;
|
||||
private final Queue<ModelProblem> problems = new ConcurrentLinkedQueue<>();
|
||||
private final DefaultModelBuilderResult problemHolder;
|
||||
|
||||
private final ProblemCollector<ModelProblem> problemCollector;
|
||||
private final List<DefaultModelBuilderResult> children = new ArrayList<>();
|
||||
|
||||
private int maxProblems;
|
||||
private Map<BuilderProblem.Severity, AtomicInteger> problemCount = new ConcurrentHashMap<>();
|
||||
|
||||
DefaultModelBuilderResult(int maxProblems) {
|
||||
this(null, maxProblems);
|
||||
}
|
||||
|
||||
DefaultModelBuilderResult(DefaultModelBuilderResult problemHolder, int maxProblems) {
|
||||
this.problemHolder = problemHolder;
|
||||
this.maxProblems = maxProblems;
|
||||
DefaultModelBuilderResult(ProblemCollector<ModelProblem> problemCollector) {
|
||||
this.problemCollector = problemCollector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelSource getSource() {
|
||||
return source;
|
||||
}
|
||||
@ -132,39 +118,8 @@ public void setActiveExternalProfiles(List<Profile> activeProfiles) {
|
||||
* guaranteed to be non-null but possibly empty.
|
||||
*/
|
||||
@Override
|
||||
public List<ModelProblem> getProblems() {
|
||||
List<ModelProblem> 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<ModelProblem> 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());
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<ModelProblem> problems = modelResult.getProblems();
|
||||
if (modelResult.getProblemCollector().hasWarningProblems()) {
|
||||
ProblemCollector<ModelProblem> 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);
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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<BuilderProblem> problems = validator.validate(model);
|
||||
assertEquals(0, problems.size());
|
||||
ProblemCollector<BuilderProblem> 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());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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.")));
|
||||
}
|
||||
|
@ -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<ModelProblem> problems = new ArrayList<>();
|
||||
final ProblemCollector<ModelProblem> problemCollector = ProblemCollector.create(100);
|
||||
|
||||
@Override
|
||||
public List<ModelProblem> 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<ModelProblem> 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<String> getWarnings() {
|
||||
}
|
||||
|
||||
private List<String> getForLevel(BuilderProblem.Severity severity) {
|
||||
return problems.stream()
|
||||
.filter(p -> p.getSeverity() == severity)
|
||||
return problemCollector
|
||||
.problems(severity)
|
||||
.map(BuilderProblem::getMessage)
|
||||
.toList();
|
||||
}
|
||||
|
@ -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("<password>testtest</password>");
|
||||
verifier.verifyTextInLog("<value>testtest</value>");
|
||||
}
|
||||
@ -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("<password>testtest</password>");
|
||||
verifier.verifyTextInLog("<value>secretHeader</value>");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user