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"));
+ }
+}