UnsatisfiedLinkError: как загрузить встроенную библиотеку opencv при запуске приложения play?

#scala #opencv #playframework #sbt

#scala #opencv #playframework #sbt

Вопрос:

Я запускаю приложение play Framework, используя opencv с scala. При запуске приложения я получаю UNSATISFIEDLINK Error из VideoCapture, так как же я могу правильно загрузить библиотеку opencv?

Когда я запускаю тест с использованием классов из opencv, все работает правильно. Я всегда устанавливаю System.LoadLibrary (Core.NATIVE_LIBRARY_NAME) и правильно определяю параметр java.path.library. Но когда я запускаю приложение play, это работает не так, как с тестами.

Судя по другим сообщениям, люди решали эту проблему, используя абстрактный класс GlobalSetting из play и загружая библиотеку методом BeforeStart(). Но это устарело, и теперь Play рекомендует выполнить обязательную привязку. Я уже пытался создать загрузчик, который будет запускаться первым при запуске сервера, на который я загружаю библиотеку таким же образом, как я запускаю тесты. Код в этом классе запускается первым, как и должно быть, но это не спасает мою проблему.

Я также пытался загрузить библиотеку в разные имеющиеся у меня контроллеры и класс, в котором я использую VideoCapture (), но ничего не работает. Более того, я попытался создать задачу sbt, в которой я загружал библиотеку, но не сработало.

 class EagerLoaderModule extends AbstractModule {
  override def configure(): Unit = bind(classOf[StartUpService]).asEagerSingleton()
}
  
 final class StartUpService {

  def loadedLibs: Seq[String] = {
    val libs = classOf[ClassLoader].getDeclaredField("loadedLibraryNames")
    libs.setAccessible(true)
    import scala.collection.JavaConverters._
    libs.get(ClassLoader.getSystemClassLoader)
      .asInstanceOf[java.util.Vector[String]]
      .asScala
  }

  def loadOpenCVOnDemand(): Unit = {
    val isLoaded = loadedLibs.map(str => str contains "opencv").reduce((x, y) => x || y)
    if(!isLoaded) {
      try {
        println("hey there dude")
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME)
        println("ksdfjsdkfh")
      } catch {
        case e: UnsatisfiedLinkError =>
          println("Loading Library OPENCV")
          System.loadLibrary("opencv_java345")
        //      e.printStackTrace()
      }
    }
  }

  loadOpenCVOnDemand()

}
  

Ошибка, которую я получаю, такова: java.lang.Ошибка неудовлетворенной ссылки: org.opencv.videoio.VideoCapture.VideoCapture_1(Ljava / lang / String;) J и я знаю, что это потому, что System.LoadLibrary (Core.NATIVE_LIBRARY_NAME) работает некорректно, но как я могу заставить это работать или где я должен это настроить, чтобы приложение правильно загружало библиотеку?

Ответ №1:

Поддержка встроенной библиотеки в режиме разработки (play run) # 2212 определяет проблему как:

Когда libopencv_java загружается из управляемого кода play-sbt-plugin, он загружается в ReloadableClassLoader, но opencv-*.jar уже находится в PlayDependencyClassLoader. Похоже, JDK не нравится такое разделение. Итак, встроенная библиотека и ее аналог java должны (must?) находиться в том же загрузчике классов. Одним из решений является удаление вызовов load / LoadLibrary из проекта на основе Play в отдельный jar, а затем добавление этого jar в качестве зависимости проекта.

Для обхода попробуйте импортировать play-native-loader

 libraryDependencies  = "com.typesafe.play" %% "play-native-loader" % "1.0.0"
  

и загрузите библиотеку с помощью NativeLoader.load вместо System.loadLibrary вот так

 NativeLoader.load(Core.NATIVE_LIBRARY_NAME)
  

play-opencv-native-example является рабочим примером.