Skip to content

Revamp/947 cleanup sbm support rewrite #951

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
958be90
Remove @Component and declare using @Bean
fabapp2 Sep 29, 2023
e3c2a15
Add dependency to rewrite-java-17
fabapp2 Sep 29, 2023
8f83962
Add dependency to rewrite-migrate-java
fabapp2 Sep 29, 2023
dd26bbb
Removed migrate-java again, it does not belong here
fabapp2 Sep 29, 2023
4482233
Replace decompiles code with actual source
fabapp2 Sep 30, 2023
96abe40
Add javadoc
fabapp2 Sep 30, 2023
d3910b0
Renamed HelperWithoutAGoodName
fabapp2 Sep 30, 2023
f65f1fd
Enhance README
fabapp2 Sep 30, 2023
6c89ffa
Enhance README
fabapp2 Sep 30, 2023
9c8e14b
Moved Maven related classes to maven package
fabapp2 Sep 30, 2023
2b47596
Add javadoc
fabapp2 Sep 30, 2023
835daab
Add TODO
fabapp2 Sep 30, 2023
65ad6e9
Cleanup
fabapp2 Sep 30, 2023
fb45c1e
Remove dead code
fabapp2 Sep 30, 2023
3ffd137
Remove ExecutionContext parameter
fabapp2 Sep 30, 2023
cc39ac5
Cleanup
fabapp2 Sep 30, 2023
022d3ff
Renamed SbmMavenProject
fabapp2 Sep 30, 2023
a4b0643
Moved logic into ParserContext
fabapp2 Sep 30, 2023
b8e9ecd
Add MavenModuleParser
fabapp2 Sep 30, 2023
a5a129f
Cleanup
fabapp2 Sep 30, 2023
1593b77
Fix javadoc
fabapp2 Sep 30, 2023
f90af26
Remove generated method
fabapp2 Sep 30, 2023
1e9bd37
Remove unused dependency
fabapp2 Sep 30, 2023
3ae7845
Remove unused properties
fabapp2 Sep 30, 2023
6d9bfe6
Cleanup pom
fabapp2 Sep 30, 2023
9b07607
WIP: delombokify
fabapp2 Sep 30, 2023
3f39bcf
Remove duplicate plugin
fabapp2 Sep 30, 2023
711bb69
Cleanup
fabapp2 Oct 9, 2023
86289b4
Cleanup pom.xml
fabapp2 Oct 9, 2023
09c88d4
Remove deprecation
fabapp2 Oct 9, 2023
e519722
Cleanup
fabapp2 Oct 9, 2023
0fea63f
Adjust upper modules to changes
fabapp2 Oct 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
*/
package org.springframework.sbm.build.api;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.openrewrite.ExecutionContext;
import org.openrewrite.SourceFile;
import org.openrewrite.maven.tree.MavenResolutionResult;
import org.springframework.sbm.build.impl.JavaSourceSetImpl;
import org.springframework.sbm.build.impl.MavenBuildFileUtil;
import org.springframework.sbm.build.impl.OpenRewriteMavenBuildFile;
import org.springframework.sbm.common.util.Verify;
import org.springframework.sbm.engine.recipe.MigrationResultProjectContextMerger;
import org.springframework.sbm.java.api.JavaSource;
import org.springframework.sbm.java.api.JavaSourceLocation;
import org.springframework.sbm.java.refactoring.JavaRefactoringFactory;
Expand All @@ -32,9 +34,6 @@
import org.springframework.sbm.project.resource.RewriteMigrationResultMerger;
import org.springframework.sbm.project.resource.RewriteSourceFileHolder;
import org.springframework.sbm.project.resource.finder.ProjectResourceFinder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.openrewrite.SourceFile;

import java.nio.file.Path;
import java.util.ArrayList;
Expand Down Expand Up @@ -67,10 +66,6 @@ public JavaSourceLocation getBaseJavaSourceLocation() {
return getMainJavaSourceSet().getJavaSourceLocation();
}

public JavaSourceLocation getBaseTestJavaSourceLocation() {
return getTestJavaSourceSet().getJavaSourceLocation();
}

public JavaSourceSet getTestJavaSourceSet() {
Path testJavaPath = Path.of("src/test/java");
// FIXME: #7 JavaParser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void commentFindings(List<? extends JavaSource> javaSources, String comme
OpenRewriteJavaSource affectedJavaSource = javaSources.stream()
.filter(js -> js.getClass().isAssignableFrom(OpenRewriteJavaSource.class))
.map(OpenRewriteJavaSource.class::cast)
.filter(js -> result.getBefore().getId().equals(js.getResource().getId()))
.filter(js -> result.getBefore().getId().equals(js.getResource().getSourceFile().getId()))
.findFirst()
.get();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public List<MethodCall> findMethodCalls(String methodPattern) {
FindMethods findMethods = new FindMethods(methodPattern,true);
MethodMatcher methodMatcher = new MethodMatcher(methodPattern);
find(findMethods).stream()
.map(m -> list().stream().filter(js -> js.getResource().getId().equals(m.getId())).findFirst().get())
.map(m -> list().stream().filter(js -> js.getResource().getSourceFile().getId().equals(m.getSourceFile().getId())).findFirst().get())
.map(m -> new MethodCall(m, methodMatcher))
.forEach(matches::add);
return matches;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ protected List<RewriteSourceFileHolder<J.CompilationUnit>> findInternal(List<Rew
.filter(r -> J.CompilationUnit.class.isAssignableFrom(r.getClass()))
.map(J.CompilationUnit.class::cast)
.map(cu -> resourceWrappers.stream()
.filter(fh -> fh.getId().equals(cu.getId()))
.filter(fh -> fh.getSourceFile().getId().equals(cu.getId()))
.map(pr -> {
J.CompilationUnit cuRemovedMarkers = removeMarkers(cu, SearchResult.class, RecipesThatMadeChanges.class);
pr.replaceWith(cuRemovedMarkers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,9 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.openrewrite.ExecutionContext;
import org.openrewrite.SourceFile;
import org.openrewrite.java.JavaParser;
import org.openrewrite.maven.utilities.MavenArtifactDownloader;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.io.Resource;
import org.springframework.sbm.build.impl.RewriteMavenParser;
import org.springframework.sbm.parsers.RewriteProjectParser;
import org.springframework.sbm.scopes.ProjectMetadata;
import org.springframework.stereotype.Component;

import java.nio.file.Path;
Expand All @@ -44,19 +38,10 @@
@Deprecated(forRemoval = true)
public class MavenProjectParser {

private final ResourceParser resourceParser;
private final RewriteMavenParser mavenParser;
private final MavenArtifactDownloader artifactDownloader;
private final ApplicationEventPublisher eventPublisher;
private final JavaProvenanceMarkerFactory javaProvenanceMarkerFactory;
private final JavaParser javaParser;
private final MavenConfigHandler mavenConfigHandler;
private final ProjectMetadata projectMetadata;
private final ExecutionContext executionContext;
private final RewriteProjectParser parser;

public List<SourceFile> parse(Path projectDirectory, List<Resource> resources) {
return parser.parse(projectDirectory, resources, executionContext).sourceFiles();
return parser.parse(projectDirectory, resources).sourceFiles();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public class ProjectContextInitializer {
private final RewriteProjectParser mavenProjectParser;
private final GitSupport gitSupport;
private final RewriteSourceFileWrapper rewriteSourceFileWrapper;
private final ExecutionContext executionContext;
private final ProjectContextHolder projectContextHolder;
private final ProjectResourceSetFactory projectResourceSetFactory;

Expand All @@ -52,7 +51,7 @@ public ProjectContext initProjectContext(Path projectDir, List<Resource> resourc
// TODO: remove git initialization, handled by precondition check
initializeGitRepoIfNoneExists(absoluteProjectDir);

List<SourceFile> parsedResources = mavenProjectParser.parse(absoluteProjectDir, resources, executionContext).sourceFiles();
List<SourceFile> parsedResources = mavenProjectParser.parse(absoluteProjectDir, resources).sourceFiles();
List<RewriteSourceFileHolder<? extends SourceFile>> rewriteSourceFileHolders = rewriteSourceFileWrapper.wrapRewriteSourceFiles(absoluteProjectDir, parsedResources);

ProjectResourceSet projectResourceSet = projectResourceSetFactory.createFromSourceFileHolders(rewriteSourceFileHolders);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void run(String... args) throws Exception {
List<Resource> resources = scanner.scan(baseDir);

// parse
RewriteProjectParsingResult parsingResult = parser.parse(baseDir, resources, executionContext);
RewriteProjectParsingResult parsingResult = parser.parse(baseDir);
List<SourceFile> sourceFiles = parsingResult.sourceFiles();
ProjectResourceSet projectResourceSet = projectResourceSetFactory.create(baseDir, sourceFiles);

Expand Down
142 changes: 109 additions & 33 deletions sbm-support-rewrite/README.adoc
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
# sbm-support-rewrite
these components

The `sbm-support-rewrite` project provides Spring beans classes to parse a given project to an OpenRewrite abstract syntax tree (AST) which can then be used to run OpenRewrite recipes that were discovered on the classpath.

- `RewriteProjectParser` - parses a project to OpenRewrite's AST representation
- `RecipeDiscovery` - provides access to discovered recipes
## Components
The following components can be used to parse a project, run recipes and write changes back to the filesystem.
These components are provided as Spring beans and can be injected into other Spring beans that require them.

_Example: Inject RewriteProjectParser into your Spring bean_
[source,java]
....
@Autowired
RewriteProjectParser parser;
....

### ProjectScanner
Scan a given path to a list of resources using filter definitions provided as application properties.

### RewriteProjectParser
Parses a project to OpenRewrite's AST representation.

### RewriteExecutionContext
OpenRewrite's `ExecutionContext` gets initialized during parsing.
This `ExecutionContext` is required for some recipes and inner workings of OpenRewrite.

### RecipeDiscovery
Discover OpenRewrite recipes on classpath

### ProjectResourceSet
Abstraction of OpenRewrite SourceFiles that allows execution of recipes against the SourceFiles while
synchronizing changes with the underlying list of SourceFiles.

### ProjectResourceSetSerializer
Write back the in-memory changed SourceFiles from the ProjectResourceSet to the filesystem.

## Getting started

Expand Down Expand Up @@ -36,15 +63,13 @@ Then the dependency can be retrieved.
</dependency>
.....

### Parse a project

`RewriteProjectParser` is provided as Spring bean and can be injected into components.
### (Optional) Scan a project
`ProjectScanner` scans a given `Path` to a list of ``Resource``s.
It filters out resources and directories matching any of the ignore patterns in
`parser.ignoredPathPatterns`.

[source,java]
.....
@Autowired
private RewriteProjectParser parser;
.....
### Parse a project
`RewriteProjectParser` parses a project to OpenRewrite AST.

The provided `parse(Path)` method can be used to parse a project under a given `Path` to OpenRewrite AST.

Expand All @@ -55,12 +80,11 @@ List<SourceFile> ast = parser.parse(baseDir);
.....

### ExecutionContext

OpenRewrite's `ExecutionContext` is populated during parsing and the settings can be important for recipes executed later.
The `ExecutionContext` is provided as scoped Spring bean.
OpenRewrite's `ExecutionContext` is populated during parsing and the settings might be required for recipes executed later.
The `ExecutionContext` is provided as scoped Spring bean and can be injected into other Spring beans.
It has the same scope as the parsing and a new instance is automatically created with every parse.

NOTE: The ExecutionContext should be injected and should not be created programmatically.
NOTE: The ExecutionContext should always be injected and should not be created programmatically.

### Discover and run recipes

Expand All @@ -78,6 +102,49 @@ Recipe recipe = discovery.getRecipe("org.openrewrite.java.spring.boot3.UpgradeSp
RecipeRun recipe = recipe.run(new InMemoryLargeSourceSet(ast), ctx));
....

### Use ProjectResourceSet
A successful recipe run will return the modified ``SourceFile``s.
Before another recipe can be applied to the AST the changed ``SourceFile``s need to be merged into the original list of ``SourceFile``s (the AST).
The `ProjectResourceSet` provides this capability.

[source,java]
....
@Component
public class SomeClass {

@Autowired
ProjectResourceSetFactory factory;

@Autowired
RewriteProjectParser parser;

void method() {
Recipe r1 = ...
Recipe r2 = ...
RewriteProjectParsingResult parsingResult = parser.parse(baseDir);
List<SourceFile> sourceFiles = parsingResult.sourceFiles();
ProjectResourceSet projectResourceSet = factory.create(baseDir, sourceFiles);
projectResourceSet.apply(r1); // internally changes get merged back to AST
projectResourceSet.apply(r2); // r2 applied against the AST with changes from r1
}
}
....

### Write changes back to filesystem
The `ProjectResourceSetSerializer` can be used to write all changes (modify, delete, add) in `ProjectResourceSet` to the filesystem.

[source,java]
....
@Autowired
private ProjectResourceSetSerializer serializer;
...
public void method() {
...
serializer.writeChanges(projectResourceSet);
}
....



### Listen to ParserEvents

Expand Down Expand Up @@ -177,21 +244,23 @@ Example code showing how to apply OpenRewrite's UpgradeSpringBoot_3_1 recipe

[source, java]
.....
package com.example;

import org.openrewrite.*;
import org.openrewrite.internal.InMemoryLargeSourceSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.Resource;
import org.springframework.sbm.parsers.ProjectScanner;
import org.springframework.sbm.parsers.RewriteMavenProjectParser;
import org.springframework.sbm.parsers.RewriteProjectParser;
import org.springframework.sbm.parsers.RewriteProjectParsingResult;
import org.springframework.sbm.project.resource.ProjectResourceSet;
import org.springframework.sbm.project.resource.ProjectResourceSetFactory;
import org.springframework.sbm.project.resource.ProjectResourceSetSerializer;
import org.springframework.sbm.recipes.RewriteRecipeDiscovery;

import java.nio.file.Path;
import java.util.List;
import java.util.Set;

@SpringBootApplication
public class BootUpgrade implements CommandLineRunner {
Expand All @@ -202,9 +271,13 @@ public class BootUpgrade implements CommandLineRunner {
@Autowired
ProjectScanner scanner;
@Autowired
RewriteMavenProjectParser parser;
RewriteProjectParser parser;
@Autowired
RewriteRecipeDiscovery discovery;
@Autowired
ProjectResourceSetSerializer serializer;
@Autowired
ProjectResourceSetFactory factory;

@Override
public void run(String... args) throws Exception {
Expand All @@ -215,21 +288,24 @@ public class BootUpgrade implements CommandLineRunner {
if(!baseDir.toFile().exists() || !baseDir.toFile().isDirectory()) {
throw new IllegalArgumentException("Given path '%s' does not exist or is not a directory.".formatted(path));
}
List<Resource> resources = scanner.scan(baseDir, Set.of("**/.idea/**", "**/.DS_Store", "**/.git/**"));
ExecutionContext ctx = new InMemoryExecutionContext(t -> {throw new RuntimeException(t);});
RewriteProjectParsingResult parsingResult = parser.parse(baseDir/*, resources*/, ctx);

// parse
RewriteProjectParsingResult parsingResult = parser.parse(baseDir);
List<SourceFile> sourceFiles = parsingResult.sourceFiles();

// create ProjectResourceSet
ProjectResourceSet projectResourceSet = factory.create(baseDir, sourceFiles);

// find recipe
String recipeName = "org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_1";
List<Recipe> recipes = discovery.discoverRecipes();
recipes.stream()
.filter(r -> recipeName.equals(r.getName()))
.forEach(r -> {
System.out.println("Applying recipe '%s'".formatted(r.getName()));
LargeSourceSet lss = new InMemoryLargeSourceSet(parsingResult.sourceFiles());
RecipeRun recipeRun = r.run(lss, ctx);
recipeRun.getChangeset().getAllResults().stream()
.map(Result::diff)
.forEach(System.out::println);
});
Recipe recipe = findRecipe(recipes, recipeName);

// apply recipe
projectResourceSet.apply(recipe);

// write changes to fs
serializer.writeChanges(projectResourceSet);
}
}
.....
Loading