diff --git a/README.adoc b/README.adoc
index 77d4bb7..fdbda3c 100644
--- a/README.adoc
+++ b/README.adoc
@@ -12,6 +12,28 @@ ____
== Get started
+=== Modules
+
+[plantuml,"models",svg]
+.....
+component srcMaven as "src-maven" {
+ component srcMavenInvoker as "src-maven-invoker" {
+ }
+ component srcMavenEmbedder as "src-maven-embedder" {
+ }
+}
+
+component srcGradle as "src-gradle" {
+ component srcGradleModel as "src-gradle-model" {
+ }
+ component srcGradleParser as "src-gradle-parser" {
+ }
+ component srcGradlePlugin as "src-gradle-plugin" {
+ }
+}
+}
+.....
+
=== Add Dependency
**Maven**
diff --git a/pom.xml b/pom.xml
index 641fa48..4092d5b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,6 +17,7 @@
spring-rewrite-commons-examples
spring-rewrite-commons-starters
spring-rewrite-commons-gradle
+ spring-rewrite-commons-maven-invoker
@@ -110,6 +111,12 @@
pom
import
+
+ org.junit-pioneer
+ junit-pioneer
+ ${junit-pioneer.version}
+ test
+
diff --git a/spring-rewrite-commons-maven-invoker/pom.xml b/spring-rewrite-commons-maven-invoker/pom.xml
new file mode 100644
index 0000000..0684cf8
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/pom.xml
@@ -0,0 +1,99 @@
+
+
+ 4.0.0
+
+ org.springframework.rewrite
+ spring-rewrite-commons
+ 0.1.0-SNAPSHOT
+
+
+ spring-rewrite-commons-maven-invoker
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.apache.maven.shared
+ maven-shared-utils
+ 3.4.2
+
+
+
+ org.apache.maven.shared
+ maven-invoker
+ ${maven-invoker.version}
+
+
+ org.apache.maven.resolver
+ maven-resolver-util
+ ${maven-resolver.version}
+
+
+ org.openrewrite.maven
+ rewrite-maven-plugin
+ ${rewrite-maven-plugin.version}
+
+
+ org.projectlombok
+ lombok
+
+
+
+ org.apache.maven.resolver
+ maven-resolver-api
+ ${maven-resolver.version}
+
+
+ org.apache.maven.resolver
+ maven-resolver-impl
+ ${maven-resolver.version}
+
+
+ org.apache.maven.resolver
+ maven-resolver-transport-wagon
+ ${maven-resolver.version}
+
+
+ org.apache.maven.resolver
+ maven-resolver-connector-basic
+ ${maven-resolver.version}
+
+
+
+ org.apache.maven.wagon
+ wagon-http
+ ${maven-wagon-http.version}
+
+
+ org.apache.maven
+ maven-resolver-provider
+ ${maven.version}
+
+
+ org.apache.maven
+ maven-compat
+ ${maven.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+ org.junit-pioneer
+ junit-pioneer
+ test
+
+
+
+
\ No newline at end of file
diff --git a/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/BuildConfig.java b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/BuildConfig.java
new file mode 100644
index 0000000..3567aa6
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/BuildConfig.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.springframework.rewrite.maven;
+
+/**
+ * @author Fabian Krüger
+ */
+public class BuildConfig {
+
+ private boolean skipTests = false;
+
+ private MemorySettings memorySettings;
+
+ BuildConfig(boolean skipTests, MemorySettings memorySettings) {
+ this.skipTests = skipTests;
+ this.memorySettings = memorySettings;
+ }
+
+ private BuildConfig(boolean skipTests) {
+ this.skipTests = skipTests;
+ }
+
+ public static BuildConfig skipTests() {
+ BuildConfig buildConfig = new BuildConfig(true);
+ return buildConfig;
+ }
+
+ public static BuildConfig defaultConfig() {
+ return new BuildConfig(false);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ boolean isSkipTests() {
+ return skipTests;
+ }
+
+ public MemorySettings getMemorySettings() {
+ return memorySettings;
+ }
+
+ public static class Builder {
+
+ private boolean skipTests;
+
+ private MemorySettings memorySettings = MemorySettings.of("31M", "256M");
+
+ public Builder skipTests(boolean b) {
+ this.skipTests = b;
+ return this;
+ }
+
+ public BuildConfig build() {
+ return new BuildConfig(skipTests, memorySettings);
+ }
+
+ public Builder withMemory(String min, String max) {
+ this.memorySettings = MemorySettings.of(min, max);
+ return this;
+ }
+
+ }
+
+}
diff --git a/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/DebugConfig.java b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/DebugConfig.java
new file mode 100644
index 0000000..08d968b
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/DebugConfig.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.springframework.rewrite.maven;
+
+/**
+ * @author Fabian Krüger
+ */
+public class DebugConfig {
+
+ private int port = 5005;
+
+ private boolean suspend = true;
+
+ private boolean isDebugEnabled = true;
+
+ public static DebugConfig from(int port, boolean suspend) {
+ return new DebugConfig(port, suspend, true);
+ }
+
+ public static DebugConfig fromDefault() {
+ return new DebugConfig(5005, false, true);
+ }
+
+ public static DebugConfig disabled() {
+ return new DebugConfig(5005, false, false);
+ }
+
+ private DebugConfig(int port, boolean suspend, boolean isDebugEnabled) {
+ this.port = port;
+ this.suspend = suspend;
+ this.isDebugEnabled = isDebugEnabled;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public char isSuspend() {
+ return suspend ? 'y' : 'n';
+ }
+
+ public boolean isDebugEnabled() {
+ return isDebugEnabled;
+ }
+
+}
diff --git a/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MavenInvocationRequestFactory.java b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MavenInvocationRequestFactory.java
new file mode 100644
index 0000000..a0ce961
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MavenInvocationRequestFactory.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.springframework.rewrite.maven;
+
+import org.apache.maven.shared.invoker.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * @author Fabian Krüger
+ */
+public class MavenInvocationRequestFactory {
+
+ @NotNull
+ public InvocationRequest createMavenInvocationRequest(Path baseDir, DebugConfig debugConfig,
+ BuildConfig buildConfig, List givenGoals, Consumer lineConsumer) {
+ List goals = new ArrayList<>(givenGoals);
+ String mavenHome = System.getenv("MAVEN_HOME");
+ if (mavenHome == null) {
+ mavenHome = System.getenv("M2_HOME");
+ if (mavenHome == null) {
+ throw new IllegalStateException(
+ "MAVEN_HOME or M2_HOME must be set but System.getenv(\"MAVEN_HOME\") and System.getenv(\"M2_HOME\") returned null.");
+ }
+ }
+
+ if (buildConfig.isSkipTests()) {
+ goals.add("-DskipTests");
+ }
+
+ InvocationRequest request = new DefaultInvocationRequest();
+ request.setGoals(goals);
+ request.setBatchMode(true);
+ request.setMavenHome(new File(mavenHome));
+ request.setBaseDirectory(baseDir.toFile());
+ request.setOutputHandler(s -> lineConsumer.accept(s));
+
+ StringBuilder mavenOpts = new StringBuilder();
+ if (buildConfig.getMemorySettings() != null) {
+ mavenOpts.append("-Xms%s -Xmx%s ".formatted(buildConfig.getMemorySettings().getMin(),
+ buildConfig.getMemorySettings().getMax()));
+ }
+ if (debugConfig != null && debugConfig.isDebugEnabled()) {
+ mavenOpts.append(" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=%s,address=%s "
+ .formatted(debugConfig.isSuspend(), debugConfig.getPort()));
+ }
+ request.setMavenOpts(mavenOpts.toString());
+ return request;
+ }
+
+}
diff --git a/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MavenInvocationRewriteRecipeLauncher.java b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MavenInvocationRewriteRecipeLauncher.java
new file mode 100644
index 0000000..2283dfc
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MavenInvocationRewriteRecipeLauncher.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.springframework.rewrite.maven;
+
+import org.apache.maven.shared.invoker.*;
+
+import javax.swing.text.html.Option;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+/**
+ * Executes OpenRewrite recipes by invoking local Maven installation. It requires
+ * MAVEN_HOME or M2_HOME to be set!
+ *
+ * @author Fabian Krüger
+ */
+public class MavenInvocationRewriteRecipeLauncher {
+
+ private MavenInvocationRequestFactory mavenInvocationRequestFactory = new MavenInvocationRequestFactory();
+
+ // private Invoker invoker = new DefaultInvoker();
+
+ public static Builders.OnDirBuilder applyRecipes(String... recipeNames) {
+ MavenInvokerRewriteRecipeLauncherBuilder builders = new MavenInvokerRewriteRecipeLauncherBuilder();
+ return builders.applyRecipes(recipeNames);
+ }
+
+ public static Builders.OnDirBuilder applyRecipes(List recipeNames) {
+ MavenInvokerRewriteRecipeLauncherBuilder builders = new MavenInvokerRewriteRecipeLauncherBuilder();
+ return builders.applyRecipes(recipeNames);
+ }
+
+ private InvocationResult invokeRewritePlugin(Invoker invoker, Path baseDir, List recipeNames,
+ List dependencies, DebugConfig debugConfig, BuildConfig buildConfig, Consumer lineConsumer,
+ RewritePluginGoals rewritePluginGoals) {
+ validateBaseDir(baseDir);
+
+ if (recipeNames.isEmpty()) {
+ throw new IllegalArgumentException("No recipe names provided.");
+ }
+
+ String openRewriteCommand = renderOpenRewriteCommand(recipeNames, dependencies, rewritePluginGoals);
+ return invokeMaven(invoker, baseDir, debugConfig, buildConfig, lineConsumer, openRewriteCommand);
+ }
+
+ public InvocationResult invokeRewritePlugin(Path baseDir, List recipeNames, List dependencies,
+ DebugConfig debugConfig, BuildConfig buildConfig, Consumer lineConsumer,
+ RewritePluginGoals rewritePluginGoals) {
+
+ validateBaseDir(baseDir);
+
+ if (recipeNames.isEmpty()) {
+ throw new IllegalArgumentException("No recipe names provided.");
+ }
+
+ String openRewriteCommand = renderOpenRewriteCommand(recipeNames, dependencies, rewritePluginGoals);
+ return invokeMaven(new DefaultInvoker(), baseDir, debugConfig, buildConfig, lineConsumer, openRewriteCommand);
+ }
+
+ private void validateBaseDir(Path baseDir) {
+ if (baseDir == null) {
+ throw new IllegalArgumentException("Given baseDir was null.");
+ }
+ else if (!Files.exists(baseDir)) {
+ throw new IllegalArgumentException("Given baseDir '%s' does not exist.".formatted(baseDir));
+ }
+ }
+
+ private InvocationResult invokeMaven(Invoker invoker, Path baseDir, DebugConfig debugConfig,
+ BuildConfig buildConfig, Consumer lineConsumer, String openRewriteCommand) {
+ List goals = new ArrayList<>();
+ goals.add("clean");
+ goals.add("package");
+ goals.add("--fail-at-end");
+ goals.add(openRewriteCommand);
+
+ InvocationRequest request = mavenInvocationRequestFactory.createMavenInvocationRequest(baseDir, debugConfig,
+ buildConfig, goals, lineConsumer);
+
+ try {
+ return invoker.execute(request);
+ }
+ catch (MavenInvocationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // public static InvocationResult invokeMaven(Path baseDir, BuildConfig buildConfig,
+ // DebugConfig debugConfig,
+ // Consumer lineConsumer, List providedGoals) {
+ // List goals = new ArrayList<>(providedGoals);
+ //
+ // InvocationRequest mavenInvocationRequest = new
+ // MavenInvocationRequestFactory().createMavenInvocationRequest(baseDir, debugConfig,
+ // buildConfig, providedGoals, lineConsumer);
+ //
+ // String mavenHome = System.getenv("MAVEN_HOME");
+ // if (mavenHome == null) {
+ // mavenHome = System.getenv("M2_HOME");
+ // if (mavenHome == null) {
+ // throw new IllegalStateException(
+ // "MAVEN_HOME or M2_HOME must be set but System.getenv(\"MAVEN_HOME\") and
+ // System.getenv(\"M2_HOME\") returned null.");
+ // }
+ // }
+ //
+ // if (buildConfig.isSkipTests()) {
+ // goals.add("-DskipTests");
+ // }
+ //
+ // Invoker invoker = new DefaultInvoker();
+ // InvocationRequest request = new DefaultInvocationRequest();
+ // request.setGoals(goals);
+ // request.setBatchMode(true);
+ // request.setMavenHome(new File(mavenHome));
+ // request.setBaseDirectory(baseDir.toFile());
+ // request.setOutputHandler(s -> lineConsumer.accept(s));
+ //
+ // StringBuilder mavenOpts = new StringBuilder();
+ // mavenOpts.append("-Xms1G -Xmx6G ");
+ // if (debugConfig != null && debugConfig.isDebugEnabled()) {
+ // mavenOpts.append(" -Xdebug
+ // -Xrunjdwp:transport=dt_socket,server=y,suspend=%s,address=%s "
+ // .formatted(debugConfig.isSuspend(), debugConfig.getPort()));
+ // }
+ // request.setMavenOpts(mavenOpts.toString());
+ //
+ // try {
+ // InvocationResult result = invoker.execute(request);
+ // return result;
+ // }
+ // catch (MavenInvocationException e) {
+ // throw new RuntimeException(e);
+ // }
+ // }
+
+ private static String renderOpenRewriteCommand(List recipeNames, List dependencies,
+ RewritePluginGoals rewritePluginGoal) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("org.openrewrite.maven:rewrite-maven-plugin:").append(rewritePluginGoal.getGoalName()).append(" ");
+ String recipesList = recipeNames.stream().collect(Collectors.joining(","));
+ sb.append("-Drewrite.activeRecipes=").append(recipesList);
+ if (!dependencies.isEmpty()) {
+ String dependenciesList = dependencies.stream().collect(Collectors.joining(","));
+ sb.append(" ").append("-Drewrite.recipeArtifactCoordinates=").append(dependenciesList);
+ }
+ return sb.toString();
+ }
+
+ public class Builders {
+
+ public interface StartBuilder {
+
+ OnDirBuilder applyRecipes(String... recipeNames);
+
+ OnDirBuilder applyRecipes(List recipeNames);
+
+ }
+
+ public interface OnDirBuilder {
+
+ OptionalBuilder onDir(Path baseDir);
+
+ }
+
+ public interface OptionalBuilder {
+
+ OptionalBuilder withDebugConfig(DebugConfig debugConfig);
+
+ OptionalBuilder withDependencies(String... dependencyGavs);
+
+ OptionalBuilder withDependencies(List dependencyGavs);
+
+ OptionalBuilder withBuildConfig(BuildConfig buildConfig);
+
+ OptionalBuilder withOutputListener(Consumer outputListener);
+
+ OptionalBuilder withInvoker(Invoker invoker);
+
+ InvocationResult run();
+
+ InvocationResult runNoFork();
+
+ InvocationResult dryRun();
+
+ InvocationResult dryRunNoFork();
+
+ InvocationResult discover();
+
+ InvocationResult cyclonedx();
+
+ }
+
+ }
+
+ public static class MavenInvokerRewriteRecipeLauncherBuilder
+ implements Builders.StartBuilder, Builders.OnDirBuilder, Builders.OptionalBuilder {
+
+ private Path baseDir;
+
+ private List recipes = new ArrayList<>();
+
+ private List dependencies = new ArrayList<>();
+
+ private BuildConfig buildConfig = BuildConfig.defaultConfig();
+
+ private DebugConfig debugConfig = DebugConfig.disabled();
+
+ private Consumer outputListener = line -> System.out.println(line);
+
+ private Invoker invoker = new DefaultInvoker();
+
+ public Builders.OptionalBuilder onDir(String baseDir) {
+ this.baseDir = Path.of(baseDir).toAbsolutePath().normalize();
+ return this;
+ }
+
+ @Override
+ public Builders.OnDirBuilder applyRecipes(String... recipeNames) {
+ validateRecipeNames(Arrays.asList(recipeNames));
+ return this.applyRecipes(Arrays.asList(recipeNames));
+ }
+
+ @Override
+ public Builders.OnDirBuilder applyRecipes(List recipeNames) {
+ validateRecipeNames(recipeNames);
+ this.recipes = recipeNames;
+ return this;
+ }
+
+ @Override
+ public Builders.OptionalBuilder onDir(Path baseDir) {
+ this.baseDir = baseDir.toAbsolutePath().normalize();
+ return this;
+ }
+
+ @Override
+ public Builders.OptionalBuilder withOutputListener(Consumer outputListener) {
+ this.outputListener = outputListener;
+ return this;
+ }
+
+ @Override
+ public Builders.OptionalBuilder withInvoker(Invoker invoker) {
+ this.invoker = invoker;
+ return this;
+ }
+
+ /**
+ * mvn rewrite:run - RunBuilder the configured recipes and apply the changes
+ * locally.
+ */
+ @Override
+ public InvocationResult run() {
+ return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
+ RewritePluginGoals.RUN);
+ }
+
+ /**
+ * mvn rewrite:runNoFork - RunBuilder the configured recipes and apply the changes
+ * locally. This variant does not fork the Maven life cycle and can be a more
+ * efficient choice when using Rewrite within a CI workflow when combined with
+ * other Maven goals.
+ */
+ @Override
+ public InvocationResult runNoFork() {
+ return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
+ RewritePluginGoals.RUN_NO_FORK);
+ }
+
+ /**
+ * mvn rewrite:dryRun - Generate warnings to the console for any recipe that would
+ * make changes and generates a diff file in each maven modules' target folder.
+ */
+ @Override
+ public InvocationResult dryRun() {
+ return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
+ RewritePluginGoals.DRY_RUN);
+ }
+
+ /**
+ * mvn rewrite:dryRunNoFork - Generate warnings to the console for any recipe that
+ * would make changes and generates a diff file in each maven modules' target
+ * folder. This variant does not fork the Maven life cycle and can be a more
+ * efficient choice when using Rewrite within a CI workflow when combined with
+ * other Maven goals.
+ */
+ @Override
+ public InvocationResult dryRunNoFork() {
+ return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
+ RewritePluginGoals.DRY_RUN_NO_FORK);
+ }
+
+ /**
+ * mvn rewrite:discover - Generate a report of available recipes found on the
+ * classpath.
+ */
+ @Override
+ public InvocationResult discover() {
+ return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
+ RewritePluginGoals.DISCOVER);
+ }
+
+ /**
+ * mvn rewrite:cyclonedx - Generate a CycloneDx bill of materials outlining the
+ * project's dependencies, including transitive dependencies.
+ *
+ * @see CycloneDx
+ */
+ @Override
+ public InvocationResult cyclonedx() {
+ return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
+ RewritePluginGoals.CYCLONEDX);
+ }
+
+ private InvocationResult executeRewriteGoal(Invoker invoker, Path baseDir, List recipeNames,
+ List dependencies, DebugConfig debugConfig, BuildConfig buildConfig,
+ Consumer lineConsumer, RewritePluginGoals rewritePluginGoals) {
+ MavenInvocationRewriteRecipeLauncher launcher = new MavenInvocationRewriteRecipeLauncher();
+ InvocationResult result = launcher.invokeRewritePlugin(invoker, baseDir, recipeNames, dependencies,
+ debugConfig, buildConfig, lineConsumer, rewritePluginGoals);
+ return result;
+ }
+
+ private void validateRecipeNames(List recipeNames) {
+ if (recipeNames.isEmpty()) {
+ throw new IllegalArgumentException("At least one recipe name must be provided.");
+ }
+
+ if (recipeNames.stream().anyMatch(String::isBlank)) {
+ throw new IllegalArgumentException("At least one recipe name was blank.");
+ }
+ }
+
+ @Override
+ public Builders.OptionalBuilder withDependencies(String... dependencyGavs) {
+ return withDependencies(Arrays.asList(dependencyGavs));
+ }
+
+ @Override
+ public Builders.OptionalBuilder withDependencies(List dependencyGavs) {
+ this.dependencies = dependencyGavs;
+ return this;
+ }
+
+ @Override
+ public Builders.OptionalBuilder withBuildConfig(BuildConfig buildConfig) {
+ this.buildConfig = buildConfig;
+ return this;
+ }
+
+ @Override
+ public Builders.OptionalBuilder withDebugConfig(DebugConfig debugConfig) {
+ this.debugConfig = debugConfig;
+ return this;
+ }
+
+ }
+
+}
diff --git a/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MavenInvoker.java b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MavenInvoker.java
new file mode 100644
index 0000000..3b1f278
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MavenInvoker.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.springframework.rewrite.maven;
+
+import org.apache.maven.shared.invoker.*;
+
+/**
+ * @author Fabian Krüger
+ */
+public class MavenInvoker {
+
+ private Invoker invoker = new DefaultInvoker();
+
+ public InvocationResult invoke(InvocationRequest request) {
+ try {
+ return invoker.execute(request);
+ }
+ catch (MavenInvocationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MemorySettings.java b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MemorySettings.java
new file mode 100644
index 0000000..71eb5eb
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/MemorySettings.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.springframework.rewrite.maven;
+
+/**
+ * @author Fabian Krüger
+ */
+public class MemorySettings {
+
+ private final String min;
+
+ private final String max;
+
+ public MemorySettings(String min, String max) {
+ this.min = min;
+ this.max = max;
+ }
+
+ public static MemorySettings of(String min, String max) {
+ return new MemorySettings(min, max);
+ }
+
+ public String getMin() {
+ return min;
+ }
+
+ public String getMax() {
+ return max;
+ }
+
+}
diff --git a/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/RewritePluginGoals.java b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/RewritePluginGoals.java
new file mode 100644
index 0000000..9429b1e
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/main/java/org/springframework/rewrite/maven/RewritePluginGoals.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.springframework.rewrite.maven;
+
+/**
+ * @author Fabian Krüger
+ */
+public enum RewritePluginGoals {
+
+ RUN("run"), RUN_NO_FORK("runNoFork"), DRY_RUN("dryRun"), DRY_RUN_NO_FORK("dryRunNoFork"), DISCOVER("discover"),
+ CYCLONEDX("cyclonedx");
+
+ private final String goalName;
+
+ RewritePluginGoals(String goalName) {
+ this.goalName = goalName;
+ }
+
+ public String getGoalName() {
+ return goalName;
+ }
+
+}
diff --git a/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/DummyInvocationResult.java b/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/DummyInvocationResult.java
new file mode 100644
index 0000000..daa010e
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/DummyInvocationResult.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.springframework.rewrite.maven;
+
+import org.apache.maven.shared.invoker.InvocationResult;
+import org.apache.maven.shared.utils.cli.CommandLineException;
+
+public class DummyInvocationResult implements InvocationResult {
+
+ @Override
+ public CommandLineException getExecutionException() {
+ return null;
+ }
+
+ @Override
+ public int getExitCode() {
+ return 0;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/MavenInvocationRequestFactoryTest.java b/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/MavenInvocationRequestFactoryTest.java
new file mode 100644
index 0000000..cc6e677
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/MavenInvocationRequestFactoryTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.springframework.rewrite.maven;
+
+import org.apache.maven.shared.invoker.InvocationRequest;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Fabian Krüger
+ */
+public class MavenInvocationRequestFactoryTest {
+
+ @Test
+ @DisplayName("build standard request")
+ void buildStandardRequest() {
+ MavenInvocationRequestFactory sut = new MavenInvocationRequestFactory();
+ List goals = List.of("clean", "package", "--fail-at-end");
+ Consumer lineConsumer = s -> {
+ };
+ Path baseDir = Path.of("./testcode/does-not-exist");
+
+ InvocationRequest request = sut.createMavenInvocationRequest(baseDir, DebugConfig.disabled(),
+ BuildConfig.defaultConfig(), goals, lineConsumer);
+
+ assertThat(request.getGoals()).isEqualTo(goals);
+ assertThat(request.getBaseDirectory()).isEqualTo(baseDir.toFile());
+ assertThat(request.getMavenOpts()).isBlank();
+ assertThat(request.getOutputHandler(null)).isNotNull();
+ }
+
+ @Test
+ @DisplayName("build debug request")
+ void buildDebugRequest() {
+ MavenInvocationRequestFactory sut = new MavenInvocationRequestFactory();
+ List goals = List.of("clean", "package", "--fail-at-end");
+ Consumer lineConsumer = s -> {
+ };
+ Path baseDir = Path.of("./testcode/does-not-exist");
+
+ InvocationRequest request = sut.createMavenInvocationRequest(baseDir, DebugConfig.from(1234, true),
+ BuildConfig.defaultConfig(), goals, lineConsumer);
+
+ assertThat(request.getMavenOpts())
+ .isEqualTo(" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=1234 ");
+ }
+
+ @Test
+ @DisplayName("throws exception on empty baseDir")
+ void errorListener() {
+ MavenInvocationRequestFactory sut = new MavenInvocationRequestFactory();
+ List lines = new ArrayList<>();
+ Path baseDir = Path.of("./testcode/does-not-exist").toAbsolutePath().normalize();
+ @NotNull
+ InvocationRequest result = sut.createMavenInvocationRequest(baseDir, DebugConfig.disabled(),
+ BuildConfig.defaultConfig(), List.of("clean"), lines::add);
+ }
+
+ @Test
+ @DisplayName("build declared memory request and skip tests")
+ void buildDeclaredMemoryRequestAndSkipTests() {
+
+ MavenInvocationRequestFactory sut = new MavenInvocationRequestFactory();
+ List goals = List.of("clean", "package", "--fail-at-end");
+ Consumer lineConsumer = s -> {
+ };
+ Path baseDir = Path.of("./testcode/does-not-exist");
+
+ InvocationRequest request = sut.createMavenInvocationRequest(baseDir, DebugConfig.disabled(),
+ BuildConfig.builder().withMemory("12M", "2G").skipTests(true).build(), goals, lineConsumer);
+
+ assertThat(request.getMavenOpts()).isEqualTo("-Xms12M -Xmx2G ");
+ assertThat(request.getGoals()).contains("-DskipTests");
+ }
+
+}
diff --git a/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/MavenInvocationRewriteRecipeLauncherIntegrationTest.java b/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/MavenInvocationRewriteRecipeLauncherIntegrationTest.java
new file mode 100644
index 0000000..0e6c74d
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/MavenInvocationRewriteRecipeLauncherIntegrationTest.java
@@ -0,0 +1,156 @@
+package org.springframework.rewrite.maven;
+
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+import org.apache.maven.shared.invoker.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.*;
+
+/**
+ * @author Fabian Krüger
+ */
+class MavenInvocationRewriteRecipeLauncherIntegrationTest {
+
+ @Mock
+ private Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
+
+ @Test
+ @DisplayName("invokeRewritePlugin() method test")
+ void invokeRewritePluginMethodTest() {
+ List capturedLines = new ArrayList<>();
+ MavenInvocationRewriteRecipeLauncher sut = new MavenInvocationRewriteRecipeLauncher();
+ sut.invokeRewritePlugin(Path.of("./testcode/maven-projects/simple"),
+ List.of("org.openrewrite.java.RemoveUnusedImports"), new ArrayList<>(), DebugConfig.from(9090, false),
+ BuildConfig.builder().skipTests(true).withMemory("64M", "256M").build(), capturedLines::add,
+ RewritePluginGoals.DRY_RUN);
+ assertThat(capturedLines).contains("Listening for transport dt_socket at address: 9090",
+ "[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ simple ---", "[INFO] Tests are skipped.",
+ "[INFO] Using active recipe(s) [org.openrewrite.java.RemoveUnusedImports]",
+ "[INFO] Using active styles(s) []", "[INFO] BUILD SUCCESS");
+ }
+
+ @Test
+ @DisplayName("mavenInvocationRequestBuilder success")
+ void mavenInvoker() {
+ List capturedLines = new ArrayList<>();
+ Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
+
+ InvocationResult result = MavenInvocationRewriteRecipeLauncher
+ .applyRecipes("org.openrewrite.java.RemoveUnusedImports", "org.openrewrite.java.format.AutoFormat")
+ .onDir(baseDir)
+ .withOutputListener(capturedLines::add)
+ .dryRun();
+
+ assertThat(result.getExitCode()).isEqualTo(0);
+ assertThat(capturedLines).contains("[INFO] Scanning for projects...",
+ "[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ simple ---",
+ "[INFO] Using active recipe(s) [org.openrewrite.java.RemoveUnusedImports, org.openrewrite.java.format.AutoFormat]",
+ "[INFO] BUILD SUCCESS");
+ }
+
+ @Test
+ @DisplayName("with debug enabled")
+ void withDebugEnabled() {
+ List capturedLines = new ArrayList<>();
+
+ InvocationResult result = MavenInvocationRewriteRecipeLauncher
+ .applyRecipes("org.openrewrite.java.RemoveUnusedImports")
+ .onDir(Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize())
+ .withOutputListener(capturedLines::add)
+ .withDebugConfig(DebugConfig.fromDefault())
+ .withBuildConfig(BuildConfig.builder().withMemory("1G", "6G").build())
+ .run();
+ assertThat(capturedLines).contains("Listening for transport dt_socket at address: 5005");
+ assertThat(result.getExitCode()).isEqualTo(0);
+ }
+
+ @Test
+ @DisplayName("cyclonedx")
+ void cyclonedx() {
+ List lines = new ArrayList<>();
+ MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
+ InvocationResult result = builder.cyclonedx();
+ assertThat(result.getExitCode()).isEqualTo(0);
+ assertThat(lines.stream().anyMatch(l -> l.endsWith(":cyclonedx (default-cli) @ simple ---"))).isTrue();
+ assertThat(baseDir.resolve("target/simple-0.1.0-SNAPSHOT-cyclonedx.xml")).exists();
+ }
+
+ @Test
+ @DisplayName("dryRun")
+ void dryRun() {
+ List lines = new ArrayList<>();
+ MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
+ InvocationResult result = builder.dryRun();
+ assertThat(result.getExitCode()).isEqualTo(0);
+ assertThat(lines.stream().anyMatch(l -> l.endsWith(":dryRun (default-cli) @ simple ---"))).isTrue();
+ }
+
+ @Test
+ @DisplayName("dryRunNoFork")
+ void dryRunNoFork() {
+ List lines = new ArrayList<>();
+ MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
+ InvocationResult result = builder.dryRunNoFork();
+ assertThat(result.getExitCode()).isEqualTo(0);
+ assertThat(lines.stream().anyMatch(l -> l.endsWith(":dryRunNoFork (default-cli) @ simple ---"))).isTrue();
+ }
+
+ @Test
+ @DisplayName("runNoFork")
+ void runNoFork() {
+ List lines = new ArrayList<>();
+ MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
+ InvocationResult result = builder.dryRunNoFork();
+ assertThat(result.getExitCode()).isEqualTo(0);
+ assertThat(lines.stream().anyMatch(l -> l.endsWith(":dryRunNoFork (default-cli) @ simple ---"))).isTrue();
+ }
+
+ @Test
+ @DisplayName("discover")
+ void discover() {
+ List lines = new ArrayList<>();
+ MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
+ InvocationResult result = builder.discover();
+ assertThat(result.getExitCode()).isEqualTo(0);
+ assertThat(lines).contains("[INFO] Available Recipes:", "[INFO] org.openrewrite.FindSourceFiles");
+ assertThat(lines.stream().anyMatch(l -> l.endsWith(":discover (default-cli) @ simple ---"))).isTrue();
+ }
+
+ private static MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder getBuilder(List lines) {
+ Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
+ MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = MavenInvocationRewriteRecipeLauncher
+ .applyRecipes("org.openrewrite.java.RemoveUnusedImports")
+ .onDir(baseDir)
+ .withOutputListener(lines::add);
+ return builder;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/MavenInvocationRewriteRecipeLauncherTest.java b/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/MavenInvocationRewriteRecipeLauncherTest.java
new file mode 100644
index 0000000..72546cb
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/src/test/java/org/springframework/rewrite/maven/MavenInvocationRewriteRecipeLauncherTest.java
@@ -0,0 +1,192 @@
+package org.springframework.rewrite.maven;
+
+/*
+ * Copyright 2021 - 2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+import org.apache.maven.shared.invoker.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.*;
+
+/**
+ * @author Fabian Krüger
+ */
+class MavenInvocationRewriteRecipeLauncherTest {
+
+ private static final int SUCCESS = 0;
+
+ private MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder invokerBuilder;
+
+ @Mock
+ Invoker mavenInvoker = mock(Invoker.class);
+
+ private Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
+
+ private List capturedLines = new ArrayList<>();
+
+ private Consumer outputListener = capturedLines::add;
+
+ @BeforeEach
+ void beforeEach() throws MavenInvocationException {
+ outputListener = capturedLines::add;
+ invokerBuilder = MavenInvocationRewriteRecipeLauncher
+ .applyRecipes("org.openrewrite.java.RemoveUnusedImports", "org.openrewrite.java.format.AutoFormat")
+ .onDir(baseDir)
+ .withInvoker(mavenInvoker)
+ .withOutputListener(outputListener);
+
+ InvocationResult result = new DummyInvocationResult();
+ when(mavenInvoker.execute(any(InvocationRequest.class))).thenReturn(result);
+ }
+
+ @Test
+ @DisplayName("minimal")
+ void minimal() throws MavenInvocationException {
+ InvocationResult actualResult = invokerBuilder.run();
+
+ assertThat(actualResult.getExitCode()).isEqualTo(0);
+ String[] expectedGoals = { "clean", "package", "--fail-at-end",
+ "org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.activeRecipes=org.openrewrite.java.RemoveUnusedImports,org.openrewrite.java.format.AutoFormat" };
+ verifyCallToMaven(expectedGoals);
+ }
+
+ @Test
+ @DisplayName("with dependencies")
+ void withDependencies() throws MavenInvocationException {
+ InvocationResult actualResult = invokerBuilder
+ .withDependencies("com.example:some-dep:1.0.0", "com.example:another-dep:1.0.0")
+ .run();
+ assertThat(actualResult.getExitCode()).isEqualTo(0);
+ String[] expectedGoals = { "clean", "package", "--fail-at-end",
+ "org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.activeRecipes=org.openrewrite.java.RemoveUnusedImports,org.openrewrite.java.format.AutoFormat -Drewrite.recipeArtifactCoordinates=com.example:some-dep:1.0.0,com.example:another-dep:1.0.0" };
+ verifyCallToMaven(expectedGoals);
+ }
+
+ @Test
+ @DisplayName("with build tools config")
+ void withBuildToolConfig() throws MavenInvocationException {
+
+ InvocationResult actualResult = invokerBuilder
+ .withBuildConfig(BuildConfig.builder().withMemory("64M", "256M").skipTests(true).build())
+ .run();
+ assertThat(actualResult.getExitCode()).isEqualTo(0);
+ String[] expectedGoals = { "clean", "package", "--fail-at-end",
+ "org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.activeRecipes=org.openrewrite.java.RemoveUnusedImports,org.openrewrite.java.format.AutoFormat" };
+ verifyCallToMaven(expectedGoals, BuildConfig.builder().withMemory("64M", "256M").skipTests(true).build());
+ }
+
+ @Test
+ @DisplayName("builder")
+ void builder(@TempDir Path tempDir) {
+ InvocationResult result = MavenInvocationRewriteRecipeLauncher.applyRecipes("recipe1").onDir(tempDir).run();
+
+ MavenInvocationRewriteRecipeLauncher.applyRecipes("recipe1").onDir(tempDir).withDependencies("", "").run();
+
+ MavenInvocationRewriteRecipeLauncher.applyRecipes(List.of("NoName")).onDir(tempDir).withDependencies("").run();
+
+ // build tool config only
+ MavenInvocationRewriteRecipeLauncher.applyRecipes(List.of("NoName"))
+ .onDir(tempDir)
+ .withDependencies("")
+ .withBuildConfig(BuildConfig.defaultConfig())
+ .run();
+
+ // debug config only
+ MavenInvocationRewriteRecipeLauncher.applyRecipes("NoName")
+ .onDir(tempDir)
+ .withDependencies("")
+ .withDebugConfig(DebugConfig.disabled())
+ .run();
+
+ MavenInvocationRewriteRecipeLauncher.applyRecipes(List.of("NoName"))
+ .onDir(tempDir)
+ .withDependencies("")
+ .withDebugConfig(DebugConfig.disabled())
+ .dryRun();
+
+ // build tool and debug config
+ MavenInvocationRewriteRecipeLauncher.applyRecipes(List.of("NoName"))
+ .onDir(tempDir)
+ .withDependencies("")
+ .withBuildConfig(BuildConfig.defaultConfig())
+ .withDebugConfig(DebugConfig.disabled())
+ .run();
+
+ MavenInvocationRewriteRecipeLauncher.applyRecipes("NoName").onDir(tempDir).run();
+ }
+
+ @Test
+ @DisplayName("missing recipeNames should throw exception")
+ void missingRecipeNamesShouldThrowException() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ MavenInvocationRewriteRecipeLauncher.applyRecipes("").onDir(Path.of("")).run();
+ });
+ }
+
+ @Test
+ @DisplayName("mavenInvocationRequestBuilder extra memory")
+ void mavenInvokerExtraMemory() {
+ List lines = new ArrayList<>();
+ Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
+
+ InvocationResult result = MavenInvocationRewriteRecipeLauncher
+ .applyRecipes("org.openrewrite.java.RemoveUnusedImports")
+ .onDir(baseDir)
+ .withBuildConfig(BuildConfig.builder().skipTests(false).withMemory("32M", "1G").build())
+ .withOutputListener(lines::add)
+ .dryRun();
+
+ assertThat(result.getExitCode()).isEqualTo(SUCCESS);
+ assertThat(lines).contains("[INFO] Scanning for projects...",
+ "[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ simple ---", "[INFO] BUILD SUCCESS");
+ }
+
+ private void verifyCallToMaven(String[] expectedGoals) throws MavenInvocationException {
+ BuildConfig buildConfig = BuildConfig.defaultConfig();
+ verifyCallToMaven(expectedGoals, buildConfig, DebugConfig.disabled(), baseDir, outputListener,
+ Arrays.asList(expectedGoals));
+ }
+
+ private void verifyCallToMaven(String[] expectedGoals, BuildConfig buildConfig) throws MavenInvocationException {
+ verifyCallToMaven(expectedGoals, buildConfig, DebugConfig.disabled(), baseDir, outputListener,
+ Arrays.asList(expectedGoals));
+ }
+
+ private void verifyCallToMaven(String[] expectedGoals, BuildConfig buildConfig, DebugConfig debugConfig,
+ Path baseDir, Consumer lineConsumer, List givenGoals) throws MavenInvocationException {
+ InvocationRequest mavenInvocationRequest = new MavenInvocationRequestFactory()
+ .createMavenInvocationRequest(baseDir, debugConfig, buildConfig, givenGoals, lineConsumer);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(InvocationRequest.class);
+ verify(mavenInvoker).execute(captor.capture());
+ InvocationRequest request = captor.getValue();
+ assertThat(request).usingRecursiveComparison().isEqualTo(mavenInvocationRequest);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/application/pom.xml b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/application/pom.xml
new file mode 100644
index 0000000..904d5c6
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/application/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ com.acme
+ parent
+ 0.0.1-SNAPSHOT
+ ../
+
+ application
+
+
+ com.acme
+ component
+ 0.0.1-SNAPSHOT
+
+
+ javax.validation
+ validation-api
+ 2.0.1.Final
+
+
+
diff --git a/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/application/src/main/resources/some-resource.properties b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/application/src/main/resources/some-resource.properties
new file mode 100644
index 0000000..eb6185e
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/application/src/main/resources/some-resource.properties
@@ -0,0 +1 @@
+the content
\ No newline at end of file
diff --git a/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/application/src/test/resources/some-resource.properties b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/application/src/test/resources/some-resource.properties
new file mode 100644
index 0000000..ce6f389
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/application/src/test/resources/some-resource.properties
@@ -0,0 +1 @@
+the test content
\ No newline at end of file
diff --git a/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/component/pom.xml b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/component/pom.xml
new file mode 100644
index 0000000..0945f51
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/component/pom.xml
@@ -0,0 +1,11 @@
+
+
+ 4.0.0
+
+ com.acme
+ parent
+ 0.0.1-SNAPSHOT
+ ../
+
+ component
+
diff --git a/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/pom.xml b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/pom.xml
new file mode 100644
index 0000000..51098cd
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/resources/pom.xml
@@ -0,0 +1,12 @@
+
+
+ 4.0.0
+ com.acme
+ parent
+ 0.0.1-SNAPSHOT
+ pom
+
+ application
+ component
+
+
diff --git a/spring-rewrite-commons-maven-invoker/testcode/maven-projects/simple/pom.xml b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/simple/pom.xml
new file mode 100644
index 0000000..df899a0
--- /dev/null
+++ b/spring-rewrite-commons-maven-invoker/testcode/maven-projects/simple/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+ com.example.maven
+ simple
+ 0.1.0-SNAPSHOT
+
+
+ 17
+ 17
+ UTF-8
+
+
+
\ No newline at end of file