A Java library and CLI tool for executing OpenRewrite recipes programmatically, without requiring Maven plugin execution.
OpenRewrite is a powerful framework for automated code refactoring.
Traditionally, it's used via Maven goals (mvn rewrite:run or mvn rewrite:dryRun), but this project provides an alternative approach that allows you to:
- Execute recipes programmatically from your Java code
- Run recipes via CLI without Maven
- Process results directly without parsing console logs or csv files generated.
- Integrate easily into custom migration tools and workflows
Add the dependency to your project:
<dependency>
<groupId>dev.snowdrop.openrewrite</groupId>
<artifactId>rewrite-client</artifactId>
<version>0.2.0</version>
</dependency>Use the RewriteService to process a Java project:
import dev.snowdrop.openrewrite.RewriteService;
import dev.snowdrop.openrewrite.Config;
import dev.snowdrop.openrewrite.ResultsContainer;
Config cfg = new Config();
cfg.setAppPath(Paths.get("/path/to/your/project"));
cfg.setRecipes(List.of("org.openrewrite.java.format.AutoFormat"));
RewriteService scanner = new RewriteService(cfg);
scanner.init();
ResultsContainer results = scanner.run();
// Access results
RecipeRun run = results.getRecipeRuns().get("org.openrewrite.java.format.AutoFormat");
// Process datatables, changesets, etc.Install using jbang:
jbang app install openrewrite@snowdrop/rewrite-clientNote
To install a released version, append to the reference of the version
jbang app install openrewrite@snowdrop/rewrite-client/0.2.0Run a recipe:
openrewrite /path/to/project -r org.openrewrite.java.format.AutoFormatImportant Considerations:
When using the rewrite-client, keep these key points in mind:
-
Recipe Resolution: Recipes are specified by their fully qualified class name (e.g., org.openrewrite.java.format.AutoFormat). The tool loads recipe classes from the runtime classpath, so ensure the recipe's JAR is available.
-
Parameter Configuration: Recipe fields can be configured using comma-separated key-value pairs. Values are automatically type-converted to match the recipe's field types (String, boolean, int, etc.).
-
External Recipe JARs: If your recipe is packaged in a separate JAR file, you can load it dynamically using either a local file path or Maven GAV coordinates. The tool will resolve and add it to the classpath before execution.
-
YAML Configuration Files: For complex recipe configurations or multiple recipes, YAML files provide a cleaner alternative. They support the OpenRewrite YAML specification format and allow you to define recipe chains with parameters.
Execute a recipe by its fully qualified class name:
openrewrite /path/to/project -r org.openrewrite.java.format.AutoFormatIn code:
Config cfg = new Config();
cfg.setAppPath(Paths.get("/path/to/project"));
cfg.setFqNameRecipe("org.openrewrite.java.format.AutoFormat");
RewriteService scanner = new RewriteService(cfg);
scanner.init();
ResultsContainer results = scanner.run();Many recipes accept configuration parameters. Pass them using the -o option:
openrewrite /path/to/project \
-r org.openrewrite.java.search.FindAnnotations \
-o annotationPattern=@org.springframework.boot.autoconfigure.SpringBootApplication,matchMetaAnnotations=falseIn code:
cfg.setRecipeOptions(Set.of(
"annotationPattern=@org.springframework.boot.autoconfigure.SpringBootApplication",
"matchMetaAnnotations=false"
));Multiple parameters are separated by commas in the format key=value,key2=value2.
Define recipes in a YAML file:
# rewrite.yml
type: specs.openrewrite.org/v1beta/recipe
name: com.example.MyRecipe
displayName: My Custom Recipe
recipeList:
- org.openrewrite.java.format.AutoFormat
- org.openrewrite.java.search.FindAnnotations:
annotationPattern: '@SpringBootApplication'Execute with:
openrewrite /path/to/project -c rewrite.ymlIn code:
cfg.setConfigPath(Paths.get("rewrite.yml"));Load recipes from external JAR files using Maven GAV coordinates:
openrewrite --jar dev.snowdrop:openrewrite-recipes:1.0.0-SNAPSHOT \
/path/to/project \
-r dev.snowdrop.custom.MyRecipeOr use a local JAR path:
openrewrite --jar /path/to/recipes.jar /path/to/project -r com.example.MyRecipeThe ResultsContainer provides access to recipe execution results:
ResultsContainer results = scanner.run();
// Get results for a specific recipe
RecipeRun run = results.getRecipeRuns().get("org.openrewrite.java.search.FindAnnotations");
// Access data tables (e.g., search results)
Optional<Map.Entry<DataTable<?>, List<?>>> resultMap = run.getDataTables()
.entrySet()
.stream()
.filter(entry -> entry.getKey().getName().contains("SearchResults"))
.findFirst();
if (resultMap.isPresent()) {
List<?> rows = resultMap.get().getValue();
for (Object row : rows) {
SearchResults.Row record = (SearchResults.Row) row;
System.out.println("Found in: " + record.getSourcePath());
System.out.println("Match: " + record.getResult());
}
}
// Access changesets (code modifications)
// run.getChangeset()...When you use the method init() of the RewriteService, then the library will create the following OpenRewrite class during the call to the method:
Environmentholding the resource loaders able to find Recipe classes from jar, classes loaded or YamlExecutionContextable to collect from the execution of the different Recipe the messages containing the Map of the DataTable, etcLargeSourceSetusing different parsers able to read: Java, Maven, Properties, XML, JSON, etc files
It is nevertheless possible, to scan the resources one time and apply different recipes. In this case, we recommend you to create a singleton of the RewriteService and next to call separately the following methods:
// Create a singleton instance of the RewriteService to allow to load only one time all the resources of a project to scan
RewriteService svc = createRewriteServiceInstance(cfg);
if (svc.isSourceSetInitialized()) {
svc.createExecutionContext();
svc.updateConfig(cfg);
} else {
svc.init();
}
// Execute now a recipe
ResultsContainer run = svc.run();- JDK 21
- Apache Maven 3.9+
git clone https://github.com/snowdrop/rewrite-client.git
cd rewrite-client
mvn clean installmvn quarkus:dev -Dquarkus.args="test-project/simple -r org.openrewrite.java.format.AutoFormat"Configure the Java agent for debugging:
java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address="*:5005" \
target/quarkus-app/quarkus-run.jar \
test-project/simple -r org.openrewrite.java.format.AutoFormatThen attach your debugger to port 5005.
Check the sample/rewritescanner directory for a complete Maven example demonstrating library usage.
openrewrite test-project/demo-spring-boot-todo-app \
-r org.openrewrite.java.search.FindAnnotations \
-o annotationPattern=@org.springframework.boot.autoconfigure.SpringBootApplicationopenrewrite test-project/demo-spring-boot-todo-app \
-r org.openrewrite.maven.search.FindDependency \
-o groupId=org.springframework.boot,artifactId=spring-boot-starter-data-jpa,version=3.5.3openrewrite test-project/simple -r org.openrewrite.java.format.AutoFormatThis library is particularly useful for:
- Migration Tools: Integrate OpenRewrite scanning into custom migration frameworks
- CI/CD Pipelines: Run recipe checks without full Maven builds
- Custom Tooling: Build specialized refactoring or analysis tools
- Batch Processing: Execute recipes across multiple projects programmatically
This project is licensed under the Apache License 2.0.
Contributions are welcome! Please feel free to submit a Pull Request.