Recipe Development Environment
Prerequisites, tools, and recommendations for developing with OpenRewrite
This getting started guide covers setting up your development environment for creating your own OpenRewrite recipes.

Prerequisites

  • JDK (version 1.8+)
    • A JRE alone is insufficient since OpenRewrite uses compiler internals and tools only found in the JDK
  • Gradle (version 4.0+ ) or Maven (version 3.2+)
  • Text Editor or IDE with Java support. Kotlin support optional but recommended for a good test authoring experience

Automatic Project Setup

The easiest way to get started developing your own recipes is to visit the rewrite-recipe-starter repository and click the "Use this template" button. That template comes already set up with all the necessary dependencies, build configuration, an example recipe, and tests of the example recipe.
If you've chosen to use the template, skip to Recipe Distribution.

Manual Project Setup

Gradle and Maven both provide helpful commands for initializing a new project. Either of these commands will lay out an appropriate directory structure and a basic build.gradle or pom.xml.
Gradle
Maven
1
gradle init
Copied!
1
mvn -B archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4
Copied!

Dependencies

Which rewrite libraries you take dependencies on is determined by which languages/data formats you want to write Recipes for.
Gradle
Maven
1
dependencies {
2
// rewrite-java dependencies only necessary for Java Recipe development
3
implementation("org.openrewrite:rewrite-java:7.21.3")
4
runtimeOnly("org.openrewrite:rewrite-java-11:7.21.3")
5
runtimeOnly("org.openrewrite:rewrite-java-8:7.21.3")
6
7
// rewrite-maven dependency only necessary for Maven Recipe development
8
implementation("org.openrewrite:rewrite-maven:7.21.3")
9
10
// rewrite-yaml dependency only necessary for Yaml Recipe development
11
implementation("org.openrewrite:rewrite-yaml:7.21.3")
12
13
// rewrite-properties dependency only necessary for Properties Recipe development
14
implementation("org.openrewrite:rewrite-properties:7.21.3")
15
16
// rewrite-xml dependency only necessary for XML Recipe development
17
implementation("org.openrewrite:rewrite-xml:7.21.3")
18
19
// For authoring tests for any kind of Recipe
20
testImplementation("org.openrewrite:rewrite-test:7.21.3")
21
testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release")
22
testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release")
23
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release")
24
}
Copied!
1
<dependencies>
2
<!-- rewrite-java depedencies only necessary for Java Recipe development -->
3
<dependency>
4
<groupId>org.openrewrite</groupId>
5
<artifactId>rewrite-java</artifactId>
6
<version>7.21.3</version>
7
<scope>compile</scope>
8
</dependency>
9
<dependency>
10
<groupId>org.openrewrite</groupId>
11
<artifactId>rewrite-java-8</artifactId>
12
<version>7.21.3</version>
13
<scope>runtime</scope>
14
</dependency>
15
<dependency>
16
<groupId>org.openrewrite</groupId>
17
<artifactId>rewrite-java-11</artifactId>
18
<version>7.21.3</version>
19
<scope>runtime</scope>
20
</dependency>
21
22
<!-- rewrite-maven dependency only necessary for Maven Recipe development -->
23
<dependency>
24
<groupId>org.openrewrite</groupId>
25
<artifactId>rewrite-maven</artifactId>
26
<version>7.21.3</version>
27
<scope>compile</scope>
28
</dependency>
29
30
<!-- rewrite-yaml dependency only necessary for Yaml Recipe development -->
31
<dependency>
32
<groupId>org.openrewrite</groupId>
33
<artifactId>rewrite-yaml</artifactId>
34
<version>7.21.3</version>
35
<scope>compile</scope>
36
</dependency>
37
38
<!-- rewrite-properties dependency only necessary for Properties Recipe development -->
39
<dependency>
40
<groupId>org.openrewrite</groupId>
41
<artifactId>rewrite-properties</artifactId>
42
<version>7.21.3</version>
43
<scope>compile</scope>
44
</dependency>
45
46
<!-- rewrite-xml dependency only necessary for XML Recipe development -->
47
<dependency>
48
<groupId>org.openrewrite</groupId>
49
<artifactId>rewrite-xml</artifactId>
50
<version>7.21.3</version>
51
<scope>compile</scope>
52
</dependency>
53
54
<!-- For authoring tests for any kind of Recipe -->
55
<dependency>
56
<groupId>org.openrewrite</groupId>
57
<artifactId>rewrite-test</artifactId>
58
<version>7.21.3</version>
59
<scope>test</scope>
60
</dependency>
61
</dependencies>
Copied!
rewrite-test uses JUnit 5.
rewrite-java-11 and rewrite-java-8 can happily coexist on the same classpath. At runtime the appropriate module for the current JDK will be selected.

Set Language Level and Bytecode Level

In order to be able to use OpenRewrite to modernize old projects it's important to be able to run on older JDK implementations. So configure the Java compiler to target Java Language and Bytecode level 1.8.
Gradle
Maven
build.gradle
1
// See https://docs.gradle.org/current/userguide/building_java_projects.html#sec:java_cross_compilation
2
tasks.withType(JavaCompile) {
3
sourceCompatibility = JavaVersion.VERSION_1_8.toString()
4
targetCompatibility = JavaVersion.VERSION_1_8.toString()
5
}
Copied!
pom.xml
1
<!-- see https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html -->
2
<properties>
3
<maven.compiler.source>1.8</maven.compiler.source>
4
<maven.compiler.target>1.8</maven.compiler.target>
5
</properties>
Copied!

Kotlin Plugin (Optional)

For a good test authoring experience we recommend also applying the Kotlin plugin.
Gradle
Maven
build.gradle
1
// https://kotlinlang.org/docs/gradle.html#targeting-the-jvm
2
plugins {
3
id("org.jetbrains.kotlin.jvm") version("1.5.20")
4
}
Copied!
pom.xml
1
<!-- https://kotlinlang.org/docs/maven.html -->
2
<properties>
3
<kotlin.version>1.4.31</kotlin.version>
4
</properties>
5
6
<dependencies>
7
<dependency>
8
<groupId>org.jetbrains.kotlin</groupId>
9
<artifactId>kotlin-stdlib</artifactId>
10
<version>${kotlin.version}</version>
11
</dependency>
12
</dependencies>
13
14
<build>
15
<plugins>
16
<plugin>
17
<groupId>org.jetbrains.kotlin</groupId>
18
<artifactId>kotlin-maven-plugin</artifactId>
19
<version>${kotlin.version}</version>
20
<executions>
21
<execution>
22
<id>compile</id>
23
<goals>
24
<goal>compile</goal>
25
</goals>
26
<configuration>
27
<sourceDirs>
28
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
29
<sourceDir>${project.basedir}/src/main/java</sourceDir>
30
</sourceDirs>
31
</configuration>
32
</execution>
33
<execution>
34
<id>test-compile</id>
35
<goals> <goal>test-compile</goal> </goals>
36
<configuration>
37
<sourceDirs>
38
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
39
<sourceDir>${project.basedir}/src/test/java</sourceDir>
40
</sourceDirs>
41
</configuration>
42
</execution>
43
</executions>
44
</plugin>
45
<plugin>
46
<groupId>org.apache.maven.plugins</groupId>
47
<artifactId>maven-compiler-plugin</artifactId>
48
<version>3.5.1</version>
49
<executions>
50
<!-- Replacing default-compile as it is treated specially by maven -->
51
<execution>
52
<id>default-compile</id>
53
<phase>none</phase>
54
</execution>
55
<!-- Replacing default-testCompile as it is treated specially by maven -->
56
<execution>
57
<id>default-testCompile</id>
58
<phase>none</phase>
59
</execution>
60
<execution>
61
<id>java-compile</id>
62
<phase>compile</phase>
63
<goals>
64
<goal>compile</goal>
65
</goals>
66
</execution>
67
<execution>
68
<id>java-test-compile</id>
69
<phase>test-compile</phase>
70
<goals>
71
<goal>testCompile</goal>
72
</goals>
73
</execution>
74
</executions>
75
</plugin>
76
</plugins>
77
</build>
Copied!
Throughout OpenRewrite's documentation Java is used for Recipe authoring and Kotlin is used for test authoring. You do not have to be constrained by this recommendation: Recipes and tests can be authored in any language that runs on the JVM.

Project Layout

Having configured the project per these recommendations, you're now able to begin Recipe development. With Gradle and Maven's default project layout, here is where to put the various kinds of sources that go into a OpenRewrite Module:
  • src/main/java - Recipe implementations in Java
  • src/main/kotlin - Recipe implementations in Kotlin
  • src/main/resources/META-INF/rewrite - Yaml files defining declarative OpenRewrite Recipes
  • src/test/java - Recipe tests in Java
  • src/test/kotlin - Recipe tests in Kotlin
Project setup is complete. You are ready create a Recipe!

IDE Configuration

Starting with java 16, the default JVM behavior is not to allow access to protected modules. The workaround for this issue is to add explicit exports for packages used by rewrite's java parser.
Updating the IDE's JUnit template configuration avoids having to update the configuration for each Test.
Add the following exports to the IDE's JVM run configuratons.
1
--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAME --add-exports jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
Copied!

Recipe Distribution

For your recipes to be usable by the OpenRewrite build plugins or on app.moderne.io they have to be published to an artifact repository.

Local Publishing for Testing

Before you publish your recipe module to an artifact repository, you may want to try it out locally. To do this on the command line, run ./gradlew publishToMavenLocal (or equivalently ./gradlew pTML). This will publish to your local maven repository, typically under ~/.m2/repository.

Publishing to Artifact Repositories

The rewrite-recipe-starter project is configured to publish to Moderne's open artifact repository. app.moderne.io can draw recipes from this repository, as well as from Maven Central.
Also see:

Running your Recipes

Once your recipe module is published, either locally for testing or to an external artifact repository for broader distribution, you can configure the build plugins to download your artifact and run its recipes.
Gradle
Maven
1
plugins {
2
id("java")
3
id("org.openrewrite.rewrite") version("5.20.0")
4
}
5
6
repositories {
7
mavenLocal()
8
mavenCentral()
9
}
10
11
dependencies {
12
rewrite("[your recipe module's groupId]:[your recipe module's artifactId]:[your recipe module's version]")
13
}
14
15
rewrite {
16
activeRecipe("[your recipe name]")
17
}
Copied!
Now you can run your recipe with ./gradlew rewriteRun or ./gradlew rewriteDryRun
1
<project>
2
<build>
3
<plugins>
4
<plugin>
5
<groupId>org.openrewrite.maven</groupId>
6
<artifactId>rewrite-maven-plugin</artifactId>
7
<version>4.23.0</version>
8
<configuration>
9
<activeRecipes>
10
<recipe> [your recipe name] </recipe>
11
</activeRecipes>
12
</configuration>
13
<dependencies>
14
<dependency>
15
<groupId> [your recipe module's groupId] </groupId>
16
<artifactId> [your recipe module's artifactId] </artifactId>
17
<version> [your recipe module's version] </version>
18
</dependency>
19
</dependencies>
20
</plugin>
21
</plugins>
22
</build>
23
</project>
Copied!
Now you can run your recipe with mvn rewrite:run or mvn rewrite:dryRun

Next Steps