fail to load a native library using activator (Pla

2019-02-16 02:44发布

问题:

I'm trying to load a native library in my Play 2.4.x application. I have written a simple test that works fine both in the IDE (IntelliJ) and in SBT. In both case I'm setting the java.library.path to get the tests to run.

In the IDE, I set -Djava.library.path=$USER_HOME$/dev/lindoapi/bin/linux64 in the test run configuration.

As per the sbt documentation, my build.sbt is forking the JVM and setting the java.library.path.

javaOptions += "-Djava.library.path=/home/aczerwon/dev/lindoapi/bin/linux64"

fork := true

The following test passes just fine in both the IDE and from activator test.

class LindoApiSpec extends Specification {

  System.loadLibrary("lindojni")

  "The Lindo API" should {

    "have a valid license" in {
      val lindo = new LindoEnvironment()
      lindo.apiVerion() must beSuccessfulTry.withValue("LINDO API Version 9.0.2120.225")
    }

}

When outside of the testing context, I load the native library in Play's startup lifecycle.

object Global extends GlobalSettings {

  override def beforeStart(app: Application) = {
    System.loadLibrary("lindojni")
  }

}

When I call that same method from the webapi (activator ~run), I'm getting an UnsatisfiedLinkError error.

1) Error injecting constructor, java.lang.UnsatisfiedLinkError: no lindojni in java.library.path
  at play.api.GlobalPlugin.<init>(GlobalSettings.scala:262)
  at play.api.GlobalPlugin.class(GlobalSettings.scala:262)
  while locating play.api.GlobalPlugin

The web api looks like this:

class OptimizationApi extends Controller {

  def version() = Action {
    val lindo = new LindoEnvironment()
    lindo.apiVerion() match {
      case Success(version) => Ok(version)
      case Failure(e) => BadRequest(e.getMessage)
    }
  }

}

I assumed that my build.sbt would fork the JVM and set the java.library.path for both test and run contexts. Any clues as to what I'm doing wrong?

New Information

When I start activator -Djava.library.path=$USER_HOME$/dev/lindoapi/bin/linux64 or set JAVA_OPTS, the call to System.loadLibrary(...) in the startup lifecycle passes. I still get the UnsatisfiedLinkError, but it happens later when I make a call to the native library via JNI. Very strange.

回答1:

I found a solution to the issue here.

The native library and its java counterpart must be in the same class loader.

Create a class similar to:

public final class PlayNativeLibraryLoader {
    public static void load(String libraryPath) {
        System.load(libraryPath);
    }
}

And now you can use it in the Play startup lifecycle.

object Global extends GlobalSettings {

  override def beforeStart(app: Application) = {
    PlayNativeLibraryLoader.load(app.getFile("./lib/lindoapi/linux64/liblindojni.so").getPath)
    Logger.info("Lindo native library loaded")
  }

}