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 4e9c2c6bded1..b0558271d98c 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 @@ -76,6 +76,7 @@ import org.apache.maven.eventspy.internal.EventSpyDispatcher; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.impl.SettingsUtilsV4; +import org.apache.maven.internal.build.DefaultDiagnosticCollector; import org.apache.maven.jline.FastTerminal; import org.apache.maven.jline.MessageUtils; import org.apache.maven.logging.BuildEventListener; @@ -750,6 +751,13 @@ protected Runnable settings(C context, boolean emitSettingsWarnings, SettingsBui } } context.logger.info(""); + + // Pipe structured problems directly to DiagnosticCollector so that + // key, suggestion, documentationUrl, and source location are preserved + // in the build report (instead of being lost to plain-text logging). + // This runs before SessionStarted, so the SLF4J auto-collection hook + // is not active yet — no double-counting risk. + pipeSettingsProblems(context, settingsResult); } return () -> { context.installationSettingsPath = null; @@ -761,6 +769,20 @@ protected Runnable settings(C context, boolean emitSettingsWarnings, SettingsBui }; } + /** + * Pipes structured settings validation problems to the DiagnosticCollector. + * This preserves key, suggestion, documentationUrl, and source location + * that would otherwise be lost when problems are logged as plain text. + */ + private void pipeSettingsProblems(C context, SettingsBuilderResult settingsResult) { + context.lookup.lookupOptional(DefaultDiagnosticCollector.class).ifPresent(collector -> { + for (BuilderProblem problem : + settingsResult.getProblems().problems().toList()) { + collector.report(problem); + } + }); + } + protected void customizeSettingsRequest(C context, SettingsBuilderRequest settingsBuilderRequest) throws Exception {} 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 bc03026990d7..b04d377174a5 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 @@ -69,6 +69,7 @@ import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.execution.ProfileActivation; import org.apache.maven.execution.ProjectActivation; +import org.apache.maven.internal.build.DefaultDiagnosticCollector; import org.apache.maven.jline.MessageUtils; import org.apache.maven.lifecycle.LifecycleExecutionException; import org.apache.maven.logging.BuildEventListener; @@ -220,6 +221,17 @@ protected void toolchains(MavenContext context, MavenExecutionRequest request) t } context.logger.info(""); + + // Pipe structured problems directly to DiagnosticCollector so that + // key, suggestion, documentationUrl, and source location are preserved + // in the build report. This runs before SessionStarted, so the SLF4J + // auto-collection hook is not active yet — no double-counting risk. + context.lookup.lookupOptional(DefaultDiagnosticCollector.class).ifPresent(collector -> { + for (BuilderProblem problem : + toolchainsResult.getProblems().problems().toList()) { + collector.report(problem); + } + }); } } diff --git a/impl/maven-core/pom.xml b/impl/maven-core/pom.xml index fe8f6d10de38..6a246c3d045d 100644 --- a/impl/maven-core/pom.xml +++ b/impl/maven-core/pom.xml @@ -31,26 +31,6 @@ under the License. Maven 4 Core Maven Core classes. - - - 3.4.0 - 3.13.0 - 3.1.3 - 3.3.0 - 3.2.1 - 3.1.3 - 3.4.2 - 3.15.1 - 3.0.0 - 3.3.1 - 3.21.0 - 3.5.2 - 3.4.0 - - @@ -280,74 +260,6 @@ under the License. - - - org.apache.maven.plugins - maven-clean-plugin - ${version.maven-clean-plugin} - - - org.apache.maven.plugins - maven-compiler-plugin - ${version.maven-compiler-plugin} - - - org.apache.maven.plugins - maven-deploy-plugin - ${version.maven-deploy-plugin} - - - org.apache.maven.plugins - maven-ear-plugin - ${version.maven-ear-plugin} - - - org.apache.maven.plugins - maven-ejb-plugin - ${version.maven-ejb-plugin} - - - org.apache.maven.plugins - maven-install-plugin - ${version.maven-install-plugin} - - - org.apache.maven.plugins - maven-jar-plugin - ${version.maven-jar-plugin} - - - org.apache.maven.plugins - maven-plugin-plugin - ${version.maven-plugin-plugin} - - - org.apache.maven.plugins - maven-rar-plugin - ${version.maven-rar-plugin} - - - org.apache.maven.plugins - maven-resources-plugin - ${version.maven-resources-plugin} - - - org.apache.maven.plugins - maven-site-plugin - ${version.maven-site-plugin} - - - org.apache.maven.plugins - maven-surefire-plugin - ${version.maven-surefire-plugin} - - - org.apache.maven.plugins - maven-war-plugin - ${version.maven-war-plugin} - org.apache.rat apache-rat-plugin @@ -484,10 +396,12 @@ under the License. org.apache.maven.toolchain.ToolchainManagerPrivate org.apache.maven.toolchain.ToolchainPrivate org.apache.maven.toolchain.ToolchainsBuilder + + org.apache.maven.plugin.PluginValidationManager - + \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java index ba5ea30d1db0..ec4c8c1ddd5e 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java +++ b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java @@ -43,6 +43,7 @@ import org.apache.maven.api.model.Model; import org.apache.maven.api.model.Prerequisites; import org.apache.maven.api.model.Profile; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.Lookup; import org.apache.maven.api.services.LookupException; import org.apache.maven.artifact.ArtifactUtils; @@ -59,6 +60,7 @@ import org.apache.maven.execution.ProjectDependencyGraph; import org.apache.maven.graph.GraphBuilder; import org.apache.maven.graph.ProjectSelector; +import org.apache.maven.internal.build.DefaultDiagnosticCollector; import org.apache.maven.internal.impl.DefaultSessionFactory; import org.apache.maven.internal.impl.InternalMavenSession; import org.apache.maven.lifecycle.LifecycleExecutionException; @@ -113,6 +115,8 @@ public class DefaultMaven implements Maven { private final ProjectSelector projectSelector; + private final DefaultDiagnosticCollector diagnosticCollector; + @Inject @SuppressWarnings("checkstyle:ParameterNumber") public DefaultMaven( @@ -126,7 +130,8 @@ public DefaultMaven( BuildResumptionDataRepository buildResumptionDataRepository, SuperPomProvider superPomProvider, DefaultSessionFactory defaultSessionFactory, - @Nullable @Named("ide") WorkspaceReader ideWorkspaceReader) { + @Nullable @Named("ide") WorkspaceReader ideWorkspaceReader, + DefaultDiagnosticCollector diagnosticCollector) { this.lookup = lookup; this.eventCatapult = eventCatapult; this.legacySupport = legacySupport; @@ -138,6 +143,7 @@ public DefaultMaven( this.superPomProvider = superPomProvider; this.ideWorkspaceReader = ideWorkspaceReader; this.defaultSessionFactory = defaultSessionFactory; + this.diagnosticCollector = diagnosticCollector; this.projectSelector = new ProjectSelector(); // if necessary switch to DI } @@ -210,22 +216,20 @@ private MavenExecutionResult doExecute(MavenExecutionRequest request) { // so that @SessionScoped components can be @Injected into AbstractLifecycleParticipants. // sessionScope.enter(); - try { - MavenChainedWorkspaceReader chainedWorkspaceReader = - new MavenChainedWorkspaceReader(request.getWorkspaceReader(), ideWorkspaceReader); - try (CloseableSession closeableSession = newCloseableSession(request, chainedWorkspaceReader)) { - MavenSession session = new MavenSession(closeableSession, request, result); - session.setSession(defaultSessionFactory.newSession(session)); + MavenChainedWorkspaceReader chainedWorkspaceReader = + new MavenChainedWorkspaceReader(request.getWorkspaceReader(), ideWorkspaceReader); + try (CloseableSession closeableSession = newCloseableSession(request, chainedWorkspaceReader)) { + MavenSession session = new MavenSession(closeableSession, request, result); + session.setSession(defaultSessionFactory.newSession(session)); - sessionScope.seed(MavenSession.class, session); - sessionScope.seed(RepositorySystemSession.class, closeableSession); // fixed in Maven 3.10.x - sessionScope.seed(Session.class, session.getSession()); - sessionScope.seed(InternalMavenSession.class, InternalMavenSession.from(session.getSession())); + sessionScope.seed(MavenSession.class, session); + sessionScope.seed(RepositorySystemSession.class, closeableSession); // fixed in Maven 3.10.x + sessionScope.seed(Session.class, session.getSession()); + sessionScope.seed(InternalMavenSession.class, InternalMavenSession.from(session.getSession())); - legacySupport.setSession(session); + legacySupport.setSession(session); - return doExecute(request, session, result, chainedWorkspaceReader); - } + return doExecute(request, session, result, chainedWorkspaceReader); } finally { sessionScope.exit(); } @@ -650,6 +654,10 @@ private Result buildGraph(MavenSession session } else { logger.error(problem.getMessage()); } + // Pipe structured problem directly to DiagnosticCollector so that + // source location and severity are preserved in the build report. + // The SLF4J hook excludes this logger to avoid double-counting. + diagnosticCollector.report(toBuilderProblem(problem)); } if (!graphResult.hasErrors()) { @@ -662,9 +670,31 @@ private Result buildGraph(MavenSession session return graphResult; } + /** + * Converts a compat {@link ModelProblem} to the Maven 4 {@link BuilderProblem} API, + * preserving source, line, column, severity, and message. + */ + private static BuilderProblem toBuilderProblem(ModelProblem problem) { + BuilderProblem.Severity severity = + switch (problem.getSeverity()) { + case FATAL -> BuilderProblem.Severity.FATAL; + case ERROR -> BuilderProblem.Severity.ERROR; + default -> BuilderProblem.Severity.WARNING; + }; + return BuilderProblem.builder() + .source(problem.getSource()) + .lineNumber(problem.getLineNumber()) + .columnNumber(problem.getColumnNumber()) + .exception(problem.getException()) + .message(problem.getMessage()) + .severity(severity) + .key("model:" + problem.getMessage().hashCode()) + .build(); + } + @Deprecated // 5 January 2014 protected Logger getLogger() { return logger; } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/build/BuildReportCollector.java b/impl/maven-core/src/main/java/org/apache/maven/internal/build/BuildReportCollector.java index 908c9b7be4c8..e1f0158963d1 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/build/BuildReportCollector.java +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/build/BuildReportCollector.java @@ -91,6 +91,19 @@ public final class BuildReportCollector extends AbstractEventSpy { private static final int MAX_STACKTRACE_LINES = 30; + /** + * Logger names excluded from SLF4J auto-collection because these classes + * already pipe structured {@link org.apache.maven.api.services.BuilderProblem} + * objects directly to the {@link DefaultDiagnosticCollector}. Without this + * exclusion, each problem would be counted twice: once from the direct pipe + * and once from the SLF4J WARN interception. + */ + private static final Set EXCLUDED_LOGGERS = Set.of( + BuildReportCollector.class.getName(), + "org.apache.maven.DefaultMaven", + "org.apache.maven.project.collector.DefaultProjectsSelector", + "org.apache.maven.plugin.internal.DefaultPluginValidationManager"); + private final DefaultDiagnosticCollector diagnosticCollector; @Inject @@ -353,10 +366,12 @@ private void captureLogEvent(LogEvent event) { // Auto-collect WARN-level log events as build problems, giving Maven 3 plugins // automatic deduplication and summary at end of build without code changes. - // Skip our own logger to avoid feedback loops from problem summary printing. + // Skip loggers that already pipe structured BuilderProblems directly to the + // DiagnosticCollector (avoiding double-counting), and our own logger to avoid + // feedback loops from problem summary printing. if (event.level() == LogLevel.WARN && event.message() != null - && !event.loggerName().equals(BuildReportCollector.class.getName())) { + && !EXCLUDED_LOGGERS.contains(event.loggerName())) { String syntheticKey = syntheticDiagnosticKey(event.loggerName(), event.message()); diagnosticCollector.report(BuilderProblem.builder() .source(event.loggerName()) diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginValidationManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginValidationManager.java index d65f8dad681a..83ec14478246 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginValidationManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginValidationManager.java @@ -18,6 +18,7 @@ */ package org.apache.maven.plugin; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.eclipse.aether.RepositorySystemSession; @@ -54,27 +55,94 @@ enum IssueLocality { * This method should be used in "early" phase of plugin execution, possibly even when plugin or mojo descriptor * does not exist yet. In turn, this method will not record extra information like plugin occurrence or declaration * location as those are not yet available. + * + * @since 4.1.0 */ void reportPluginValidationIssue( - IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, String issue); + IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, BuilderProblem problem); /** * Reports plugin issues applicable to the plugin as a whole. *

* This method will record extra information as well, like plugin occurrence or declaration location. + * + * @since 4.1.0 */ void reportPluginValidationIssue( - IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, String issue); + IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, BuilderProblem problem); /** * Reports plugin Mojo issues applicable to the Mojo itself. *

* This method will record extra information as well, like plugin occurrence or declaration location. + * + * @since 4.1.0 */ void reportPluginMojoValidationIssue( IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, Class mojoClass, - String issue); -} + BuilderProblem problem); + + // ---- Deprecated String-based adapters for external callers ---- + + /** + * @deprecated Use {@link #reportPluginValidationIssue(IssueLocality, RepositorySystemSession, Artifact, + * BuilderProblem)} instead. + */ + @Deprecated(since = "4.1.0", forRemoval = true) + default void reportPluginValidationIssue( + IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, String issue) { + reportPluginValidationIssue( + locality, + session, + pluginArtifact, + BuilderProblem.builder() + .message(issue) + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:" + issue.hashCode()) + .build()); + } + + /** + * @deprecated Use {@link #reportPluginValidationIssue(IssueLocality, MavenSession, MojoDescriptor, + * BuilderProblem)} instead. + */ + @Deprecated(since = "4.1.0", forRemoval = true) + default void reportPluginValidationIssue( + IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, String issue) { + reportPluginValidationIssue( + locality, + mavenSession, + mojoDescriptor, + BuilderProblem.builder() + .message(issue) + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:" + issue.hashCode()) + .build()); + } + + /** + * @deprecated Use {@link #reportPluginMojoValidationIssue(IssueLocality, MavenSession, MojoDescriptor, Class, + * BuilderProblem)} instead. + */ + @Deprecated(since = "4.1.0", forRemoval = true) + default void reportPluginMojoValidationIssue( + IssueLocality locality, + MavenSession mavenSession, + MojoDescriptor mojoDescriptor, + Class mojoClass, + String issue) { + reportPluginMojoValidationIssue( + locality, + mavenSession, + mojoDescriptor, + mojoClass, + BuilderProblem.builder() + .message(issue) + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:" + issue.hashCode()) + .build()); + } +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/AbstractMavenPluginParametersValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/AbstractMavenPluginParametersValidator.java index 9e7426b168ff..d0ddbaa13fb2 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/AbstractMavenPluginParametersValidator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/AbstractMavenPluginParametersValidator.java @@ -18,6 +18,7 @@ */ package org.apache.maven.plugin.internal; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.PluginValidationManager; import org.apache.maven.plugin.descriptor.MojoDescriptor; @@ -97,6 +98,11 @@ protected boolean isIgnoredProperty(String strValue) { protected abstract String getParameterLogReason(Parameter parameter); + /** + * Returns the validation key prefix for this validator (e.g. "deprecated-param", "readonly-param"). + */ + protected abstract String getValidationKeyPrefix(); + protected String formatParameter(Parameter parameter) { StringBuilder stringBuilder = new StringBuilder() .append("Parameter '") @@ -112,4 +118,12 @@ protected String formatParameter(Parameter parameter) { return stringBuilder.toString(); } -} + + protected BuilderProblem buildParameterProblem(Parameter parameter) { + return BuilderProblem.builder() + .message(formatParameter(parameter)) + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:" + getValidationKeyPrefix() + ":" + parameter.getName()) + .build(); + } +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java index 7e0197651b99..fe72dc3810fb 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java @@ -34,16 +34,11 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Properties; -import java.util.Set; import java.util.function.Supplier; import java.util.jar.JarFile; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.zip.ZipEntry; @@ -56,6 +51,7 @@ import org.apache.maven.api.Service; import org.apache.maven.api.Session; import org.apache.maven.api.plugin.descriptor.Resolution; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.DependencyResolver; import org.apache.maven.api.services.DependencyResolverResult; import org.apache.maven.api.services.PathScopeRegistry; @@ -111,7 +107,6 @@ import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException; import org.codehaus.plexus.component.configurator.ComponentConfigurationException; import org.codehaus.plexus.component.configurator.ComponentConfigurator; @@ -601,10 +596,6 @@ private T loadV4Mojo( validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator); } - // MNG-8765: Pre-interpolate configuration values before the ComponentConfigurator - // processes them. See interpolateConfiguration() javadoc for details. - pomConfiguration = interpolateConfiguration(pomConfiguration, expressionEvaluator, session); - populateMojoExecutionFields( mojo, mojoExecution.getExecutionId(), @@ -761,7 +752,13 @@ private T loadV3Mojo( session, mojoDescriptor, mojo.getClass(), - "Mojo implements `Contextualizable` interface from Plexus Container, which is EOL."); + BuilderProblem.builder() + .message( + "Mojo implements `Contextualizable` interface from Plexus Container, which is EOL.") + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:contextualizable") + .suggestion("Migrate from Contextualizable to javax.inject dependency injection") + .build()); } XmlNode dom = mojoExecution.getConfiguration() != null @@ -782,14 +779,6 @@ private T loadV3Mojo( validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator); } - // MNG-8765: Pre-interpolate configuration values using the expression evaluator before - // the ComponentConfigurator processes them. This ensures that ${...} property references - // are fully resolved before type converters (like UriConverter) attempt to parse the values. - // Without this, properties that are not available during model interpolation (e.g., set - // dynamically at runtime by scripts) would reach type converters unresolved, causing - // failures like URISyntaxException for URI-typed parameters containing ${...}. - pomConfiguration = interpolateConfiguration(pomConfiguration, expressionEvaluator, session); - populateMojoExecutionFields( mojo, mojoExecution.getExecutionId(), @@ -890,167 +879,6 @@ private void populateMojoExecutionFields( } } - /** Pattern to extract property names from {@code ${propName}} expressions. */ - private static final Pattern EXPRESSION_PATTERN = Pattern.compile("\\$\\{([^}]+)}"); - - /** - * Pre-interpolates a plugin configuration tree by resolving {@code ${...}} property references - * that were set dynamically at runtime (not available during model interpolation). Returns a new - * {@link PlexusConfiguration} backed by a fully-interpolated {@link XmlNode} tree. - * - *

This is necessary because {@link XmlNode} is immutable — mutating transient - * {@link XmlPlexusConfiguration} wrappers does not propagate changes back to the underlying - * tree. Instead, this method rebuilds the {@code XmlNode} tree bottom-up with interpolated - * values, ensuring that all access paths ({@code getChild(int)}, {@code getChildren()}, etc.) - * see the resolved values.

- * - *

Only expressions referencing properties that were NOT available during model interpolation - * are resolved. Properties from the POM's {@code }, user properties ({@code -D}), - * and system properties are skipped because model interpolation already had a chance to resolve - * them — any surviving {@code ${...}} for those properties was intentionally escaped (MNG-3558).

- * - * @param configuration the plugin configuration to interpolate - * @param evaluator the expression evaluator to resolve {@code ${...}} references - * @param session the Maven session, used to determine model-time properties - * @return a new {@link PlexusConfiguration} wrapping an interpolated {@link XmlNode} tree - */ - private PlexusConfiguration interpolateConfiguration( - PlexusConfiguration configuration, ExpressionEvaluator evaluator, MavenSession session) { - if (!(configuration instanceof XmlPlexusConfiguration xmlConfig)) { - return configuration; - } - - Set modelTimePropertyNames = collectModelTimePropertyNames(session); - - XmlNode original = xmlConfig.toXmlNode(); - XmlNode interpolated = interpolateXmlNode(original, evaluator, modelTimePropertyNames); - if (interpolated == original) { - return configuration; - } - return new XmlPlexusConfiguration(interpolated); - } - - /** - * Collects the set of property names that were available during model interpolation. - * Properties in this set should NOT be re-interpolated, because any surviving {@code ${...}} - * reference to them was intentionally escaped. - */ - private Set collectModelTimePropertyNames(MavenSession session) { - Set names = new HashSet<>(); - // User properties (from -D on CLI) - Properties userProps = session.getUserProperties(); - if (userProps != null) { - names.addAll(userProps.stringPropertyNames()); - } - // System properties - Properties sysProps = session.getSystemProperties(); - if (sysProps != null) { - names.addAll(sysProps.stringPropertyNames()); - } - // POM properties from the original model (before runtime additions) - MavenProject project = session.getCurrentProject(); - if (project != null && project.getOriginalModel() != null) { - Properties origProps = project.getOriginalModel().getProperties(); - if (origProps != null) { - names.addAll(origProps.stringPropertyNames()); - } - } - return names; - } - - /** - * Checks whether a string value contains any {@code ${propName}} expression where - * {@code propName} is NOT in the model-time property set. Only such expressions need - * pre-interpolation (they were set at runtime and model interpolation couldn't see them). - */ - private boolean containsRuntimeExpression(String value, Set modelTimePropertyNames) { - Matcher matcher = EXPRESSION_PATTERN.matcher(value); - while (matcher.find()) { - String propName = matcher.group(1); - if (!modelTimePropertyNames.contains(propName)) { - return true; - } - } - return false; - } - - /** - * Recursively interpolates an {@link XmlNode} tree, resolving only {@code ${...}} expressions - * that reference runtime properties (not available at model interpolation time). Returns the - * original node if no interpolation was needed, or a new node with resolved values otherwise. - */ - private XmlNode interpolateXmlNode( - XmlNode node, ExpressionEvaluator evaluator, Set modelTimePropertyNames) { - boolean changed = false; - - // Interpolate the text value (only if it contains runtime expressions) - String value = node.value(); - String newValue = value; - if (value != null && value.contains("${") && containsRuntimeExpression(value, modelTimePropertyNames)) { - try { - Object evaluated = evaluator.evaluate(value); - if (evaluated instanceof String evaluatedStr && !evaluatedStr.equals(value)) { - newValue = evaluatedStr; - changed = true; - } - } catch (ExpressionEvaluationException e) { - logger.debug("Failed to interpolate configuration value '{}': {}", value, e.getMessage()); - } - } - - // Interpolate the default-value attribute if present (only if it contains runtime expressions) - Map attributes = node.attributes(); - Map newAttributes = attributes; - String defaultValue = attributes.get("default-value"); - if (defaultValue != null - && defaultValue.contains("${") - && containsRuntimeExpression(defaultValue, modelTimePropertyNames)) { - try { - Object evaluated = evaluator.evaluate(defaultValue); - if (evaluated instanceof String evaluatedStr && !evaluatedStr.equals(defaultValue)) { - newAttributes = new HashMap<>(attributes); - newAttributes.put("default-value", evaluatedStr); - changed = true; - } - } catch (ExpressionEvaluationException e) { - logger.debug( - "Failed to interpolate configuration default-value '{}': {}", defaultValue, e.getMessage()); - } - } - - // Recurse into children - List children = node.children(); - List newChildren = children; - for (int i = 0; i < children.size(); i++) { - XmlNode child = children.get(i); - XmlNode newChild = interpolateXmlNode(child, evaluator, modelTimePropertyNames); - if (newChild != child && newChildren == children) { - newChildren = new ArrayList<>(children.size()); - for (int j = 0; j < i; j++) { - newChildren.add(children.get(j)); - } - changed = true; - } - if (newChildren != children) { - newChildren.add(newChild); - } - } - - if (!changed) { - return node; - } - - return XmlNode.newBuilder() - .name(node.name()) - .value(newValue) - .attributes(newAttributes) - .children(newChildren) - .namespaceUri(node.namespaceUri()) - .prefix(node.prefix()) - .inputLocation(node.inputLocation()) - .build(); - } - private void validateParameters( MojoDescriptor mojoDescriptor, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator) throws ComponentConfigurationException, PluginParameterException { @@ -1204,15 +1032,6 @@ public ExtensionRealmCache.CacheRecord setupExtensionsRealm( } } extensionRecord = extensionRealmCache.put(extensionKey, extensionRealm, extensionDescriptor, artifacts); - - // If another thread already cached a record for this key, dispose the redundant realm - if (extensionRecord.getRealm() != extensionRealm) { - try { - extensionRealm.getWorld().disposeRealm(extensionRealm.getId()); - } catch (NoSuchRealmException e) { - // ignore — realm was already disposed - } - } } extensionRealmCache.register(project, extensionKey, extensionRecord); pluginRealms.put(pluginKey, extensionRecord); @@ -1227,4 +1046,4 @@ private List resolveExtensionArtifacts( pluginDependenciesResolver.resolvePluginAndFlatten(extensionPlugin, null, null, repositories, session); return toMavenArtifacts(root); } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginValidationManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginValidationManager.java index 0bd14d1635d1..4132ff96b48e 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginValidationManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginValidationManager.java @@ -18,6 +18,7 @@ */ package org.apache.maven.plugin.internal; +import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; @@ -39,9 +40,11 @@ import java.util.stream.Collectors; import org.apache.maven.api.Constants; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.eventspy.AbstractEventSpy; import org.apache.maven.execution.ExecutionEvent; import org.apache.maven.execution.MavenSession; +import org.apache.maven.internal.build.DefaultDiagnosticCollector; import org.apache.maven.model.InputLocation; import org.apache.maven.plugin.PluginValidationManager; import org.apache.maven.plugin.descriptor.MojoDescriptor; @@ -83,6 +86,13 @@ private enum ValidationReportLevel { private final Logger logger = LoggerFactory.getLogger(getClass()); + private final DefaultDiagnosticCollector diagnosticCollector; + + @Inject + DefaultPluginValidationManager(DefaultDiagnosticCollector diagnosticCollector) { + this.diagnosticCollector = diagnosticCollector; + } + @Override public void onEvent(Object event) { if (event instanceof ExecutionEvent executionEvent) { @@ -158,28 +168,30 @@ private void mayReportInline(RepositorySystemSession session, IssueLocality loca @Override public void reportPluginValidationIssue( - IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, String issue) { + IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, BuilderProblem problem) { String pluginKey = pluginKey(pluginArtifact); if (validationPluginExcludes(session).contains(pluginKey)) { return; } PluginValidationIssues pluginIssues = pluginIssues(session).computeIfAbsent(pluginKey, k -> new PluginValidationIssues()); - pluginIssues.reportPluginIssue(locality, null, issue); - mayReportInline(session, locality, issue); + pluginIssues.reportPluginIssue(locality, null, problem.getMessage()); + diagnosticCollector.report(problem); + mayReportInline(session, locality, problem.getMessage()); } @Override public void reportPluginValidationIssue( - IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, String issue) { + IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, BuilderProblem problem) { String pluginKey = pluginKey(mojoDescriptor); if (validationPluginExcludes(mavenSession.getRepositorySession()).contains(pluginKey)) { return; } PluginValidationIssues pluginIssues = pluginIssues(mavenSession.getRepositorySession()) .computeIfAbsent(pluginKey, k -> new PluginValidationIssues()); - pluginIssues.reportPluginIssue(locality, pluginDeclaration(mavenSession, mojoDescriptor), issue); - mayReportInline(mavenSession.getRepositorySession(), locality, issue); + pluginIssues.reportPluginIssue(locality, pluginDeclaration(mavenSession, mojoDescriptor), problem.getMessage()); + diagnosticCollector.report(problem); + mayReportInline(mavenSession.getRepositorySession(), locality, problem.getMessage()); } @Override @@ -188,7 +200,7 @@ public void reportPluginMojoValidationIssue( MavenSession mavenSession, MojoDescriptor mojoDescriptor, Class mojoClass, - String issue) { + BuilderProblem problem) { String pluginKey = pluginKey(mojoDescriptor); if (validationPluginExcludes(mavenSession.getRepositorySession()).contains(pluginKey)) { return; @@ -196,8 +208,12 @@ public void reportPluginMojoValidationIssue( PluginValidationIssues pluginIssues = pluginIssues(mavenSession.getRepositorySession()) .computeIfAbsent(pluginKey, k -> new PluginValidationIssues()); pluginIssues.reportPluginMojoIssue( - locality, pluginDeclaration(mavenSession, mojoDescriptor), mojoInfo(mojoDescriptor, mojoClass), issue); - mayReportInline(mavenSession.getRepositorySession(), locality, issue); + locality, + pluginDeclaration(mavenSession, mojoDescriptor), + mojoInfo(mojoDescriptor, mojoClass), + problem.getMessage()); + diagnosticCollector.report(problem); + mayReportInline(mavenSession.getRepositorySession(), locality, problem.getMessage()); } private void reportSessionCollectedValidationIssues(MavenSession mavenSession) { @@ -371,4 +387,4 @@ private synchronized void reportPluginMojoIssue( .add(issue); } } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedCoreExpressionValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedCoreExpressionValidator.java index faaac5cf1c53..8fcaf70a9a1b 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedCoreExpressionValidator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedCoreExpressionValidator.java @@ -63,6 +63,11 @@ protected String getParameterLogReason(Parameter parameter) { + DEPRECATED_CORE_PARAMETERS.get(parameter.getDefaultValue()); } + @Override + protected String getValidationKeyPrefix() { + return "deprecated-expr"; + } + @Override protected void doValidate( MavenSession mavenSession, @@ -76,9 +81,13 @@ protected void doValidate( mojoDescriptor.getParameters().stream() .filter(this::isDeprecated) - .map(this::formatParameter) - .forEach(m -> pluginValidationManager.reportPluginMojoValidationIssue( - PluginValidationManager.IssueLocality.EXTERNAL, mavenSession, mojoDescriptor, mojoClass, m)); + .map(this::buildParameterProblem) + .forEach(problem -> pluginValidationManager.reportPluginMojoValidationIssue( + PluginValidationManager.IssueLocality.EXTERNAL, + mavenSession, + mojoDescriptor, + mojoClass, + problem)); } private boolean isDeprecated(Parameter parameter) { @@ -86,4 +95,4 @@ private boolean isDeprecated(Parameter parameter) { org.apache.maven.artifact.repository.ArtifactRepository.class.getName(), parameter.getType()) && DEPRECATED_CORE_PARAMETERS.containsKey(parameter.getDefaultValue()); } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java index a4d784c403dc..019f83f0ef92 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java @@ -22,6 +22,7 @@ import javax.inject.Named; import javax.inject.Singleton; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.MessageBuilderFactory; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.PluginValidationManager; @@ -52,6 +53,11 @@ protected String getParameterLogReason(Parameter parameter) { return "is deprecated: " + parameter.getDeprecated(); } + @Override + protected String getValidationKeyPrefix() { + return "deprecated-param"; + } + @Override protected void doValidate( MavenSession mavenSession, @@ -65,7 +71,11 @@ protected void doValidate( mavenSession, mojoDescriptor, mojoClass, - logDeprecatedMojo(mojoDescriptor)); + BuilderProblem.builder() + .message(logDeprecatedMojo(mojoDescriptor)) + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:deprecated-goal:" + mojoDescriptor.getGoal()) + .build()); } if (mojoDescriptor.getParameters() != null) { @@ -92,7 +102,7 @@ private void checkParameter( mavenSession, mojoDescriptor, mojoClass, - formatParameter(parameter)); + buildParameterProblem(parameter)); } } @@ -105,4 +115,4 @@ private String logDeprecatedMojo(MojoDescriptor mojoDescriptor) { .warning(mojoDescriptor.getDeprecated()) .toString(); } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven2DependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven2DependenciesValidator.java index d2f8dd89ce77..d84a2da1aa5e 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven2DependenciesValidator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven2DependenciesValidator.java @@ -25,6 +25,7 @@ import java.util.Set; import java.util.stream.Collectors; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.plugin.PluginValidationManager; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -64,7 +65,12 @@ protected void doValidate( PluginValidationManager.IssueLocality.EXTERNAL, session, pluginArtifact, - "Plugin is a Maven 2.x plugin, which will be not supported in Maven 4.x"); + BuilderProblem.builder() + .message("Plugin is a Maven 2.x plugin, which will be not supported in Maven 4.x") + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:maven2-plugin") + .suggestion("Upgrade to a Maven 3.x/4.x compatible version of this plugin") + .build()); } } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven3CompatDependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven3CompatDependenciesValidator.java index 0cf8fee6ad35..dc219bfea053 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven3CompatDependenciesValidator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven3CompatDependenciesValidator.java @@ -23,6 +23,7 @@ import javax.inject.Singleton; import org.apache.maven.api.DependencyScope; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.plugin.PluginValidationManager; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -55,8 +56,14 @@ protected void doValidate( PluginValidationManager.IssueLocality.EXTERNAL, session, pluginArtifact, - "Plugin depends on the deprecated Maven 2.x compatibility layer, which will be not supported in Maven 4.x"); + BuilderProblem.builder() + .message( + "Plugin depends on the deprecated Maven 2.x compatibility layer, which will be not supported in Maven 4.x") + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:maven-compat-dep") + .suggestion("Remove the maven-compat dependency from the plugin") + .build()); } } } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenMixedDependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenMixedDependenciesValidator.java index 27003836d3fd..1eaf3d775139 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenMixedDependenciesValidator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenMixedDependenciesValidator.java @@ -25,6 +25,7 @@ import java.util.Set; import java.util.stream.Collectors; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.plugin.PluginValidationManager; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -63,7 +64,12 @@ protected void doValidate( PluginValidationManager.IssueLocality.EXTERNAL, session, pluginArtifact, - "Plugin mixes multiple Maven versions: " + mavenVersions); + BuilderProblem.builder() + .message("Plugin mixes multiple Maven versions: " + mavenVersions) + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:mixed-maven-versions") + .suggestion("Align all Maven dependencies to a single version to avoid classloading issues") + .build()); } } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenScopeDependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenScopeDependenciesValidator.java index 460a91c6ec18..6459e596d5c1 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenScopeDependenciesValidator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenScopeDependenciesValidator.java @@ -26,6 +26,7 @@ import java.util.stream.Collectors; import org.apache.maven.api.DependencyScope; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.plugin.PluginValidationManager; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -65,8 +66,15 @@ protected void doValidate( PluginValidationManager.IssueLocality.EXTERNAL, session, pluginArtifact, - "Plugin should declare Maven artifacts in `provided` scope. If the plugin already declares them in `provided` scope, update the maven-plugin-plugin to latest version. Artifacts found with wrong scope: " - + mavenArtifacts); + BuilderProblem.builder() + .message( + "Plugin should declare Maven artifacts in `provided` scope. If the plugin already declares them in `provided` scope, update the maven-plugin-plugin to latest version. Artifacts found with wrong scope: " + + mavenArtifacts) + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:wrong-scope") + .suggestion( + "Change Maven artifact dependencies to 'provided' scope or update maven-plugin-plugin") + .build()); } } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/PlexusContainerDefaultDependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/PlexusContainerDefaultDependenciesValidator.java index 31638139a3f7..88f8b99e95d4 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/PlexusContainerDefaultDependenciesValidator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/PlexusContainerDefaultDependenciesValidator.java @@ -22,6 +22,7 @@ import javax.inject.Named; import javax.inject.Singleton; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.plugin.PluginValidationManager; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -55,7 +56,12 @@ protected void doValidate( PluginValidationManager.IssueLocality.EXTERNAL, session, pluginArtifact, - "Plugin depends on plexus-container-default, which is EOL"); + BuilderProblem.builder() + .message("Plugin depends on plexus-container-default, which is EOL") + .severity(BuilderProblem.Severity.WARNING) + .key("plugin-validation:plexus-container-eol") + .suggestion("Migrate from plexus-container-default to javax.inject / Eclipse Sisu") + .build()); } } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/ReadOnlyPluginParametersValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/ReadOnlyPluginParametersValidator.java index 2989fb5166a2..1600250bb766 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/ReadOnlyPluginParametersValidator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/ReadOnlyPluginParametersValidator.java @@ -47,6 +47,11 @@ protected String getParameterLogReason(Parameter parameter) { return "is read-only, must not be used in configuration"; } + @Override + protected String getValidationKeyPrefix() { + return "readonly-param"; + } + @Override protected void doValidate( MavenSession mavenSession, @@ -79,7 +84,7 @@ private void checkParameter( mavenSession, mojoDescriptor, mojoClass, - formatParameter(parameter)); + buildParameterProblem(parameter)); } } -} +} \ No newline at end of file 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 0be344384adf..db48e8239cb8 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 @@ -26,7 +26,9 @@ import java.util.ArrayList; import java.util.List; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.internal.build.DefaultDiagnosticCollector; import org.apache.maven.model.building.ModelProblem; import org.apache.maven.model.building.ModelProblemUtils; import org.apache.maven.project.MavenProject; @@ -45,10 +47,12 @@ public class DefaultProjectsSelector implements ProjectsSelector { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultProjectsSelector.class); private final ProjectBuilder projectBuilder; + private final DefaultDiagnosticCollector diagnosticCollector; @Inject - public DefaultProjectsSelector(ProjectBuilder projectBuilder) { + public DefaultProjectsSelector(ProjectBuilder projectBuilder, DefaultDiagnosticCollector diagnosticCollector) { this.projectBuilder = projectBuilder; + this.diagnosticCollector = diagnosticCollector; } @Override @@ -83,6 +87,14 @@ public List selectProjects(List files, MavenExecutionRequest LOGGER.warn("{}{}", problem.getMessage(), ((loc != null && !loc.isEmpty()) ? " @ " + loc : "")); } } + + // Pipe structured problems directly to DiagnosticCollector so that + // source location and severity are preserved in the build report + // (instead of being lost to plain-text logging). The SLF4J hook + // excludes this logger to avoid double-counting. + for (ModelProblem problem : result.getProblems()) { + diagnosticCollector.report(toBuilderProblem(problem)); + } } } @@ -100,4 +112,26 @@ public List selectProjects(List files, MavenExecutionRequest return projects; } -} + + /** + * Converts a compat {@link ModelProblem} to the Maven 4 {@link BuilderProblem} API, + * preserving source, line, column, severity, and message. + */ + private static BuilderProblem toBuilderProblem(ModelProblem problem) { + BuilderProblem.Severity severity = + switch (problem.getSeverity()) { + case FATAL -> BuilderProblem.Severity.FATAL; + case ERROR -> BuilderProblem.Severity.ERROR; + default -> BuilderProblem.Severity.WARNING; + }; + return BuilderProblem.builder() + .source(problem.getSource()) + .lineNumber(problem.getLineNumber()) + .columnNumber(problem.getColumnNumber()) + .exception(problem.getException()) + .message(problem.getMessage()) + .severity(severity) + .key("model:" + problem.getMessage().hashCode()) + .build(); + } +} \ No newline at end of file diff --git a/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultGraphBuilderTest.java b/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultGraphBuilderTest.java index 72dad0b244d5..fa68b919bad9 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultGraphBuilderTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultGraphBuilderTest.java @@ -101,7 +101,8 @@ class DefaultGraphBuilderTest { private final MavenSession session = mock(MavenSession.class); private final MavenExecutionRequest mavenExecutionRequest = mock(MavenExecutionRequest.class); - private final ProjectsSelector projectsSelector = new DefaultProjectsSelector(projectBuilder); + private final ProjectsSelector projectsSelector = new DefaultProjectsSelector( + projectBuilder, new org.apache.maven.internal.build.DefaultDiagnosticCollector()); // Not using mocks for these strategies - a mock would just copy the actual implementation. @@ -560,4 +561,4 @@ private String prependWithColonIfNeeded(String selector) { return selector.indexOf(':') == -1 ? ":" + selector : selector; } } -} +} \ No newline at end of file diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportCollectorTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportCollectorTest.java index 8cffb11c30bf..0edb4e8e69c3 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportCollectorTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportCollectorTest.java @@ -35,7 +35,6 @@ import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.execution.MavenSession; -import org.apache.maven.impl.DefaultBuilderProblem; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; @@ -334,8 +333,12 @@ void testDiagnosticSuppressionWildcard() { // ---- Test helpers ---- private static BuilderProblem warning(String key, String message, String source) { - return new DefaultBuilderProblem( - source, -1, -1, null, message, BuilderProblem.Severity.WARNING, key, null, null); + return BuilderProblem.builder() + .source(source) + .message(message) + .severity(BuilderProblem.Severity.WARNING) + .key(key) + .build(); } private MavenProject createProject(String groupId, String artifactId, String version) { diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportIntegrationTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportIntegrationTest.java index 7cf25141a42d..0c6744b327c8 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportIntegrationTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportIntegrationTest.java @@ -35,7 +35,6 @@ import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionResult; import org.apache.maven.execution.MavenSession; -import org.apache.maven.impl.DefaultBuilderProblem; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; @@ -72,21 +71,41 @@ void setUp() { } private static BuilderProblem warning(String key, String message, String source) { - return new DefaultBuilderProblem( - source, -1, -1, null, message, BuilderProblem.Severity.WARNING, key, null, null); + return BuilderProblem.builder() + .source(source) + .message(message) + .severity(BuilderProblem.Severity.WARNING) + .key(key) + .build(); } private static BuilderProblem warning(String key, String message, String source, String suggestion, String docUrl) { - return new DefaultBuilderProblem( - source, -1, -1, null, message, BuilderProblem.Severity.WARNING, key, suggestion, docUrl); + return BuilderProblem.builder() + .source(source) + .message(message) + .severity(BuilderProblem.Severity.WARNING) + .key(key) + .suggestion(suggestion) + .documentationUrl(docUrl) + .build(); } private static BuilderProblem info(String key, String message, String source) { - return new DefaultBuilderProblem(source, -1, -1, null, message, BuilderProblem.Severity.INFO, key, null, null); + return BuilderProblem.builder() + .source(source) + .message(message) + .severity(BuilderProblem.Severity.INFO) + .key(key) + .build(); } private static BuilderProblem error(String key, String message, String source) { - return new DefaultBuilderProblem(source, -1, -1, null, message, BuilderProblem.Severity.ERROR, key, null, null); + return BuilderProblem.builder() + .source(source) + .message(message) + .severity(BuilderProblem.Severity.ERROR) + .key(key) + .build(); } /** diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportJsonWriterTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportJsonWriterTest.java index 9c28632b2171..c84a5fcfeab2 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportJsonWriterTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/internal/build/BuildReportJsonWriterTest.java @@ -30,7 +30,6 @@ import org.apache.maven.api.build.report.ModuleReport; import org.apache.maven.api.build.report.MojoReport; import org.apache.maven.api.services.BuilderProblem; -import org.apache.maven.impl.DefaultBuilderProblem; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -404,27 +403,23 @@ void testLogEventWithJulMetadata() { @Test void testProblemsSerialization() { - BuilderProblem p1 = new DefaultBuilderProblem( - "maven-compiler-plugin:3.15.0:compile", - 42, - 1, - null, - "source/target value 8 is deprecated", - BuilderProblem.Severity.WARNING, - "deprecated-source-target", - "Update to 11 or higher", - "https://example.com/docs/compiler"); - - BuilderProblem p2 = new DefaultBuilderProblem( - "maven-compiler-plugin", - -1, - -1, - null, - "3 errors found", - BuilderProblem.Severity.ERROR, - "compilation-failure", - null, - null); + BuilderProblem p1 = BuilderProblem.builder() + .source("maven-compiler-plugin:3.15.0:compile") + .lineNumber(42) + .columnNumber(1) + .message("source/target value 8 is deprecated") + .severity(BuilderProblem.Severity.WARNING) + .key("deprecated-source-target") + .suggestion("Update to 11 or higher") + .documentationUrl("https://example.com/docs/compiler") + .build(); + + BuilderProblem p2 = BuilderProblem.builder() + .source("maven-compiler-plugin") + .message("3 errors found") + .severity(BuilderProblem.Severity.ERROR) + .key("compilation-failure") + .build(); BuildReport report = new DefaultBuildReport( BuildStatus.FAILURE, diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/build/DefaultDiagnosticCollectorTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/build/DefaultDiagnosticCollectorTest.java index c4c938de03e7..933daa6a5120 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/internal/build/DefaultDiagnosticCollectorTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/internal/build/DefaultDiagnosticCollectorTest.java @@ -27,7 +27,6 @@ import java.util.concurrent.Future; import org.apache.maven.api.services.BuilderProblem; -import org.apache.maven.impl.DefaultBuilderProblem; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -45,16 +44,30 @@ void setUp() { } private static BuilderProblem warning(String key, String message, String source) { - return new DefaultBuilderProblem( - source, -1, -1, null, message, BuilderProblem.Severity.WARNING, key, null, null); + return BuilderProblem.builder() + .source(source) + .message(message) + .severity(BuilderProblem.Severity.WARNING) + .key(key) + .build(); } private static BuilderProblem error(String key, String message, String source) { - return new DefaultBuilderProblem(source, -1, -1, null, message, BuilderProblem.Severity.ERROR, key, null, null); + return BuilderProblem.builder() + .source(source) + .message(message) + .severity(BuilderProblem.Severity.ERROR) + .key(key) + .build(); } private static BuilderProblem info(String key, String message, String source) { - return new DefaultBuilderProblem(source, -1, -1, null, message, BuilderProblem.Severity.INFO, key, null, null); + return BuilderProblem.builder() + .source(source) + .message(message) + .severity(BuilderProblem.Severity.INFO) + .key(key) + .build(); } @Test @@ -175,16 +188,16 @@ void testSummaryListIsUnmodifiable() { @Test void testFullProblemFields() { - BuilderProblem p = new DefaultBuilderProblem( - "maven-compiler-plugin:3.15.0:compile", - 42, - 15, - null, - "unchecked cast from Object to List", - BuilderProblem.Severity.WARNING, - "unchecked-cast", - "Add @SuppressWarnings(\"unchecked\") or use a type-safe alternative", - "https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html"); + BuilderProblem p = BuilderProblem.builder() + .source("maven-compiler-plugin:3.15.0:compile") + .lineNumber(42) + .columnNumber(15) + .message("unchecked cast from Object to List") + .severity(BuilderProblem.Severity.WARNING) + .key("unchecked-cast") + .suggestion("Add @SuppressWarnings(\"unchecked\") or use a type-safe alternative") + .documentationUrl("https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html") + .build(); collector.report(p);