Java library path at runtime

Java library path at runtime

There are several ways to make it possible for the Java runtime to find and load a dynamic library (DLL) at runtime. I will list them briefly here, followed by examples and further explanation below.

  1. Call System.load to load the DLL from an explicitly specified absolute path.
  2. Copy the DLL to one of the paths already listed in java.library.path
  3. Modify the PATH environment variable to include the directory where the DLL is located.
  4. Specify the java.library.path on the command line by using the -D option.
  5. If using Eclipse, set the java.library.path in Eclipse for development/debugging.

Note: To help resolve an UnsatisfiedLinkError Runtime Error, see How to Handle the UnsatisfiedLinkError Runtime Error in Java

1. Call System.load to load the DLL from an explicitly specified absolute path.

This choice removes all uncertainty, but embeds a hard-coded path within your Java application. Example:

import com.chilkatsoft.CkZip; public class Test < static < try < System.load("C:/chilkatJava/chilkat.dll"); > catch (UnsatisfiedLinkError e) < System.err.println("Native code library failed to load.\n" + e); System.exit(1); >> public static void main(String argv[]) < CkZip zip = new CkZip(); System.out.println(zip.version()); >>

2. Copy the DLL to one of the paths already listed in java.library.path

To see the current value of the PATH environment variable, open a MS-DOS prompt and type:

Another way of viewing the java.library.path is to run this Java code:

String property = System.getProperty(«java.library.path»); StringTokenizer parser = new StringTokenizer(property, «;»); while (parser.hasMoreTokens())

Note: The java.library.path is initialized from the PATH environment variable. The directories may be listed in a different order, and the current directory «.» should be present in java.library.path, but may not be listed in the PATH environment variable.

The loadLibrary method may be used when the directory containing the DLL is in java.library.path. To load «chilkat.dll», call System.loadLibrary(«chilkat»), as shown here:

import com.chilkatsoft.CkZip; public class Test < static < try < System.loadLibrary("chilkat"); > catch (UnsatisfiedLinkError e) < System.err.println("Native code library failed to load.\n" + e); System.exit(1); >> public static void main(String argv[]) < CkZip zip = new CkZip(); System.out.println(zip.version()); >>

3. Modify the PATH environment variable to include the directory where the DLL is located.

Do this by modifying the PATH environment variable from the Windows Control Panel.

  1. Start -> Control Panel -> System -> Advanced
  2. Click on Environment Variables, under System Variables, find PATH, and click on it.
  3. In the Edit windows, modify PATH by adding the location of the class to the value for PATH. If you do not have the item PATH, you may select to add a new variable and add PATH as the name and the location of the class as the value.
  4. Close the window.
  5. Reopen Command prompt window, and run your java code.
  1. Right click «My Computer» icon
  2. Choose «Properties» from context menu
  3. Click «Advanced» tab («Advanced system settings» link in Vista)
  4. In the Edit windows, modify PATH by adding the location of the class to the value for PATH. If you do not have the item PATH, you may select to add a new variable and add PATH as the name and the location of the class as the value.
  5. Reopen Command prompt window, and run your java code.
Читайте также:  Creating header with css

Important: Setting the PATH environment variable from a MS-DOS command prompt has no effect on java.library.path. For example, this does not work:

set PATH=c:\chilkatJava;%PATH% java Test

Also, modifying the java.library.path from within Java code does not work either:

static < try < // Adding a directory to java.library.path here will not change anything. // System.loadLibrary will still look in the directories listed in java.library.path // as it existed at the very start of the program. // The extra directory path added to java.library.path will not // be searched by loadLibrary. String libpath = System.getProperty("java.library.path"); libpath = libpath + ";C:/chilkatJava"; System.setProperty("java.library.path",libpath); System.loadLibrary("chilkat"); >catch (UnsatisfiedLinkError e) < System.err.println("Native code library failed to load.\n" + e); System.exit(1); >>

4. Specify the java.library.path on the command line by using the -D option.

java -Djava.library.path=c:\chilkatJava TestApp

5. If using Eclipse, set the java.library.path in Eclipse for development/debugging.

  1. Open Project->Properties, select «Java Build Path», click on the «Add External JARs. » button and add the «chilkat.jar»
  2. (still within the Project Properties dialog) Click on the «Run/Debug Settings», select your Java class, then click on the «Edit. » button. Select the «Arguments» tab, then add -Djava.library.path=»C:\chilkatJava;$» where «C:\chilkatJava» is the directory path containing the «chilkat.dll» file.

Privacy Statement. Copyright 2000-2022 Chilkat Software, Inc. All rights reserved.
(Regarding the usage of the Android logo) Portions of this page are reproduced from work created and shared by Google and used according to terms
described in the Creative Commons 3.0 Attribution License.

Send feedback to info@chilkatsoft.com
Software APIs, modules, components, and libraries for Windows, Linux, MacOS, iOS, Android™, Alpine Linux, Solaris, MinGW, .

Источник

Java library path at runtime

If you want to add some native libraries at runtime, it was tricky with Java 8+:

Читайте также:  From ppt to html

This was a valid solution:

final Field fldUsrPaths = ClassLoader . class . getDeclaredField ( «usr_paths» ) ;
fldUsrPaths. setAccessible ( true ) ;

//get array of paths
final String [ ] saPath = ( String [ ] ) fldUsrPaths. get ( null ) ;

//check if the path to add is already present
for ( String path : saPath )
{
if ( path. equals ( pPath ) )
{
return ;
}
}

//add the new path
final String [ ] saNewPaths = Arrays . copyOf ( saPath, saPath. length + 1 ) ;
saNewPaths [ saNewPaths. length — 1 ] = pPath ;
fldUsrPaths. set ( null , saNewPaths ) ;

Since Java 10+ it’s not possible anymore, because of: JDK-8210522
Some details from Stackoverflow

If someone still needs a solution for Java 10+, here it is:

Lookup cl = MethodHandles. privateLookupIn ( ClassLoader . class , MethodHandles. lookup ( ) ) ;
VarHandle usr_paths = cl. findStaticVarHandle ( ClassLoader . class , «usr_paths» , String [ ] . class ) ;
String [ ] path = ( String [ ] ) usr_paths. get ( ) ;

usr_paths . set ( new String [ ] { «A» , «B» } ) ;

This code won’t work if you are using Java 8. If you want support for e.g. Java clsMHandles = Class . forName ( «java.lang.invoke.MethodHandles» ) ;

Method mStaticLookup = clsMHandles. getMethod ( «lookup» ) ;
Object oStaticLookup = mStaticLookup. invoke ( null ) ;

Method mLookup = clsMHandles. getMethod ( «privateLookupIn» , Class . class , Class . forName ( «java.lang.invoke.MethodHandles$Lookup» ) ) ;
Object oLookup = mLookup. invoke ( null , ClassLoader . class , oStaticLookup ) ;

Method mFindStatic = oLookup. getClass ( ) . getMethod ( «findStaticVarHandle» , Class . class , String . class , Class . class ) ;

Object oVarHandle = mFindStatic. invoke ( oLookup, ClassLoader . class , «usr_paths» , String [ ] . class ) ;

//MethodHandle mh = MethodHandles.lookup().findVirtual(VarHandle.class, «get», MethodType.methodType(Object.class));
//mh.invoke(oVarHandle);

Method mFindVirtual = oStaticLookup. getClass ( ) . getMethod ( «findVirtual» , Class . class , String . class , Class . forName ( «java.lang.invoke.MethodType» ) ) ;

Class clsMethodType = Class . forName ( «java.lang.invoke.MethodType» ) ;
Method mMethodType = clsMethodType. getMethod ( «methodType» , Class . class ) ;

Object oMethodHandleGet = mFindVirtual. invoke ( oStaticLookup, Class . forName ( «java.lang.invoke.VarHandle» ) , «get» , mMethodType. invoke ( null , Object . class ) ) ;

Method mMethodHandleGet = oMethodHandleGet. getClass ( ) . getMethod ( «invokeWithArguments» , Object [ ] . class ) ;

String [ ] saPath = ( String [ ] ) mMethodHandleGet. invoke ( oMethodHandleGet, new Object [ ] { new Object [ ] { oVarHandle } } ) ;

//check if the path to add is already present
for ( String path : saPath )
{
if ( path. equals ( pPath ) )
{
return ;
}
}

//add the new path
final String [ ] saNewPaths = Arrays . copyOf ( saPath, saPath. length + 1 ) ;
saNewPaths [ saNewPaths. length — 1 ] = pPath ;

//MethodHandle mh = MethodHandles.lookup().findVirtual(VarHandle.class, «set», MethodType.methodType(Void.class, Object[].class));
//mh.invoke(oVarHandle, new String[] );

mMethodType = clsMethodType. getMethod ( «methodType» , Class . class , Class . class ) ;

Object oMethodHandleSet = mFindVirtual. invoke ( oStaticLookup, Class . forName ( «java.lang.invoke.VarHandle» ) , «set» , mMethodType. invoke ( null , Void . class , Object [ ] . class ) ) ;

Method mMethodHandleSet = oMethodHandleSet. getClass ( ) . getMethod ( «invokeWithArguments» , Object [ ] . class ) ;

mMethodHandleSet. invoke ( oMethodHandleSet, new Object [ ] { new Object [ ] { oVarHandle, saNewPaths } } ) ;

Summarized: It’s not easy but it’s still possible. So. why are things getting more and more complex?

5 Responses to “Adding new paths for native libraries at runtime in Java”

Hi, This is just brillant, thanks a tonn! — however there is a little curveball to this. It won’t wotk with java 13 unfortunately. Check my stacktrace below. If you could make it java 13 compliant, that would be highly appreciated. Thanks again. Stacktrace from java 13 (openjdk):
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access using Lookup on com.foo.bar.NativeLoader (file:/classes/) to class java.lang.ClassLoader
WARNING: Please consider reporting this to the maintainers of com.foo.bar.NativeLoader
WARNING: Use —illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Exception in thread «main» java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException The NPE is the saPath from your example being null. I guess it would be oodd for usr_paths to be null, so I guess reflection filetered it away.

Читайте также:  String set in cpp

With JDK 15.01 it becomes even worse. Now usr_paths are reflected as not even being part of the ClassLoader class: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.foo.bar.(NativeLoader.java:48)
Caused by: java.lang.NoSuchFieldException: no such field: java.lang.ClassLoader.usr_paths/[Ljava.lang.String;/getStatic
at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:980)
at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1117)
at java.base/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3424)
at java.base/java.lang.invoke.MethodHandles$Lookup.findStaticVarHandle(MethodHandles.java:3024)
. 5 more
Caused by: java.lang.NoSuchFieldError: usr_paths
at java.base/java.lang.invoke.MethodHandleNatives.resolve(Native Method)
at java.base/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:1087)
at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1114)
. 7 more

Bad news, but maybe only because the class structure has changed. I’ll check both problems tomorrow. Hopefully there’s a possibility to enable this again.

Источник

Adjust java.library.path at Runtime

At my current task, I had to make sure a native library is able to be loaded. I didn’t want to adjust the parameters of the JVM for this, so I had to patch the java.library.path at runtime.

So, this code (in Scala, but its quite similar in Java) consists of two parts:

  1. Getting the current value of the java.library.path system property
  2. Making sure its actually used by the JVM by doing some reflection hacky stuff.

Getting the current value of the java.library.path system property L7-16

This is straightforward. We do a System.getProperty and then just retrieve it, check if the path we want to add is already there or not. If not, we add it and then set the property.

Making sure its actually used by the JVM L18-23

This is the tricky part. We have to use reflection to get access to the sys_path field of the Classloader. This is usually set to the path where the RT.jar file is located. We make the field accessible, get its current value, then set it to null and set it back to its previous value. Changing the fields value actually triggers the reloading of the paths’, so that the java.library.path variable is read again and used from now on.

Compared to the inspiration post by Fahd Shariff, we make sure to set the sys_paths field back to a reasonable value, else the JVM is unable to use other native-functions, e.g. `java.nio` and you would get errors like:

[info] java.lang.ExceptionInInitializerError [info] at java.base/sun.nio.fs.UnixFileSystemProvider.copy(UnixFileSystemProvider.java:258) [info] at java.base/java.nio.file.Files.copy(Files.java:1295) [info] at com.acme.calculation.demo.GamsIntegrationSpec.$anonfun$new$52(GamsIntegrationSpec.scala:278) [info] at zio.internal.FiberContext.evaluateNow(FiberContext.scala:361) [info] at zio.internal.FiberContext.$anonfun$evaluateLater$1(FiberContext.scala:775) [info] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [info] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [info] at java.base/java.lang.Thread.run(Thread.java:834) Code language: PHP (php)

Источник

Оцените статью