2017-06-29 76 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”的推理基本相同。

相关问题