I have a project setup that follows the Standard Directory Layout (not using Maven though):
src/main
| java
| resources
| library.dll
<
I have done something very similar in JNativeHook, you will need some helper code to determine the correct arch and os to load code for (See NativeSystem Class)
// The following code covered under the GNU Lesser General Public License v3.
static {
String libName = System.getProperty("jnativehook.lib.name", "JNativeHook");
try {
// Try to load the native library assuming the java.library.path was
// set correctly at launch.
System.loadLibrary(libName);
}
catch (UnsatisfiedLinkError linkError) {
// Get the package name for the GlobalScreen.
String basePackage = GlobalScreen.class.getPackage().getName().replace('.', '/');
// Compile the resource path for the native lib.
StringBuilder libResourcePath = new StringBuilder("/");
libResourcePath.append(basePackage).append("/lib/");
libResourcePath.append(NativeSystem.getFamily()).append('/');
libResourcePath.append(NativeSystem.getArchitecture()).append('/');
// Get what the system "thinks" the library name should be.
String libNativeName = System.mapLibraryName(libName);
// Hack for OS X JRE 1.6 and earlier.
libNativeName = libNativeName.replaceAll("\\.jnilib$", "\\.dylib");
// Slice up the library name.
int i = libNativeName.lastIndexOf('.');
String libNativePrefix = libNativeName.substring(0, i) + '-';
String libNativeSuffix = libNativeName.substring(i);
String libNativeVersion = null;
// This may return null in some circumstances.
InputStream libInputStream = GlobalScreen.class.getResourceAsStream(libResourcePath.toString().toLowerCase(Locale.English) + libNativeName);
if (libInputStream != null) {
try {
// Try and load the Jar manifest as a resource stream.
URL jarFile = GlobalScreen.class.getProtectionDomain().getCodeSource().getLocation();
JarInputStream jarInputStream = new JarInputStream(jarFile.openStream());
// Try and extract a version string from the Manifest.
Manifest manifest = jarInputStream.getManifest();
if (manifest != null) {
Attributes attributes = manifest.getAttributes(basePackage);
if (attributes != null) {
String version = attributes.getValue("Specification-Version");
String revision = attributes.getValue("Implementation-Version");
libNativeVersion = version + '.' + revision;
}
else {
Logger.getLogger(GlobalScreen.class.getPackage().getName()).warning("Invalid library manifest!\n");
}
}
else {
Logger.getLogger(GlobalScreen.class.getPackage().getName()).warning("Cannot find library manifest!\n");
}
}
catch (IOException e) {
Logger.getLogger(GlobalScreen.class.getPackage().getName()).severe(e.getMessage());
}
try {
// The temp file for this instance of the library.
File libFile;
// If we were unable to extract a library version from the manifest.
if (libNativeVersion != null) {
libFile = new File(System.getProperty("java.io.tmpdir"), libNativePrefix + libNativeVersion + libNativeSuffix);
}
else {
libFile = File.createTempFile(libNativePrefix, libNativeSuffix);
}
byte[] buffer = new byte[4 * 1024];
int size;
// Check and see if a copy of the native lib already exists.
FileOutputStream libOutputStream = new FileOutputStream(libFile);
// Setup a digest...
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
DigestInputStream digestInputStream = new DigestInputStream(libInputStream, sha1);
// Read from the digest stream and write to the file steam.
while ((size = digestInputStream.read(buffer)) != -1) {
libOutputStream.write(buffer, 0, size);
}
// Close all the streams.
digestInputStream.close();
libInputStream.close();
libOutputStream.close();
// Convert the digest from byte[] to hex string.
String sha1Sum = new BigInteger(1, sha1.digest()).toString(16).toUpperCase();
if (libNativeVersion == null) {
// Use the sha1 sum as a version finger print.
libNativeVersion = sha1Sum;
// Better late than never.
File newFile = new File(System.getProperty("java.io.tmpdir"), libNativePrefix + libNativeVersion + libNativeSuffix);
if (libFile.renameTo(newFile)) {
libFile = newFile;
}
}
// Set the library version property.
System.setProperty("jnativehook.lib.version", libNativeVersion);
// Load the native library.
System.load(libFile.getPath());
Logger.getLogger(GlobalScreen.class.getPackage().getName())
.info("Library extracted successfully: " + libFile.getPath() + " (0x" + sha1Sum + ").\n");
}
catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
else {
Logger.getLogger(GlobalScreen.class.getPackage().getName())
.severe("Unable to extract the native library " + libResourcePath.toString().toLowerCase(Locale.English) + libNativeName + "!\n");
throw new UnsatisfiedLinkError();
}
}
}
There is an old-time hack that still works as of today ( 1.7.0_55 & 1.8.0_05 ) to allow you to do to a runtime update using System.setProperty() and have the JVM notice the change. In general, you do the following:
System.setProperty("java.library.path", yourPath);
Field sysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
sysPath.setAccessible( true );
sysPath.set( null, null );
System.loadLibrary(libraryName);
Google java sys_paths and look for articles about this technique.
Take care to handle errors/exceptions. Restore original paths as needed.
I wanted to offer an alternative solution, because @Java42's answer is not portable (JVM dependant) and doesn't work starting from Oracle's/OpenJDK's Java 12.
You should use your own custom ClassLoader
implementation. In the ClassLoader
there's a method findLibary(String libname)
. This method returns the full path to the library to load. This can be used to load libraries in arbitrary places.
public class MyClassLoader extends ClassLoader {
@Override
protected String findLibrary(String libname) {
return "/actual/path/to/library.so";
// or return null if unknown, then the path will be searched
}
}
Next step is to have you custom ClassLoader
used by the JVM. So set it very soon in your code as the current threads contextClassLoader
:
public static void main(String[] args) {
Thread.currentThread().setContextClassLoader(new StarterClassLoader());
// ... your code
}