sbt发布assembly解决jar包冲突问题 deduplicate: different file contents found in the following

转载 2016年02月04日 16:52:57

一、问题定义

    最近在用sbt打assembly包时出现问题,在package的时候,发生jar包冲突/文件冲突问题,两个相同的class来自不同的jar包在classpath内引起冲突。

具体是:我有一个self4j的jar, 还有一个hadoop-common-hdfs的jar包,其中hadoop-common-hdfs.jar内包含了self4j这个jar包,导致冲突。

此类异常一般是由于打包不规范和打包疏忽引起的。

(个人认为正确的打包策略是:只打包自己核心功能,不将依赖打包在一起,但是有时为了方便或者有时不得不打在一起,要注意可能会出现上述问题)


异常log如下

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. [trace] Stack trace suppressed: run last *:assembly for the full output.  
  2. [error] (*:assembly) deduplicate: different file contents found in the following:  
  3. [error] C:\Users\shengli.victor\.ivy2\cache\org.slf4j\slf4j-api\jars\slf4j-api-1.7.7.jar:org/slf4j/IMarkerFactory.class  
  4. [error] C:\Users\shengli.victor\.ivy2\cache\com.xxx.xx.hdfsfile\hdfscommon\jars\hdfscommon-1.1.jar:org/slf4j/IMarkerFactory.class  
  5. [error] Total time: 4 s, completed 2014-11-20 19:07:33  

异常很明显,来自2个不同的jar包self4j,  hdfscommon-1.1.jar里,在org/slf4j/IMarkerFactory.class这个类冲突了。

如下图:

hdfscommon-1.1/jar



slf4j-api-1.7.2.jar



二、解决方案

解决jar包冲突有两种方案:

1、删除其中的某个jar,或者说,在打包的时候,不将2个相同的class打在同一个jar包内的classpath内,即exclude jar。

2、合并冲突


1. Excluding JARs and files

% "provided"

将相同的jar中排除一个,因为重复,可以使用"provided"关键字。

例如spark是一个容器类,编写spark应用程序我们需要spark core jar. 但是真正打包提交到集群上执行,则不需要将它打入jar包内。

这是我们使用 % "provided" 关键字来exclude它。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. libraryDependencies ++= Seq(  
  2.   "org.apache.spark" %% "spark-core" % "0.8.0-incubating" % "provided",  
  3.   "org.apache.hadoop" % "hadoop-client" % "2.0.0-cdh4.4.0" % "provided"  
  4. )  

Maven defines "provided" as:

This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scopeprovided because the web container provides those classes. This scope is only available on the compilation and test classpath, and is not transitive.

2、Merge Strategy

如果在相对路径下,有多个相同的文件或者jar,这时我们可以使用Merge策略。

在build.sbt中对assemblyMergeStrategy 进行定义。

如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>  
  2.   {  
  3.     case PathList("org""slf4j", xs @ _*)         => MergeStrategy.first  
  4.     case PathList(ps @ _*) if ps.last endsWith "axiom.xml" => MergeStrategy.filterDistinctLines  
  5.     case PathList(ps @ _*) if ps.last endsWith "Log$Logger.class" => MergeStrategy.first  
  6.     case PathList(ps @ _*) if ps.last endsWith "ILoggerFactory.class" => MergeStrategy.first  
  7.     case x => old(x)  
  8.   }  
  9. }  

解决方法:将org, slf4j 这个下的所有类和文件,都做合并, 采用的策略是:在classpath里,2选1,选的是classpath顺序里第一个self4j。

这里支持多种格式,例如ps.lat endsWith "axiom.xml" ,是以axiom.xml为结尾的文件,都采用filterDistinctLines策略,即合并两个文件,舍去重复的部分。


通过以上修改,终于解决了slf4j冲突的问题,即deduplicate: different file contents found in the following问题。


再次sbt assembly:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. [warn] Merging 'META-INF\INDEX.LIST' with strategy 'discard'  
  2. [warn] Merging 'META-INF\MANIFEST.MF' with strategy 'discard'  
  3. [warn] Merging 'META-INF\maven\log4j\log4j\pom.properties' with strategy 'first'  
  4. [warn] Merging 'META-INF\maven\log4j\log4j\pom.xml' with strategy 'first'  
  5. [warn] Merging 'META-INF\maven\org.slf4j\slf4j-api\pom.properties' with strategy 'first'  
  6. [warn] Merging 'META-INF\maven\org.slf4j\slf4j-api\pom.xml' with strategy 'first'  
  7. [warn] Merging 'META-INF\maven\org.slf4j\slf4j-log4j12\pom.properties' with strategy 'first'  
  8. [warn] Merging 'META-INF\maven\org.slf4j\slf4j-log4j12\pom.xml' with strategy 'first'  
  9. [warn] Merging 'com\esotericsoftware\minlog\Log$Logger.class' with strategy 'first'  
  10. [warn] Merging 'com\esotericsoftware\minlog\Log.class' with strategy 'first'  
  11. [warn] Merging 'org\apache\log4j\helpers\LogLog.class' with strategy 'first'  
  12. [warn] Merging 'org\slf4j\ILoggerFactory.class' with strategy 'first'  
  13. [warn] Merging 'org\slf4j\IMarkerFactory.class' with strategy 'first'  
  14. [warn] Merging 'org\slf4j\Logger.class' with strategy 'first'  
  15. [warn] Merging 'org\slf4j\LoggerFactory.class' with strategy 'first'  
  16. [warn] Merging 'org\slf4j\MDC.class' with strategy 'first'  
  17. [warn] Merging 'org\slf4j\Marker.class' with strategy 'first'  
  18. [warn] Merging 'org\slf4j\MarkerFactory.class' with strategy 'first'  
  19. [warn] Merging 'org\slf4j\helpers\BasicMDCAdapter.class' with strategy 'first'  
  20. [warn] Merging 'org\slf4j\helpers\BasicMarker.class' with strategy 'first'  
  21. [warn] Merging 'org\slf4j\helpers\BasicMarkerFactory.class' with strategy 'first'  
  22. [warn] Merging 'org\slf4j\helpers\FormattingTuple.class' with strategy 'first'  
  23. [warn] Merging 'org\slf4j\helpers\MarkerIgnoringBase.class' with strategy 'first'  
  24. [warn] Merging 'org\slf4j\helpers\MessageFormatter.class' with strategy 'first'  
  25. [warn] Merging 'org\slf4j\helpers\NOPLogger.class' with strategy 'first'  
  26. [warn] Merging 'org\slf4j\helpers\NOPLoggerFactory.class' with strategy 'first'  
  27. [warn] Merging 'org\slf4j\helpers\NOPMDCAdapter.class' with strategy 'first'  
  28. [warn] Merging 'org\slf4j\helpers\NamedLoggerBase.class' with strategy 'first'  
  29. [warn] Merging 'org\slf4j\helpers\SubstituteLoggerFactory.class' with strategy 'first'  
  30. [warn] Merging 'org\slf4j\helpers\Util.class' with strategy 'first'  
  31. [warn] Merging 'org\slf4j\impl\Log4jLoggerAdapter.class' with strategy 'first'  
  32. [warn] Merging 'org\slf4j\impl\Log4jLoggerFactory.class' with strategy 'first'  
  33. [warn] Merging 'org\slf4j\impl\Log4jMDCAdapter.class' with strategy 'first'  
  34. [warn] Merging 'org\slf4j\impl\StaticLoggerBinder.class' with strategy 'first'  
  35. [warn] Merging 'org\slf4j\impl\StaticMDCBinder.class' with strategy 'first'  
  36. [warn] Merging 'org\slf4j\impl\StaticMarkerBinder.class' with strategy 'first'  
  37. [warn] Merging 'org\slf4j\spi\LocationAwareLogger.class' with strategy 'first'  
  38. [warn] Merging 'org\slf4j\spi\LoggerFactoryBinder.class' with strategy 'first'  
  39. [warn] Merging 'org\slf4j\spi\MDCAdapter.class' with strategy 'first'  
  40. [warn] Merging 'org\slf4j\spi\MarkerFactoryBinder.class' with strategy 'first'  
  41. [warn] Merging 'rootdoc.txt' with strategy 'concat'  
  42. [warn] Strategy 'concat' was applied to a file  
  43. [info] Strategy 'deduplicate' was applied to 373 files (Run the task at debug level to see details)  
  44. [warn] Strategy 'discard' was applied to 2 files  
  45. [warn] Strategy 'first' was applied to 38 files  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. [info] Done packaging.  
  2. [success] Total time: 84 s, completed 2014-11-20 19:04:52  

合并策略有很多种:

可以参考官方sbt assembly文档:https://github.com/sbt/sbt-assembly

http://stackoverflow.com/questions/19606243/resolving-dependencies-in-creating-jar-through-sbt-assembly

  • MergeStrategy.deduplicate is the default described above
  • MergeStrategy.first picks the first of the matching files in classpath order
  • MergeStrategy.last picks the last one
  • MergeStrategy.singleOrError bails out with an error message on conflict
  • MergeStrategy.concat simply concatenates all matching files and includes the result
  • MergeStrategy.filterDistinctLines also concatenates, but leaves out duplicates along the way
  • MergeStrategy.rename renames the files originating from jar files
  • MergeStrategy.discard simply discards matching files

更多的写法,example:

assemblyMergeStrategy in assembly := {
  case PathList("javax", "servlet", xs @ _*)         => MergeStrategy.first
  case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first
  case "application.conf"                            => MergeStrategy.concat
  case "unwanted.txt"                                => MergeStrategy.discard
  case x =>
    val oldStrategy = (assemblyMergeStrategy in assembly).value
    oldStrategy(x)
}

Final Qucik Hack:

如果以上写法都不奏效,还有最好一种,强制默认全部合并,不到万不得已,不要用。。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. mergeStrategy in assembly <<= (mergeStrategy in assembly) { mergeStrategy => {  
  2.  case entry => {  
  3.    val strategy = mergeStrategy(entry)  
  4.    if (strategy == MergeStrategy.deduplicate) MergeStrategy.first  
  5.    else strategy  
  6.  }  
  7. }}  

三、总结

  碰到类似的问题不要慌张,仔细看log描述的是什么意思。

  异常报出内容冗余的冲突,在看路径,发现在classpath内有完全相同的2个类,这是导致问题的根本原因。

  找出原因,解决方发,消除冲突两种方法,一直是去除法,另一种是合并法。

  相对于maven和gradle,sbt的冲突解决方法还是比较接近底层。如果没记错的话,maven和gradle都能自动解决冲突。

  本文仅针对该问题提出解决方案和思路,具体的各个配置还需要继续研究。

——EOF——

原创文章,转载请注明出自:http://blog.csdn.net/oopsoom/article/details/41318599

sbt公布assembly解决jar包冲突 deduplicate: different file contents found in the following

sbt assembly     近期使用sbt战斗assembly发生故障时,包,在package什么时候,发生jar包冲突/文件冲突,两个相同class来自不同jar包classpath内心...

sbt-assembly 发布 Scala 项目

sbt-assembly 是一个非常实用的sbt插件,可以将当前项目的二进制包以及依赖的所有第三方库都打包成一个jar包发布,即one-jar, 对于那种直接运行的应用程序很方便。 sbt版本:0....

使用sbt assembly构建Spark项目

sbt-assembly是一个sbt插件,作用类似于Maven,用于创建一个包含依赖的JAR包场景:我在用sbt构建spark项目,但是在用sbt package打包生成jar包时,这个jar包并不没...

sbt打jar包总结

利用sbt管理依赖,打jar包以及fat jar

Java中对象的深复制(深克隆)和浅复制(浅克隆)介绍

1.浅复制与深复制概念  ⑴浅复制(浅克隆)      被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不...

spark-2.0.0提交jar任务的几种方式

//(集群模式)限制资源,后台执行 spark-submit --class test.Streamings --master spark://10.102.34.248:7077 --deploy-...

用curl 代替file_get_contents 解决获取网络资源的超时问题

初学php的朋友们,很容易翻一个错误,在写采集程序或者调用api接口总会有线考虑到使用file_get_contents函数来或许内容,程序的访问量不大倒是没什么影响,但是访问量提升了那非常的悲剧了,...

jstl Jar包 以及版本冲突问题解决

  • 2013年11月21日 17:51
  • 1.06MB
  • 下载

53.You set the following parameters in the parameter file and restarted the database: MEMORY_MAX_TAR

53.You set the following parameters in the parameter file and restarted the database: MEMORY_MAX_TAR...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:sbt发布assembly解决jar包冲突问题 deduplicate: different file contents found in the following
举报原因:
原因补充:

(最多只允许输入30个字)