2017-06-29 55 views
1

SBT好像是用不同的類加載器,使一些測試時,在SBT會話中運行不止一次失敗,並出現以下錯誤:SBT使用多個類加載器

[info] java.lang.ClassCastException: net.i2p.crypto.eddsa.EdDSAPublicKey cannot be cast to net.i2p.crypto.eddsa.EdDSAPublicKey 
[info] at com.advancedtelematic.libtuf.crypt.EdcKeyPair$.generate(RsaKeyPair.scala:120) 

I tried equivalent code使用模式的匹配,而不是asInstanceOf和我得到相同的結果。

如何確保sbt在同一會話中對所有測試執行使用相同的類加載器?

+0

你能否在你的問題中包含重現此問題的代碼?爲什麼你認爲類加載器是問題? –

+0

我最終爲sbt寫了一個問題報告來嘗試和調試這個,請查看https://github.com/sbt/sbt/issues/3306我寫了一個小項目來重現這個問題 – simao

回答

1

我認爲它與此有關:Do security providers cause ClassLoader leaks in Java?。基本上Security是重新使用舊的類加載器的提供者。所以這可能發生在任何多類路徑環境(如OSGi)中,而不僅僅是SBT。

修復您build.sbt(無分叉):

testOptions in Test += Tests.Cleanup(() => 
    java.security.Security.removeProvider("BC")) 

實驗:

sbt-classloader-issue$ sbt 
> test 
[success] Total time: 1 s, completed Jul 6, 2017 11:43:53 PM 
> test 
[success] Total time: 0 s, completed Jul 6, 2017 11:43:55 PM 

說明:

,我可以從你的代碼中看到(發表here):

Security.addProvider(new BouncyCastleProvider) 

您每次運行測試時都重複使用相同的BouncyCastleProvider提供程序,因爲您的Security.addProvider工程only first time。由於sbt爲每次「測試」運行創建了新的類加載器,但重新使用相同的JVM - Security是JVM-bootstrap加載的JVM範圍的單例,因此classOf[java.security.Security].getClassLoader() == null和sbt無法重新加載/重新初始化此類。

而且你可以方便地查看

classOf[org.bouncycastle.jce.spec.ECParameterSpec].getClassLoader() 
res30: ClassLoader = URLClassLoader with NativeCopyLoader with RawResources 

org.bouncycastle類加載自定義類加載(從SBT),它改變了你運行test的每次。

所以這個代碼:

val generator = KeyPairGenerator.getInstance("ECDSA", "BC") 

會從舊的類加載器(一個用於第一次「測試」運行)加載的類的實例,你想與新的類加載器的規格將其初始化:

generator.initialize(ecSpec) 

這就是爲什麼你會得到「參數對象不是ECParameterSpec」異常。 「net.i2p.crypto.eddsa.EdDSAPublicKey不能轉換爲net.i2p.crypto.eddsa.EdDSAPublicKey」的推理基本相同。