Mar 20, 2015

Storm slf4j multiple binding problem

We are using Storm 0.9.3 and when we try to start supervisor node we found it reported the error below:


Clearly this is a slf4j multiple binding problem. Basically this famous library is used in different jars, when there are multiple slf4j find in the path only one will be picked up so that some jars called in a different version of slf4j will cause these compatibility problems. 

For the log above it clear states that there are 3 slf4j bindings under lib
  • slf4j-jdk14-1.5.6.jar
  • slf4j-nop-1.5.3.jar
  • logback-classic-1.0.13.jar

And it needs 1.6 above as it said

SLF4J: slf4j-api 1.6.x (or later) is incompatible with this binding.
SLF4J: Your binding is version 1.5.5 or earlier.
SLF4J: Upgrade your binding to version 1.6.x.

Storm is compiled and tested, why it will have multiple slf4j* jars? The actual result is Storm vanilla package is good, but we add some jars to its lib folder:


So that when we package our Storm jar, we don't need each time to include all the packages it required, this is to save time: we develop on windows and upload developed package jar into cluster, if we can have a smaller generated jar file we save our time. 
Let's not judge whether this method is good or not, but the problem here is to solve the binding problem.

The solution is simple: 
From eclipse dependency 

Let's remove these two jars which are  slf4j 1.5.*
  • slf4j-jdk14-1.5.6.jar
  • slf4j-nop-1.5.3.jar

It can successfully launch now.

Then here comes another question: when we package Storm application, we want solve the slf4j multiple binding problem?

There is a nice pom.xml for you to reference (partial of pom.xml), this is what I searched and copied from internet, honor and thanks go to the original author.



 org.codehaus.gmaven
 gmaven-plugin
 1.5
 
  
   package
   
    execute
   
   
    
    File targetDir = new
    File("${project.basedir.path}/target".toString())
    println "dir is ${targetDir.path}"
    String jarBaseName = "${project.artifactId}-${project.version}"
    File jarWithUnwantedStuff = new File(targetDir,
    "${jarBaseName}-jar-with-dependencies.jar".toString())

    def explodedJarDir = new File(targetDir, "explodedJar".toString())
    def ant = new AntBuilder() // create an antbuilder
    ant.unzip(src: "${jarWithUnwantedStuff.path}",
    dest: explodedJarDir.path,
    overwrite: "false")
    File finalJar = new File(targetDir, "${jarBaseName}-deployable.jar")
    unwantedClassesDir = new File(explodedJarDir,
    "/org/slf4j/impl".toString())
    unwantedClassesDir.deleteDir()
    ant.zip(basedir: explodedJarDir.path, destFile: finalJar.path)
   
  
 


If you find this blog is useful, please kindly click the ads on this page to help. Thank you very much.

No comments: