Z badań robiłem w przeszłości, istnieją tylko dwa sposoby, aby uzyskać natywne biblioteki załadowane: Modyfikacja java.library.path
i obsługiwanie System.loadLibrary
(czuję się jak większość ludzi to zrobić), lub stosując System.load
bezwzględną ścieżkę.
Jak wspomnieliście, mieszanie się z java.library.path
może być denerwujące pod względem konfiguracji SBT i Eclipse, i nie sądzę, że można to zrobić automatycznie dla pliku wykonywalnego.
Pozostawia to System.load
. Jeśli chodzi o pisanie własnych bibliotek natywnych, co można zrobić, to:
- Utwórz zadanie SBT że kompiluje swoje rodzime źródła (
javah
i gcc
), bierze otrzymany .so plików oraz wszelkie pliki .so to zależy on, umieszcza je w słoiku (jako zasoby) w katalogu docelowym i dodaje ścieżkę do słoika do unmanagedJars in Compile
.
- Utwórz metodę Scala, aby załadować bibliotekę. Zamiast wywoływania
System.loadLibrary
, będzie używał Class.getResourceAsStream
do odczytu biblioteki, File.createTempFile
, aby zapisać go gdzieś w systemie plików, i System.load
, aby załadować go do JVM.
- Teraz zamiast dzwonić pod numer
System.loadLibrary
, zadzwoń pod numer MyClasspathJniLoader.loadLibrary
.
To zadziała z SBT run, Eclipse i wykonywanymi słojami bez dodatkowej konfiguracji (chociaż nie wiem, w jaki sposób proguard wie, które zasoby uwzględnić).
Co do bibliotek natywnych stron trzecich, które zostały już napisane, niektóre z nich, takie jak jblas, już używają tego "grubego słoika". Jeśli oczekują, że skonfigurujesz java.library.path
, a następnie zadzwonią pod numer System.loadLibrary
, kiedy będą mieli na to ochotę, musisz zrobić trochę magii, aby wykonać tę pracę.
nie próbowałem tego, ale to rozwiązanie może działać:
- użyć podobnego zadania SBT umieścić wszystkich bibliotekach na ich rodzimej ścieżce do słoika jako zasobów, i umieścić ten słoik na clsaspath.
- Utwórz metodę Scala, która przyjmuje funkcję i listę nazw bibliotek, tworzy katalog tymczasowy, odbiera te biblioteki z zasobów słoika do plików w katalogu tymczasowym, dodaje katalog tymczasowy do
java.library.path
, wywołuje przekazaną funkcję, i na koniec cofa java.library.path
z powrotem do tego, co było wcześniej.
- Po pierwszym wywołaniu w bibliotece macierzystej (gdy prawdopodobnie będzie inicjować statycznie i wykonać wywołanie
System.loadLibrary
), należy zawinąć to konkretne wywołanie w swojej metodzie z listą bibliotek, które będzie ładować. W ten sposób, gdy zadzwoni System.loadLibrary
, wszystkie jego biblioteki będą na java.library.path
i zostaną załadowane pomyślnie.
Oczywiście jest to denerwujące, ponieważ trzeba ręcznie zainicjować bibliotekę stron trzecich przed jej użyciem, ale wydaje się bardziej możliwe zawijanie wszystkich punktów inicjalizacji (głównych funkcji i inicjalizacji testów), niż uzyskanie wszystkich narzędzi do prawidłowego ustawienia java.library.path
. I może być jeszcze łatwiejsze, jeśli masz już własną warstwę abstrakcji nad biblioteką osób trzecich, tak naprawdę jest tylko jeden punkt inicjalizacji, który musisz zawinąć.
Jeśli to wydaje się być realistycznym rozwiązaniem, mogę dodać więcej szczegółów na temat zadania SBT lub metod pakowania Scala, jeśli jesteś zdezorientowany.