From a448a8320cdaf4f1ecf73cfdbd6478eb4a37bc44 Mon Sep 17 00:00:00 2001 From: Adam Pocock Date: Wed, 10 Apr 2019 15:41:44 -0400 Subject: [PATCH] [jvm-packages] Fixing the NativeLibLoader on Java 9+ (#4351) The old NativeLibLoader had a short-circuit load path which modified java.library.path and attempted to load the xgboost library from outside the jar first, falling back to loading the library from inside the jar. This path is a no-op every time when using XGBoost outside of it's source tree. Additionally it triggers an illegal reflective access warning in the module system in 9, 10, and 11. On Java 12 the ClassLoader fields are not accessible via reflection (separately from the illegal reflective acces warning), and so it fails in a way that isn't caught by the code which falls back to loading the library from inside the jar. This commit removes that code path and always loads the xgboost library from inside the jar file as it's a valid technique across multiple JVM implementations and works with all versions of Java. --- .../dmlc/xgboost4j/java/NativeLibLoader.java | 59 +++---------------- 1 file changed, 7 insertions(+), 52 deletions(-) diff --git a/jvm-packages/xgboost4j/src/main/java/ml/dmlc/xgboost4j/java/NativeLibLoader.java b/jvm-packages/xgboost4j/src/main/java/ml/dmlc/xgboost4j/java/NativeLibLoader.java index 1b5438cd5..8e19c5b70 100644 --- a/jvm-packages/xgboost4j/src/main/java/ml/dmlc/xgboost4j/java/NativeLibLoader.java +++ b/jvm-packages/xgboost4j/src/main/java/ml/dmlc/xgboost4j/java/NativeLibLoader.java @@ -30,14 +30,19 @@ class NativeLibLoader { private static final Log logger = LogFactory.getLog(NativeLibLoader.class); private static boolean initialized = false; - private static final String nativePath = "../../lib/"; private static final String nativeResourcePath = "/lib/"; private static final String[] libNames = new String[]{"xgboost4j"}; static synchronized void initXGBoost() throws IOException { if (!initialized) { for (String libName : libNames) { - smartLoad(libName); + try { + String libraryFromJar = nativeResourcePath + System.mapLibraryName(libName); + loadLibraryFromJar(libraryFromJar); + } catch (IOException ioe) { + logger.error("failed to load " + libName + " library from jar"); + throw ioe; + } } initialized = true; } @@ -135,54 +140,4 @@ class NativeLibLoader { return temp.getAbsolutePath(); } - /** - * load native library, this method will first try to load library from java.library.path, then - * try to load library in jar package. - * - * @param libName library path - * @throws IOException exception - */ - private static void smartLoad(String libName) throws IOException { - addNativeDir(nativePath); - try { - System.loadLibrary(libName); - } catch (UnsatisfiedLinkError e) { - try { - String libraryFromJar = nativeResourcePath + System.mapLibraryName(libName); - loadLibraryFromJar(libraryFromJar); - } catch (IOException ioe) { - logger.error("failed to load library from both native path and jar"); - throw ioe; - } - } - } - - /** - * Add libPath to java.library.path, then native library in libPath would be load properly - * - * @param libPath library path - * @throws IOException exception - */ - private static void addNativeDir(String libPath) throws IOException { - try { - Field field = ClassLoader.class.getDeclaredField("usr_paths"); - field.setAccessible(true); - String[] paths = (String[]) field.get(null); - for (String path : paths) { - if (libPath.equals(path)) { - return; - } - } - String[] tmp = new String[paths.length + 1]; - System.arraycopy(paths, 0, tmp, 0, paths.length); - tmp[paths.length] = libPath; - field.set(null, tmp); - } catch (IllegalAccessException e) { - logger.error(e.getMessage()); - throw new IOException("Failed to get permissions to set library path"); - } catch (NoSuchFieldException e) { - logger.error(e.getMessage()); - throw new IOException("Failed to get field handle to set library path"); - } - } }