From: leo Date: Tue, 28 Apr 2009 03:18:13 +0000 (+0000) Subject: git-svn-id: http://www.xerial.org/svn/project/XerialJ/trunk/sqlite-jdbc@3265 ae02f08e... X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=77114780387d6b1d29cb8b167d662dbf6a18cc9c;p=xerial%2Fsqlite-jdbc.git git-svn-id: xerial.org/svn/project/XerialJ/trunk/sqlite-jdbc@3265 ae02f08e-27ec-0310-ae8c-8ba02fe2eafd --- diff --git a/Makefile b/Makefile index 34ffd01..97bc2d2 100644 --- a/Makefile +++ b/Makefile @@ -71,12 +71,14 @@ $(UPDATE_FLAG): $(OSINFO_PROG) $(SQLITE_DLL) touch $(UPDATE_FLAG) native: $(UPDATE_FLAG) + mvn test NATIVE_DLL=$(WORK_DIR)/$(LIB_FOLDER)/$(LIBNAME) package: $(UPDATE_FLAG) mkdir -p $(RESOURCE_DIR)/native/$(LIB_FOLDER) cp $(NATIVE_DLL) $(RESOURCE_DIR)/native/$(LIB_FOLDER) + rm -rf target/dependency-maven-plugin-markers mvn package clean-native: diff --git a/pom.xml b/pom.xml index 79fa785..1d0be96 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 org.xerial sqlite-jdbc - 3.6.13 + 3.6.13-SNAPSHOT SQLite JDBC SQLite JDBC library @@ -55,7 +55,7 @@ unpack-dependency - generate-sources + package unpack @@ -68,6 +68,7 @@ jar + true target/classes diff --git a/src/main/java/org/sqlite/Conn.java b/src/main/java/org/sqlite/Conn.java index f0b6685..33b23c5 100644 --- a/src/main/java/org/sqlite/Conn.java +++ b/src/main/java/org/sqlite/Conn.java @@ -16,6 +16,11 @@ package org.sqlite; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -49,45 +54,69 @@ class Conn implements Connection db.enable_load_extension(enableLoadExtension); } + private static final String RESOURCE_NAME_PREFIX = ":resource:"; + public Conn(String url, String filename) throws SQLException { // check the path to the file exists if (!":memory:".equals(filename)) { - File file = new File(filename).getAbsoluteFile(); - File parent = file.getParentFile(); - if (parent != null && !parent.exists()) + if (filename.startsWith(RESOURCE_NAME_PREFIX)) { - for (File up = parent; up != null && !up.exists();) + String resourceName = filename.substring(RESOURCE_NAME_PREFIX.length()); + + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + URL resourceAddr = contextCL.getResource(resourceName); + if (resourceAddr == null) + throw new SQLException(String.format("resource not found: %s", resourceName)); + + try { - parent = up; - up = up.getParentFile(); + filename = extractResource(resourceAddr).getAbsolutePath(); + } + catch (IOException e) + { + throw new SQLException(String.format("failed to load %s: %s", resourceName, e)); } - throw new SQLException("path to '" + filename + "': '" + parent + "' does not exist"); - } - - // check write access if file does not exist - try - { - // The extra check to exists() is necessary as createNewFile() - // does not follow the JavaDoc when used on read-only shares. - if (!file.exists() && file.createNewFile()) - file.delete(); } - catch (Exception e) + else { - throw new SQLException("opening db: '" + filename + "': " + e.getMessage()); + File file = new File(filename).getAbsoluteFile(); + File parent = file.getParentFile(); + if (parent != null && !parent.exists()) + { + for (File up = parent; up != null && !up.exists();) + { + parent = up; + up = up.getParentFile(); + } + throw new SQLException("path to '" + filename + "': '" + parent + "' does not exist"); + } + + // check write access if file does not exist + try + { + // The extra check to exists() is necessary as createNewFile() + // does not follow the JavaDoc when used on read-only shares. + if (!file.exists() && file.createNewFile()) + file.delete(); + } + catch (Exception e) + { + throw new SQLException("opening db: '" + filename + "': " + e.getMessage()); + } + filename = file.getAbsolutePath(); } - filename = file.getAbsolutePath(); } - // TODO: library variable to explicitly control load type - // attempt to use the Native library first + // tries to load native library first try { - Class nativedb = Class.forName("org.sqlite.NativeDB"); - if (((Boolean) nativedb.getDeclaredMethod("load", null).invoke(null, null)).booleanValue()) + Class< ? > nativedb = Class.forName("org.sqlite.NativeDB"); + if (((Boolean) nativedb.getDeclaredMethod("load", (Class< ? >[]) null).invoke((Object) null, + (Object[]) null)).booleanValue()) db = (DB) nativedb.newInstance(); + } catch (Exception e) {} // fall through to nested library @@ -110,6 +139,77 @@ class Conn implements Connection setTimeout(3000); } + /** + * @param resourceAddr + * @return extracted file name + * @throws IOException + */ + private File extractResource(URL resourceAddr) throws IOException + { + if (resourceAddr.getProtocol().equals("file")) + { + try + { + return new File(resourceAddr.toURI()); + } + catch (URISyntaxException e) + { + throw new IOException(e.getMessage()); + } + } + + String tempFolder = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath(); + String dbFileName = String.format("sqlite-jdbc-tmp-%d.db", resourceAddr.hashCode()); + File dbFile = new File(tempFolder, dbFileName); + + if (dbFile.exists()) + { + long resourceLastModified = resourceAddr.openConnection().getLastModified(); + long tmpFileLastModified = dbFile.lastModified(); + if (resourceLastModified < tmpFileLastModified) + { + return dbFile; + } + else + { + // remove the old DB file + boolean deletionSucceeded = dbFile.delete(); + if (!deletionSucceeded) + { + throw new IOException("failed to remove existing DB file: " + dbFile.getAbsolutePath()); + } + } + + // String md5sum1 = SQLiteJDBCLoader.md5sum(resourceAddr.openStream()); + // String md5sum2 = SQLiteJDBCLoader.md5sum(new FileInputStream(dbFile)); + // + // if (md5sum1.equals(md5sum2)) + // return dbFile; // no need to extract the DB file + // else + // { + // } + } + + byte[] buffer = new byte[8192]; // 8K buffer + FileOutputStream writer = new FileOutputStream(dbFile); + InputStream reader = resourceAddr.openStream(); + try + { + int bytesRead = 0; + while ((bytesRead = reader.read(buffer)) != -1) + { + writer.write(buffer, 0, bytesRead); + } + return dbFile; + } + finally + { + writer.close(); + reader.close(); + } + + } + int getTimeout() { return timeout; diff --git a/src/main/java/org/sqlite/JDBC.java b/src/main/java/org/sqlite/JDBC.java index afc248b..cae0561 100644 --- a/src/main/java/org/sqlite/JDBC.java +++ b/src/main/java/org/sqlite/JDBC.java @@ -66,7 +66,12 @@ public class JDBC implements Driver sharedCache.description = "Enable SQLite Shared-Cache mode, native driver only."; sharedCache.required = false; - return new DriverPropertyInfo[] { sharedCache }; + DriverPropertyInfo loadExt = new DriverPropertyInfo("enable_load_extension", "false"); + loadExt.choices = new String[] { "true", "false" }; + loadExt.description = "Enable SQLite load_extension() functionality"; + loadExt.required = false; + + return new DriverPropertyInfo[] { sharedCache, loadExt }; } public Connection connect(String url, Properties info) throws SQLException diff --git a/src/main/java/org/sqlite/ResourceFinder.java b/src/main/java/org/sqlite/ResourceFinder.java new file mode 100644 index 0000000..0ee3c7e --- /dev/null +++ b/src/main/java/org/sqlite/ResourceFinder.java @@ -0,0 +1,102 @@ +/*-------------------------------------------------------------------------- + * Copyright 2009 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 +// +// ResourceFinder.java +// Since: Apr 28, 2009 +// +// $URL$ +// $Author$ +//-------------------------------------- +package org.sqlite; + +import java.net.URL; + +/** + * Resource address finder for files inside the jar file + * + * @author leo + * + */ +public class ResourceFinder +{ + /** + * Gets the {@link URL} of the file resource + * + * @param referenceClass + * the base class for finding resources files. This method will + * search the package containing the given referenceClass. + * @param resourceFileName + * the resource file name relative to the package of the + * referenceClass + * @return the URL of the file resource + */ + public static URL find(Class< ? > referenceClass, String resourceFileName) + { + return find(referenceClass.getClassLoader(), referenceClass.getPackage(), resourceFileName); + } + + /** + * Finds the {@link URL} of the resource + * + * @param basePackage + * the base package to find the resource + * @param resourceFileName + * the resource file name relative to the package folder + * @return the URL of the specified resource + */ + public static URL find(ClassLoader classLoader, Package basePackage, String resourceFileName) + { + return find(classLoader, basePackage.getName(), resourceFileName); + } + + /** + * Finds the {@link URL} of the resource + * + * @param packageName + * the base package name to find the resource + * @param resourceFileName + * the resource file name relative to the package folder + * @return the URL of the specified resource + */ + public static URL find(ClassLoader classLoader, String packageName, String resourceFileName) + { + String packagePath = packagePath(packageName); + String resourcePath = packagePath + resourceFileName; + if (!resourcePath.startsWith("/")) + resourcePath = "/" + resourcePath; + + return classLoader.getResource(resourcePath); + } + + private static String packagePath(Class< ? > referenceClass) + { + return packagePath(referenceClass.getPackage()); + } + + private static String packagePath(Package basePackage) + { + return packagePath(basePackage.getName()); + } + + private static String packagePath(String packageName) + { + String packageAsPath = packageName.replaceAll("\\.", "/"); + return packageAsPath.endsWith("/") ? packageAsPath : packageAsPath + "/"; + } + +} diff --git a/src/main/java/org/sqlite/SQLiteJDBCLoader.java b/src/main/java/org/sqlite/SQLiteJDBCLoader.java index c38503a..6c0e434 100644 --- a/src/main/java/org/sqlite/SQLiteJDBCLoader.java +++ b/src/main/java/org/sqlite/SQLiteJDBCLoader.java @@ -76,7 +76,7 @@ public class SQLiteJDBCLoader * @throws IOException * @throws NoSuchAlgorithmException */ - private static String md5sum(InputStream input) throws IOException, NoSuchAlgorithmException + static String md5sum(InputStream input) throws IOException { BufferedInputStream in = new BufferedInputStream(input); @@ -92,6 +92,10 @@ public class SQLiteJDBCLoader md5out.write(digest.digest()); return md5out.toString(); } + catch (NoSuchAlgorithmException e) + { + throw new IllegalStateException("MD5 algorithm is not available: " + e); + } finally { in.close(); @@ -170,11 +174,6 @@ public class SQLiteJDBCLoader System.err.println(e.getMessage()); return false; } - catch (NoSuchAlgorithmException e) - { - System.err.println(e.getMessage()); - return false; - } } diff --git a/src/test/java/org/sqlite/ConnectionTest.java b/src/test/java/org/sqlite/ConnectionTest.java index 5b6f642..1e466e3 100644 --- a/src/test/java/org/sqlite/ConnectionTest.java +++ b/src/test/java/org/sqlite/ConnectionTest.java @@ -8,6 +8,7 @@ import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import org.junit.BeforeClass; import org.junit.Test; @@ -68,4 +69,17 @@ public class ConnectionTest Connection conn = DriverManager.getConnection("jdbc:sqlite:/"); conn.close(); } + + @Test + public void openResource() throws SQLException + { + Connection conn = DriverManager.getConnection("jdbc:sqlite::resource:org/sqlite/sample.db"); + Statement stat = conn.createStatement(); + ResultSet rs = stat.executeQuery("select * from coordinate"); + assertTrue(rs.next()); + rs.close(); + stat.close(); + conn.close(); + + } } diff --git a/src/test/java/org/sqlite/sample.db b/src/test/java/org/sqlite/sample.db new file mode 100644 index 0000000..abea678 Binary files /dev/null and b/src/test/java/org/sqlite/sample.db differ