All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sqlite.SQLiteJDBCLoader Maven / Gradle / Ivy

There is a newer version: 3.47.0.0
Show newest version
/*--------------------------------------------------------------------------
 *  Copyright 2007 Taro L. Saito
 *
 *  Licensed 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.
 *--------------------------------------------------------------------------*/
// --------------------------------------
// SQLite JDBC Project
//
// SQLite.java
// Since: 2007/05/10
//
// $URL$
// $Author$
// --------------------------------------
package org.sqlite;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sqlite.util.LibraryLoaderUtil;
import org.sqlite.util.OSInfo;
import org.sqlite.util.StringUtils;

/**
 * Set the system properties, org.sqlite.lib.path, org.sqlite.lib.name, appropriately so that the
 * SQLite JDBC driver can find *.dll, *.dylib and *.so files, according to the current OS (win,
 * linux, mac).
 *
 * 

The library files are automatically extracted from this project's package (JAR). * *

usage: call {@link #initialize()} before using SQLite JDBC driver. * * @author leo */ public class SQLiteJDBCLoader { private static final Logger logger = LoggerFactory.getLogger(SQLiteJDBCLoader.class); private static final String LOCK_EXT = ".lck"; private static boolean extracted = false; /** * Loads SQLite native JDBC library. * * @return True if SQLite native library is successfully loaded; false otherwise. */ public static synchronized boolean initialize() throws Exception { // only cleanup before the first extract if (!extracted) { cleanup(); } loadSQLiteNativeLibrary(); return extracted; } private static File getTempDir() { return new File( System.getProperty("org.sqlite.tmpdir", System.getProperty("java.io.tmpdir"))); } /** * Deleted old native libraries e.g. on Windows the DLL file is not removed on VM-Exit (bug #80) */ static void cleanup() { String searchPattern = "sqlite-" + getVersion(); try (Stream dirList = Files.list(getTempDir().toPath())) { dirList.filter( path -> !path.getFileName().toString().endsWith(LOCK_EXT) && path.getFileName() .toString() .startsWith(searchPattern)) .forEach( nativeLib -> { Path lckFile = Paths.get(nativeLib + LOCK_EXT); if (Files.notExists(lckFile)) { try { Files.delete(nativeLib); } catch (Exception e) { logger.atError() .setCause(e) .log("Failed to delete old native lib"); } } }); } catch (IOException e) { logger.atError().setCause(e).log("Failed to open directory"); } } /** * Checks if the SQLite JDBC driver is set to native mode. * * @return True if the SQLite JDBC driver is set to native Java mode; false otherwise. */ public static boolean isNativeMode() throws Exception { // load the driver initialize(); return extracted; } /** * Computes the MD5 value of the input stream. * * @param input InputStream. * @return Encrypted string for the InputStream. * @throws IOException * @throws NoSuchAlgorithmException */ static String md5sum(InputStream input) throws IOException { BufferedInputStream in = new BufferedInputStream(input); try { MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); DigestInputStream digestInputStream = new DigestInputStream(in, digest); for (; digestInputStream.read() >= 0; ) {} ByteArrayOutputStream md5out = new ByteArrayOutputStream(); md5out.write(digest.digest()); return md5out.toString(); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("MD5 algorithm is not available: " + e); } finally { in.close(); } } private static boolean contentsEquals(InputStream in1, InputStream in2) throws IOException { if (!(in1 instanceof BufferedInputStream)) { in1 = new BufferedInputStream(in1); } if (!(in2 instanceof BufferedInputStream)) { in2 = new BufferedInputStream(in2); } int ch = in1.read(); while (ch != -1) { int ch2 = in2.read(); if (ch != ch2) { return false; } ch = in1.read(); } int ch2 = in2.read(); return ch2 == -1; } /** * Extracts and loads the specified library file to the target folder * * @param libFolderForCurrentOS Library path. * @param libraryFileName Library name. * @param targetFolder Target folder. * @return */ private static boolean extractAndLoadLibraryFile( String libFolderForCurrentOS, String libraryFileName, String targetFolder) throws FileException { String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName; // Include architecture name in temporary filename in order to avoid conflicts // when multiple JVMs with different architectures running at the same time String uuid = UUID.randomUUID().toString(); String extractedLibFileName = String.format("sqlite-%s-%s-%s", getVersion(), uuid, libraryFileName); String extractedLckFileName = extractedLibFileName + LOCK_EXT; Path extractedLibFile = Paths.get(targetFolder, extractedLibFileName); Path extractedLckFile = Paths.get(targetFolder, extractedLckFileName); try { // Extract a native library file into the target directory try (InputStream reader = getResourceAsStream(nativeLibraryFilePath)) { if (Files.notExists(extractedLckFile)) { Files.createFile(extractedLckFile); } Files.copy(reader, extractedLibFile, StandardCopyOption.REPLACE_EXISTING); } finally { // Delete the extracted lib file on JVM exit. extractedLibFile.toFile().deleteOnExit(); extractedLckFile.toFile().deleteOnExit(); } // Set executable (x) flag to enable Java to load the native library extractedLibFile.toFile().setReadable(true); extractedLibFile.toFile().setWritable(true, true); extractedLibFile.toFile().setExecutable(true); // Check whether the contents are properly copied from the resource folder { try (InputStream nativeIn = getResourceAsStream(nativeLibraryFilePath); InputStream extractedLibIn = Files.newInputStream(extractedLibFile)) { if (!contentsEquals(nativeIn, extractedLibIn)) { throw new FileException( String.format( "Failed to write a native library file at %s", extractedLibFile)); } } } return loadNativeLibrary(targetFolder, extractedLibFileName); } catch (IOException e) { logger.atError().setCause(e).log(); return false; } } // Replacement of java.lang.Class#getResourceAsStream(String) to disable sharing the resource // stream // in multiple class loaders and specifically to avoid // https://bugs.openjdk.java.net/browse/JDK-8205976 private static InputStream getResourceAsStream(String name) { // Remove leading '/' since all our resource paths include a leading directory // See: // https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Class.java#L3054 String resolvedName = name.substring(1); ClassLoader cl = SQLiteJDBCLoader.class.getClassLoader(); URL url = cl.getResource(resolvedName); if (url == null) { return null; } try { URLConnection connection = url.openConnection(); connection.setUseCaches(false); return connection.getInputStream(); } catch (IOException e) { logger.atError().setCause(e).log(); return null; } } /** * Loads native library using the given path and name of the library. * * @param path Path of the native library. * @param name Name of the native library. * @return True for successfully loading; false otherwise. */ private static boolean loadNativeLibrary(String path, String name) { File libPath = new File(path, name); if (libPath.exists()) { try { System.load(new File(path, name).getAbsolutePath()); return true; } catch (UnsatisfiedLinkError e) { logger.atError() .setCause(e) .setMessage("Failed to load native library: {}. osinfo: {}") .addArgument(name) .addArgument(OSInfo::getNativeLibFolderPathForCurrentOS) .log(); return false; } } else { return false; } } private static boolean loadNativeLibraryJdk() { try { System.loadLibrary(LibraryLoaderUtil.NATIVE_LIB_BASE_NAME); return true; } catch (UnsatisfiedLinkError e) { logger.atError() .setCause(e) .log("Failed to load native library through System.loadLibrary"); return false; } } /** * Loads SQLite native library using given path and name of the library. * * @throws */ private static void loadSQLiteNativeLibrary() throws Exception { if (extracted) { return; } List triedPaths = new LinkedList<>(); // 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(); } if (sqliteNativeLibraryPath != null) { if (loadNativeLibrary(sqliteNativeLibraryPath, sqliteNativeLibraryName)) { extracted = true; return; } else { triedPaths.add(sqliteNativeLibraryPath); } } // Load the os-dependent library from the jar file sqliteNativeLibraryPath = LibraryLoaderUtil.getNativeLibResourcePath(); boolean hasNativeLib = LibraryLoaderUtil.hasNativeLib(sqliteNativeLibraryPath, sqliteNativeLibraryName); if (hasNativeLib) { // temporary library folder String tempFolder = getTempDir().getAbsolutePath(); // Try extracting the library from jar if (extractAndLoadLibraryFile( sqliteNativeLibraryPath, sqliteNativeLibraryName, tempFolder)) { extracted = true; return; } else { triedPaths.add(sqliteNativeLibraryPath); } } // As a last resort try from java.library.path String javaLibraryPath = System.getProperty("java.library.path", ""); for (String ldPath : javaLibraryPath.split(File.pathSeparator)) { if (ldPath.isEmpty()) { continue; } if (loadNativeLibrary(ldPath, sqliteNativeLibraryName)) { extracted = true; return; } else { triedPaths.add(ldPath); } } // As an ultimate last resort, try loading through System.loadLibrary if (loadNativeLibraryJdk()) { extracted = true; return; } 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))); } @SuppressWarnings("unused") private static void getNativeLibraryFolderForTheCurrentOS() { String osName = OSInfo.getOSName(); String archName = OSInfo.getArchName(); } /** @return The major version of the SQLite JDBC driver. */ public static int getMajorVersion() { String[] c = getVersion().split("\\."); return (c.length > 0) ? Integer.parseInt(c[0]) : 1; } /** @return The minor version of the SQLite JDBC driver. */ public static int getMinorVersion() { String[] c = getVersion().split("\\."); return (c.length > 1) ? Integer.parseInt(c[1]) : 0; } /** @return The version of the SQLite JDBC driver. */ public static String getVersion() { return VersionHolder.VERSION; } /** * This class will load the version from resources during . By initializing this at * build-time in native-image, the resources do not need to be included in the native * executable, and we're eliminating the IO operations as well. */ public static final class VersionHolder { private static final String VERSION; static { URL versionFile = VersionHolder.class.getResource( "/META-INF/maven/org.xerial/sqlite-jdbc/pom.properties"); if (versionFile == null) { versionFile = VersionHolder.class.getResource( "/META-INF/maven/org.xerial/sqlite-jdbc/VERSION"); } String version = "unknown"; try { if (versionFile != null) { Properties versionData = new Properties(); versionData.load(versionFile.openStream()); version = versionData.getProperty("version", version); version = version.trim().replaceAll("[^0-9\\.]", ""); } } catch (IOException e) { // inline creation of logger to avoid build-time initialization of the logging // framework in native-image LoggerFactory.getLogger(VersionHolder.class) .atError() .setCause(e) .log("Could not read version from file: {}", versionFile); } VERSION = version; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy