Skip to content

Commit

Permalink
Add initial recipes for Micronaut 4. (#50)
Browse files Browse the repository at this point in the history
* OpenRewrite 8.0 upgrade

* Update recipes to new `JavaTemplate` API

* Add recipes for Micronaut 4.

* Add license headers.

* Fix missing import.

* Use parserClasspath to test multiple Micronaut versions.

* Unnest recipeDependencies in build.gradle.kts

* Use existing recipe for Jakarta annotaitons update.

* Add recipe test dependencies to repository.

* Update licenseHeader.txt and copyight year on new files

* Use Preconditions.check

* Clean up Groovy compile warnings in tests.

* Refactor to unified Gradle/Maven AddDependency.

* Update Security recipes with conditional checks.

* Add missing license header.

* Use rewrite-migrate-java to update build to Java 17.

* Use unified Gradle/Maven RemoveDependency.

* Use provided  from rewrite-migrate-java.

* Use provided JavaxValidationMigrationToJakartaValidation from rewrite-migrate-java.

* Typo in recipe description.

Co-authored-by: Tim te Beek <[email protected]>

* Invoke provided Jakarta migrations directly.

Co-authored-by: Tim te Beek <[email protected]>

* Invoke Jakarta migration recipes directly from tests.

* Inline fromRuntimeClasspath and limit package scan

Also only scan once instead of twice

* Similarly limit runtime classpath scanning

---------

Co-authored-by: Knut Wannheden <[email protected]>
Co-authored-by: Tim te Beek <[email protected]>
Co-authored-by: Tim te Beek <[email protected]>
  • Loading branch information
4 people authored Jun 7, 2023
1 parent 3d23f51 commit f777d3d
Show file tree
Hide file tree
Showing 59 changed files with 2,640 additions and 48 deletions.
48 changes: 34 additions & 14 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ dependencies {
implementation(platform("org.openrewrite:rewrite-bom:${rewriteVersion}"))
implementation("org.openrewrite:rewrite-java")
implementation("org.openrewrite:rewrite-maven")
implementation("org.openrewrite:rewrite-gradle")
implementation("org.openrewrite:rewrite-properties")
implementation("org.openrewrite:rewrite-yaml")
implementation("org.openrewrite.recipe:rewrite-migrate-java:2.+")
implementation("org.openrewrite.recipe:rewrite-java-dependencies:1.+")

runtimeOnly("org.openrewrite:rewrite-java-8")
runtimeOnly("org.openrewrite:rewrite-java-11")
runtimeOnly("org.openrewrite:rewrite-java-17")
runtimeOnly("org.openrewrite.recipe:rewrite-migrate-java:${rewriteVersion}")

testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release")
testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release")
Expand All @@ -31,18 +35,34 @@ dependencies {
testImplementation("org.openrewrite:rewrite-test")
testImplementation("org.openrewrite:rewrite-java-tck")
testImplementation("org.assertj:assertj-core:latest.release")
testRuntimeOnly(gradleApi())
}

recipeDependencies {
parserClasspath("io.micronaut:micronaut-core:2.+")
parserClasspath("io.micronaut:micronaut-context:2.+")
parserClasspath("io.micronaut:micronaut-inject:2.+")
parserClasspath("io.micronaut:micronaut-validation:2.+")
parserClasspath("io.micronaut:micronaut-http:2.+")
parserClasspath("io.micronaut:micronaut-http-server:2.+")
parserClasspath("io.micronaut:micronaut-http-server-netty:2.+")
parserClasspath("io.micronaut:micronaut-http-client:2.+")
parserClasspath("io.micronaut:micronaut-http-client-core:2.+")
parserClasspath("javax.inject:javax.inject:1")
parserClasspath("jakarta.inject:jakarta.inject-api:2.+")
parserClasspath("org.reactivestreams:reactive-streams:1.0.4")

testImplementation("com.google.guava:guava:29.0-jre")

testRuntimeOnly("io.micronaut:micronaut-core:2.+")
testRuntimeOnly("io.micronaut:micronaut-inject-java:2.+")
testRuntimeOnly("io.micronaut:micronaut-http:2.+")
testRuntimeOnly("io.micronaut:micronaut-http-server:2.+")
testRuntimeOnly("io.micronaut:micronaut-http-server-netty:2.+")
testRuntimeOnly("io.micronaut:micronaut-http-client:2.+")
testRuntimeOnly("io.micronaut:micronaut-http-client-core:2.+")
testRuntimeOnly("io.micronaut:micronaut-validation:2.+")
testRuntimeOnly("jakarta.inject:jakarta.inject-api:2.+")
testRuntimeOnly("javax.validation:validation-api:2.+")
testRuntimeOnly("org.reactivestreams:reactive-streams:1.0.4")
parserClasspath("io.micronaut:micronaut-context:4.0.0-M2")
parserClasspath("io.micronaut:micronaut-websocket:4.0.0-M2")
parserClasspath("io.micronaut.validation:micronaut-validation:4.0.0-M5")
parserClasspath("io.micronaut:micronaut-retry:4.0.0-M4")
parserClasspath("io.micronaut.email:micronaut-email:2.0.0-M1")
parserClasspath("javax.annotation:javax.annotation-api:1.3.2")
parserClasspath("javax.validation:validation-api:2.0.1.Final")
parserClasspath("jakarta.validation:jakarta.validation-api:3.0.2")
parserClasspath("javax.persistence:javax.persistence-api:2.2")
parserClasspath("jakarta.persistence:jakarta.persistence-api:3.1.0")
parserClasspath("javax.mail:javax.mail-api:1.6.2")
parserClasspath("jakarta.mail:jakarta.mail-api:2.1.1")
parserClasspath("jakarta.annotation:jakarta.annotation-api:2.1.1")
}
2 changes: 1 addition & 1 deletion gradle/licenseHeader.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright 2021 the original author or authors.
Copyright 2023 the original author or authors.
<p>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.micronaut;

import org.openrewrite.*;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.dependencies.AddDependency;

import java.util.ArrayList;
import java.util.List;

import static java.util.Objects.requireNonNull;

public class AddSnakeYamlDependencyIfNeeded extends ScanningRecipe<AddSnakeYamlDependencyIfNeeded.YamlAccumulator> {

private final List<Recipe> recipeList = new ArrayList<>();

static class YamlAccumulator {
boolean usingYamlConfig = false;
}

public AddSnakeYamlDependencyIfNeeded() {
recipeList.add(new AddDependency("org.yaml", "snakeyaml", null, null, "io.micronaut.runtime.Micronaut",
null, null, null, "runtimeOnly", "runtime", null, null, null, null));
}

@Override
public YamlAccumulator getInitialValue(ExecutionContext ctx) {
return new YamlAccumulator();
}

@Override
public TreeVisitor<?, ExecutionContext> getScanner(YamlAccumulator acc) {
return new TreeVisitor<Tree, ExecutionContext>() {
@Override
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
SourceFile sourceFile = (SourceFile) requireNonNull(tree);
acc.usingYamlConfig |= sourceFile != new FindYamlConfig().getVisitor().visit(sourceFile, ctx);
return tree;
}
};
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor(YamlAccumulator acc) {
return Preconditions.check(!acc.usingYamlConfig, new TreeVisitor<Tree, ExecutionContext>() {
@Override
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext executionContext) {
if (!recipeList.isEmpty()) {
recipeList.clear();
}
return super.visit(tree, executionContext);
}
});
}

@Override
public String getDisplayName() {
return "Add `snakeyaml` dependency if needed";
}

@Override
public String getDescription() {
return "This recipe will add the `snakeyaml` dependency to a Micronaut 4 application that uses yaml configuration.";
}

@Override
public List<Recipe> getRecipeList() {
return this.recipeList;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.micronaut;

import lombok.EqualsAndHashCode;
import lombok.Value;
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.AddOrUpdateChild;
import org.openrewrite.xml.ChangeTagValueVisitor;
import org.openrewrite.xml.tree.Xml;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.openrewrite.xml.FilterTagChildrenVisitor.filterTagChildren;
import static org.openrewrite.xml.MapTagChildrenVisitor.mapTagChildren;

@Value
@EqualsAndHashCode(callSuper = true)
public class ChangeAnnotationProcessorPath extends Recipe {

@Option(displayName = "Old groupId",
description = "The old groupId to replace. The groupId is the first part of a plugin coordinate 'com.google.guava:guava:VERSION'. Supports glob expressions.",
example = "org.openrewrite.recipe")
String oldGroupId;

@Option(displayName = "Old artifactId",
description = "The old artifactId to replace. The artifactId is the second part of a plugin coordinate 'com.google.guava:guava:VERSION'. Supports glob expressions.",
example = "my-deprecated-annotation-processor")
String oldArtifactId;

@Option(displayName = "New groupId",
description = "The new groupId to use. Defaults to the existing group id.",
example = "corp.internal.openrewrite.recipe",
required = false)
@Nullable
String newGroupId;

@Option(displayName = "New artifactId",
description = "The new artifactId to use. Defaults to the existing artifact id.",
example = "my-new-annotation-processor",
required = false)
@Nullable
String newArtifactId;

@Option(displayName = "New version",
description = "An exact version string for the annotation processor path.",
example = "${micronaut.validation}",
required = false)
@Nullable
String newVersion;

@Option(displayName = "Exclusions",
description = "A list of exclusions to apply to the annotation processor path in the format groupId:artifactId",
example = "io.micronaut:micronaut-inject",
required = false)
@Nullable
List<String> exclusions;

@Override
public String getDisplayName() {
return "Change Maven annotation processor path";
}

@Override
public String getDescription() {
return "Change the groupId, artifactId, and version of a Maven annotation processor path.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new MavenVisitor<ExecutionContext>() {

@Override
public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) {
Xml.Tag plugin = (Xml.Tag) super.visitTag(tag, ctx);
if (isPluginTag("org.apache.maven.plugins", "maven-compiler-plugin")) {
plugin = maybeUpdatePlugin(plugin, ctx);
if (plugin != tag) {
maybeUpdateModel();
}
}
return plugin;
}

private Xml.Tag maybeUpdatePlugin(Xml.Tag plugin, ExecutionContext ctx) {
return mapTagChildren(plugin, childTag -> "configuration".equals(childTag.getName()) ? maybeUpdateConfiguration(childTag, ctx) : childTag);
}

private Xml.Tag maybeUpdateConfiguration(Xml.Tag configuration, ExecutionContext ctx) {
return mapTagChildren(configuration, childTag -> "annotationProcessorPaths".equals(childTag.getName()) ? maybeUpdateAnnotationProcessorPaths(childTag, ctx) : childTag);
}

private Xml.Tag maybeUpdateAnnotationProcessorPaths(Xml.Tag annotationProcessorPaths, ExecutionContext ctx) {
return mapTagChildren(annotationProcessorPaths, childTag -> {
if ("path".equals(childTag.getName()) && isPathMatch(childTag)) {
Xml.Tag path = childTag;
if (newGroupId != null) {
path = changeChildTagValue(path, "groupId", newGroupId, ctx);
}
if (newArtifactId != null) {
path = changeChildTagValue(path, "artifactId", newArtifactId, ctx);
}
if (newVersion != null) {
path = changeChildTagValue(path, "version", newVersion, ctx);
}
if (exclusions == null) {
path = filterTagChildren(path, child -> !("exclusions".equals(child.getName())));
} else if (exclusions != null) {
path = addExclusionsToPath(path, ctx);
}
childTag = path;
}
return childTag;
});
}

private Xml.Tag addExclusionsToPath(Xml.Tag path, ExecutionContext ctx) {
Xml.Tag exclusionsTag = Xml.Tag.build("\n<exclusions>\n" + buildExclusionsContent() + "</exclusions>");
doAfterVisit(new AddOrUpdateChild<>(path, exclusionsTag));
return path;
}

private String buildExclusionsContent() {
if (exclusions == null) {
return "";
}
return exclusions.stream().map(exclusion -> {
StringBuilder exclusionContent = new StringBuilder("<exclusion>\n");
String[] exclusionParts = exclusion.split(":");
if (exclusionParts.length != 2) {
throw new IllegalStateException("Expected an exclusion in the form of groupId:artifactId but was '" + exclusion + "'");
}
exclusionContent.append("<groupId>").append(exclusionParts[0]).append("</groupId>\n")
.append("<artifactId>").append(exclusionParts[1]).append("</artifactId>\n")
.append("</exclusion>\n");
return exclusionContent.toString();
}).collect(Collectors.joining());
}

private boolean isPathMatch(Xml.Tag path) {
return oldGroupId.equals(path.getChildValue("groupId").orElse(null)) &&
oldArtifactId.equals(path.getChildValue("artifactId").orElse(null));
}

private Xml.Tag changeChildTagValue(Xml.Tag tag, String childTagName, String newValue, ExecutionContext ctx) {
Optional<Xml.Tag> childTag = tag.getChild(childTagName);
if (childTag.isPresent() && !newValue.equals(childTag.get().getValue().orElse(null))) {
tag = (Xml.Tag) new ChangeTagValueVisitor<>(childTag.get(), newValue).visitNonNull(tag, ctx);
}
return tag;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.micronaut;

import org.openrewrite.ExecutionContext;
import org.openrewrite.HasSourcePath;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;

public class FindPropertiesConfig extends Recipe {

@Override
public String getDisplayName() {
return "Find Micronaut properties config";
}

@Override
public String getDescription() {
return "Find Micronaut properties configuration files.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new HasSourcePath<>("**/{application,application-*,bootstrap,bootstrap-*}.{properties}");
}
}
Loading

0 comments on commit f777d3d

Please sign in to comment.