Skip to content

Commit

Permalink
Recipe to migrate from AbstractLogEnabled to SLF4J (#41)
Browse files Browse the repository at this point in the history
* Recipe to migrate from AbstractLogEnabled to SLF4J

* Adopt naming convention

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Correct code-suggester error

* Remove awkward line wrapping seein in practice

* Remove local variables named `logger`

* Fix imports

* Add missing nullable annotation

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Remove suggested nullable annotation

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
timtebeek and github-actions[bot] authored Nov 16, 2024
1 parent 72c0877 commit 51cc7eb
Show file tree
Hide file tree
Showing 4 changed files with 328 additions and 0 deletions.
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation(platform("org.openrewrite:rewrite-bom:$rewriteVersion"))
implementation("org.openrewrite:rewrite-java")
implementation("org.openrewrite.recipe:rewrite-java-dependencies:$rewriteVersion")
implementation("org.openrewrite.recipe:rewrite-logging-frameworks:$rewriteVersion")
implementation("org.openrewrite.recipe:rewrite-static-analysis:$rewriteVersion")
implementation("org.openrewrite:rewrite-templating:$rewriteVersion")

Expand Down Expand Up @@ -48,6 +49,8 @@ dependencies {
testImplementation("commons-lang:commons-lang:2.6")
testImplementation("org.apache.commons:commons-lang3:3.+")

testRuntimeOnly("org.codehaus.plexus:plexus-container-default:2.+")

testImplementation("org.junit.jupiter:junit-jupiter-engine:latest.release")

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright 2024 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.codehaus.plexus;

import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.*;
import org.openrewrite.java.logging.AddLogger;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JRightPadded;
import org.openrewrite.java.tree.TypeTree;
import org.openrewrite.java.tree.TypeUtils;

import java.util.concurrent.atomic.AtomicReference;

public class AbstractLogEnabledToSlf4j extends Recipe {

private static final String ABSTRACT_LOG_ENABLED = "org.codehaus.plexus.logging.AbstractLogEnabled";
private static final MethodMatcher GET_LOGGER_MATCHER = new MethodMatcher(ABSTRACT_LOG_ENABLED + " getLogger()", true);
private static final String PLEXUS_LOGGER = "org.codehaus.plexus.logging.Logger";
private static final MethodMatcher PLEXUS_LOGGER_MATCHER = new MethodMatcher("org.codehaus.plexus.logging.Logger *(..)");

@Override
public String getDisplayName() {
return "Migrate from Plexus `AbstractLogEnabled` to SLF4J";
}

@Override
public String getDescription() {
return "Introduce a SLF4J `Logger` field and replace calls to `getLogger()` with calls to the field.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
new UsesType<>(ABSTRACT_LOG_ENABLED, true),
new JavaIsoVisitor<ExecutionContext>() {

public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
J.ClassDeclaration cd = classDecl;
if (TypeUtils.isAssignableTo(ABSTRACT_LOG_ENABLED, cd.getType())) {

// If we directly extend AbstractLogEnabled, remove the extends clause
TypeTree anExtends = cd.getExtends();
if (anExtends != null && TypeUtils.isOfClassType(anExtends.getType(), ABSTRACT_LOG_ENABLED)) {
maybeRemoveImport(ABSTRACT_LOG_ENABLED);
cd = cd.withExtends(null);
}

// Remove local variables named `logger`
cd = (J.ClassDeclaration) new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.@Nullable VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
if (multiVariable.getVariables().stream()
.map(J.VariableDeclarations.NamedVariable::getSimpleName)
.anyMatch("logger"::equals)) {
return null;
}
return super.visitVariableDeclarations(multiVariable, ctx);
}
}.visitNonNull(cd, ctx, getCursor().getParentTreeCursor());

// Add a logger field
maybeAddImport("org.slf4j.Logger");
maybeAddImport("org.slf4j.LoggerFactory");
cd = (J.ClassDeclaration) AddLogger.addSlf4jLogger(cd, "logger", ctx)
.visitNonNull(cd, ctx, getCursor().getParentTreeCursor());
AtomicReference<J.Identifier> loggerFieldReference = new AtomicReference<>();
new JavaIsoVisitor<AtomicReference<J.Identifier>>() {
@Override
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, AtomicReference<J.Identifier> ref) {
for (J.VariableDeclarations.NamedVariable var : multiVariable.getVariables()) {
if (TypeUtils.isOfClassType(var.getType(), "org.slf4j.Logger")) {
ref.set(var.getName());
}
}
return super.visitVariableDeclarations(multiVariable, ref);
}
}.visitClassDeclaration(cd, loggerFieldReference);

// Replace calls to getLogger() with the logger field
cd = (J.ClassDeclaration) new JavaVisitor<ExecutionContext>() {
@Override
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(method, ctx);
if (GET_LOGGER_MATCHER.matches(mi)) {
return loggerFieldReference.get().withPrefix(mi.getPrefix());
}
if (PLEXUS_LOGGER_MATCHER.matches(mi)) {
return mi.getPadding().withSelect(JRightPadded.build(mi.getSelect()));
}
return mi;
}
}.visitNonNull(cd, ctx, getCursor().getParentTreeCursor());

// Replace `fatal` calls with `error`
cd = (J.ClassDeclaration) new ChangeMethodName(PLEXUS_LOGGER + " fatalError(..)", "error", false, false)
.getVisitor().visitNonNull(cd, ctx, getCursor().getParentTreeCursor());
cd = (J.ClassDeclaration) new ChangeMethodName(PLEXUS_LOGGER + " isFatalErrorEnabled(..)", "isErrorEnabled", false, false)
.getVisitor().visitNonNull(cd, ctx, getCursor().getParentTreeCursor());

// Change any leftover `org.codehaus.plexus.logging.Logger` types to SLF4J Logger
maybeRemoveImport(PLEXUS_LOGGER);
cd = (J.ClassDeclaration) new ChangeType(PLEXUS_LOGGER, "org.slf4j.Logger", false)
.getVisitor().visitNonNull(cd, ctx, getCursor().getParentTreeCursor());

}
return super.visitClassDeclaration(cd, ctx);
}
}
);
}
}
21 changes: 21 additions & 0 deletions src/main/java/org/openrewrite/codehaus/plexus/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2021 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.
*/
@NullMarked
@NonNullFields
package org.openrewrite.codehaus.plexus;

import org.jspecify.annotations.NullMarked;
import org.openrewrite.internal.lang.NonNullFields;
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright 2024 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.codehaus.plexus;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

@SuppressWarnings({"RedundantSlf4jDefinition", "UnnecessaryLocalVariable"})
class AbstractLogEnabledToSlf4jTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec.recipe(new AbstractLogEnabledToSlf4j())
.parser(JavaParser.fromJavaVersion().classpath("plexus-container-default"));
}

@Test
@DocumentExample
void addAndUseLoggerField(){
rewriteRun(
//language=java
java(
"""
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
class A extends AbstractLogEnabled {
void method() {
getLogger().info("Hello");
}
void method2() {
Logger log = getLogger();
log.info("Hello");
}
}
""",
"""
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class A {
private static final Logger logger = LoggerFactory.getLogger(A.class);
void method() {
logger.info("Hello");
}
void method2() {
Logger log = logger;
log.info("Hello");
}
}
"""
)
);
}

@Test
void renameFatal(){
rewriteRun(
//language=java
java(
"""
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
class A extends AbstractLogEnabled {
void method() {
if (getLogger().isFatalErrorEnabled()) {
getLogger().fatalError("Hello");
}
}
}
""",
"""
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class A {
private static final Logger logger = LoggerFactory.getLogger(A.class);
void method() {
if (logger.isErrorEnabled()) {
logger.error("Hello");
}
}
}
"""
)
);
}

@Test
void removeLineWrap(){
rewriteRun(
//language=java
java(
"""
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
class A extends AbstractLogEnabled {
void method() {
getLogger()
.info("Really long line that caused the previous line to be wrapped, but looks add with field");
}
}
""",
"""
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class A {
private static final Logger logger = LoggerFactory.getLogger(A.class);
void method() {
logger.info("Really long line that caused the previous line to be wrapped, but looks add with field");
}
}
"""
)
);
}

@Test
void removeLocalVariableDeclaration(){
rewriteRun(
//language=java
java(
"""
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
class A extends AbstractLogEnabled {
void method() {
Logger logger = getLogger();
logger.info("Hello");
}
}
""",
"""
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class A {
private static final Logger logger = LoggerFactory.getLogger(A.class);
void method() {
logger.info("Hello");
}
}
"""
)
);
}

}

0 comments on commit 51cc7eb

Please sign in to comment.