OSDN Git Service

git-svn-id: http://www.xerial.org/svn/project/XerialJ/trunk/sqlite-jdbc@3265 ae02f08e...
authorleo <leo@ae02f08e-27ec-0310-ae8c-8ba02fe2eafd>
Tue, 28 Apr 2009 03:18:13 +0000 (03:18 +0000)
committerleo <leo@ae02f08e-27ec-0310-ae8c-8ba02fe2eafd>
Tue, 28 Apr 2009 03:18:13 +0000 (03:18 +0000)
Makefile
pom.xml
src/main/java/org/sqlite/Conn.java
src/main/java/org/sqlite/JDBC.java
src/main/java/org/sqlite/ResourceFinder.java [new file with mode: 0644]
src/main/java/org/sqlite/SQLiteJDBCLoader.java
src/test/java/org/sqlite/ConnectionTest.java
src/test/java/org/sqlite/sample.db [new file with mode: 0644]

index 34ffd01..97bc2d2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -71,12 +71,14 @@ $(UPDATE_FLAG): $(OSINFO_PROG) $(SQLITE_DLL)
        touch $(UPDATE_FLAG)\r
 \r
 native: $(UPDATE_FLAG)\r
+       mvn test\r
 \r
 NATIVE_DLL=$(WORK_DIR)/$(LIB_FOLDER)/$(LIBNAME)\r
 \r
 package: $(UPDATE_FLAG)\r
        mkdir -p $(RESOURCE_DIR)/native/$(LIB_FOLDER)\r
        cp $(NATIVE_DLL) $(RESOURCE_DIR)/native/$(LIB_FOLDER)\r
+       rm -rf target/dependency-maven-plugin-markers\r
        mvn package\r
 \r
 clean-native:\r
diff --git a/pom.xml b/pom.xml
index 79fa785..1d0be96 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
   <modelVersion>4.0.0</modelVersion>\r
   <groupId>org.xerial</groupId>\r
   <artifactId>sqlite-jdbc</artifactId>\r
-  <version>3.6.13</version>\r
+  <version>3.6.13-SNAPSHOT</version>\r
   <name>SQLite JDBC</name>\r
   <description>SQLite JDBC library</description>\r
 \r
@@ -55,7 +55,7 @@
         <executions>\r
           <execution>\r
             <id>unpack-dependency</id>\r
-            <phase>generate-sources</phase>\r
+            <phase>package</phase>\r
             <goals>\r
               <goal>unpack</goal>\r
             </goals>\r
@@ -68,6 +68,7 @@
                   <type>jar</type>\r
                 </artifactItem>\r
               </artifactItems>\r
+              <overWrite>true</overWrite>\r
               <outputDirectory>target/classes</outputDirectory>\r
             </configuration>\r
           </execution>\r
index f0b6685..33b23c5 100644 (file)
 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;
index afc248b..cae0561 100644 (file)
@@ -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 (file)
index 0000000..0ee3c7e
--- /dev/null
@@ -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 + "/";
+    }
+
+}
index c38503a..6c0e434 100644 (file)
@@ -76,7 +76,7 @@ public class SQLiteJDBCLoader
      * @throws IOException\r
      * @throws NoSuchAlgorithmException\r
      */\r
-    private static String md5sum(InputStream input) throws IOException, NoSuchAlgorithmException\r
+    static String md5sum(InputStream input) throws IOException\r
     {\r
         BufferedInputStream in = new BufferedInputStream(input);\r
 \r
@@ -92,6 +92,10 @@ public class SQLiteJDBCLoader
             md5out.write(digest.digest());\r
             return md5out.toString();\r
         }\r
+        catch (NoSuchAlgorithmException e)\r
+        {\r
+            throw new IllegalStateException("MD5 algorithm is not available: " + e);\r
+        }\r
         finally\r
         {\r
             in.close();\r
@@ -170,11 +174,6 @@ public class SQLiteJDBCLoader
             System.err.println(e.getMessage());\r
             return false;\r
         }\r
-        catch (NoSuchAlgorithmException e)\r
-        {\r
-            System.err.println(e.getMessage());\r
-            return false;\r
-        }\r
 \r
     }\r
 \r
index 5b6f642..1e466e3 100644 (file)
@@ -8,6 +8,7 @@ import java.sql.DriverManager;
 import java.sql.PreparedStatement;\r
 import java.sql.ResultSet;\r
 import java.sql.SQLException;\r
+import java.sql.Statement;\r
 \r
 import org.junit.BeforeClass;\r
 import org.junit.Test;\r
@@ -68,4 +69,17 @@ public class ConnectionTest
         Connection conn = DriverManager.getConnection("jdbc:sqlite:/");\r
         conn.close();\r
     }\r
+\r
+    @Test\r
+    public void openResource() throws SQLException\r
+    {\r
+        Connection conn = DriverManager.getConnection("jdbc:sqlite::resource:org/sqlite/sample.db");\r
+        Statement stat = conn.createStatement();\r
+        ResultSet rs = stat.executeQuery("select * from coordinate");\r
+        assertTrue(rs.next());\r
+        rs.close();\r
+        stat.close();\r
+        conn.close();\r
+\r
+    }\r
 }\r
diff --git a/src/test/java/org/sqlite/sample.db b/src/test/java/org/sqlite/sample.db
new file mode 100644 (file)
index 0000000..abea678
Binary files /dev/null and b/src/test/java/org/sqlite/sample.db differ