This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new 8122cf504 feat(java/driver/jni): allow loading shared library from 
external path (#4211)
8122cf504 is described below

commit 8122cf5041b0f7e7eb692b2fd135e096620b30f4
Author: mete <[email protected]>
AuthorDate: Tue Apr 14 17:56:28 2026 +0300

    feat(java/driver/jni): allow loading shared library from external path 
(#4211)
    
    Add `arrow.adbc.driver.jni.library.path` JVM system property to load the
    JNI native library from a custom directory. Falls back to JAR extraction
    when the property is unset or the file is not found.
    
    Consistent with [arrow-java's JniLoader
    
approach](https://github.com/apache/arrow-java/blob/0d55ba78aeed3e6b28e3d65ced37c360f5e0ed4e/c/src/main/java/org/apache/arrow/c/jni/JniLoader.java#L80-L92).
    I just make it a bit more testable.
    
    Closes apache/arrow-adbc#4207
---
 .../adbc/driver/jni/impl/JniLibraryResolver.java   | 55 +++++++++++++++++
 .../arrow/adbc/driver/jni/impl/JniLoader.java      | 25 +++-----
 .../driver/jni/impl/JniLibraryResolverTest.java    | 69 ++++++++++++++++++++++
 3 files changed, 132 insertions(+), 17 deletions(-)

diff --git 
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLibraryResolver.java
 
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLibraryResolver.java
new file mode 100644
index 000000000..0cc85956d
--- /dev/null
+++ 
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLibraryResolver.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.arrow.adbc.driver.jni.impl;
+
+import java.io.File;
+import java.util.Locale;
+
+/** Resolves the JNI native library location. */
+class JniLibraryResolver {
+
+  static final String PROPERTY = "arrow.adbc.driver.jni.library.path";
+  private static final String LIBRARY_NAME = "adbc_driver_jni";
+
+  /** Returns absolute file path if the system property points to an existing 
library, else null. */
+  static String resolve() {
+    String dir = System.getProperty(PROPERTY);
+    if (dir == null) {
+      return null;
+    }
+    File file = new File(dir, System.mapLibraryName(LIBRARY_NAME));
+    return file.isFile() ? file.getAbsolutePath() : null;
+  }
+
+  /** Returns the platform-specific resource path for JAR extraction. */
+  static String resourcePath() {
+    return LIBRARY_NAME + "/" + getNormalizedArch() + "/" + 
System.mapLibraryName(LIBRARY_NAME);
+  }
+
+  private static String getNormalizedArch() {
+    String arch = System.getProperty("os.arch").toLowerCase(Locale.US);
+    switch (arch) {
+      case "amd64":
+        return "x86_64";
+      case "aarch64":
+        return "aarch_64";
+      default:
+        throw new RuntimeException("ADBC JNI driver not supported on 
architecture " + arch);
+    }
+  }
+}
diff --git 
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLoader.java
 
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLoader.java
index 8102af7a8..ab6fd7696 100644
--- 
a/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLoader.java
+++ 
b/java/driver/jni/src/main/java/org/apache/arrow/adbc/driver/jni/impl/JniLoader.java
@@ -23,7 +23,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
-import java.util.Locale;
 import java.util.Map;
 import org.apache.arrow.adbc.core.AdbcException;
 import org.apache.arrow.c.ArrowArray;
@@ -34,10 +33,15 @@ public enum JniLoader {
   INSTANCE;
 
   JniLoader() {
+    // If 'arrow.adbc.driver.jni.library.path' is set, load from there instead 
of the JAR.
+    String resolvedPath = JniLibraryResolver.resolve();
+    if (resolvedPath != null) {
+      System.load(resolvedPath);
+      return;
+    }
+
     // The JAR may contain multiple binaries for different platforms, so load 
the appropriate one.
-    final String libraryName = "adbc_driver_jni";
-    String libraryToLoad =
-        libraryName + "/" + getNormalizedArch() + "/" + 
System.mapLibraryName(libraryName);
+    String libraryToLoad = JniLibraryResolver.resourcePath();
 
     try {
       InputStream is = 
JniLoader.class.getClassLoader().getResourceAsStream(libraryToLoad);
@@ -58,19 +62,6 @@ public enum JniLoader {
     }
   }
 
-  private String getNormalizedArch() {
-    // Be consistent with our CMake config
-    String arch = System.getProperty("os.arch").toLowerCase(Locale.US);
-    switch (arch) {
-      case "amd64":
-        return "x86_64";
-      case "aarch64":
-        return "aarch_64";
-      default:
-        throw new RuntimeException("ADBC JNI driver not supported on 
architecture " + arch);
-    }
-  }
-
   public NativeDatabaseHandle openDatabase(Map<String, String> parameters) 
throws AdbcException {
     String[] nativeParameters = new String[parameters.size() * 2];
     int index = 0;
diff --git 
a/java/driver/jni/src/test/java/org/apache/arrow/adbc/driver/jni/impl/JniLibraryResolverTest.java
 
b/java/driver/jni/src/test/java/org/apache/arrow/adbc/driver/jni/impl/JniLibraryResolverTest.java
new file mode 100644
index 000000000..4664aa7bf
--- /dev/null
+++ 
b/java/driver/jni/src/test/java/org/apache/arrow/adbc/driver/jni/impl/JniLibraryResolverTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.arrow.adbc.driver.jni.impl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class JniLibraryResolverTest {
+
+  @AfterEach
+  void clearProperty() {
+    System.clearProperty(JniLibraryResolver.PROPERTY);
+  }
+
+  @Test
+  void propertyNotSet() {
+    assertThat(JniLibraryResolver.resolve()).isNull();
+  }
+
+  @Test
+  void fileExists(@TempDir Path tempDir) throws IOException {
+    File lib = 
tempDir.resolve(System.mapLibraryName("adbc_driver_jni")).toFile();
+    assertThat(lib.createNewFile()).isTrue();
+
+    System.setProperty(JniLibraryResolver.PROPERTY, tempDir.toString());
+    assertThat(JniLibraryResolver.resolve()).isEqualTo(lib.getAbsolutePath());
+  }
+
+  @Test
+  void fileMissing(@TempDir Path tempDir) {
+    System.setProperty(JniLibraryResolver.PROPERTY, tempDir.toString());
+    assertThat(JniLibraryResolver.resolve()).isNull();
+  }
+
+  @Test
+  void directoryDoesNotExist() {
+    System.setProperty(JniLibraryResolver.PROPERTY, "/nonexistent/path");
+    assertThat(JniLibraryResolver.resolve()).isNull();
+  }
+
+  @Test
+  void resourcePathContainsLibraryAndArch() {
+    String path = JniLibraryResolver.resourcePath();
+    assertThat(path).startsWith("adbc_driver_jni/");
+    assertThat(path).containsPattern("(x86_64|aarch_64)");
+    assertThat(path).endsWith(System.mapLibraryName("adbc_driver_jni"));
+  }
+}

Reply via email to