2017-06-11 33 views
1

我有一個sbt項目和一個java類靜態加載本地庫,幷包含本地方法。它看起來像這樣:如何正確加載本地庫進行sbt測試?

public class NativeContainer { 
    static { 
    System.load("/path-to-lib"); 
    } 

    public static native void nativeFunc(int n); 
} 

我也有一個Scala測試調用像這樣的原生功能:

class TestJni extends FunSpec { 
    describe("JNI test") { 
    NativeContainer.nativeFunc(5); 
    } 
} 

當我運行通過sbt測試一次,一切工作正常。然而,在每次下一次運行時,我都會得到:

[error] Could not run test intrinsics.TestJni: java.lang.UnsatisfiedLinkError: Native Library /path-to-lib already loaded in another classloader

什麼是加載庫以避免這種情況的正確方法?重新啓動sbt的作品,但我一直在尋找更靈活的解決方案。

我不使用任何庫或插件來粘合sbtJNI

這是一個完整的堆棧跟蹤:

[debug] Running TaskDef(TestJni, [email protected], false, [SuiteSelector]) java.lang.UnsatisfiedLinkError: Native Library /path-to-lib/libNativeContainer.dylib already loaded in another classloader 
     at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1907) 
     at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824) 
     at java.lang.Runtime.load0(Runtime.java:809) 
     at java.lang.System.load(System.java:1086) 
     at NativeContainer.<clinit>(NativeContainer.java:5) 
     at TestJni$$anonfun$1.apply$mcV$sp(TestJni.scala:16) 
     at org.scalatest.SuperEngine.registerNestedBranch(Engine.scala:613) 
     at org.scalatest.FunSpecLike$class.describe(FunSpecLike.scala:357) 
     at org.scalatest.FunSpec.describe(FunSpec.scala:1626) 
     at TestJni.<init>(TestJni.scala:7) 
     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 
     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
     at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 
     at java.lang.Class.newInstance(Class.java:442) 
     at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:646) 
     at sbt.TestRunner.runTest$1(TestFramework.scala:76) 
     at sbt.TestRunner.run(TestFramework.scala:85) 
     at sbt.TestFramework$$anon$2$$anonfun$$init$$1$$anonfun$apply$8.apply(TestFramework.scala:197) 
     at sbt.TestFramework$$anon$2$$anonfun$$init$$1$$anonfun$apply$8.apply(TestFramework.scala:197) 
     at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:185) 
     at sbt.TestFramework$$anon$2$$anonfun$$init$$1.apply(TestFramework.scala:197) 
     at sbt.TestFramework$$anon$2$$anonfun$$init$$1.apply(TestFramework.scala:197) 
     at sbt.TestFunction.apply(TestFramework.scala:202) 
     at sbt.Tests$.sbt$Tests$$processRunnable$1(Tests.scala:239) 
     at sbt.Tests$$anonfun$makeSerial$1.apply(Tests.scala:245) 
     at sbt.Tests$$anonfun$makeSerial$1.apply(Tests.scala:245) 
     at sbt.std.Transform$$anon$3$$anonfun$apply$2.apply(System.scala:44) 
     at sbt.std.Transform$$anon$3$$anonfun$apply$2.apply(System.scala:44) 
     at sbt.std.Transform$$anon$4.work(System.scala:63) 
     at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226) 
     at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226) 
     at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17) 
     at sbt.Execute.work(Execute.scala:235) 
     at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226) 
     at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226) 
     at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159) 
     at sbt.CompletionService$$anon$2.call(CompletionService.scala:28) 
     at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 
     at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
     at java.lang.Thread.run(Thread.java:745) [error] Could not run test TestJni: java.lang.UnsatisfiedLinkError: Native Library /path-to-lib/libNativeContainer.dylib already loaded in another classloader 
+0

任何堆棧跟蹤?還是僅僅是錯誤? –

+0

@JornVernee添加了完整的堆棧跟蹤。 –

+0

我不確定問題出在哪裏,但是如果每個JVM都能工作一次,那麼可以通過在sbt中設置'fork:= true'來解決這個問題,這樣每次運行測試都會分配一個新的JVM 。請參閱http://www.scala-sbt.org/0.13/docs/Forking.html –

回答

1

我猜它看起來像什麼的問題是,默認情況下,SBT運行在同一個JVM,但不同的類加載器的測試每一輪,但JNI庫只能連接一次,而不能在多個ClassLoaders中多次連接。

SBT有一個設置...

fork := true 

...導致要在一個新的JVM,而不是在SBT的JVM下一個新的ClassLoader運行測試。 (請參閱the docs。)在此設置下,每個JVM只會加載一次類(因爲它們可能在生產環境中),而不會非法嘗試通過不同的類加載器來將鏈接JNI庫相乘。這應該可以解決您的問題。