openrewrite / openrewrite/rewrite
Rewrite fails to load `classpath.tsv.gz` resources from additional GAVs of recipes when used as a library
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 3.7k
- Forks
- 571
- Avg merge
- 13h 12m
- Merged PRs (30d)
- 261
Description
Issue
When we execute openrewrite part of a java quarkus application where the openrewrite GAVs have been loaded part of the AppClassLoader, then we hit a problem as rewrite during the execution of some recipes cannot load the classpath.tsv.gz of by example org.openrewrite.recipe:rewrite-java-dependencies:1.51.1 if we use an URLClassloader having as parent the AppClassloader.
The problem takes place when OpenRewrite runs a recipe like org.openrewrite.java.ReplaceAnnotation where they access on the JavaParser class the method classpathFromResources(Context,String ) able to search about such a resource.
As you can see hereafter the current code uses as ClassLoader the one having loaded the JavaParser class and not at all the URLClassloader created using the Environmen Builder and Environment.Collection<? extends ResourceLoader> resourceLoaders
// org.openrewrite.java.ReplaceAnnotation
a = JavaTemplate.builder(annotationTemplateToInsert)
.javaParser(classpathResourceName == null ?
JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath()) :
JavaParser.fromJavaVersion().classpathFromResources(ctx, classpathResourceName)) // HERE
...
// JavaParser class
public B classpathFromResources(ExecutionContext ctx, String... classpath) {
this.artifactNames = emptyList();
this.classpath = dependenciesFromResources(ctx, classpath);
return (B) this;
}
...
static List<Path> dependenciesFromResources(ExecutionContext ctx, String... artifactNamesWithVersions) {
if (artifactNamesWithVersions.length == 0) {
return emptyList();
}
List<Path> artifacts = new ArrayList<>(artifactNamesWithVersions.length);
Set<String> missingArtifactNames = new LinkedHashSet<>(Arrays.asList(artifactNamesWithVersions));
List<String> availableArtifacts = new ArrayList<>();
TypeTable typeTable = TypeTable.fromClasspath(ctx, missingArtifactNames);
...
// TypeTable class
public static @Nullable TypeTable fromClasspath(ExecutionContext ctx, Collection<String> artifactNames) {
try {
ClassLoader classLoader = findCaller().getClassLoader();
// The ClassLoader we got it here is the AppClassLoader !!
FYI: I created a project showing the issue here: https://github.com/ch007m/tsv-classloading-issue
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked reproducer and trace JavaParser.classpathFromResources through dependenciesFromResources to TypeTable.fromClasspath. Examine how the caller ClassLoader is selected when an Environment-created URLClassLoader has an AppClassLoader parent. Done means the recipe can load the additional GAV's classpath.tsv.gz resource in that setup.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100