Skip to content

refactor: Modularize SQLite native library loading logic #1254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all 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
127 changes: 87 additions & 40 deletions src/main/java/org/sqlite/SQLiteJDBCLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ static void cleanup() {
path ->
!path.getFileName().toString().endsWith(LOCK_EXT)
&& path.getFileName()
.toString()
.startsWith(searchPattern))
.toString()
.startsWith(searchPattern))
.forEach(
nativeLib -> {
Path lckFile = Paths.get(nativeLib + LOCK_EXT);
Expand Down Expand Up @@ -212,7 +212,7 @@ private static boolean extractAndLoadLibraryFile(
// Check whether the contents are properly copied from the resource folder
{
try (InputStream nativeIn = getResourceAsStream(nativeLibraryFilePath);
InputStream extractedLibIn = Files.newInputStream(extractedLibFile)) {
InputStream extractedLibIn = Files.newInputStream(extractedLibFile)) {
if (!contentsEquals(nativeIn, extractedLibIn)) {
throw new FileException(
String.format(
Expand Down Expand Up @@ -297,74 +297,121 @@ private static boolean loadNativeLibraryJdk() {
*
* @throws
*/
/**
* Loads the SQLite native library from various locations, such as system properties, the JAR file,
* or the system's library path. This method ensures the library is loaded only once by using the
* `extracted` flag.
*/
private static void loadSQLiteNativeLibrary() throws Exception {
if (extracted) {
return;
}

List<String> triedPaths = new LinkedList<>();
String sqliteNativeLibraryName = getSqliteNativeLibraryName();

// Try loading library from org.sqlite.lib.path library path */
String sqliteNativeLibraryPath = System.getProperty("org.sqlite.lib.path");
String sqliteNativeLibraryName = System.getProperty("org.sqlite.lib.name");
if (sqliteNativeLibraryName == null) {
sqliteNativeLibraryName = LibraryLoaderUtil.getNativeLibName();
// Try loading from a custom system property path.
if (tryLoadingFromSystemProperty(triedPaths, sqliteNativeLibraryName)) {
return; // Library loaded successfully.
}

// Try loading from the JAR file by extracting it to a temporary folder.
if (tryLoadingFromJar(triedPaths, sqliteNativeLibraryName)) {
return; // Library loaded successfully.
}

// Try loading from the system's `java.library.path`.
if (tryLoadingFromJavaLibraryPath(triedPaths, sqliteNativeLibraryName)) {
return; // Library loaded successfully.
}

// Try loading using `System.loadLibrary` as a last resort.
if (tryLoadingWithSystemLoadLibrary()) {
return; // Library loaded successfully.
}

// If all attempts fail, throw an exception with details about the failed paths.
throw new NativeLibraryNotFoundException(
String.format(
"No native library found for os.name=%s, os.arch=%s, paths=[%s]",
OSInfo.getOSName(),
OSInfo.getArchName(),
StringUtils.join(triedPaths, File.pathSeparator)));
}

/**
* Retrieves the SQLite native library name from system properties or defaults to the name
* provided by `LibraryLoaderUtil`.
*/
private static String getSqliteNativeLibraryName() {
String sqliteNativeLibraryName = System.getProperty("org.sqlite.lib.name");
return sqliteNativeLibraryName != null ? sqliteNativeLibraryName : LibraryLoaderUtil.getNativeLibName();
}

/**
* Attempts to load the library from a custom path specified in the system property
* `org.sqlite.lib.path`.
*/
private static boolean tryLoadingFromSystemProperty(List<String> triedPaths, String libraryName) {
String sqliteNativeLibraryPath = System.getProperty("org.sqlite.lib.path");
if (sqliteNativeLibraryPath != null) {
if (loadNativeLibrary(sqliteNativeLibraryPath, sqliteNativeLibraryName)) {
extracted = true;
return;
if (loadNativeLibrary(sqliteNativeLibraryPath, libraryName)) {
extracted = true; // Library loaded successfully.
return true;
} else {
triedPaths.add(sqliteNativeLibraryPath);
triedPaths.add(sqliteNativeLibraryPath); // Add path to the list of failed attempts.
}
}
return false; // Library not loaded.
}

// Load the os-dependent library from the jar file
sqliteNativeLibraryPath = LibraryLoaderUtil.getNativeLibResourcePath();
boolean hasNativeLib =
LibraryLoaderUtil.hasNativeLib(sqliteNativeLibraryPath, sqliteNativeLibraryName);
/**
* Attempts to extract and load the library from the JAR file into a temporary folder.
*/
private static boolean tryLoadingFromJar(List<String> triedPaths, String libraryName) throws FileException {
String sqliteNativeLibraryPath = LibraryLoaderUtil.getNativeLibResourcePath();
boolean hasNativeLib = LibraryLoaderUtil.hasNativeLib(sqliteNativeLibraryPath, libraryName);

if (hasNativeLib) {
// temporary library folder
String tempFolder = getTempDir().getAbsolutePath();
// Try extracting the library from jar
if (extractAndLoadLibraryFile(
sqliteNativeLibraryPath, sqliteNativeLibraryName, tempFolder)) {
extracted = true;
return;
if (extractAndLoadLibraryFile(sqliteNativeLibraryPath, libraryName, tempFolder)) {
extracted = true; // Library loaded successfully.
return true;
} else {
triedPaths.add(sqliteNativeLibraryPath);
triedPaths.add(sqliteNativeLibraryPath); // Add path to the list of failed attempts.
}
}
return false; // Library not loaded.
}

// As a last resort try from java.library.path
/**
* Attempts to load the library from the system's `java.library.path`.
*/
private static boolean tryLoadingFromJavaLibraryPath(List<String> triedPaths, String libraryName) {
String javaLibraryPath = System.getProperty("java.library.path", "");
for (String ldPath : javaLibraryPath.split(File.pathSeparator)) {
if (ldPath.isEmpty()) {
continue;
continue; // Skip empty paths.
}
if (loadNativeLibrary(ldPath, sqliteNativeLibraryName)) {
extracted = true;
return;
if (loadNativeLibrary(ldPath, libraryName)) {
extracted = true; // Library loaded successfully.
return true;
} else {
triedPaths.add(ldPath);
triedPaths.add(ldPath); // Add path to the list of failed attempts.
}
}
return false; // Library not loaded.
}

// As an ultimate last resort, try loading through System.loadLibrary
/**
* Attempts to load the library using `System.loadLibrary` as a last resort.
*/
private static boolean tryLoadingWithSystemLoadLibrary() {
if (loadNativeLibraryJdk()) {
extracted = true;
return;
extracted = true; // Library loaded successfully.
return true;
}

extracted = false;
throw new NativeLibraryNotFoundException(
String.format(
"No native library found for os.name=%s, os.arch=%s, paths=[%s]",
OSInfo.getOSName(),
OSInfo.getArchName(),
StringUtils.join(triedPaths, File.pathSeparator)));
return false; // Library not loaded.
}

@SuppressWarnings("unused")
Expand Down