From d5200f3da88cde7e5b3d9205e667d51947402bbb Mon Sep 17 00:00:00 2001 From: Priyansh Solanki Date: Fri, 21 Mar 2025 19:56:42 -0300 Subject: [PATCH] refactor: Modularize SQLite native library loading logic - Extracted loading logic into helper methods for better readability and maintainability. - Added clear and concise comments to explain each method's purpose. - Ensured consistent error handling and path tracking for failed attempts. - Improved code structure to make it easier to add new loading strategies in the future. This refactor does not change the functionality but makes the code more modular and self-documenting, aligning with best practices for maintainable code. --- .../java/org/sqlite/SQLiteJDBCLoader.java | 127 ++++++++++++------ 1 file changed, 87 insertions(+), 40 deletions(-) diff --git a/src/main/java/org/sqlite/SQLiteJDBCLoader.java b/src/main/java/org/sqlite/SQLiteJDBCLoader.java index cfd41abc8..902bb9541 100644 --- a/src/main/java/org/sqlite/SQLiteJDBCLoader.java +++ b/src/main/java/org/sqlite/SQLiteJDBCLoader.java @@ -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); @@ -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( @@ -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 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 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 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 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")