Frequently asked questions (FAQ)
- Does OpenRewrite collect any data from our projects?
- What is the difference between OpenRewrite and Moderne?
- Is it possible to update only a few files with a recipe rather than all of them?
- Why do artifact scanners detect vulnerabilities in recipe artifacts/JARs?
- Is it possible to apply recipes on a step-by-step basis (pausing after certain recipes, so smaller commits can be made)?
- I'm getting
java.lang.OutOfMemoryError: Java heap space
when running OpenRewrite. - I'm getting
Failed to parse or resolve the Maven POM file or one of its dependencies; We cannot reliably continue without this information.
when running OpenRewrite. - My recipe appears to hang when running. What's happening? Is there a progress report?
- Can I create a report or summary of the changes made through OpenRewrite?
- My recipe runs, but is not making any changes. What's happening?
- I want to exclude a single recipe from a collection of recipes.
- How do I remove an element from an LST?
- How do I replace one statement with multiple new statements using JavaTemplates?
- I'm seeing
LST contains missing or invalid type information
in my recipe unit tests. How to resolve? - Is it possible to pass arguments to a recipe from the command line?
- What order do recipes run in?
- Does OpenRewrite support Bazel?
- How can I configure custom timeouts for my artifact repositories?
- What is the estimated time save log we are seeing? How is this value calculated?
Does OpenRewrite collect any data from our projects?
No. The rewrite-maven-plugin and rewrite-gradle-plugin run locally on your machine, without any connections to Moderne or OpenRewrite. The plugins calls out to Maven Central (or a locally configured mirror) to check for new dependencies. Other than that, you should not see any other outbound traffic.
What is the difference between OpenRewrite and Moderne?
Is it possible to update only a few files with a recipe rather than all of them?
Yes. There are two main options for this:
- (Recommended) Use some of the common preconditions to limit the files or directories the recipe runs on.
- Use exclusions to limit the folders the recipe can run on:
Exclusion paths are relative to the project root, not the system file path.
- Gradle
- Maven
In your build.gradle
file:
rewrite {
exclusions = ["folderA", "folderB"]
}
In the command line:
mvn rewrite:run -Drewrite.exclusions="folderA,folderB"
Why do artifact scanners detect vulnerabilities in recipe artifacts/JARs?
In order to modernize and upgrade old or vulnerable code, some OpenRewrite recipe modules bundle copies of old libraries. Libraries bundled into recipe modules are never executed. That being said, these libraries are never executed.
OpenRewrite exercises the Java compiler internally to compile code patterns that exist in these old and/or vulnerable libraries. These patterns are then used to match old or vulnerable code for the sake of modernizing or repairing it.
Using a library in compilation in this way does not trigger class initialization in the way that reflection might, for example. In other words, code paths in libraries used in compilation are never executed.
As an example of this, consider the case of rewrite-spring. It has libraries bundled inside of the META-INF/rewrite/classpath directory. However, those JARs are not made into a Fat Jar or a shaded library in the traditional sense. It is not possible that by using rewrite-spring
that one of those libraries gets called.
Is it possible to apply recipes on a step-by-step basis (pausing after certain recipes, so smaller commits can be made)?
This question comes up a lot with bigger migration recipes such as the Migrate to Java 17 recipe. Before we dive into the options you have, it's important to note that OpenRewrite recipes are highly hierarchical in nature. When you execute the Java 17 recipe, you're actually executing 180 individual migration recipes. Some of these recipes are partial steps, that by themselves, would not make sense. Consider, for instance, how different recipes change imports versus add a dependency; you'd need both for the change to make sense, and committing just a part of that would lead to failing intermediate steps.
This nuance is why we don't support intermediate steps to commit results; it would simply be too much to handle feasibly. Furthermore, we'd have to write out to disk repeatedly, which would slow the migration down even more.
That being said, there are two things you can do:
-
You can run individual parts of the hierarchy. For instance, the Migrate to Java 17 recipe contains the Migrate to Java 11 recipe, which contains the Migrate to Java 8 recipe. You could run each of those child recipes and commit the results - which would limit the number of changes being done at a time.
-
You can also use Preconditions to limit the changes to a particular source set or set of files.
Using these two approaches together in separate runs means you can likely create something that's feasible to review.
Worth noting, though, is that each recipe run will need to build up the Lossless Semantic Tree (LST) – which can take some time (especially for larger projects). If you'd like to speed that up, you can have a look at Moderne where we allow you to use precomputed LSTs.
I'm getting java.lang.OutOfMemoryError: Java heap space
when running OpenRewrite.
You can either increase the size of the Java heap or build and run recipes with the Moderne CLI.
Java heap instructions
- Gradle
- Maven
gradle -Dorg.gradle.jvmargs=-Xmx8G rewrite:run
export MAVEN_OPTS="-Xmx8G"
mvn rewrite:run
Moderne CLI
The Moderne CLI builds the LST artifacts for your repository in pieces if the repository can't fit into memory entirely. It also allows you to run recipes against multiple repositories at once.
I'm getting Failed to parse or resolve the Maven POM file or one of its dependencies; We cannot reliably continue without this information.
when running OpenRewrite.
OpenRewrite needs to resolve the Maven POM file and its dependencies to build up the Lossless Semantic Tree (LST).
We parse the POM files of your project, your dependencies, and any plugins to enrich our type information – and to resolve any transitive dependencies.
When POM files cannot be found or parsed, we are not able to type attribute some elements of your code, and recipes will fail to match those elements.
If you're seeing this error, it's likely that OpenRewrite is having trouble resolving the POM file or its dependencies.
The output will indicate with markers which <!--~~(... Unable to download POM ...)~~>-->
which POM failed, and which repositories were tried.
There's also a trio of recipes that we offer to help you troubleshoot which Maven repositories are selected, and in what order:
Double check and adjust your dependency version, or add additional repository configuration or credentials, to resolve this issue.
My recipe appears to hang when running. What's happening? Is there a progress report?
OpenRewrite is likely building up a model of your code and resolving types – this can take a while. Right now, there is not a progress report for recipe runs. However, there is a suggestion to add progress indicators that you can +1 here.
Can I create a report or summary of the changes made through OpenRewrite?
When recipes run with the OSS Maven and Gradle plugins, they produce a summary of what files where changes by which recipes. If you'd like more information, then you can look at the data tables additionally produced through getting started with data tables guide for OpenRewrite.
Moderne offers this functionality through the Moderne CLI and the Moderne Platform. For more information, check out getting started with data tables guide in the Moderne docs.
My recipe runs, but is not making any changes. What's happening?
When a recipe fails to make changes, that's often because of malformed or missing type information. You can use the diagnostic Find missing types recipe, and the data table it produces, to find missing types in your codebase. If there are any missing types you might want to double-check your dependencies are set up correctly, or whether you're using Lombok, as Lombok leads to missing types.
It could also be that a particular file is not parsed correctly. In such cases you'll see log line output which files failed to parse. You can use the Find source files with ParseExceptionResult markers diagnostic recipe to find & report these issues. Note that this again produces a data table for you to inspect.
In some cases missing changes can be attributed to a failure to connect to Artifactory, Nexus, while building up the LST model, or while running recipes.
Quite a few recipes emit a org.openrewrite.maven.table.MavenMetadataFailures
data table where such issues were detected.
You might then want to run the Dependency resolution diagnostic recipe to troubleshoot your connectivity.
If none of those issues are present, then you might want to look at some diagnostic data tables we produce for each recipe run. Once enabled you can get insight into which recipes had results, or errors, and their runtime statistics. Look for these tables in your target folder to learn which recipes had results.
org.openrewrite.table.SourcesFileResults
org.openrewrite.table.SourcesFileErrors
org.openrewrite.table.RecipeRunStats
If you're still having trouble, it might help to attach a debugger to the running process and see what's going on.
Use the typical debug flow for your build tool, and set a breakpoint in the recipe you're running to see what's happening.
For Maven, that would be using mvnDebug
instead of mvn
, and for Gradle, that would be using the --debug-jvm
flag.
I want to exclude a single recipe from a collection of recipes.
You can create your own custom recipe based off of an existing recipe most easily through the Moderne recipe builder. You can read more about the new recipe builder on the Moderne blog.
Excluding a single recipe from an existing unmodified recipe list would be quite complicated. For a detailed explanation as to why, check out this post.
How do I remove an element from an LST?
Visitors can remove LST elements by returning null
when visiting the element to be removed. Please keep in mind, though, that it may not always be safe to remove an element.
For instance, we have a very simple recipe that checks for certain System.out.println
methods and removes them. We use this recipe as part of our Java Recipe best practices. However, this can result in incorrect removals as demonstrated in a PR to OpenRewrite.
To learn more about manipulating LSTs, check out our recipe conventions and best practices doc.
How do I replace one statement with multiple new statements using JavaTemplates?
Conceptually, you will want to modify or replace a single LST element with a single other LST element.
For instance, let's say you wanted to replace int i = 5
with int i = 5; i++;
. You might be inclined to set the template string to "int i = 5; i++;"
- but that wouldn't work. Instead, you should wrap those statements up into a J.Block: { int i = 5; i++; }
.
After doing that, you would want to run a doAfterVisit(new RemoveUnneededBlock.getVisitor())
to remove the unnecessary block again – giving you the desired result.
I'm seeing LST contains missing or invalid type information
in my recipe unit tests. How to resolve?
The unit test framework for recipes has additional checks beyond the visible code. One such check is to ensure all LST elements have valid type information, such that recipes can reliably be chained together. Any missing types would mean subsequent recipes can't match or modify the generated code.
A common source of missing type information is when you use JavaTemplate
, but fail to provide any .classpath*(...)
or .imports(...)
.
These method calls are required to resolve types referenced in your code template, and serve as input to the generated class stub.
Also note that transitive dependencies are not resolved, so in deeper class hierarchies you might need to add multiple .classpath*(...)
entries.
The error message will indicate which types are missing; Using the debugger it should be possible to pinpoint the source of the issue.
If for some reason you're unable to resolve the missing types issue, and are OK with a limited ability to chain recipes together,
then you can disable the type validation through either org.openrewrite.test.RecipeSpec.afterTypeValidationOptions
or org.openrewrite.test.RecipeSpec.typeValidationOptions
.
Is it possible to pass arguments to a recipe from the command line?
This is a challenging problem for a couple of reasons:
- Some recipes can be composed of other recipes, which could in turn include other recipes, and so on.
- Some recipes can be used multiple times in one recipe with different parameters such as in this example.
- The rewrite-gradle-plugin requires you to either change your build file, or add an
init.gradle
script to run recipes.
In general, we recommend folks write a rewrite.yml
file to configure recipes, as that clears out any ambiguity as to which recipe instances to configure, and this approach is portable across the various tools that run OpenRewrite recipes.
For folks using the rewrite-maven-plugin we recently added an option to pass in arguments to recipes, for single recipes only.
Using the below command you remove an argument plugin without modifying a rewrite.yml
or pom.xml
file:
mvn org.openrewrite.maven:rewrite-maven-plugin:run \
-Drewrite.activeRecipes=org.openrewrite.maven.RemovePlugin \
-Drewrite.options=groupId=org.springframework.boot,artifactId=spring-boot-maven-plugin
Note that this approach does not scale well; each recipe invocation would build up the Lossless Semantic Tree (LST) from scratch, which can be slow for larger projects.
A better approach then is to leverage serialized LSTs through the Moderne CLI.
What order do recipes run in?
Recipes are run in the order they are activated.
Let's take this example snippet of a build.gradle
file:
rewrite {
activeRecipe(
'com.yourorg.Foo',
'com.yourorg.RecipeA'
)
}
and this example of a rewrite.yml
file:
---
type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.RecipeA
displayName: Recipe A
description: Applies Recipe B.
recipeList:
- com.yourorg.RecipeB
- com.yourorg.RecipeC
---
type: specs.openrewrite.org/v1beta/recipe
name: com.yourorg.Foo
...
recipeList:
- com.yourorg.bar
- com.yourorg.bash
In this example, the recipes would be run in this order:
com.yourorg.Foo
com.yourorg.bar
com.yourorg.bash
com.yourorg.RecipeA
com.yourorg.RecipeB
com.yourorg.RecipeC
Does OpenRewrite support Bazel?
There is currently no open source build plugin for Bazel. Moderne, on the other hand, does offer Bazel support through the Moderne CLI. This is partially due to the fact that many Bazel repositories are mono repos that wouldn't fit into memory as easily – which is an OpenRewrite constraint. The Moderne CLI, on the other hand, serializes LSTs so that they don't have the same problem.
For more information about the differences between the Moderne CLI and the OpenRewrite build plugins, please see the Moderne CLI docs.
How can I configure custom timeouts for my artifact repositories?
You can configure custom timeouts for your artifact repositories by setting the timeout
property in a Maven settings.xml
file.
This file is read from the default location in ~/.m2/settings.xml
, even if you are using Gradle to run your recipes.
The specification is described in this Maven mini guide on HTTP settings.
<settings>
<servers>
<server>
<id>my-server</id>
<configuration>
<timeout>60000</timeout> <!-- milliseconds -->
</configuration>
</server>
</servers>
</settings>
Note that the per HTTP method <httpConfiguration>
format is not supported.
What is the estimated time save log we are seeing? How is this value calculated?
Time savings estimates provide you with a general idea of how much time was saved from running this recipe. Every recipe defaults to saying that it saves 5 minutes per issue it found/fixed - but recipe authors can override that with a more accurate estimate. Some recipes may have zero time saved whereas others may have significantly more.
The assumption is that this time saved number represents how long it would take a developer to change the code, create a PR to review said change, and then merge/deploy it once it was reviewed. It does not estimate how long it would take a developer to find this issue in the code (which, in some cases, can be quite a bit longer than making the change itself).