From b0a0ef38c850aa8f29023a424e9c507273f8095c Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Tue, 10 May 2022 15:36:15 -0400 Subject: [PATCH] Spring Boot 3 migration recipe initial steps --- .../java/rewrite/RewriteRecipeRepository.java | 44 +++--- .../codeaction/ConvertAutowiredField.java | 2 +- ...iredParameterIntoConstructorParameter.java | 3 +- .../maven/ChangeDependencyClassifier.java | 131 ++++++++++++++++++ .../META-INF/rewrite/spring-boot3-upgrade.yml | 25 ++++ .../META-INF/rewrite/spring-data3-upgrade.yml | 24 ++++ 6 files changed, 208 insertions(+), 21 deletions(-) rename headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/{java/rewrite => rewrite/java}/ConvertAutowiredParameterIntoConstructorParameter.java (98%) create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/rewrite/maven/ChangeDependencyClassifier.java create mode 100644 headless-services/spring-boot-language-server/src/main/resources/META-INF/rewrite/spring-boot3-upgrade.yml create mode 100644 headless-services/spring-boot-language-server/src/main/resources/META-INF/rewrite/spring-data3-upgrade.yml diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java index 66b31b41a..5baf03f57 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRecipeRepository.java @@ -156,31 +156,37 @@ public class RewriteRecipeRepository { private String createGlobalCommand(Recipe r) { String commandId = "sts/rewrite/recipe/" + r.getName(); server.onCommand(commandId, params -> { - String progressToken = params.getWorkDoneToken() == null || params.getWorkDoneToken().getLeft() == null ? r.getName() : params.getWorkDoneToken().getLeft(); + final String progressToken = params.getWorkDoneToken() == null || params.getWorkDoneToken().getLeft() == null ? r.getName() : params.getWorkDoneToken().getLeft(); return CompletableFuture.supplyAsync(() -> { JsonElement uri = (JsonElement) params.getArguments().get(0); server.getProgressService().progressEvent(progressToken, r.getDisplayName() + ": initiated..."); return projectFinder.find(new TextDocumentIdentifier(uri.getAsString())); }).thenCompose(p -> { if (p.isPresent()) { - return CompletableFuture.completedFuture(apply(r, p.get())).thenCompose(we -> { - if (we.isPresent()) { - server.getProgressService().progressEvent(progressToken, - r.getDisplayName() + ": applying document changes..."); - return server.getClient().applyEdit(new ApplyWorkspaceEditParams(we.get(), r.getDisplayName())).thenCompose(res -> { - if (res.isApplied()) { - server.getProgressService().progressEvent(progressToken, null); - return CompletableFuture.completedFuture("success"); - } else { - server.getProgressService().progressEvent(progressToken, null); - return CompletableFuture.completedFuture(null); - } - }); - } else { - server.getProgressService().progressEvent(progressToken, null); - return CompletableFuture.completedFuture(null); - } - }); + try { + Optional edit = apply(r, p.get()); + return CompletableFuture.completedFuture(edit).thenCompose(we -> { + if (we.isPresent()) { + server.getProgressService().progressEvent(progressToken, + r.getDisplayName() + ": applying document changes..."); + return server.getClient().applyEdit(new ApplyWorkspaceEditParams(we.get(), r.getDisplayName())).thenCompose(res -> { + if (res.isApplied()) { + server.getProgressService().progressEvent(progressToken, null); + return CompletableFuture.completedFuture("success"); + } else { + server.getProgressService().progressEvent(progressToken, null); + return CompletableFuture.completedFuture(null); + } + }); + } else { + server.getProgressService().progressEvent(progressToken, null); + return CompletableFuture.completedFuture(null); + } + }); + } catch (Throwable t) { + server.getProgressService().progressEvent(progressToken, null); + throw t; + } } return CompletableFuture.completedFuture(null); }); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/ConvertAutowiredField.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/ConvertAutowiredField.java index d364871b0..32ca72e1a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/ConvertAutowiredField.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/codeaction/ConvertAutowiredField.java @@ -30,9 +30,9 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.springframework.ide.vscode.boot.java.Annotations; -import org.springframework.ide.vscode.boot.java.rewrite.ConvertAutowiredParameterIntoConstructorParameter; import org.springframework.ide.vscode.boot.java.rewrite.ORCompilationUnitCache; import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings; +import org.springframework.ide.vscode.boot.rewrite.java.ConvertAutowiredParameterIntoConstructorParameter; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/ConvertAutowiredParameterIntoConstructorParameter.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/rewrite/java/ConvertAutowiredParameterIntoConstructorParameter.java similarity index 98% rename from headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/ConvertAutowiredParameterIntoConstructorParameter.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/rewrite/java/ConvertAutowiredParameterIntoConstructorParameter.java index 62b6cd2c4..95b0c4dd7 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/ConvertAutowiredParameterIntoConstructorParameter.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/rewrite/java/ConvertAutowiredParameterIntoConstructorParameter.java @@ -8,7 +8,7 @@ * Contributors: * VMware, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.rewrite; +package org.springframework.ide.vscode.boot.rewrite.java; import java.util.List; import java.util.Objects; @@ -36,6 +36,7 @@ import org.openrewrite.java.tree.Statement; import org.openrewrite.java.tree.TypeTree; import org.openrewrite.java.tree.TypeUtils; import org.springframework.ide.vscode.boot.java.Annotations; +import org.springframework.ide.vscode.boot.java.rewrite.ORAstUtils; public class ConvertAutowiredParameterIntoConstructorParameter extends Recipe { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/rewrite/maven/ChangeDependencyClassifier.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/rewrite/maven/ChangeDependencyClassifier.java new file mode 100644 index 000000000..3446bcba1 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/rewrite/maven/ChangeDependencyClassifier.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2022 VMware, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.rewrite.maven; + +import java.util.Objects; +import java.util.Optional; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Option; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.maven.MavenVisitor; +import org.openrewrite.xml.AddToTagVisitor; +import org.openrewrite.xml.ChangeTagValueVisitor; +import org.openrewrite.xml.RemoveContentVisitor; +import org.openrewrite.xml.tree.Xml; + +public class ChangeDependencyClassifier extends Recipe { + + @Option(displayName = "Group", + description = "The first part of a dependency coordinate 'com.google.guava:guava:VERSION'.", + example = "com.google.guava") + String groupId; + + @Option(displayName = "Artifact", + description = "The second part of a dependency coordinate 'com.google.guava:guava:VERSION'.", + example = "guava") + String artifactId; + + /** + * If null, strips the scope from an existing dependency. + */ + @Option(displayName = "New classifier", + description = "Classifier to apply to specified Maven dependency. " + + "May be omitted, which indicates that no classifier should be added and any existing scope be removed from the dependency.", + example = "jar", + required = false) + @Nullable + String newClassifier; + + @Override + public String getDisplayName() { + return "Change Maven dependency classifier"; + } + + @Override + public String getDescription() { + return "Add or alter the classifier of the specified dependency."; + } + + @Override + protected TreeVisitor getVisitor() { + return new MavenVisitor() { + @Override + public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) { + if (isDependencyTag()) { + if (groupId.equals(tag.getChildValue("groupId").orElse(getResolutionResult().getPom().getGroupId())) && + artifactId.equals(tag.getChildValue("artifactId").orElse(null))) { + Optional scope = tag.getChild("classifier"); + if (scope.isPresent()) { + if (newClassifier == null) { + doAfterVisit(new RemoveContentVisitor<>(scope.get(), false)); + } else if (!newClassifier.equals(scope.get().getValue().orElse(null))) { + doAfterVisit(new ChangeTagValueVisitor<>(scope.get(), newClassifier)); + } + } else if (newClassifier != null) { + doAfterVisit(new AddToTagVisitor<>(tag, Xml.Tag.build("" + newClassifier + ""))); + } + } + } + + return super.visitTag(tag, ctx); + } + }; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public String getNewClassifier() { + return newClassifier; + } + + public void setNewClassifier(String newClassifier) { + this.newClassifier = newClassifier; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(artifactId, groupId, newClassifier); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + ChangeDependencyClassifier other = (ChangeDependencyClassifier) obj; + return Objects.equals(artifactId, other.artifactId) && Objects.equals(groupId, other.groupId) + && Objects.equals(newClassifier, other.newClassifier); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/resources/META-INF/rewrite/spring-boot3-upgrade.yml b/headless-services/spring-boot-language-server/src/main/resources/META-INF/rewrite/spring-boot3-upgrade.yml new file mode 100644 index 000000000..1fda34ef4 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/resources/META-INF/rewrite/spring-boot3-upgrade.yml @@ -0,0 +1,25 @@ +--- +######################################################################################################################## +# SpringBoot 3_0 +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0 +displayName: Upgrade to Spring Boot 3.0 from 2.x +description: 'Upgrade to Spring Boot 3.0 from prior 2.x version.' +recipeList: + # Upgrade 3.0.x from 2.x + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: org.springframework.boot + artifactId: "*" + newVersion: 3.0.0-SNAPSHOT + trustParent: true + - org.openrewrite.maven.UpgradeParentVersion: + groupId: org.springframework.boot + artifactId: spring-boot-starter-parent + newVersion: 3.0.0-SNAPSHOT + - org.openrewrite.maven.ChangePropertyValue: + key: 'java.version' + newValue: 17 + addIfMissing: true + + - org.openrewrite.java.spring.data.UpgradeSpringData_3_0 + \ No newline at end of file diff --git a/headless-services/spring-boot-language-server/src/main/resources/META-INF/rewrite/spring-data3-upgrade.yml b/headless-services/spring-boot-language-server/src/main/resources/META-INF/rewrite/spring-data3-upgrade.yml new file mode 100644 index 000000000..4218bca96 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/resources/META-INF/rewrite/spring-data3-upgrade.yml @@ -0,0 +1,24 @@ +######################################################################################################################## +# Spring Data 3.0 +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.spring.data.UpgradeSpringData_3_0 +displayName: Upgrade to Spring Data 3.0 +description: 'Upgrade to Spring Data to 3.0 from any prior version.' +recipeList: + - org.springframework.ide.vscode.boot.rewrite.maven.ChangeDependencyClassifier: + groupId: org.ehcache + artifactId: ehcache + newClassifier: jakarta + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.persistence + newPackageName: jakarta.persistence + recursive: true + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.validation + newPackageName: jakarta.validation + recursive: true + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.xml.bind + newPackageName: jakarta.xml.bind + recursive: true + \ No newline at end of file