This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information

Adding new paths for native libraries at runtime in Java (part 2)

Part 1 of this article is available here.

Since Java 14, ClassLoader was changed a little bit and usr_paths it not available as field anymore. It's still possible to change the java.library.path at runtime, but it's still tricky and dirty:

Method getDeclaredFields0 =
    Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
getDeclaredFields0.setAccessible(true);

Field[] fieldsClassLoader =
    (Field[])getDeclaredFields0.invoke(ClassLoader.class, Boolean.FALSE);

for (Field fldClassLoader : fieldsClassLoader) {
  if ("libraries".equals(fldClassLoader.getName())) {
    fldClassLoader.setAccessible(true);

    Class<?>[] classesClassLoader =
        fldClassLoader.getType().getDeclaredClasses();

    for (Class<?> clLibraryPaths : classesClassLoader) {
      if ("jdk.internal.loader.NativeLibraries$LibraryPaths".equals(
              clLibraryPaths.getName())) {
        Field[] fieldsLibraryPaths =
            (Field[])getDeclaredFields0.invoke(clLibraryPaths, Boolean.FALSE);

        for (Field fldLibPath : fieldsLibraryPaths) {
          if ("USER_PATHS".equals(fldLibPath.getName())) {
            final Field fldUsrPaths = fldLibPath;
            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
            String[] saNewPaths = Arrays.copyOf(saPath, saPath.length + 1);
            saNewPaths[saNewPaths.length - 1] = pPath;

            Object unsafe;

            // Unsafe is a hack

            Class<?> clsUnsafe = Class.forName("sun.misc.Unsafe");

            final Field unsafeField = clsUnsafe.getDeclaredField("theUnsafe");
            unsafeField.setAccessible(true);
            unsafe = unsafeField.get(null);

            Method m1 = clsUnsafe.getMethod("staticFieldBase", Field.class);
            Method m2 = clsUnsafe.getMethod("staticFieldOffset", Field.class);

            Object fieldBase = m1.invoke(unsafe, fldUsrPaths);
            Long fieldOffset = (Long)m2.invoke(unsafe, fldUsrPaths);

            Method m3 = clsUnsafe.getMethod("putObject", Object.class,
                                            long.class, Object.class);

            m3.invoke(unsafe, fieldBase, fieldOffset, saNewPaths);
          }
        }
      }
    }

    return;
  }
}

Here's the complete solution: toPDF project (search for addLibraryPath(String pPath))

Above code requires:

--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED

for JDK > 14.