diff --git a/src/main/java/org/openrewrite/java/migrate/AddSurefireFailsafeArgLine.java b/src/main/java/org/openrewrite/java/migrate/AddSurefireFailsafeArgLine.java index 1ce44c1bf4..d0f06c29c1 100644 --- a/src/main/java/org/openrewrite/java/migrate/AddSurefireFailsafeArgLine.java +++ b/src/main/java/org/openrewrite/java/migrate/AddSurefireFailsafeArgLine.java @@ -44,7 +44,11 @@ public class AddSurefireFailsafeArgLine extends Recipe { String displayName = "Add `argLine` to surefire and failsafe plugins"; String description = "Adds the specified arguments to the `argLine` configuration of the Maven Surefire and Failsafe plugins, " + - "merging with any existing argLine value without duplicating arguments."; + "merging with any existing argLine value without duplicating arguments. " + + "The `@{argLine}` [late property reference](https://maven.apache.org/surefire/maven-surefire-plugin/faq.html) is prepended " + + "so that an agent injected by another plugin during the build, such as the JaCoCo coverage agent from " + + "`jacoco-maven-plugin:prepare-agent`, is preserved rather than overwritten. It is not added when the existing " + + "`argLine` already references the `argLine` property."; @Override public TreeVisitor getVisitor() { @@ -81,7 +85,7 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { return t.withContent( ListUtils.map( (List) t.getContent(), c -> c == config ? updatedConfig : c ) ); } - Xml.Tag newArgLine = Xml.Tag.build( "" + argLine + "" ); + Xml.Tag newArgLine = Xml.Tag.build( "" + newArgLineValue() + "" ); Xml.Tag updatedConfig = config.withContent( ListUtils.concat( newArgLine, (List) config.getContent() ) ); t = t.withContent( ListUtils.map( (List) t.getContent(), c -> @@ -89,11 +93,16 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { return autoFormat( t, ctx ); } Xml.Tag newConfig = Xml.Tag.build( - "\n" + argLine + "\n" ); + "\n" + newArgLineValue() + "\n" ); t = t.withContent( ListUtils.concat( (List) t.getContent(), newConfig ) ); return autoFormat( t, ctx ); } + // Value for a freshly created argLine: prepend the `@{argLine}` reference unless one is already present. + private String newArgLineValue() { + return argLine.contains("{argLine}") ? argLine : ARG_LINE_REFERENCE + " " + argLine; + } + private boolean isPluginTag(Xml.Tag tag) { return "plugin".equals(tag.getName()) && getCursor().getParentTreeCursor().getValue() instanceof Xml.Tag && @@ -104,6 +113,15 @@ private boolean isPluginTag(Xml.Tag tag) { private static final Pattern ARG_PATTERN = Pattern.compile("(--add-opens\\s+\\S+|-\\S+(?:\\s+(?!-)\\S+)*)"); + /** + * Surefire's late property reference. Prepended to a newly written or merged {@code argLine} so that an + * {@code -javaagent} injected into the {@code argLine} property by an earlier plugin in the build (most notably + * the JaCoCo coverage agent set by {@code jacoco-maven-plugin:prepare-agent}) is preserved instead of overwritten. + * Unlike {@code ${argLine}}, the {@code @{...}} form is resolved when Surefire executes rather than at POM + * interpolation, so it picks up the value set during the build and resolves to an empty string when unset. + */ + static final String ARG_LINE_REFERENCE = "@{argLine}"; + static String mergeArgLine(String existing, String toAdd) { // Parse compound args like "--add-opens module/pkg=target" as single units Set existingArgs = parseArgs(existing); @@ -118,10 +136,19 @@ static String mergeArgLine(String existing, String toAdd) { existingArgs.add(normalized); } } - if (argsToAdd.isEmpty()) { + // Prepend `@{argLine}` unless the existing value already references the `argLine` property (e.g. `${argLine}`) + boolean needsArgLineReference = !existing.contains("{argLine}"); + if (argsToAdd.isEmpty() && !needsArgLineReference) { return existing; } - StringBuilder result = new StringBuilder(existing); + StringBuilder result = new StringBuilder(); + if (needsArgLineReference) { + result.append(ARG_LINE_REFERENCE); + if (!existing.isEmpty()) { + result.append(' '); + } + } + result.append(existing); for (String arg : argsToAdd) { result.append(' ').append(arg); } diff --git a/src/main/resources/META-INF/rewrite/examples.yml b/src/main/resources/META-INF/rewrite/examples.yml index aad2e26a7b..f8c67c8414 100644 --- a/src/main/resources/META-INF/rewrite/examples.yml +++ b/src/main/resources/META-INF/rewrite/examples.yml @@ -240,7 +240,7 @@ examples: maven-surefire-plugin 3.5.2 - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED diff --git a/src/main/resources/META-INF/rewrite/recipes.csv b/src/main/resources/META-INF/rewrite/recipes.csv index 0f8af09307..af68e865ae 100644 --- a/src/main/resources/META-INF/rewrite/recipes.csv +++ b/src/main/resources/META-INF/rewrite/recipes.csv @@ -16,7 +16,7 @@ maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.A maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.AddMockitoJavaAgentToMavenSurefirePlugin,Add Mockito Java Agent to Maven Surefire Plugin,Adds required configuration to specifically enable the Mockito/Bytebuddy Java agent in the Maven Surefire plugin for Java 21 compatibility.,1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.AddStaticVariableOnProducerSessionBean,Adds `static` modifier to `@Produces` fields that are in session beans,"Ensures that the fields annotated with `@Produces` which is inside the session bean (`@Stateless`, `@Stateful`, or `@Singleton`) are declared `static`.",1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.AddSuppressionForIllegalReflectionWarningsPlugin,Add maven jar plugin to suppress illegal reflection warnings,Adds a maven jar plugin that's configured to suppress Illegal Reflection Warnings.,1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,"[{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""An exact version number, or node-style semver selector used to select the version number."",""example"":""29.X""}]", -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.AddSurefireFailsafeArgLine,Add `argLine` to surefire and failsafe plugins,"Adds the specified arguments to the `argLine` configuration of the Maven Surefire and Failsafe plugins, merging with any existing argLine value without duplicating arguments.",1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,"[{""name"":""argLine"",""type"":""String"",""displayName"":""Arg line"",""description"":""The arguments to add to the surefire and failsafe plugin `argLine` configuration. Individual arguments are space-separated. Arguments already present in the existing argLine are not duplicated."",""example"":""--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED"",""required"":true}]", +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.AddSurefireFailsafeArgLine,Add `argLine` to surefire and failsafe plugins,"Adds the specified arguments to the `argLine` configuration of the Maven Surefire and Failsafe plugins, merging with any existing argLine value without duplicating arguments. The `@{argLine}` [late property reference](https://maven.apache.org/surefire/maven-surefire-plugin/faq.html) is prepended so that an agent injected by another plugin during the build, such as the JaCoCo coverage agent from `jacoco-maven-plugin:prepare-agent`, is preserved rather than overwritten. It is not added when the existing `argLine` already references the `argLine` property.",1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,"[{""name"":""argLine"",""type"":""String"",""displayName"":""Arg line"",""description"":""The arguments to add to the surefire and failsafe plugin `argLine` configuration. Individual arguments are space-separated. Arguments already present in the existing argLine are not duplicated."",""example"":""--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED"",""required"":true}]", maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.ArrayStoreExceptionToTypeNotPresentException,Catch `TypeNotPresentException` thrown by `Class.getAnnotation()`,Replace catch blocks for `ArrayStoreException` around `Class.getAnnotation()` with `TypeNotPresentException` to ensure compatibility with Java 11+.,1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.BeanDiscovery,Behavior change to bean discovery in modules with `beans.xml` file with no version specified,Alters beans with missing version attribute to include this attribute as well as the bean-discovery-mode="all" attribute to maintain an explicit bean archive.,1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.BeansXmlNamespace,Change `beans.xml` `schemaLocation` to match XML namespace,Set the `schemaLocation` that corresponds to the `xmlns` set in `beans.xml` files.,1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, diff --git a/src/test/java/org/openrewrite/java/migrate/AddSurefireFailsafeArgLineTest.java b/src/test/java/org/openrewrite/java/migrate/AddSurefireFailsafeArgLineTest.java index 915e3e6372..a129683d21 100644 --- a/src/test/java/org/openrewrite/java/migrate/AddSurefireFailsafeArgLineTest.java +++ b/src/test/java/org/openrewrite/java/migrate/AddSurefireFailsafeArgLineTest.java @@ -69,7 +69,7 @@ void surefireWithNoConfiguration() { maven-surefire-plugin 3.5.2 - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED @@ -119,7 +119,7 @@ void surefireWithExistingConfigurationButNoArgLine() { maven-surefire-plugin 3.5.2 - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED **/*Test.java @@ -170,7 +170,7 @@ void surefireWithExistingArgLine() { maven-surefire-plugin 3.5.2 - -Xmx512m --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} -Xmx512m --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED @@ -182,6 +182,64 @@ void surefireWithExistingArgLine() { ); } + @Test + void prependsArgLineReferenceToPreserveJacocoCoverage() { + rewriteRun( + mavenProject("project", + pomXml( + """ + + com.mycompany.app + my-app + 1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.2 + + -Dfoo=bar + + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + + """, + """ + + com.mycompany.app + my-app + 1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.2 + + @{argLine} -Dfoo=bar --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + + """ + ) + ) + ); + } + @Test void idempotentWhenAllFlagsPresent() { rewriteRun( @@ -199,7 +257,7 @@ void idempotentWhenAllFlagsPresent() { maven-surefire-plugin 3.5.2 - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED @@ -244,7 +302,7 @@ void failsafePlugin() { maven-failsafe-plugin 3.5.2 - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED @@ -294,7 +352,7 @@ void bothSurefireAndFailsafe() { maven-surefire-plugin 3.5.2 - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED @@ -302,7 +360,7 @@ void bothSurefireAndFailsafe() { maven-failsafe-plugin 3.5.2 - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED @@ -367,7 +425,7 @@ void pluginInPluginManagement() { maven-surefire-plugin 3.5.2 - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED @@ -428,6 +486,54 @@ void preservesArgLinePropertyReference() { ); } + @Test + void doesNotDuplicateExistingArgLineReference() { + rewriteRun( + mavenProject("project", + pomXml( + """ + + com.mycompany.app + my-app + 1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.2 + + @{argLine} -Xmx512m + + + + + + """, + """ + + com.mycompany.app + my-app + 1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.2 + + @{argLine} -Xmx512m --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + + + + + + """ + ) + ) + ); + } + @Test void surefireWithImplicitGroupId() { rewriteRun( @@ -459,7 +565,7 @@ void surefireWithImplicitGroupId() { maven-surefire-plugin 3.5.2 - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + @{argLine} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED