Dependency management is a complex and often frustrating part of software development. Sometimes a transitive dependency you'd never expect makes it onto your runtime classpath. Sometimes your build tool resolves a conflict between requested dependency versions in an unexpected way. Despite the headaches, it wouldn't make sense for most projects to forgo dependencies.
Rewrite can help. In this tutorial we'll learn how to automate common dependency management tasks by migrating a project from one slf4j implementation to another.
Clone our fork of spring-petclinic, or select your own project
Familiarize yourself with the basics of applying the rewrite-maven-plugin as described in our quickstart guide.
The first step in changing your dependencies is knowing what they are. First let's find out which of the existing dependencies are using logback-classic. There's no mention of logback in the pom.xml itself, but any of the dependencies that are there could be bringing it in transitively.
First add the rewrite-maven-plugin to the pom.xml:
pom.xml<!-- inside of project/build/plugins --><plugin><groupId>org.openrewrite.maven</groupId><artifactId>rewrite-maven-plugin</artifactId><version>4.5.0</version><configuration><activeRecipes><recipe>com.yourorg.LogbackInsight</recipe></activeRecipes></configuration></plugin>
Then create a rewrite.yml file at the project root with these contents:
rewrite.yml---type: specs.openrewrite.org/v1beta/recipename: com.yourorg.LogbackInsightdisplayName: Find Logback UsagerecipeList:- org.openrewrite.maven.search.DependencyInsight:groupIdPattern: ch.qos.logbackartifactIdPattern: "*"scope: compile
mvn rewrite:dryRun. This won't make changes to the project's files. It will produce a rewrite.patch file in the reports directory, with a link in the console log:
Console LogThese recipes would make changes to pom.xml:org.openrewrite.maven.search.DependencyInsightReport available:/mnt/f/Projects/openrewrite/spring-petclinic-migration/target/site/rewrite/rewrite.patchRun 'mvn rewrite:run' to apply the recipes.
Use your preferred diff viewer to inspect the rewrite.patch file, which reveals all of the dependencies that transitively depend on logback.
At this point you have all of the information you need to manually exclude logback-classic from those other dependencies and add a dependency on your preferred slf4j implementation. But that wouldn't prevent logback-classic from being added back in the future. There is an easier, future-proof way to do it.
Use the rewrite recipes ExcludeDependency and AddDependency to ensure that only your preferred slf4j dependency is used. If a new transitive dependency on logback-classic appears in the future, ExcludeDependency will detect and exclude it.
Add this to your rewrite.yml:
rewrite.yml---type: specs.openrewrite.org/v1beta/recipename: com.yourorg.UseSlf4jSimpledisplayName: Use slf4j SimplerecipeList:- org.openrewrite.maven.ExcludeDependency:groupId: ch.qos.logbackartifactId: logback-classic- org.openrewrite.maven.AddDependency:groupId: org.slf4jartifactId: slf4j-simpleversion: 1.7.X
And set the
com.yourorg.UseSlf4jSimple recipe as active in your pom.xml:
pom.xml<!-- inside of project/build/plugins --><plugin><groupId>org.openrewrite.maven</groupId><artifactId>rewrite-maven-plugin</artifactId><version>4.5.0</version><configuration><activeRecipes><recipe>com.yourorg.UseSlf4jSimple</recipe></activeRecipes></configuration></plugin>
You can now run
mvn rewrite:dryRun again to preview the changes that will be made and
mvn rewrite:run to apply the changes.
mvn rewrite:dryRun only produces warnings in the console output and a rewrite.patch file if there are active recipes that would make changes. This means
dryRun can be used in your CI pipeline to prevent new logback-classic dependencies being added in an ongoing way. Configure the CI step to fail if
dryRun emits any warnings to the console log, or if a rewrite.patch file is produced, and you have an effective guard against regression.
Of course, CI failures are always at least a little bit frustrating for developers. Another option, at least in organizations where the commit is made by CI, is to run
mvn rewrite:run before the build & test step. Then the build wont need to fail because rewrite will automatically fix the dependency problem.
The dependency management recipes used in this guide aren't the only such recipes included in rewrite. See the Maven recipe reference for the full listing of dependency management recipes.