From ccabc79865f803ee5c656480791def6a241a62a8 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 29 Jul 2026 13:46:06 +0200 Subject: [PATCH 1/4] Experiment: add Maven 4 BuildContext API for incremental resource processing Inject the new Maven 4 BuildContext (org.apache.maven.api.build.context) into ResourcesMojo and use it for incremental change detection: - Register all resource files as inputs via buildContext.registerInputs() - Check Metadata.getStatus() for NEW/MODIFIED status - Skip resource processing entirely when no changes detected - Call buildContext.markSkipExecution() to carry over state Also update to maven-filtering 4.0.0-beta-2-SNAPSHOT which has been migrated to use the new BuildContext API directly, removing the need for the old plexus-build-api dependency. Depends on: apache/maven#12576 (BuildContext API in maven-api-core) Co-Authored-By: Claude Opus 4.6 --- pom.xml | 10 ++-- .../maven/plugins/resources/Providers.java | 10 +--- .../plugins/resources/ResourcesMojo.java | 54 +++++++++++++++++++ 3 files changed, 59 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 3efb7873..cd4db1a1 100644 --- a/pom.xml +++ b/pom.xml @@ -72,11 +72,11 @@ under the License. - 4.0.0-rc-4 + 4.1.0-SNAPSHOT 17 7.0.0 - 4.0.0-beta-1 + 4.0.0-beta-2-SNAPSHOT 4.0.0-beta-2 4.0.0-beta-4 0.0.7 @@ -139,11 +139,7 @@ under the License. maven-filtering ${mavenFilteringVersion} - - org.sonatype.plexus - plexus-build-api - ${plexusBuildApiVersion} - + org.eclipse.sisu org.eclipse.sisu.plexus diff --git a/src/main/java/org/apache/maven/plugins/resources/Providers.java b/src/main/java/org/apache/maven/plugins/resources/Providers.java index a9fe718c..b4ce1fcc 100644 --- a/src/main/java/org/apache/maven/plugins/resources/Providers.java +++ b/src/main/java/org/apache/maven/plugins/resources/Providers.java @@ -19,15 +19,9 @@ package org.apache.maven.plugins.resources; import org.apache.maven.api.di.Named; -import org.apache.maven.api.di.Provides; -import org.sonatype.plexus.build.incremental.BuildContext; -import org.sonatype.plexus.build.incremental.ThreadBuildContext; @Named class Providers { - - @Provides - static BuildContext buildContext() { - return new ThreadBuildContext(); - } + // Old plexus BuildContext provider removed — maven-filtering now uses + // the new Maven 4 BuildContext API from maven-api-core directly. } diff --git a/src/main/java/org/apache/maven/plugins/resources/ResourcesMojo.java b/src/main/java/org/apache/maven/plugins/resources/ResourcesMojo.java index 63539e0a..e880134b 100644 --- a/src/main/java/org/apache/maven/plugins/resources/ResourcesMojo.java +++ b/src/main/java/org/apache/maven/plugins/resources/ResourcesMojo.java @@ -18,6 +18,7 @@ */ package org.apache.maven.plugins.resources; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; @@ -32,6 +33,10 @@ import org.apache.maven.api.ProjectScope; import org.apache.maven.api.Session; import org.apache.maven.api.SourceRoot; +import org.apache.maven.api.build.context.BuildContext; +import org.apache.maven.api.build.context.Input; +import org.apache.maven.api.build.context.Metadata; +import org.apache.maven.api.build.context.Status; import org.apache.maven.api.di.Inject; import org.apache.maven.api.plugin.Log; import org.apache.maven.api.plugin.MojoException; @@ -291,6 +296,9 @@ public class ResourcesMojo implements org.apache.maven.api.plugin.Mojo { @Inject private Log logger; + @Inject + protected BuildContext buildContext; + /** {@inheritDoc} */ public void execute() throws MojoException { if (isSkip()) { @@ -303,9 +311,55 @@ public void execute() throws MojoException { .map(ResourcesMojo::newResource) .toList(); } + + // Register resource directories with the incremental build context + // to enable change detection across builds + boolean hasChanges = registerResourceInputs(); + if (!hasChanges) { + getLog().info("No resource changes detected, skipping resource processing."); + buildContext.markSkipExecution(); + return; + } + doExecute(); } + /** + * Registers all resource files with the incremental {@link BuildContext} + * and determines whether any processing is required. + * + * @return {@code true} if at least one resource file is new or modified + */ + protected boolean registerResourceInputs() { + if (getResources() == null || getResources().isEmpty()) { + return false; + } + boolean hasChanges = false; + for (Resource resource : getResources()) { + Path resourceDir = Path.of(resource.getDirectory()); + if (!Files.isDirectory(resourceDir)) { + continue; + } + List includes = resource.getIncludes(); + if (includes == null || includes.isEmpty()) { + includes = List.of("**"); + } + List excludes = resource.getExcludes(); + if (excludes == null) { + excludes = List.of(); + } + Collection> inputs = buildContext.registerInputs(resourceDir, includes, excludes); + for (Metadata input : inputs) { + Status status = input.getStatus(); + if (status == Status.NEW || status == Status.MODIFIED) { + hasChanges = true; + } + } + } + // Also check if processing is required based on plugin configuration changes + return hasChanges || buildContext.isProcessingRequired(); + } + static Resource newResource(SourceRoot res) { Resource resource = new Resource(); resource.setDirectory(res.directory().toString()); From c0d445cdfa7dbf1235090d1be62d1590de497d49 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 29 Jul 2026 14:11:20 +0200 Subject: [PATCH 2/4] Experiment: simplify mojo, delegate incremental processing to maven-filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove registerResourceInputs() from the mojo — BuildContext interaction now lives entirely in maven-filtering's DefaultMavenResourcesFiltering. This eliminates double-registration and keeps a single registration point for all plugins that use maven-filtering. The mojo now only: - Resolves resource roots - Calls doExecute() which delegates to maven-filtering - Uses markSkipExecution() for explicit skip scenarios Co-Authored-By: Claude Opus 4.6 --- .../plugins/resources/ResourcesMojo.java | 53 ++----------------- 1 file changed, 4 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/resources/ResourcesMojo.java b/src/main/java/org/apache/maven/plugins/resources/ResourcesMojo.java index e880134b..ad9990db 100644 --- a/src/main/java/org/apache/maven/plugins/resources/ResourcesMojo.java +++ b/src/main/java/org/apache/maven/plugins/resources/ResourcesMojo.java @@ -18,7 +18,6 @@ */ package org.apache.maven.plugins.resources; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; @@ -34,9 +33,6 @@ import org.apache.maven.api.Session; import org.apache.maven.api.SourceRoot; import org.apache.maven.api.build.context.BuildContext; -import org.apache.maven.api.build.context.Input; -import org.apache.maven.api.build.context.Metadata; -import org.apache.maven.api.build.context.Status; import org.apache.maven.api.di.Inject; import org.apache.maven.api.plugin.Log; import org.apache.maven.api.plugin.MojoException; @@ -303,6 +299,7 @@ public class ResourcesMojo implements org.apache.maven.api.plugin.Mojo { public void execute() throws MojoException { if (isSkip()) { getLog().info("Skipping the execution."); + buildContext.markSkipExecution(); return; } if (resources == null) { @@ -312,54 +309,12 @@ public void execute() throws MojoException { .toList(); } - // Register resource directories with the incremental build context - // to enable change detection across builds - boolean hasChanges = registerResourceInputs(); - if (!hasChanges) { - getLog().info("No resource changes detected, skipping resource processing."); - buildContext.markSkipExecution(); - return; - } - + // Incremental build support is handled by maven-filtering: + // it registers inputs with the BuildContext, processes only changed files, + // and associates outputs for stale cleanup. doExecute(); } - /** - * Registers all resource files with the incremental {@link BuildContext} - * and determines whether any processing is required. - * - * @return {@code true} if at least one resource file is new or modified - */ - protected boolean registerResourceInputs() { - if (getResources() == null || getResources().isEmpty()) { - return false; - } - boolean hasChanges = false; - for (Resource resource : getResources()) { - Path resourceDir = Path.of(resource.getDirectory()); - if (!Files.isDirectory(resourceDir)) { - continue; - } - List includes = resource.getIncludes(); - if (includes == null || includes.isEmpty()) { - includes = List.of("**"); - } - List excludes = resource.getExcludes(); - if (excludes == null) { - excludes = List.of(); - } - Collection> inputs = buildContext.registerInputs(resourceDir, includes, excludes); - for (Metadata input : inputs) { - Status status = input.getStatus(); - if (status == Status.NEW || status == Status.MODIFIED) { - hasChanges = true; - } - } - } - // Also check if processing is required based on plugin configuration changes - return hasChanges || buildContext.isProcessingRequired(); - } - static Resource newResource(SourceRoot res) { Resource resource = new Resource(); resource.setDirectory(res.directory().toString()); From d6eac05d93ab54f0394e5ccdb4a01eabb8036fc5 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 29 Jul 2026 15:23:55 +0200 Subject: [PATCH 3/4] Experiment: fix test DI for BuildContext and PathMatcherFactory Provide BuildContext and PathMatcherFactory via @Provides @Priority(10) methods in each test class so the test DI container can resolve DefaultMavenResourcesFiltering without requiring the MojoExecutionScoped infrastructure from the full Maven runtime. Co-Authored-By: Claude Opus 4.6 --- .../resources/CopyResourcesMojoTest.java | 22 +++++++++++++++++++ .../plugins/resources/ResourcesMojoTest.java | 22 +++++++++++++++++++ .../plugins/resources/TestResourcesTest.java | 22 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/src/test/java/org/apache/maven/plugins/resources/CopyResourcesMojoTest.java b/src/test/java/org/apache/maven/plugins/resources/CopyResourcesMojoTest.java index fdc45c0b..9e19ff28 100644 --- a/src/test/java/org/apache/maven/plugins/resources/CopyResourcesMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/resources/CopyResourcesMojoTest.java @@ -21,15 +21,22 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; +import java.util.HashMap; import org.apache.maven.api.Project; +import org.apache.maven.api.build.context.BuildContext; +import org.apache.maven.api.di.Priority; import org.apache.maven.api.di.Provides; import org.apache.maven.api.di.Singleton; import org.apache.maven.api.plugin.testing.Basedir; import org.apache.maven.api.plugin.testing.InjectMojo; import org.apache.maven.api.plugin.testing.MojoTest; import org.apache.maven.api.plugin.testing.stubs.SessionMock; +import org.apache.maven.api.services.PathMatcherFactory; +import org.apache.maven.impl.DefaultPathMatcherFactory; import org.apache.maven.impl.InternalSession; +import org.apache.maven.internal.build.context.impl.DefaultBuildContext; +import org.apache.maven.internal.build.context.impl.FilesystemWorkspace; import org.apache.maven.plugins.resources.stub.MavenProjectResourcesStub; import org.apache.maven.shared.filtering.Resource; import org.junit.jupiter.api.Test; @@ -76,4 +83,19 @@ private static InternalSession getMockSession() { private static Project createProject(ExtensionContext context) throws Exception { return new MavenProjectResourcesStub(); } + + @Provides + @Priority(10) + @SuppressWarnings("unused") + private static PathMatcherFactory pathMatcherFactory() { + return new DefaultPathMatcherFactory(); + } + + @Provides + @Priority(10) + @SuppressWarnings("unused") + private static BuildContext buildContext() { + return new DefaultBuildContext( + new FilesystemWorkspace(), null, new HashMap<>(), null, new DefaultPathMatcherFactory()); + } } diff --git a/src/test/java/org/apache/maven/plugins/resources/ResourcesMojoTest.java b/src/test/java/org/apache/maven/plugins/resources/ResourcesMojoTest.java index 67a04b49..48749264 100644 --- a/src/test/java/org/apache/maven/plugins/resources/ResourcesMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/resources/ResourcesMojoTest.java @@ -25,18 +25,25 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Properties; import org.apache.maven.api.Project; +import org.apache.maven.api.build.context.BuildContext; +import org.apache.maven.api.di.Priority; import org.apache.maven.api.di.Provides; import org.apache.maven.api.di.Singleton; import org.apache.maven.api.plugin.testing.Basedir; import org.apache.maven.api.plugin.testing.InjectMojo; import org.apache.maven.api.plugin.testing.MojoTest; import org.apache.maven.api.plugin.testing.stubs.SessionMock; +import org.apache.maven.api.services.PathMatcherFactory; +import org.apache.maven.impl.DefaultPathMatcherFactory; import org.apache.maven.impl.InternalSession; +import org.apache.maven.internal.build.context.impl.DefaultBuildContext; +import org.apache.maven.internal.build.context.impl.FilesystemWorkspace; import org.apache.maven.plugins.resources.stub.MavenProjectResourcesStub; import org.apache.maven.shared.filtering.Resource; import org.junit.jupiter.api.Test; @@ -630,6 +637,21 @@ private static Project createProject() throws Exception { return new MavenProjectResourcesStub(); } + @Provides + @Priority(10) + @SuppressWarnings("unused") + private static PathMatcherFactory pathMatcherFactory() { + return new DefaultPathMatcherFactory(); + } + + @Provides + @Priority(10) + @SuppressWarnings("unused") + private static BuildContext buildContext() { + return new DefaultBuildContext( + new FilesystemWorkspace(), null, new HashMap<>(), null, new DefaultPathMatcherFactory()); + } + static List getResources(MavenProjectResourcesStub project) { return project.getBuild().getResources().stream() .map(ResourcesMojoTest::newResource) diff --git a/src/test/java/org/apache/maven/plugins/resources/TestResourcesTest.java b/src/test/java/org/apache/maven/plugins/resources/TestResourcesTest.java index 998b11cb..12dc03df 100644 --- a/src/test/java/org/apache/maven/plugins/resources/TestResourcesTest.java +++ b/src/test/java/org/apache/maven/plugins/resources/TestResourcesTest.java @@ -21,16 +21,23 @@ import java.io.File; import java.nio.file.Paths; import java.util.Collections; +import java.util.HashMap; import java.util.List; import org.apache.maven.api.Project; +import org.apache.maven.api.build.context.BuildContext; +import org.apache.maven.api.di.Priority; import org.apache.maven.api.di.Provides; import org.apache.maven.api.di.Singleton; import org.apache.maven.api.plugin.testing.Basedir; import org.apache.maven.api.plugin.testing.InjectMojo; import org.apache.maven.api.plugin.testing.MojoTest; import org.apache.maven.api.plugin.testing.stubs.SessionMock; +import org.apache.maven.api.services.PathMatcherFactory; +import org.apache.maven.impl.DefaultPathMatcherFactory; import org.apache.maven.impl.InternalSession; +import org.apache.maven.internal.build.context.impl.DefaultBuildContext; +import org.apache.maven.internal.build.context.impl.FilesystemWorkspace; import org.apache.maven.plugins.resources.stub.MavenProjectResourcesStub; import org.apache.maven.shared.filtering.Resource; import org.junit.jupiter.api.Test; @@ -106,6 +113,21 @@ private static Project createProject() throws Exception { return new MavenProjectResourcesStub(); } + @Provides + @Priority(10) + @SuppressWarnings("unused") + private static PathMatcherFactory pathMatcherFactory() { + return new DefaultPathMatcherFactory(); + } + + @Provides + @Priority(10) + @SuppressWarnings("unused") + private static BuildContext buildContext() { + return new DefaultBuildContext( + new FilesystemWorkspace(), null, new HashMap<>(), null, new DefaultPathMatcherFactory()); + } + private static List getResources(MavenProjectResourcesStub project) { return ResourcesMojoTest.getResources(project); } From d83e11dc40124307e4d90d10905d8844216eda85 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 29 Jul 2026 15:57:14 +0200 Subject: [PATCH 4/4] Experiment: remove dead plexus-build-api remnants Delete the empty Providers.java (was the old plexus BuildContext provider, now unused) and remove the stale comment from pom.xml. Co-Authored-By: Claude Opus 4.6 --- pom.xml | 1 - .../maven/plugins/resources/Providers.java | 27 ------------------- 2 files changed, 28 deletions(-) delete mode 100644 src/main/java/org/apache/maven/plugins/resources/Providers.java diff --git a/pom.xml b/pom.xml index cd4db1a1..5da4ad6b 100644 --- a/pom.xml +++ b/pom.xml @@ -139,7 +139,6 @@ under the License. maven-filtering ${mavenFilteringVersion} - org.eclipse.sisu org.eclipse.sisu.plexus diff --git a/src/main/java/org/apache/maven/plugins/resources/Providers.java b/src/main/java/org/apache/maven/plugins/resources/Providers.java deleted file mode 100644 index b4ce1fcc..00000000 --- a/src/main/java/org/apache/maven/plugins/resources/Providers.java +++ /dev/null @@ -1,27 +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.plugins.resources; - -import org.apache.maven.api.di.Named; - -@Named -class Providers { - // Old plexus BuildContext provider removed — maven-filtering now uses - // the new Maven 4 BuildContext API from maven-api-core directly. -}