Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions java/src/main/java/ai/onnxruntime/OnnxRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -105,6 +106,9 @@ final class OnnxRuntime {
/** Tracks if the shared providers have been extracted */
private static final Set<String> extractedSharedProviders = new HashSet<>();

/** Test-only hook to observe calls to {@link #extractFromResources(String)}. */
private static volatile Consumer<String> extractFromResourcesHook;

/** The API handle. */
static long ortApiHandle;

Expand Down Expand Up @@ -291,6 +295,15 @@ static boolean extractQNN() {
return extractProviderLibrary(ONNXRUNTIME_LIBRARY_QNN_NAME);
}

/**
* Test-only hook to observe whether extraction from resources is attempted for a library.
*
* @param hook A consumer invoked with the requested library name, or null to clear the hook.
*/
static void setExtractFromResourcesHook(Consumer<String> hook) {
extractFromResourcesHook = hook;
}

/**
* Extracts a shared provider library from the classpath resources if present, or checks to see if
* that library is in the directory specified by {@link #ONNXRUNTIME_NATIVE_PATH}.
Expand All @@ -307,26 +320,22 @@ static synchronized boolean extractProviderLibrary(String libraryName) {
if (extractedSharedProviders.contains(libraryName)) {
return true;
}
// Otherwise extract the file from the classpath resources
// If a native library directory is configured, prefer it and skip extraction when present.
if (libraryDirPathProperty != null) {
String libraryFileName = mapLibraryName(libraryName);
File libraryFile = Paths.get(libraryDirPathProperty, libraryFileName).toFile();
if (libraryFile.exists()) {
extractedSharedProviders.add(libraryName);
return true;
}
}
// Otherwise extract the file from the classpath resources.
Optional<File> file = extractFromResources(libraryName);
if (file.isPresent()) {
extractedSharedProviders.add(libraryName);
return true;
} else {
// If we failed to extract it, check if there is a valid cache directory
// that contains it
if (libraryDirPathProperty != null) {
String libraryFileName = mapLibraryName(libraryName);
File libraryFile = Paths.get(libraryDirPathProperty, libraryFileName).toFile();
if (libraryFile.exists()) {
extractedSharedProviders.add(libraryName);
return true;
} else {
return false;
}
} else {
return false;
}
return false;
}
}

Expand Down Expand Up @@ -427,6 +436,10 @@ private static void load(String library) throws IOException {
* if it failed to extract or couldn't be found.
*/
private static Optional<File> extractFromResources(String library) {
Consumer<String> hook = extractFromResourcesHook;
if (hook != null) {
hook.accept(library);
}
String libraryFileName = mapLibraryName(library);
String resourcePath = "/ai/onnxruntime/native/" + OS_ARCH_STR + '/' + libraryFileName;
File tempFile = tempDirectory.resolve(libraryFileName).toFile();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* Licensed under the MIT License.
*/
package ai.onnxruntime;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assumptions;

public class OnnxRuntimeNativePathExtractionTest {
private static final String TEST_PROVIDER_LIBRARY = "onnxruntime_providers_test_native_path";

private String previousNativePath;
private String previousLibraryDirPathProperty;
private Path previousTempDirectory;

private Path nativePathDir;
private Path tempExtractionDir;

@BeforeEach
@SuppressWarnings("unchecked")
void setUp() throws Exception {
previousNativePath = System.getProperty(OnnxRuntime.ONNXRUNTIME_NATIVE_PATH);
previousLibraryDirPathProperty = (String) getPrivateStaticField("libraryDirPathProperty");
previousTempDirectory = (Path) getPrivateStaticField("tempDirectory");
((Set<String>) getPrivateStaticField("extractedSharedProviders")).remove(TEST_PROVIDER_LIBRARY);
OnnxRuntime.setExtractFromResourcesHook(null);
}

@AfterEach
@SuppressWarnings("unchecked")
void tearDown() throws Exception {
OnnxRuntime.setExtractFromResourcesHook(null);
if (previousNativePath == null) {
System.clearProperty(OnnxRuntime.ONNXRUNTIME_NATIVE_PATH);
} else {
System.setProperty(OnnxRuntime.ONNXRUNTIME_NATIVE_PATH, previousNativePath);
}
setPrivateStaticField("libraryDirPathProperty", previousLibraryDirPathProperty);
setPrivateStaticField("tempDirectory", previousTempDirectory);
((Set<String>) getPrivateStaticField("extractedSharedProviders")).remove(TEST_PROVIDER_LIBRARY);
if (nativePathDir != null && Files.exists(nativePathDir)) {
TestHelpers.deleteDirectoryTree(nativePathDir);
}
if (tempExtractionDir != null && Files.exists(tempExtractionDir)) {
TestHelpers.deleteDirectoryTree(tempExtractionDir);
}
}

@Test
void providerAlreadyInNativePathShouldNotAttemptResourceExtraction() throws Exception {
Assumptions.assumeFalse(OnnxRuntime.isAndroid(), "Provider extraction is not used on Android.");

nativePathDir = Files.createTempDirectory("ort-native-path");
tempExtractionDir = Files.createTempDirectory("ort-extract-temp");

String libraryFileName = System.mapLibraryName(TEST_PROVIDER_LIBRARY).replace("jnilib", "dylib");
Files.write(nativePathDir.resolve(libraryFileName), new byte[] {1});

System.setProperty(OnnxRuntime.ONNXRUNTIME_NATIVE_PATH, nativePathDir.toString());
setPrivateStaticField("libraryDirPathProperty", nativePathDir.toString());
setPrivateStaticField("tempDirectory", tempExtractionDir);

AtomicInteger extractionAttempts = new AtomicInteger(0);
OnnxRuntime.setExtractFromResourcesHook(
library -> {
if (TEST_PROVIDER_LIBRARY.equals(library)) {
extractionAttempts.incrementAndGet();
}
});

boolean libraryReady = OnnxRuntime.extractProviderLibrary(TEST_PROVIDER_LIBRARY);

assertTrue(libraryReady, "Sanity check: provider library should be found in native path.");
// Regression expectation for issue #27655:
// when onnxruntime.native.path already contains the requested provider library,
// extraction from JAR/resources should not be attempted.
assertEquals(
0,
extractionAttempts.get(),
"Bug #27655: extraction from resources was attempted even though the provider library "
+ "already exists in onnxruntime.native.path.");
}

private static Object getPrivateStaticField(String fieldName) throws Exception {
Field field = OnnxRuntime.class.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(null);
}

private static void setPrivateStaticField(String fieldName, Object value) throws Exception {
Field field = OnnxRuntime.class.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(null, value);
}
}