[轉載] sbt發(fā)布assembly解決jar包沖突問題 deduplicate: different file contents found in the following

原文地址:http://blog.csdn.net/oopsoom/article/details/41318599

一、問題定義

最近在用sbt打assembly包時出現(xiàn)問題事扭,在package的時候,發(fā)生jar包沖突/文件沖突問題乐横,兩個相同的class來自不同的jar包在classpath內引起沖突求橄。

具體是:我有一個self4j的jar, 還有一個hadoop-common-hdfs的jar包葡公,其中hadoop-common-hdfs.jar內包含了self4j這個jar包谈撒,導致沖突。
此類異常一般是由于打包不規(guī)范和打包疏忽引起的匾南。
(個人認為正確的打包策略是:只打包自己核心功能,不將依賴打包在一起蛔外,但是有時為了方便或者有時不得不打在一起蛆楞,要注意可能會出現(xiàn)上述問題)

異常log如下:

[trace] Stack trace suppressed: run last *:assembly for the full output.  
[error] (*:assembly) deduplicate: different file contents found in the following:  
[error] C:\Users\shengli.victor\.ivy2\cache\org.slf4j\slf4j-api\jars\slf4j-api-1.7.7.jar:org/slf4j/IMarkerFactory.class  
[error] C:\Users\shengli.victor\.ivy2\cache\com.xxx.xx.hdfsfile\hdfscommon\jars\hdfscommon-1.1.jar:org/slf4j/IMarkerFactory.class  
[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. 但是真正打包提交到集群上執(zhí)行权她,則不需要將它打入jar包內虹茶。
這是我們使用 % "provided" 關鍵字來exclude它。

libraryDependencies ++= Seq(  
  "org.apache.spark" %% "spark-core" % "0.8.0-incubating" % "provided",  
  "org.apache.hadoop" % "hadoop-client" % "2.0.0-cdh4.4.0" % "provided"  
)  

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 進行定義靠娱。
如下:

mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>  
  {  
    case PathList("org", "slf4j", xs @ _*)         => MergeStrategy.first  
    case PathList(ps @ _*) if ps.last endsWith "axiom.xml" => MergeStrategy.filterDistinctLines  
    case PathList(ps @ _*) if ps.last endsWith "Log$Logger.class" => MergeStrategy.first  
    case PathList(ps @ _*) if ps.last endsWith "ILoggerFactory.class" => MergeStrategy.first  
    case x => old(x)  
  }  
}  

解決方法:將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:

[warn] Merging 'META-INF\INDEX.LIST' with strategy 'discard'  
[warn] Merging 'META-INF\MANIFEST.MF' with strategy 'discard'  
[warn] Merging 'META-INF\maven\log4j\log4j\pom.properties' with strategy 'first'  
[warn] Merging 'META-INF\maven\log4j\log4j\pom.xml' with strategy 'first'  
[warn] Merging 'META-INF\maven\org.slf4j\slf4j-api\pom.properties' with strategy 'first'  
[warn] Merging 'META-INF\maven\org.slf4j\slf4j-api\pom.xml' with strategy 'first'  
[warn] Merging 'META-INF\maven\org.slf4j\slf4j-log4j12\pom.properties' with strategy 'first'  
[warn] Merging 'META-INF\maven\org.slf4j\slf4j-log4j12\pom.xml' with strategy 'first'  
[warn] Merging 'com\esotericsoftware\minlog\Log$Logger.class' with strategy 'first'  
[warn] Merging 'com\esotericsoftware\minlog\Log.class' with strategy 'first'  
[warn] Merging 'org\apache\log4j\helpers\LogLog.class' with strategy 'first'  
[warn] Merging 'org\slf4j\ILoggerFactory.class' with strategy 'first'  
[warn] Merging 'org\slf4j\IMarkerFactory.class' with strategy 'first'  
[warn] Merging 'org\slf4j\Logger.class' with strategy 'first'  
[warn] Merging 'org\slf4j\LoggerFactory.class' with strategy 'first'  
[warn] Merging 'org\slf4j\MDC.class' with strategy 'first'  
[warn] Merging 'org\slf4j\Marker.class' with strategy 'first'  
[warn] Merging 'org\slf4j\MarkerFactory.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\BasicMDCAdapter.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\BasicMarker.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\BasicMarkerFactory.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\FormattingTuple.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\MarkerIgnoringBase.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\MessageFormatter.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\NOPLogger.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\NOPLoggerFactory.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\NOPMDCAdapter.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\NamedLoggerBase.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\SubstituteLoggerFactory.class' with strategy 'first'  
[warn] Merging 'org\slf4j\helpers\Util.class' with strategy 'first'  
[warn] Merging 'org\slf4j\impl\Log4jLoggerAdapter.class' with strategy 'first'  
[warn] Merging 'org\slf4j\impl\Log4jLoggerFactory.class' with strategy 'first'  
[warn] Merging 'org\slf4j\impl\Log4jMDCAdapter.class' with strategy 'first'  
[warn] Merging 'org\slf4j\impl\StaticLoggerBinder.class' with strategy 'first'  
[warn] Merging 'org\slf4j\impl\StaticMDCBinder.class' with strategy 'first'  
[warn] Merging 'org\slf4j\impl\StaticMarkerBinder.class' with strategy 'first'  
[warn] Merging 'org\slf4j\spi\LocationAwareLogger.class' with strategy 'first'  
[warn] Merging 'org\slf4j\spi\LoggerFactoryBinder.class' with strategy 'first'  
[warn] Merging 'org\slf4j\spi\MDCAdapter.class' with strategy 'first'  
[warn] Merging 'org\slf4j\spi\MarkerFactoryBinder.class' with strategy 'first'  
[warn] Merging 'rootdoc.txt' with strategy 'concat'  
[warn] Strategy 'concat' was applied to a file  
[info] Strategy 'deduplicate' was applied to 373 files (Run the task at debug level to see details)  
[warn] Strategy 'discard' was applied to 2 files  
[warn] Strategy 'first' was applied to 38 files  
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
[info] Done packaging.  
[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:
如果以上寫法都不奏效受啥,還有最好一種,強制默認全部合并,不到萬不得已滚局,不要用居暖。。

mergeStrategy in assembly <<= (mergeStrategy in assembly) { mergeStrategy => {  
 case entry => {  
   val strategy = mergeStrategy(entry)  
   if (strategy == MergeStrategy.deduplicate) MergeStrategy.first  
   else strategy  
 }  
}}  

三藤肢、總結

碰到類似的問題不要慌張太闺,仔細看log描述的是什么意思。
異常報出內容冗余的沖突嘁圈,在看路徑省骂,發(fā)現(xiàn)在classpath內有完全相同的2個類,這是導致問題的根本原因最住。
找出原因钞澳,解決方發(fā),消除沖突兩種方法温学,一直是去除法略贮,另一種是合并法。
相對于maven和gradle仗岖,sbt的沖突解決方法還是比較接近底層逃延。如果沒記錯的話,maven和gradle都能自動解決沖突轧拄。
本文僅針對該問題提出解決方案和思路揽祥,具體的各個配置還需要繼續(xù)研究。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末檩电,一起剝皮案震驚了整個濱河市拄丰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌俐末,老刑警劉巖料按,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卓箫,居然都是意外死亡载矿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門烹卒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闷盔,“玉大人,你說我怎么就攤上這事旅急》旯矗” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵藐吮,是天一觀的道長溺拱。 經(jīng)常有香客問我逃贝,道長,這世上最難降的妖魔是什么盟迟? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任秋泳,我火速辦了婚禮,結果婚禮上攒菠,老公的妹妹穿的比我還像新娘。我一直安慰自己歉闰,他們只是感情好辖众,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著和敬,像睡著了一般凹炸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上昼弟,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天啤它,我揣著相機與錄音,去河邊找鬼舱痘。 笑死变骡,一個胖子當著我的面吹牛,可吹牛的內容都是我干的芭逝。 我是一名探鬼主播塌碌,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼旬盯!你這毒婦竟也來了台妆?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤胖翰,失蹤者是張志新(化名)和其女友劉穎接剩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萨咳,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡懊缺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了某弦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桐汤。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖靶壮,靈堂內的尸體忽然破棺而出怔毛,到底是詐尸還是另有隱情,我是刑警寧澤腾降,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布拣度,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏抗果。R本人自食惡果不足惜筋帖,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冤馏。 院中可真熱鬧日麸,春花似錦、人聲如沸逮光。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涕刚。三九已至嗡综,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間杜漠,已是汗流浹背极景。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留驾茴,地道東北人盼樟。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像沟涨,于是被迫代替她去往敵國和親恤批。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內容