Resolve multiple SLF4J bindings in maven project

27,595

Solution 1

Your problem isn't getting two copies of the SLF4J API, it's getting two different SLF4J implementations. You need to exclude Gossip, not the API. That means something like:

<dependency>
    <groupId>org.codehaus.gmaven.runtime</groupId>
    <artifactId>gmaven-runtime-1.7</artifactId>
    <version>1.3</version>
    <exclusions>
      <exclusion>
        <groupId>org.sonatype.gossip</groupId>
        <artifactId>gossip</artifactId>
      </exclusion>
    </exclusions>
</dependency>

The Gossip dependency is declared by gshell-io; hopefully, it doesn't actually need Gossip, it just needs an SLF4J SLF4J, which you are supplying in the shape of Logback.

Solution 2

All you need to do is add something like this

compile "org.sonatype.gossip:gossip:1.0" {
    exclude module:'slf4j-jcl'
    exclude module:'slf4j-log4j12'
}

Solution 3

I guess you need to play with scope of dependencies, see: http://www.mojohaus.org/exec-maven-plugin/java-mojo.html

classpathScope - defines the scope of the classpath passed to the plugin. Set to compile,test,runtime or system depending on your needs.

Share:
27,595
shabunc
Author by

shabunc

Just another javascripter, coding since 1998, wanna sleep.

Updated on July 05, 2022

Comments

  • shabunc
    shabunc almost 2 years

    This is a question sounds like bunch of similar questions on SE sites, so I should be quite verbose to make my question clear. So, here is project's minimal pom.xml:

    <dependencies>
         <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.0.6</version>
        </dependency>
    
       <dependency>
            <groupId>org.codehaus.gmaven.runtime</groupId>
            <artifactId>gmaven-runtime-1.7</artifactId>
            <version>1.3</version>
       </dependency>
    
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.shabunc.App</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    Here is the dependency tree produced by maven.

    mvn dependency:tree -Dverbose -Dincludes=org.slf4j:

    [INFO] [dependency:tree {execution: default-cli}]
    [INFO] org.shabunc:logdebug:jar:1.0-SNAPSHOT
    [INFO] \- ch.qos.logback:logback-classic:jar:1.0.6:compile
    [INFO]    \- org.slf4j:slf4j-api:jar:1.6.5:compile
    

    Now, let's remove exclusion and check dependencies again. We'll get:

     [INFO] org.shabunc:logdebug:jar:1.0-SNAPSHOT
    [INFO] +- ch.qos.logback:logback-classic:jar:1.0.6:compile
    [INFO] |  \- org.slf4j:slf4j-api:jar:1.6.5:compile
    [INFO] \- org.codehaus.gmaven.runtime:gmaven-runtime-1.7:jar:1.3:compile
    [INFO]    +- (org.slf4j:slf4j-api:jar:1.5.10:compile - omitted for conflict with 1.6.5)
    [INFO]    +- org.codehaus.gmaven.feature:gmaven-feature-support:jar:1.3:compile
    [INFO]    |  \- (org.slf4j:slf4j-api:jar:1.5.10:compile - omitted for conflict with 1.6.5)
    [INFO]    \- org.codehaus.gmaven.runtime:gmaven-runtime-support:jar:1.3:compile
    [INFO]       +- (org.slf4j:slf4j-api:jar:1.5.10:compile - omitted for conflict with 1.6.5)
    [INFO]       \- org.sonatype.gshell:gshell-io:jar:2.0:compile
    [INFO]          \- org.sonatype.gossip:gossip:jar:1.0:compile
    [INFO]             \- (org.slf4j:slf4j-api:jar:1.5.8:compile - omitted for conflict with 1.6.5)
    

    So, as we can see, everything works as expected, and conflicting dependency is actually get excluded. But the thing is that even with dependency excluded I still get following message while compiling and calling mvn exec:java:

    SLF4J: Class path contains multiple SLF4J bindings.
    SLF4J: Found binding in [jar:file:/home/shabunc/.m2/repository/ch/qos/logback/logback-classic/1.0.6/logback-classic-1.0.6.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: Found binding in [jar:file:/home/shabunc/.m2/repository/org/sonatype/gossip/gossip/1.0/gossip-1.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    

    The question is: Why I still see this warning and what exactly should be done to make only one version of slf4j reachable during execution?

  • shabunc
    shabunc almost 12 years
    can you, please, be more specific, I still don't get how setting classpathScope will help resolve this very problem.
  • Byter
    Byter almost 12 years
    The issue is caused due to transitive dependency of gossip jar used. The above code will exclude the dependency of sl4j implementation from gossip.
  • Tom Anderson
    Tom Anderson almost 12 years
    "Something like this" here means "the Maven equivalent of this Gradle code"!
  • Byter
    Byter almost 12 years
    @TomAnderson ya...sorry i didnt mention that :)
  • shabunc
    shabunc almost 12 years
    @Byter can you, please, provide a maven equivalent, since I still don't get what do you exactly mean. If you mean I should add this exclusions as well, this won't work - as I've already tell in the question, dependency:tree already does not consider gossip/slf4j dependency to be a part of the project.
  • shabunc
    shabunc almost 12 years
    so, you mean I can exclude gossip dependency for free, without any dramatic consequences? ))) Well, and if there are even more dependencies which, in turn, depend on slf4j, it will quickly become, well, a nightmare (((
  • Tom Anderson
    Tom Anderson almost 12 years
    The only way to make SLF4J happy is to either remove your dependency on Logback, or exclude gshell-io's dependency on Gossip. However, i can't promise that this won't have dramatic consequences. I would hope it doesn't - it would be very poor for a library to have a direct dependency on a logging implementation.
  • Byter
    Byter almost 12 years
    The issue is because of 2 different providers... gossip jar has a slf4j implementation within it.. so you either should remove logback or gossip from your dependency tree..
  • Tom Anderson
    Tom Anderson almost 12 years
    The way you can check if gshell-io has a real Gossip dependency is to download the jar (or the source), and search for references to types from Gossip. If there are none, you are safe. If there are some, you might still be safe it the code was carefully written.
  • Tom Anderson
    Tom Anderson almost 12 years
    However, i have just had a look, and it seems that it was not carefully written. There are direct dependencies - see Closer.log, StreamJack.log and Flusher.log. This is really bad. Whoever wrote that code should go to jail.
  • shabunc
    shabunc almost 12 years
    @TomAnderson, I guess this is the answer, thank you very much. Yet I have one last question. You've said that "it would be very poor for a library to have direct dependency on a logging". Does this mean that it's almost always to wrap logging dependency, thus creating transitive dependency?
  • Tom Anderson
    Tom Anderson almost 12 years
    @shabunc: What i mean is that the code can have a direct dependency on a logging API (in this case, SLF4J), but should not depend on a specific implementation. That way, it's up to the user of the code (in this case, you) to choose the right implementation. It's even okay for a library to specify a runtime dependency on a particular implementation, to supply a default, but it should tolerate that dependency being excluded and replaced. It's like when you use JDBC - you depend on the JDBC API, but not on classes from a particular driver.
  • shabunc
    shabunc almost 12 years
    @TomAnderson, thanks again. It's a pity I can not upvote twice :)
  • Tom Anderson
    Tom Anderson almost 12 years
    PS to Jason Dillon in case he ever reads this comment thread: i don't actually think you should go to jail. That's just a figure of speech hyperbolically expressing the opinion that improvements could be made to the code in question. I think community service or a fine would be sufficient.