Skip to content

Commit 0d790f5

Browse files
mcebanupgradeclaudetimtebeek
authored
Skip AddAnnotationProcessor when processor already in effective POM via ancestor (#6759)
* Skip AddAnnotationProcessor when processor already in effective POM via ancestor When an in-reactor parent POM inherits the annotation processor configuration from a parent outside the reactor (present in the effective POM but not in own XML), the recipe would previously add a redundant entry, duplicating what the ancestor already provides. Fix: in the scanner phase, compare `ResolvedPom.getPlugins()/getPluginManagement()` (the merged effective lists including ancestor POMs) against `Pom.getPlugins()/getPluginManagement()` (the current POM's own XML only). If the processor is in the effective POM but not in the own XML, mark the path as already-configured and skip it in the visitor phase. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Trigger CI re-run Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Trigger CI re-run Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Tim te Beek <tim@moderne.io>
1 parent 447d382 commit 0d790f5

2 files changed

Lines changed: 143 additions & 1 deletion

File tree

rewrite-maven/src/main/java/org/openrewrite/maven/AddAnnotationProcessor.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
*/
1616
package org.openrewrite.maven;
1717

18+
import com.fasterxml.jackson.databind.JsonNode;
1819
import lombok.EqualsAndHashCode;
1920
import lombok.Value;
2021
import org.jspecify.annotations.Nullable;
2122
import org.openrewrite.*;
2223
import org.openrewrite.internal.ListUtils;
2324
import org.openrewrite.maven.trait.MavenPlugin;
2425
import org.openrewrite.maven.tree.MavenResolutionResult;
26+
import org.openrewrite.maven.tree.Plugin;
27+
import org.openrewrite.maven.tree.Pom;
28+
import org.openrewrite.maven.tree.ResolvedPom;
2529
import org.openrewrite.semver.Semver;
2630
import org.openrewrite.semver.VersionComparator;
2731
import org.openrewrite.xml.XmlIsoVisitor;
@@ -94,6 +98,13 @@ public static class Scanned {
9498
*/
9599
Set<Path> aggregatorPaths = new HashSet<>();
96100

101+
/**
102+
* Paths of POMs where the annotation processor is already present in the effective POM
103+
* (via a parent POM outside the reactor), but not in the POM's own XML.
104+
* These POMs should be skipped to avoid redundant configuration.
105+
*/
106+
Set<Path> alreadyConfiguredInEffectivePomPaths = new HashSet<>();
107+
97108
/**
98109
* Get the actual orphan paths (candidates minus aggregator-only POMs).
99110
* A true orphan has no parent in reactor and is not an aggregator-only POM.
@@ -121,7 +132,20 @@ public TreeVisitor<?, ExecutionContext> getScanner(Scanned acc) {
121132
@Override
122133
public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) {
123134
MavenResolutionResult mrr = getResolutionResult();
124-
Path sourcePath = mrr.getPom().getRequested().getSourcePath();
135+
ResolvedPom resolvedPom = mrr.getPom();
136+
Path sourcePath = resolvedPom.getRequested().getSourcePath();
137+
138+
// Check if the annotation processor is already in the effective POM (merged from parent POMs)
139+
// but NOT in the current POM's own XML. In that case, skip this POM to avoid adding
140+
// redundant configuration that duplicates what the parent already provides.
141+
boolean inEffectivePom = hasAnnotationProcessor(resolvedPom.getPlugins()) ||
142+
hasAnnotationProcessor(resolvedPom.getPluginManagement());
143+
Pom requestedPom = resolvedPom.getRequested();
144+
boolean inCurrentPomXml = hasAnnotationProcessor(requestedPom.getPlugins()) ||
145+
hasAnnotationProcessor(requestedPom.getPluginManagement());
146+
if (sourcePath != null && inEffectivePom && !inCurrentPomXml) {
147+
acc.alreadyConfiguredInEffectivePomPaths.add(sourcePath);
148+
}
125149

126150
if (mrr.parentPomIsProjectPom()) {
127151
// This module has a parent within the reactor
@@ -177,6 +201,12 @@ public TreeVisitor<?, ExecutionContext> getVisitor(Scanned acc) {
177201
return tree;
178202
}
179203

204+
// Skip POMs where the annotation processor is already configured via a parent POM
205+
// outside the reactor (present in effective POM but not in own XML)
206+
if (acc.alreadyConfiguredInEffectivePomPaths.contains(sourcePath)) {
207+
return tree;
208+
}
209+
180210
// First, ensure the plugin exists - use the source path as file pattern
181211
tree = new AddPluginVisitor(isParent,
182212
MAVEN_COMPILER_PLUGIN_GROUP_ID, MAVEN_COMPILER_PLUGIN_ARTIFACT_ID, null,
@@ -254,4 +284,33 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
254284
}
255285
};
256286
}
287+
288+
private boolean hasAnnotationProcessor(List<Plugin> plugins) {
289+
for (Plugin plugin : plugins) {
290+
if (!MAVEN_COMPILER_PLUGIN_GROUP_ID.equals(plugin.getGroupId()) ||
291+
!MAVEN_COMPILER_PLUGIN_ARTIFACT_ID.equals(plugin.getArtifactId())) {
292+
continue;
293+
}
294+
JsonNode config = plugin.getConfiguration();
295+
if (config == null || config.isMissingNode()) {
296+
continue;
297+
}
298+
JsonNode paths = config.path("annotationProcessorPaths").path("path");
299+
if (paths.isArray()) {
300+
for (JsonNode path : paths) {
301+
if (isMatchingProcessor(path)) {
302+
return true;
303+
}
304+
}
305+
} else if (!paths.isMissingNode() && isMatchingProcessor(paths)) {
306+
return true;
307+
}
308+
}
309+
return false;
310+
}
311+
312+
private boolean isMatchingProcessor(JsonNode path) {
313+
return groupId.equals(path.path("groupId").asText("")) &&
314+
artifactId.equals(path.path("artifactId").asText(""));
315+
}
257316
}

rewrite-maven/src/test/java/org/openrewrite/maven/AddAnnotationProcessorTest.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,89 @@ void addToBuildPluginsWhenInBothLocations() {
597597
}
598598
}
599599

600+
@Nested
601+
class EffectivePomCheck {
602+
603+
@Test
604+
void doesNotAddWhenAlreadyInEffectivePomViaAncestor() {
605+
// 3-level hierarchy: grandparent has the processor in pluginManagement,
606+
// intermediate parent inherits it via effective POM but has no own XML config.
607+
// Expected: intermediate parent is NOT modified (annotation processor already in effective POM)
608+
rewriteRun(
609+
pomXml(
610+
// Grandparent already has the processor configured in pluginManagement
611+
"""
612+
<project>
613+
<modelVersion>4.0.0</modelVersion>
614+
<groupId>com.mycompany.app</groupId>
615+
<artifactId>grandparent</artifactId>
616+
<version>1</version>
617+
<packaging>pom</packaging>
618+
<modules>
619+
<module>parent-module</module>
620+
</modules>
621+
<build>
622+
<pluginManagement>
623+
<plugins>
624+
<plugin>
625+
<groupId>org.apache.maven.plugins</groupId>
626+
<artifactId>maven-compiler-plugin</artifactId>
627+
<configuration>
628+
<annotationProcessorPaths>
629+
<path>
630+
<groupId>org.projectlombok</groupId>
631+
<artifactId>lombok-mapstruct-binding</artifactId>
632+
<version>0.2.0</version>
633+
</path>
634+
</annotationProcessorPaths>
635+
</configuration>
636+
</plugin>
637+
</plugins>
638+
</pluginManagement>
639+
</build>
640+
</project>
641+
"""
642+
),
643+
mavenProject("parent-module",
644+
pomXml(
645+
// Intermediate parent: inherits the processor from grandparent via effective POM,
646+
// but does NOT have it in its own XML. Should not be modified.
647+
"""
648+
<project>
649+
<modelVersion>4.0.0</modelVersion>
650+
<parent>
651+
<groupId>com.mycompany.app</groupId>
652+
<artifactId>grandparent</artifactId>
653+
<version>1</version>
654+
</parent>
655+
<artifactId>parent-module</artifactId>
656+
<packaging>pom</packaging>
657+
<modules>
658+
<module>child-module</module>
659+
</modules>
660+
</project>
661+
"""
662+
)
663+
),
664+
mavenProject("child-module",
665+
pomXml(
666+
"""
667+
<project>
668+
<modelVersion>4.0.0</modelVersion>
669+
<parent>
670+
<groupId>com.mycompany.app</groupId>
671+
<artifactId>parent-module</artifactId>
672+
<version>1</version>
673+
</parent>
674+
<artifactId>child-module</artifactId>
675+
</project>
676+
"""
677+
)
678+
)
679+
);
680+
}
681+
}
682+
600683
@Nested
601684
class CombinedAggregatorAndParent {
602685

0 commit comments

Comments
 (0)