Skip to content

Commit

Permalink
Add recipe for adding/replacing a Maven runtime config (#4363)
Browse files Browse the repository at this point in the history
* Add recipe for adding a Maven runtime config

* Update rewrite-maven/src/main/java/org/openrewrite/maven/AddRuntimeConfig.java

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update rewrite-maven/src/test/java/org/openrewrite/maven/AddRuntimeConfigTest.java

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Polish

* Update rewrite-maven/src/main/java/org/openrewrite/maven/AddRuntimeConfig.java

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tim te Beek <[email protected]>
Co-authored-by: Tim te Beek <[email protected]>
  • Loading branch information
4 people authored Jul 29, 2024
1 parent fc2c4b4 commit e15e821
Show file tree
Hide file tree
Showing 2 changed files with 347 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.openrewrite.maven;

import lombok.*;
import org.openrewrite.*;
import org.openrewrite.internal.StringUtils;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.text.PlainText;
import org.openrewrite.text.PlainTextVisitor;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Value
@EqualsAndHashCode(callSuper = false)
public class AddRuntimeConfig extends ScanningRecipe<AddRuntimeConfig.Accumulator> {
static final String POM_FILENAME = "pom.xml";
static final String MVN_CONFIG_DIR = ".mvn";
static final String MAVEN_CONFIG_FILENAME = "maven.config";
static final String MAVEN_CONFIG_PATH = MVN_CONFIG_DIR + "/" + MAVEN_CONFIG_FILENAME;
static final String JVM_CONFIG_FILENAME = "jvm.config";
static final String JVM_CONFIG_PATH = MVN_CONFIG_DIR + "/" + JVM_CONFIG_FILENAME;

@Option(displayName = "Config file",
description = "The file name for setting the runtime configuration.",
valid = {MAVEN_CONFIG_FILENAME, JVM_CONFIG_FILENAME},
example = "maven.config")
String relativeConfigFileName;

@Option(displayName = "Runtime flag",
description = "The runtime flag name to be set.",
example = "-T")
String flag;

@Option(displayName = "Runtime flag argument",
description = "The argument to set for the runtime flag. Some flags do not need to provide a value.",
required = false,
example = "3")
@Nullable
String argument;

@Option(displayName = "Separator between runtime flag and argument",
description = "The separator to use if flag and argument have been provided.",
valid = {"", " ", "="},
example = "=")
Separator separator;

@Getter
public enum Separator {
NONE(""),
SPACE(" "),
EQUALS("=");

private final String notation;

Separator(String notation) {
this.notation = notation;
}
}

@Override
public String getDisplayName() {
return "Add a configuration option for the Maven runtime";
}

@Override
public String getDescription() {
return "Add a new configuration option for the Maven runtime if not already present.";
}

@Data
@RequiredArgsConstructor
public static class Accumulator {
final String targetRepresentation;
boolean mavenProject;
Path matchingRuntimeConfigFile;

}

@Override
public Accumulator getInitialValue(ExecutionContext ctx) {
String targetRepresentation = argument == null ? flag : flag + separator.getNotation() + argument;
return new Accumulator(targetRepresentation);
}

@Override
public TreeVisitor<?, ExecutionContext> getScanner(Accumulator acc) {
return new TreeVisitor<Tree, ExecutionContext>() {
@Override
public Tree preVisit(Tree tree, ExecutionContext ctx) {
stopAfterPreVisit();
if (tree instanceof SourceFile) {
Path sourcePath = ((SourceFile) tree).getSourcePath();
switch (PathUtils.separatorsToUnix(sourcePath.toString())) {
case POM_FILENAME:
acc.setMavenProject(true);
break;
case MAVEN_CONFIG_PATH:
case JVM_CONFIG_PATH:
acc.setMatchingRuntimeConfigFile(sourcePath);
break;
default:
break;
}
}
return tree;
}
};
}

@Override
public Collection<? extends SourceFile> generate(Accumulator acc, ExecutionContext ctx) {
if (acc.isMavenProject() && acc.getMatchingRuntimeConfigFile() == null) {
return Collections.singletonList(PlainText.builder()
.text(acc.getTargetRepresentation())
.sourcePath(Paths.get(MVN_CONFIG_DIR, relativeConfigFileName))
.build());
}
return Collections.emptyList();
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor(Accumulator acc) {
return Preconditions.check(acc.isMavenProject() && acc.getMatchingRuntimeConfigFile() != null,
new PlainTextVisitor<ExecutionContext>() {
@Override
public PlainText visitText(PlainText plainText, ExecutionContext ctx) {
if (plainText.getSourcePath().equals(acc.getMatchingRuntimeConfigFile())) {
return addOrReplaceConfig(plainText, acc);
}
return plainText;
}

private PlainText addOrReplaceConfig(PlainText plainText, Accumulator acc) {
String existingContent = plainText.getText();
Matcher matcher = Pattern.compile(Pattern.quote(flag) + "[=\\s]?[a-zA-Z0-9]*").matcher(existingContent);
if (matcher.find()) {
return plainText.withText(matcher.replaceAll(acc.getTargetRepresentation()));
}

String newText = StringUtils.isBlank(existingContent) ? existingContent : existingContent + determineConfigSeparator(plainText);
return plainText.withText(newText + acc.getTargetRepresentation());
}

private String determineConfigSeparator(PlainText plainText) {
// Use new line for maven.config, space for jvm.config
if (Paths.get(JVM_CONFIG_PATH).equals(plainText.getSourcePath())) {
return " ";
}
return plainText.getText().contains("\r\n") ? "\r\n" : "\n";
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.openrewrite.maven;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.openrewrite.DocumentExample;
import org.openrewrite.test.RewriteTest;
import org.openrewrite.test.SourceSpecs;

import static org.openrewrite.maven.AddRuntimeConfig.*;
import static org.openrewrite.maven.Assertions.pomXml;
import static org.openrewrite.test.SourceSpecs.text;

class AddRuntimeConfigTest implements RewriteTest {
private static final SourceSpecs POM_XML_SOURCE_SPEC = pomXml(
"""
<project>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</project>
"""
);

@Test
@DocumentExample
void createConfigFileWithRuntimeConfigIfFileDoesNotExist() {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(MAVEN_CONFIG_FILENAME, "-T", "3", Separator.EQUALS)),
POM_XML_SOURCE_SPEC,
text(
null,
"-T=3",
spec -> spec.path(MAVEN_CONFIG_PATH)
)
);
}

@Test
void appendRuntimeFlagToEmptyConfigFile() {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(MAVEN_CONFIG_FILENAME, "-T", "3", Separator.EQUALS)),
POM_XML_SOURCE_SPEC,
text(
"",
"-T=3",
spec -> spec.path(MAVEN_CONFIG_PATH)
)
);
}

@ParameterizedTest
@EnumSource(Separator.class)
void createConfigFileWithRuntimeConfigForAllSeparators(Separator separator) {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(MAVEN_CONFIG_FILENAME, "-T", "3", separator)),
POM_XML_SOURCE_SPEC,
text(
"",
"-T" + separator.getNotation() + "3",
spec -> spec.path(MAVEN_CONFIG_PATH)
)
);
}

@Test
void appendRuntimeFlagIfItDoesNotExist() {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(MAVEN_CONFIG_FILENAME, "-T", "3", Separator.EQUALS)),
POM_XML_SOURCE_SPEC,
text(
"-U",
"""
-U
-T=3
""",
spec -> spec.path(MAVEN_CONFIG_PATH)
)
);
}

@Test
void doesNotModifyRuntimeFlagIfExistingWithoutArgument() {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(MAVEN_CONFIG_FILENAME, "-U", null, Separator.EQUALS)),
POM_XML_SOURCE_SPEC,
text(
"-U",
spec -> spec.path(MAVEN_CONFIG_PATH)
)
);
}

@Test
void doesNotModifyRuntimeFlagIfExistingWithSameArgument() {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(MAVEN_CONFIG_FILENAME, "-T", "3", Separator.EQUALS)),
POM_XML_SOURCE_SPEC,
text(
"-T=3",
spec -> spec.path(MAVEN_CONFIG_PATH)
)
);
}

@ParameterizedTest
@ValueSource(strings = {"--threads=2", "--threads=3"})
void appendRuntimeFlagIfExistingForFlagFormatMismatch(String existingConfig) {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(MAVEN_CONFIG_FILENAME, "-T", "3", Separator.EQUALS)),
POM_XML_SOURCE_SPEC,
text(
existingConfig,
existingConfig + System.lineSeparator() + "-T=3",
spec -> spec.path(MAVEN_CONFIG_PATH)
)
);
}

@ParameterizedTest
@ValueSource(strings = {"-T 3", "-T3", "-T=3"})
void replaceRuntimeFlagIfExistingWithDifferentArgument(String existingConfig) {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(MAVEN_CONFIG_FILENAME, "-T", "4", Separator.EQUALS)),
POM_XML_SOURCE_SPEC,
text(
existingConfig,
"-T=4",
spec -> spec.path(MAVEN_CONFIG_PATH)
)
);
}

@Test
void addJvmRuntimeFlagOnTheSameLine() {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(JVM_CONFIG_FILENAME, "-XX:MaxPermSize", "512m", Separator.EQUALS)),
POM_XML_SOURCE_SPEC,
text(
"-Xmx2048m -Xms1024m",
"-Xmx2048m -Xms1024m -XX:MaxPermSize=512m",
spec -> spec.path(JVM_CONFIG_PATH)
)
);
}

@Test
void replaceJvmRuntimeFlagOnTheSameLine() {
rewriteRun(
spec -> spec.recipe(new AddRuntimeConfig(JVM_CONFIG_FILENAME, "-XX:MaxPermSize", "1024m", Separator.EQUALS)),
POM_XML_SOURCE_SPEC,
text(
"-Xmx2048m -XX:MaxPermSize=512m -Xms1024m",
"-Xmx2048m -XX:MaxPermSize=1024m -Xms1024m",
spec -> spec.path(JVM_CONFIG_PATH)
)
);
}
}

0 comments on commit e15e821

Please sign in to comment.