Home > OS >  Which dependency is my duplicate Slf4j binding coming from in this maven dependency tree?
Which dependency is my duplicate Slf4j binding coming from in this maven dependency tree?

Time:01-15

I have been slamming my head against my desk for the past week trying to interpret this warning.

I am getting the following notification in my run output when trying to run my multi-module Spring/Kotlin/Maven application:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/vboxuser/Scratch/demo-server/controller/target/controller-0.1.0.jar!/BOOT-INF/classes!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/vboxuser/Scratch/demo-server/controller/target/controller-0.1.0.jar!/BOOT-INF/lib/logback-classic-1.2.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

Here we see there are two bindings coming from my controller module, but i'm having troubles determining where they are both coming from.

My project is structured like the following with 3 total pom files:

parent
 |--controller
 |--utilities

Here is my maven dependency tree for the controller module:

[INFO] --- maven-dependency-plugin:3.3.0:tree (default-cli) @ controller ---
[INFO] app.demo.server.controller:controller:jar:0.1.0
[INFO]  - org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar:1.7.21:compile
[INFO] |   - (org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile - version managed from 1.7.21; omitted for duplicate)
[INFO] |  \- org.jetbrains.kotlin:kotlin-stdlib-jdk7:jar:1.7.21:compile (version managed from 1.7.21)
[INFO] |     \- (org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile - version managed from 1.7.21; omitted for duplicate)
[INFO]  - org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile
[INFO] |   - org.jetbrains.kotlin:kotlin-stdlib-common:jar:1.7.21:compile (version managed from 1.7.21)
[INFO] |  \- org.jetbrains:annotations:jar:13.0:compile
[INFO]  - org.jetbrains.kotlin:kotlin-reflect:jar:1.7.21:compile
[INFO] |  \- (org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile - version managed from 1.7.21; omitted for duplicate)
[INFO] \- org.springframework.boot:spring-boot-starter:jar:2.7.7:compile
[INFO]     - org.springframework.boot:spring-boot:jar:2.7.7:compile (version managed from 2.7.7)
[INFO]    |   - (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO]    |  \- org.springframework:spring-context:jar:5.3.24:compile (version managed from 5.3.24)
[INFO]    |      - org.springframework:spring-aop:jar:5.3.24:compile (version managed from 5.3.24)
[INFO]    |     |   - (org.springframework:spring-beans:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO]    |     |  \- (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO]    |      - org.springframework:spring-beans:jar:5.3.24:compile (version managed from 5.3.24)
[INFO]    |     |  \- (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO]    |      - (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO]    |     \- org.springframework:spring-expression:jar:5.3.24:compile (version managed from 5.3.24)
[INFO]    |        \- (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO]     - org.springframework.boot:spring-boot-autoconfigure:jar:2.7.7:compile (version managed from 2.7.7)
[INFO]    |  \- (org.springframework.boot:spring-boot:jar:2.7.7:compile - version managed from 2.7.7; omitted for duplicate)
[INFO]     - org.springframework.boot:spring-boot-starter-logging:jar:2.7.7:compile (version managed from 2.7.7)
[INFO]    |   - ch.qos.logback:logback-classic:jar:1.2.11:compile (version managed from 1.2.11)
[INFO]    |  |   - ch.qos.logback:logback-core:jar:1.2.11:compile (version managed from 1.2.11)
[INFO]    |  |  \- org.slf4j:slf4j-api:jar:1.7.36:compile (version managed from 1.7.32)
[INFO]    |   - org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.2:compile (version managed from 2.17.2)
[INFO]    |  |   - (org.slf4j:slf4j-api:jar:1.7.36:compile - version managed from 1.7.35; omitted for duplicate)
[INFO]    |  |  \- org.apache.logging.log4j:log4j-api:jar:2.17.2:compile (version managed from 2.17.2)
[INFO]    |  |     \- org.osgi:org.osgi.core:jar:4.3.1:provided
[INFO]    |  \- org.slf4j:jul-to-slf4j:jar:1.7.36:compile (version managed from 1.7.36)
[INFO]    |     \- (org.slf4j:slf4j-api:jar:1.7.36:compile - version managed from 1.7.36; omitted for duplicate)
[INFO]     - jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile (version managed from 1.3.5)
[INFO]     - org.springframework:spring-core:jar:5.3.24:compile (version managed from 5.3.24)
[INFO]    |  \- org.springframework:spring-jcl:jar:5.3.24:compile (version managed from 5.3.24)
[INFO]    \- org.yaml:snakeyaml:jar:1.33:compile (version managed from 1.30)

Specifically, you might notice that slf4j is coming from the spring-boot-starter-logging dependency from spring-boot-starter

Taking a closer look at the spring boot logging dependency, i'm still not entirely sure where my issue is coming from. i see there are three dependencies here with slf4j, but they don't appear to be duplicates:

[INFO]     - org.springframework.boot:spring-boot-starter-logging:jar:2.7.7:compile (version managed from 2.7.7)
[INFO]    |   - ch.qos.logback:logback-classic:jar:1.2.11:compile (version managed from 1.2.11)
[INFO]    |  |   - ch.qos.logback:logback-core:jar:1.2.11:compile (version managed from 1.2.11)
[INFO]    |  |  \- org.slf4j:slf4j-api:jar:1.7.36:compile (version managed from 1.7.32)
[INFO]    |   - org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.2:compile (version managed from 2.17.2)
[INFO]    |  |   - (org.slf4j:slf4j-api:jar:1.7.36:compile - version managed from 1.7.35; omitted for duplicate)
[INFO]    |  |  \- org.apache.logging.log4j:log4j-api:jar:2.17.2:compile (version managed from 2.17.2)
[INFO]    |  |     \- org.osgi:org.osgi.core:jar:4.3.1:provided
[INFO]    |  \- org.slf4j:jul-to-slf4j:jar:1.7.36:compile (version managed from 1.7.36)
[INFO]    |     \- (org.slf4j:slf4j-api:jar:1.7.36:compile - version managed from 1.7.36; omitted for duplicate)

So what's the issue here? I'm having troubles finding anyone with a similar issue, where most folks are seeing multiple occurrences of slf4j in their dependency tree, though i seem to be struggling to either 1. see where the duplicate is coming from or 2. have a separate issue going on here.

Here is the pom file that's giving me issues:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>app.demo.server</groupId>
        <artifactId>demo-server</artifactId>
        <version>${revision}</version>
    </parent>

    <groupId>app.demo.server.controller</groupId>
    <artifactId>controller</artifactId>
    <version>${revision}</version>
    <packaging>jar</packaging>

    <build>
        <plugins>
            <!-- main module kotlin required plugins begin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>${maven-jar-plugin.version}</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>${main.class}</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.version}</version>
                <configuration>
                    <fork>true</fork>
                    <mainClass>${main.class}</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!--main module kotlin required plugins end-->
        </plugins>
    </build>

    <properties>
        <main.class>app.demo.server.driver.demoController</main.class>
    </properties>
</project>

Here is the parent pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>app.demo.server</groupId>
    <artifactId>demo-server</artifactId>
    <version>${revision}</version>
    <packaging>pom</packaging>

    <modules>
        <module>controller</module>
        <module>utilities</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
            <version>${kotlin.version}</version>
        </dependency>

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib</artifactId>
            <version>${kotlin.version}</version>
        </dependency>

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
            <version>${kotlin.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${spring.version}</version>
        </dependency>

    </dependencies>

    <build>
        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>

        <plugins>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <configuration>
                    <jvmTarget>${java.jvm.version}</jvmTarget>
                    <compilerPlugins>
                        <plugin>spring</plugin>
                    </compilerPlugins>
                </configuration>

                <dependencies>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-allopen</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-noarg</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                </dependencies>

                <executions>
                    <execution>
                        <id>compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>

                    <execution>
                        <id>test-compile</id>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${java.jvm.version}</source>
                    <target>${java.jvm.version}</target>
                    <release>${java.jvm.version}</release>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>${maven-jar-plugin.version}</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>${maven-assembly-plugin.version}</version>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals> <goal>single</goal> </goals>
                        <configuration>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                            <appendAssemblyId>false</appendAssemblyId>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <properties>
        <revision>0.1.0</revision>
        <kotlin.version>1.7.21</kotlin.version>
        <java.version>11</java.version>
        <java.jvm.version>11</java.jvm.version>
        <maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
        <maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
        <maven-assembly-plugin.version>3.4.2</maven-assembly-plugin.version>
        <spring.version>2.7.7</spring.version>
        <snakeyaml.version>1.33</snakeyaml.version>
        <kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
        <logback-classic.version>1.4.5</logback-classic.version>
        <logback-core.version>1.4.5</logback-core.version>
    </properties>
</project>

I am compiling by running mvn clean install then running java -jar controller/target/controller-${version}.jar.

I am using kotlin version 7.21 java 11 and mvn 3.6.3

CodePudding user response:

I believe that is clear what is going on:

spring-boot-maven-plugin typically does the following: it takes primary artifact (jar file), unpacks it and creates another jar file with "executable jar" structure. The problem is, in your case "primary artifact" gets overridden due to maven-assembly-plugin configuration coming from parent pom:

   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>${maven-assembly-plugin.version}</version>
    <executions>
     <execution>
      <id>make-assembly</id>
      <phase>package</phase>
      <goals> <goal>single</goal> </goals>
      <configuration>
       <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
       </descriptorRefs>
       <appendAssemblyId>false</appendAssemblyId>
      </configuration>
     </execution>
    </executions>
   </plugin>

So, instead of repackaging original jar file spring-boot-maven-plugin repackages result of maven-assembly-plugin, and thus you are getting duplicate classes in "classpath" (I believe the size of resulting jar is twice more than expected).

You have following options:

  1. remove maven-assembly-plugin configuration from parent pom (or move it under <pluginManagement>)
  2. do not override primary artifact but create supplemental ones, i.e. set <appendAssemblyId>true</appendAssemblyId>
  3. disable maven-assembly-plugin configuration in controller pom, via specifying <goal>none</goal> or <skipAssembly>true</skipAssembly> something like:
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>${maven-assembly-plugin.version}</version>
    <executions>
     <execution>
      <id>make-assembly</id>
      <phase>package</phase>
      <goals> <goal>none</goal> </goals>
     </execution>
    </executions>
   </plugin>

or:

   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>${maven-assembly-plugin.version}</version>
    <executions>
     <execution>
      <id>make-assembly</id>
      <phase>package</phase>
      <goals> <goal>single</goal> </goals>
      <configuration>
       <skipAssembly>true</skipAssembly>
      </configuration>
     </execution>
    </executions>
   </plugin>
  • Related