如何正确加载本地库以进行sbt测试? - java

我有一个sbt项目和一个java类,它们静态加载本机库并包含本机方法。看起来像这样:

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运行测试时,一切正常。但是,在每次下一次运行时,我都会得到:

[错误]无法运行测试内在函数。
java.lang.UnsatisfiedLinkError:本机库/ path-to-lib已经
在另一个类加载器中加载

避免这种情况的加载库的正确方法是什么?重新启动sbt可行,但我一直在寻找更灵活的解决方案。

我不使用任何库或插件将sbtJNI粘合在一起。

这是完整的堆栈跟踪:

[debug] Running TaskDef(TestJni, org.scalatest.tools.Framework$$anon$1@1d29c60d, 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

java大神给出的解决方案

我猜似乎是问题所在,默认情况下,sbt在同一JVM但在不同的ClassLoader中运行每一轮测试,但是JNI库只能链接一次,而不能在多个ClassLoader中链接多次。

sbt有设置...

fork := true

...这导致测试在新的JVM上而不是在新的ClassLoader下的sbt的JVM中运行。 (请参阅the docs。)在此设置下,每个JVM仅将您的类加载一次(就像在生产场景中那样),而不会通过非法尝试通过不同的ClassLoader来增加链接JNI库。这样可以解决您的问题。

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

Java-如何将此字符串转换为日期? - java

我从服务器收到此消息,我不明白T和Z的含义,2012-08-24T09:59:59Z将此字符串转换为Date对象的正确SimpleDateFormat模式是什么? java大神给出的解决方案 这是ISO 8601标准。您可以使用SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM…

如何使用BorderLayout(Java)扩展JTextField - java

我有一个Java程序,其中使用的是JTextField,但如果我未指定默认大小,则其宽度为0。我将其插入BorderLayout中,因此如何制作它展开以填充整个容器? java大神给出的解决方案 在上面的示例中,文本字段将正常工作。但是,如果您插入EAST或WEST,则将不起作用。import java.awt.BorderLayout; import ja…

从较小的字节数组(Java)进行长时间转换 - java

我正在尝试将字节数组转换为long,但是接收到BufferUnderflowException ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); byte[] arg1 = new byte[] {0x04, (byte)0xB0}; buffer.put(arg1, 0, arg1.length); …

java.net.URI.create异常 - java

java.net.URI.create("http://adserver.adtech.de/adlink|3.0") 抛出java.net.URISyntaxException: Illegal character in path at index 32: http://adserver.adtech.de/adlink|3.0 虽然n…