I am shipping an application that uses OpenCV as a single executable jar. To avoid the end user needing to download OpenCV on their own system, the OpenCV binaries are stored inside the jar, and extracted to a temp file, and loaded using System.load() with the tempfile path, per this stackoverflow post suggestion, as follows:
public class NativeLibraryLoader {
private static Logger logger = LogManager.getLogger();
/**
* This is a safe way to load a native library from a jar, assuming that library
* is inside your resource director/classpath.
*/
public static void loadLibraryFromJar(String libraryName) throws IOException {
logger.debug("Loading native library %s".formatted(libraryName));
InputStream is = NativeLibraryLoader.class.getResourceAsStream("/" + libraryName);
File library = File.createTempFile("jni-temp-file", "tmp");
library.deleteOnExit();
try (OutputStream os = new FileOutputStream(library)) {
logger.debug("Copying library input stream to temp file " + library.getPath());
os.write(is.readAllBytes());
}
logger.debug("Loading library " + library.getPath());
System.load(library.getPath().replace("\\", "/"));
}
}
This method works just fine on linux, when I am loading opencv_490.so. The library loads, and I am able to use OpenCV. However when running the same command on Windows using opencv_490.dll, it cannot find the library and I get an UnsatisfiedLink. The loadLibraryFromJar method throws the error at the call to System.load(), and produces the following error during the unit test:
[ERROR] Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.188 s <<< FAILURE! - in com.toughdata.autogrowth.TestOpenCVCaptchaProcessor
[ERROR] testSolveRotateCaptchaIsKnownValue Time elapsed: 0.187 s <<< ERROR!
java.lang.UnsatisfiedLinkError: C:\Users\greg\AppData\Local\Temp\jni-temp-file78576686543799615tmp: Can't find dependent libraries
at com.toughdata.autogrowth.TestOpenCVCaptchaProcessor.testSolveRotateCaptchaIsKnownValue(TestOpenCVCaptchaProcessor.java:16)
[ERROR] testSolvePuzzleCaptchaIsKnownValue Time elapsed: 0 s <<< ERROR!
java.lang.NoClassDefFoundError: Could not initialize class com.toughdata.autogrowth.automation.captcha.OpenCVCaptchaProcessor
at com.toughdata.autogrowth.TestOpenCVCaptchaProcessor.testSolvePuzzleCaptchaIsKnownValue(TestOpenCVCaptchaProcessor.java:24)
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.UnsatisfiedLinkError: C:\Users\greg\AppData\Local\Temp\jni-temp-file78576686543799615tmp: Can't find dependent libraries [in thread "main"]
at com.toughdata.autogrowth.TestOpenCVCaptchaProcessor.testSolveRotateCaptchaIsKnownValue(TestOpenCVCaptchaProcessor.java:16)
[INFO]
[INFO] Results:
[INFO]
[ERROR] Errors:
[ERROR] TestOpenCVCaptchaProcessor.testSolvePuzzleCaptchaIsKnownValue:24 NoClassDefFound
[ERROR] TestOpenCVCaptchaProcessor.testSolveRotateCaptchaIsKnownValue:16 » UnsatisfiedLink
[INFO]
[ERROR] Tests run: 19, Failures: 0, Errors: 2, Skipped: 8
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13.035 s
[INFO] Finished at: 2024-03-11T09:04:46+11:00
[INFO] ------------------------------------------------------------------------
I can see that the tempfile is created, and that the library is extracted and written to the tempfile. Does anyone know what could cause the call to System.load() to fail on windows when it works fine on Linux?
Alright, I was able to figure this out. I went out on a limb and changed the
tmpextension to.dllwhen creating the temp file. It turns out that Windows expects the libraries to end with the.dllfile extension. Linux does not care whether your libraries end with.soor not, but on windows they must end with.dllThe correct code for using System.load() to laod a dll from a jar on windows is as follows: