diff --git a/pom.xml b/pom.xml
index b73b935..72add43 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,7 +30,7 @@
UTF-8
- 3.1.3
+ 3.2.1
8.13.4
1.8.4
1.8.11
diff --git a/spring-rewrite-commons-examples/pom.xml b/spring-rewrite-commons-examples/pom.xml
index 01ab9e2..db3dddb 100644
--- a/spring-rewrite-commons-examples/pom.xml
+++ b/spring-rewrite-commons-examples/pom.xml
@@ -38,22 +38,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
org.springframework.rewrite
spring-rewrite-commons-starter-boot-upgrade
diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/boot/autoconfigure/RewriteLauncherConfiguration.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/boot/autoconfigure/RewriteLauncherConfiguration.java
new file mode 100644
index 0000000..6314bef
--- /dev/null
+++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/boot/autoconfigure/RewriteLauncherConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * 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.boot.autoconfigure;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.rewrite.execution.RewriteRecipeLauncher;
+import org.springframework.rewrite.parsers.RewriteProjectParser;
+import org.springframework.rewrite.project.resource.ProjectResourceSetFactory;
+import org.springframework.rewrite.project.resource.ProjectResourceSetSerializer;
+import org.springframework.rewrite.recipes.RewriteRecipeDiscovery;
+
+/**
+ * @author Fabian Krüger
+ */
+@AutoConfiguration(after = { ProjectResourceSetConfiguration.class, RecipeDiscoveryConfiguration.class })
+@Import({ ProjectResourceSetConfiguration.class, RecipeDiscoveryConfiguration.class })
+public class RewriteLauncherConfiguration {
+
+ @Bean
+ RewriteRecipeLauncher rewriteRecipeLaucnher(RewriteProjectParser parser, RewriteRecipeDiscovery dicovery,
+ ProjectResourceSetFactory resourceSetFactory, ProjectResourceSetSerializer deserializer) {
+ return new RewriteRecipeLauncher(parser, dicovery, resourceSetFactory, deserializer);
+ }
+
+}
diff --git a/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/execution/RewriteRecipeLauncher.java b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/execution/RewriteRecipeLauncher.java
new file mode 100644
index 0000000..233e090
--- /dev/null
+++ b/spring-rewrite-commons-launcher/src/main/java/org/springframework/rewrite/execution/RewriteRecipeLauncher.java
@@ -0,0 +1,127 @@
+/*
+ * 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.execution;
+
+import org.jetbrains.annotations.NotNull;
+import org.openrewrite.Recipe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.rewrite.parsers.RewriteProjectParser;
+import org.springframework.rewrite.parsers.RewriteProjectParsingResult;
+import org.springframework.rewrite.project.resource.ProjectResourceSet;
+import org.springframework.rewrite.project.resource.ProjectResourceSetFactory;
+import org.springframework.rewrite.project.resource.ProjectResourceSetSerializer;
+import org.springframework.rewrite.recipes.RewriteRecipeDiscovery;
+import org.springframework.util.StopWatch;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Fabian Krüger
+ */
+public class RewriteRecipeLauncher {
+
+ private static final Logger logger = LoggerFactory.getLogger(RewriteRecipeLauncher.class);
+
+ private final RewriteProjectParser parser;
+
+ private final RewriteRecipeDiscovery discovery;
+
+ private final ProjectResourceSetFactory resourceSetFactory;
+
+ private final ProjectResourceSetSerializer serializer;
+
+ public interface RewriteRecipeRunnerProgressListener {
+
+ void progress(String progressMessage);
+
+ }
+
+ public RewriteRecipeLauncher(RewriteProjectParser parser, RewriteRecipeDiscovery discovery,
+ ProjectResourceSetFactory resourceSetFactory, ProjectResourceSetSerializer serializer) {
+ this.parser = parser;
+ this.discovery = discovery;
+ this.resourceSetFactory = resourceSetFactory;
+ this.serializer = serializer;
+ }
+
+ public void run(String recipeName, String path) {
+ run(recipeName, path, __ -> {
+ });
+ }
+
+ public void run(String recipeName, String path, RewriteRecipeRunnerProgressListener listener) {
+ Path baseDir = getBaseDir(path);
+ RewriteProjectParsingResult parsingResult = parseProject(baseDir, listener);
+ Optional recipe = discoverRecipe(recipeName);
+ if (recipe.isPresent()) {
+ applyRecipe(recipeName, baseDir, parsingResult, recipe, listener);
+ }
+ else {
+ logger.error("Could not find recipe " + recipeName + ".");
+ }
+ }
+
+ @NotNull
+ private RewriteProjectParsingResult parseProject(Path baseDir, RewriteRecipeRunnerProgressListener listener) {
+ listener.progress("Start parsing dir '%s'".formatted(baseDir));
+ StopWatch stopWatch = new StopWatch("parse");
+ stopWatch.start();
+ RewriteProjectParsingResult parsingResult = parser.parse(baseDir);
+ stopWatch.stop();
+ double parseTime = stopWatch.getTotalTime(TimeUnit.SECONDS);
+ listener.progress("Parsed %d resources in %f sec.".formatted(parsingResult.sourceFiles().size(), parseTime));
+ return parsingResult;
+ }
+
+ private void applyRecipe(String recipeName, Path baseDir, RewriteProjectParsingResult parsingResult,
+ Optional recipe, RewriteRecipeRunnerProgressListener listener) {
+ StopWatch stopWatch = new StopWatch("parse");
+ stopWatch.start();
+ // Use ProjectResourceSet abstraction
+ ProjectResourceSet projectResourceSet = resourceSetFactory.create(baseDir, parsingResult.sourceFiles());
+ // To apply recipes
+ listener.progress("Applying recipe %s, this may take a few minutes.".formatted(recipeName));
+ projectResourceSet.apply(recipe.get());
+ stopWatch.stop();
+ double recipeRunTime = stopWatch.getTotalTime(TimeUnit.MINUTES);
+ listener.progress("Applied recipe %s in %f min.".formatted(recipeName, recipeRunTime));
+ // Synchronize changes with filesystem
+ listener.progress("Write changes from %s.".formatted(recipeName));
+ serializer.writeChanges(projectResourceSet);
+ }
+
+ @NotNull
+ private Optional discoverRecipe(String recipeName) {
+ // discover recipe
+ List recipes = discovery.discoverRecipes();
+ Optional recipe = recipes.stream().filter(r -> recipeName.equals(r.getName())).findFirst();
+ return recipe;
+ }
+
+ @NotNull
+ private static Path getBaseDir(String path) {
+ Path baseDir = Path.of(".").toAbsolutePath().normalize();
+ if (path != null) {
+ baseDir = Path.of(path).toAbsolutePath().normalize();
+ }
+ return baseDir;
+ }
+
+}