db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From krist...@apache.org
Subject svn commit: r835334 - in /db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb: BasicInMemoryDbTest.java DropWhileConnectingTest.java MemoryDbManager.java MogTest.java
Date Thu, 12 Nov 2009 11:15:52 GMT
Author: kristwaa
Date: Thu Nov 12 11:15:52 2009
New Revision: 835334

URL: http://svn.apache.org/viewvc?rev=835334&view=rev
Log:
DERBY-4436: Refactor existing in-memory back end tests and add new tests for dropping database.
Added a new utility class to deal with in-memory databases (convenience methods, much like
BaseJDBCTestCase and friends).
Adjusted existing tests to use MemoryDbManager.
Added new test DropWhileConnectingTest to study the behavior of Derby when several threads
connect and/or execute queries when the database is being shut down. Written primarily to
test the upcoming feature for blocking new connections while certain operations are taking
place (for instance database shutdown and drop).
Note: Some features have been disabled in the committed code, awaiting the drop database functionality
(DERBY-4428). Some additional changes may also be required.

Patch file: derby-4436-1b-inmem_test_changes.diff


Added:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/DropWhileConnectingTest.java
  (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MemoryDbManager.java
  (with props)
Modified:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/BasicInMemoryDbTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MogTest.java

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/BasicInMemoryDbTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/BasicInMemoryDbTest.java?rev=835334&r1=835333&r2=835334&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/BasicInMemoryDbTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/BasicInMemoryDbTest.java
Thu Nov 12 11:15:52 2009
@@ -47,18 +47,36 @@
 public class BasicInMemoryDbTest
         extends BaseJDBCTestCase {
 
+    /**
+     * Helper for dealing with memory databases. For now we use a single
+     * instance for all test classes / cases, as the tests are run single
+     * threaded.
+     */
+    private static final MemoryDbManager dbm =
+            MemoryDbManager.getSharedInstance();
+
     public BasicInMemoryDbTest(String name) {
         super(name);
     }
 
     /**
+     * Closes all opened statements and connections that are known, and also
+     * deletes all known in-memory databases.
+     *
+     * @throws Exception if something goes wrong
+     */
+    public void tearDown()
+            throws Exception {
+        dbm.cleanUp();
+    }
+
+    /**
      * Tries to connect to a non-existing database with the in-memory protocol,
      * expecting an error saying the database doesn't exist.
      */
     public void testFunctionalityPresent() {
         try {
-            getConnection(); // Make sure the driver is loaded (slight hack).
-            DriverManager.getConnection("jdbc:derby:memory:nonExistingDb");
+            dbm.getConnection("nonExistingDb");
         } catch (SQLException e) {
             // Expect a database not found exception.
             assertSQLState("XJ004", e);
@@ -84,15 +102,14 @@
     public void testCreateBackupBootRestore()
             throws IOException, SQLException {
         // 1. Create the database with the in-memory protocol.
-        Connection memCon = DriverManager.getConnection(
-                "jdbc:derby:memory:newMemDb;create=true");
+        Connection memCon = dbm.createDatabase("newMemDb");
         // Make sure the database is newly created.
         assertNull(memCon.getWarnings());
-        Statement stmt = memCon.createStatement();
+        Statement stmt = dbm.createStatement(memCon);
         stmt.executeUpdate("create table toverify(" +
                 "id int, val1 varchar(10), val2 clob, primary key(id))");
-        PreparedStatement ps = memCon.prepareStatement("insert into toverify " +
-                "values (?,?,?)");
+        PreparedStatement ps = dbm.prepareStatement(memCon,
+                "insert into toverify values (?,?,?)");
         // The content to insert into the table.
         String[][] firstContent = new String[][] {
             {"1", "one", getString(1000, CharAlphabet.modernLatinLowercase())},
@@ -145,24 +162,21 @@
         }
 
         // 5. Restore modified backup into memory.
-        memCon = DriverManager.getConnection("jdbc:derby:memory:newMemDb2" +
-                ";createFrom=" + dbPathBackedUp);
+        memCon = dbm.getConnection("newMemDb2;createFrom=" + dbPathBackedUp);
 
         // 6. Verify the new content, where the original in-memory database was
         //    backed up and the directory protocol was used to add one more row
         //    to the backed up database. Now we have restored the on-disk
         //    modified backup, again representing it as an in-memory database.
-        stmt = memCon.createStatement();
+        stmt = dbm.createStatement(memCon);
         rs = stmt.executeQuery("select * from toverify");
         String[][] secondContent = new String[4][3];
         System.arraycopy(firstContent, 0, secondContent, 0, 3);
         System.arraycopy(rowToAdd, 0, secondContent[3], 0, 3);
         JDBC.assertFullResultSet(rs, secondContent);
-        stmt.close();
-        memCon.close();
 
-        // The data will probably hang around in memory at this point.
-        // How to fix that?
+        // Delete the second in memory database.
+        dbm.dropDatabase("newMemDb2");
     }
 
     /**
@@ -172,7 +186,7 @@
      */
     public void testShutdown()
             throws SQLException {
-        DriverManager.getConnection("jdbc:derby:memory:/tmp/myDB;create=true");
+        dbm.createDatabase("/tmp/myDB");
         try {
             DriverManager.getConnection(
                     "jdbc:derby:memory:/tmp/myDB;shutdown=true");
@@ -215,9 +229,8 @@
             throws SQLException {
         final String dbName = "BSDDSSP";
         // Connect to the in-memory database and create a table.
-        Connection con1 = DriverManager.getConnection(
-                "jdbc:derby:memory:" + dbName + ";create=true");
-        Statement stmt1 = con1.createStatement();
+        Connection con1 = dbm.createDatabase(dbName);
+        Statement stmt1 = dbm.createStatement(con1);
         stmt1.execute("create table t (text varchar(255))");
         stmt1.execute("insert into t values ('Inserted into in-memory db')");
         // Connect to the on-disk database. The table we created in the
@@ -233,7 +246,126 @@
             assertSQLState("42X05", sqle);
         }
         con2.close();
-        con1.close();
+    }
+
+    /**
+     * Test deletion of an in-memory database:
+     *  - create database
+     *  - delete database
+     *  - try to connection to database, should fail
+     *  - recreate and delete again
+     *
+     * @throws SQLException if something else goes wrong
+     */
+    // DISABLED because the feature isn't implemented yet (see DERBY-4428)
+    public void DISABLED_testDelete()
+            throws SQLException {
+            Connection conCreate = DriverManager.getConnection(
+                    "jdbc:derby:memory:deleteDbSimple;create=true");
+            Statement stmt = dbm.createStatement(conCreate);
+            JDBC.assertDrainResults(stmt.executeQuery(
+                    "select * from sys.systables"));
+            // Delete the database.
+            try {
+                DriverManager.getConnection(
+                    "jdbc:derby:memory:deleteDbSimple;drop=true");
+                fail("Dropping database should have raised exception.");
+            } catch (SQLException sqle) {
+                assertSQLState("08006", sqle);
+            }
+            // Try to connect to the database again, without creation.
+            try {
+                DriverManager.getConnection(
+                    "jdbc:derby:memory:deleteDbSimple;create=false");
+                fail("Database should not exist after deletion.");
+            } catch (SQLException sqle) {
+                assertSQLState("XJ004", sqle);
+            }
+
+            // Recreate and delete again.
+            conCreate = DriverManager.getConnection(
+                    "jdbc:derby:memory:deleteDbSimple;create=true");
+            stmt = dbm.createStatement(conCreate);
+            JDBC.assertDrainResults(stmt.executeQuery(
+                    "select * from sys.systables"));
+            // Delete the database.
+            try {
+                DriverManager.getConnection(
+                    "jdbc:derby:memory:deleteDbSimple;delete=true");
+                fail("Dropping database should have raised exception.");
+            } catch (SQLException sqle) {
+                assertSQLState("08006", sqle);
+            }
+    }
+
+    /**
+     * Deletes the database when in use by a different connection.
+     * <p>
+     * The expected outcome is that the first connection will be closed when
+     * the second one deletes the database.
+     *
+     * @throws IOException if something goes wrong
+     * @throws SQLException if something goes wrong
+     */
+    // DISABLED because the feature isn't implemented yet (see DERBY-4428)
+    public void DISABLED_testDeleteWhenInUse()
+            throws IOException, SQLException {
+        Connection con = DriverManager.getConnection(
+                "jdbc:derby:memory:deleteDb;create=true");
+        PreparedStatement ps = dbm.prepareStatement(con,
+                "select * from sys.systables");
+        JDBC.assertDrainResults(ps.executeQuery());
+        // Delete the database.
+        try {
+            DriverManager.getConnection(
+                "jdbc:derby:memory:deleteDb;drop=true");
+            fail("Dropping database should have raised exception.");
+        } catch (SQLException sqle) {
+            assertSQLState("08006", sqle);
+        }
+        // Execute query from first connection again.
+        assertTrue(con.isClosed());
+        try {
+            JDBC.assertDrainResults(ps.executeQuery());
+            fail("Database has been dropped, query shouldn't work.");
+        } catch (SQLException sqle) {
+            // Expect no current connection.
+            assertSQLState("08003", sqle);
+        }
+    }
+
+    /**
+     * Shuts down the database when in use by a different connection.
+     * <p>
+     * The expected outcome is that the first connection will be closed when
+     * the second one shuts down the database.
+     *
+     * @throws IOException if something goes wrong
+     * @throws SQLException if something goes wrong
+     */
+    public void testShutdownWhenInUse()
+            throws IOException, SQLException {
+        Connection con = dbm.createDatabase("deleteDb");
+        PreparedStatement ps = dbm.prepareStatement(con,
+                "select * from sys.systables");
+        JDBC.assertDrainResults(ps.executeQuery());
+        // Delete the database.
+        try {
+            DriverManager.getConnection(
+                "jdbc:derby:memory:deleteDb;shutdown=true");
+            fail("Database shutdown should have raised exception.");
+        } catch (SQLException sqle) {
+            assertSQLState("08006", sqle);
+        }
+        // Execute query from first connection again.
+        assertTrue(con.isClosed());
+        try {
+            JDBC.assertDrainResults(ps.executeQuery());
+            fail("Database has been shut down, query shouldn't work.");
+        } catch (SQLException sqle) {
+            // Expect no current connection.
+            assertSQLState("08003", sqle);
+        }
     }
 
     public static Test suite() {

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/DropWhileConnectingTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/DropWhileConnectingTest.java?rev=835334&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/DropWhileConnectingTest.java
(added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/DropWhileConnectingTest.java
Thu Nov 12 11:15:52 2009
@@ -0,0 +1,423 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.functionsTests.tests.memorydb.DropWhileConnectingTest
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you 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.
+
+ */
+package org.apache.derbyTesting.functionTests.tests.memorydb;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Random;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.derbyTesting.functionTests.util.PrivilegedFileOpsForTests;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+
+/**
+ * <em>WARNING: This test isn't finalized!</em>
+ *
+ * Tests the behavior when several threads are accessing the database
+ * and one thread comes in and drops the database.
+ * The success of the test is defined by allowing only certain exceptions
+ * from the access threads / connections. During connection, the following
+ * exceptions are allowed:
+ * <ul> <li>XJ004: Database not found.</li>
+ *      <li>?????: Database access blocked.<li>
+ * </ul>
+ * During normal operation, the set is:
+ * <ul>
+ *      <li>XJ001*: Shutdown exception.</li>
+ *      <li>08003: No current connection.</li>
+ *  </ul>
+ * <p>
+ * This test has a known weakness in that it doesn't execute long-running
+ * queries. It is not clear how these react when the database is dropped
+ * under their feet.
+ */
+public class DropWhileConnectingTest
+        extends BaseJDBCTestCase {
+
+    public DropWhileConnectingTest(String name) {
+        super(name);
+    }
+
+    public void testConcurrentAccessAndDrop()
+            throws SQLException {
+        final String url = "jdbc:derby:memory:testDB";
+        // Database owner is the default user APP.
+        Connection con =
+                MemoryDbManager.getSharedInstance().createDatabase("testDB");
+        con.close();
+        String threadsStr = getSystemProperty("derby.tests.threadCount");
+        if (threadsStr == null) {
+            threadsStr = "20";
+        }
+        int accessThreads = Integer.parseInt(threadsStr);
+        println("threadCount=" + accessThreads);
+        Report report = new Report(getFailureFolder(), accessThreads);
+        // Start the access threads.
+        for (int i=0; i < accessThreads; i++) {
+            Thread t = new Thread(new AccessThread(report, url));
+            t.start();
+        }
+        // Signal start, then wait a little before deleting the database.
+        report.start();
+        sleep(2500);
+        try {
+            MemoryDbManager.getSharedInstance().dropDatabase("testDB");
+            fail("Dropping database should have raised exception.");
+        } catch (SQLException sqle) {
+            assertSQLState("08006", sqle);
+        }
+        println("Drop database request executed.");
+        // Wait for all the threads to finish (they may be sleeping).
+        while (!report.allThreadsDone()) {
+            println("Waiting for " + report.remainingThreads() +
+                    " remaining thread(s) to finish...");
+            sleep(500);
+        }
+        assertFalse(report.toString(), report.hasUnexpectedExceptions());
+        println(report.toString());
+    }
+
+    /**
+     * Puts the current thread to sleep, but <em>does not</em> guarantee
+     * that the thread sleeps for the specified amount of time (interrupts are
+     * not handled).
+     *
+     * @param millis number of milliseconds to sleep
+     */
+    private static void sleep(long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException ie) {
+            alarm("sleep call interrupted");
+        }
+    }
+
+    public static Test suite() {
+       return new TestSuite(DropWhileConnectingTest.class);
+    }
+
+    /**
+     * Simple report class holding results from the test run. Also used to
+     * control the start of the worker threads.
+     */
+    private static class Report {
+        /** Sync object used to start the threads. */
+        private final Object sync = new Object();
+        //@GuardedBy("sync")
+        private boolean ready;
+
+        /** Failure folder where any exceptions will be logged to file. */
+        private final File failureFolder;
+        /** Writer used to log stack traces, lazily initialized. */
+        private PrintWriter writer;
+        /** The number of successful connections made by the worker threads. */
+        private final int[] accessCounts;
+        /** Any unexpected exceptions encountered by the worker threads. */
+        private final Throwable[] exceptions;
+        private int threadsDone;
+        private boolean hasExceptions;
+       
+        /**
+         * Creates a report object.
+         *
+         * @param failureFolder where to write exceptions to
+         * @param accessThreads number of worker threads
+         */
+        public Report(File failureFolder, int accessThreads) {
+            this.failureFolder = failureFolder;
+            accessCounts = new int[accessThreads];
+            exceptions = new Throwable[accessThreads];
+        }
+
+        public synchronized boolean hasUnexpectedExceptions() {
+            return this.hasExceptions;
+        }
+
+        /**
+         * Reports the access count for the specified worker thread.
+         *
+         * @param id worker thread id, must be in the range [0, threadCount>
+         * @param accessCount number of successful accesses made to the db
+         */
+        public synchronized void reportAccessCount(int id, int accessCount) {
+            accessCounts[id] = accessCount;
+            threadsDone++;
+        }
+
+        /**
+         * Reports an unexpected error and the access count for the specified
+         * worker thread.
+         *
+         * @param id worker thread id, must be in the range [0, threadCount>
+         * @param accessCount number of successful accesses made to the db
+         * @param SQL
+         */
+        public synchronized void reportError(int id, int accessCount,
+                                             Throwable error) {
+            reportAccessCount(id, accessCount);
+            exceptions[id] = error;
+            hasExceptions = true;
+            // Print the stack trace to file.
+            dumpToFile(id, error);
+        }
+
+        /**
+         * Tells if all the access threads have finished.
+         *
+         * @return {@code true} if all threads have finished,
+         *      {@code false} otherwise.
+         */
+        public synchronized boolean allThreadsDone() {
+            return (threadsDone == accessCounts.length);
+        }
+
+        public synchronized int remainingThreads() {
+            return accessCounts.length - threadsDone;
+        }
+
+        public Object getSync() {
+            return this.sync;
+        }
+
+        /**
+         * Tells if the test is ready to start.
+         *
+         * @return {@code true} if the access threads can start.
+         */
+        public boolean ready() {
+            synchronized (sync) {
+                return ready;
+            }
+        }
+
+        public void start() {
+            synchronized (sync) {
+                ready = true;
+                sync.notifyAll();
+            }
+        }
+
+        public synchronized String toString() {
+            int totalAccessCount = 0;
+            for (int i=0; i < accessCounts.length; i++) {
+                int c = accessCounts[i];
+                if (c > 0) {
+                    totalAccessCount += c;
+                }
+            }
+            String sep = "\n";
+            StringBuffer sb = new StringBuffer(sep +
+                    "Number of access threads: ").append(accessCounts.length).
+                    append(sep);
+            sb.append("Access count: " + totalAccessCount).append(sep);
+            if (hasExceptions) {
+                sb.append("Exceptions (see " + failureFolder +
+                        "/exceptions.log):" + sep);
+                for (int i=0; i < exceptions.length; i++) {
+                    Throwable t = exceptions[i];
+                    if (t instanceof SQLException) {
+                        SQLException s = (SQLException)t;
+                        sb.append("id=").append(i).append(" : (").
+                        append(s.getSQLState()).append(") ").
+                        append(s.getMessage()).append(sep);
+                    } else if (t != null) {
+                        sb.append("id=").append(i).append(" : (     ) ").
+                        append(t.getMessage()).append(sep);
+
+                       
+                    }
+                }
+            }
+            return sb.toString();
+        }
+
+        /**
+         * Dumps the exception to file.
+         *
+         * @param id internal id for the thread that got the exception
+         * @param exception the exception to dump
+         */
+        private void dumpToFile(int id, Throwable exception) {
+            if (writer == null) {
+                try {
+                    writer = new PrintWriter(
+                            PrivilegedFileOpsForTests.getFileOutputStream(
+                            new File(failureFolder, ("exceptions.log"))));
+                    writer.println(new java.util.Date());
+                } catch (IOException ioe) {
+                    alarm("Failed to create exception log file: " +
+                            ioe.getMessage());
+                }
+            }
+            if (writer != null) {
+                writer.println("-----");
+                writer.println("id=" + id);
+                writer.println("--");
+                exception.printStackTrace(writer);
+                writer.flush();
+            }
+        }
+    }
+
+    /**
+     * Access thread connection to the database and performing a simple SQL
+     * select query. Will accept a few specific exceptions as the database
+     * is deleted "under its feet", all other exceptions are considered a
+     * failure and will be reported.
+     */
+    private static class AccessThread
+            implements Runnable {
+
+        private static final Object LOCK = new Object();
+        //@GuardedBy("LOCK")
+        private static int idCounter = 0;
+        /** Whether to wait at certain points in the execution. */
+        private static final boolean noWait;
+        static {
+            String tmp = getSystemProperty("derby.tests.noWait");
+            noWait = Boolean.valueOf(tmp).booleanValue();
+            println("noWait=" + noWait);
+        }
+
+        private final int id;
+        private final Report master;
+        private final String url;
+        private final Random rnd = new Random();
+        /** Flag used to avoid waiting at multiple points in the execution. */
+        private boolean waited;
+
+        public AccessThread(Report master, String url) {
+            synchronized (LOCK) {
+                this.id = idCounter++;                
+            }
+            this.master = master;
+            // Connect with a different user than the DBO. There is no real
+            // reaon for doing this, other than making sure this user will
+            // be blocked out by Derby during the shutdown.
+            this.url = url + ";user=test;password=test";
+        }
+
+        public void run() {
+            int access = 0;
+            Connection con = null;
+            // Wait for signal to start testing.
+            while (!master.ready()) {
+                synchronized (master.getSync()) {
+                    try {
+                        master.getSync().wait();
+                    } catch (InterruptedException ie) {
+                        // Ignore, just check the loop condition again.
+                    }
+                }
+            }
+            // Start accessing the database.
+            try {
+                while (true) {
+                    waited = false;
+                    try {
+                        con = DriverManager.getConnection(url);
+                        access++;
+                    } catch (SQLException sqle) {
+                        // See if the exception says database not found.
+                        // An additional check would be to make sure this
+                        // happens after the database has been dropped, and
+                        // that it is not caused by a bug during boot.
+                        if (sqle.getSQLState().equals("XJ004")) {
+                            master.reportAccessCount(id, access);
+                            break;
+                        }
+                        // TODO: Adjust SQLState or remove.
+                        if (sqle.getSQLState().equals("XJ005")) {
+                            // Attempt blocked, keep pounding on the database.
+                            allowWait(false);
+                            continue;
+                        }
+                        // The connection process failed unexpectedly.
+                        throw sqle;
+                    }
+                    // The set of allowed exceptions here is different from
+                    // the one during connection.
+                    try {
+                        Statement stmt = con.createStatement();
+                        allowWait(true);
+                        ResultSet rs = stmt.executeQuery(
+                            "select * from sys.systables order by random()");
+                        allowWait(true);
+                        while (rs.next()) {
+                            allowWait(true);
+                            rs.getString(1);
+                        }
+                        rs.close();
+                        stmt.close();
+                        con.close();
+                        allowWait(false);
+                    } catch (SQLException sqle) {
+                        // Accept no current connection here
+                        if (sqle.getSQLState().equals("08003")) {
+                            master.reportAccessCount(id, access);
+                        } else if (sqle.getSQLState().equals("XJ001") &&
+                                sqle.getMessage().indexOf("ShutdownException")
+                                                                        != -1) {
+                            master.reportAccessCount(id, access);
+                        } else {
+                            master.reportError(id, access, sqle);
+                        }
+                    }
+                }
+            } catch (Throwable t) {
+                if (t instanceof org.apache.derby.iapi.error.ShutdownException){
+                    // Not sure if this is a good thing yet.
+                    System.out.println(
+                            "Got ShutdownException (extends RuntimeException)");
+                    master.reportAccessCount(id, access);
+                } else {
+                    master.reportError(id, access, t);
+                }
+            }
+        }
+
+        /**
+         * Method mostly doing nothing, but sometimes it decides to put the
+         * thread to sleep for a little while.
+         */
+        private void allowWait(boolean onlyWaitOnce) {
+            if (!noWait && ((!waited && onlyWaitOnce) || !onlyWaitOnce))
{
+                int split = rnd.nextInt(100);
+                if (split >= 97) {
+                    // Potentially a long sleep
+                    sleep(100 + (long)(rnd.nextDouble() * 1200));
+                    waited = true;
+                } else if (split > 80){
+                    sleep((long)(rnd.nextDouble() * 100));
+                    waited = true;
+                }
+            }
+        }
+    }
+}

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/DropWhileConnectingTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MemoryDbManager.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MemoryDbManager.java?rev=835334&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MemoryDbManager.java
(added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MemoryDbManager.java
Thu Nov 12 11:15:52 2009
@@ -0,0 +1,267 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.functionsTests.tests.memorydb.MemoryDbManager
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you 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.
+
+ */
+package org.apache.derbyTesting.functionTests.tests.memorydb;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+import java.util.ArrayList;
+
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+
+/**
+ * Collection of convenience methods for dealing with in-memory databases.
+ * The class will keep track of databases, connections and statements
+ * created through its methods, and will delete / close these when the
+ * clean up method is invoked. This is very much the same as what
+ * {@code BaseJDBCTestCase} does, with the exception of deleting the
+ * databases.
+ * <p>
+ * Note: It may be possible to integrate this functionality into the existing
+ * JUnit framework, for instance if you want to run the entire test suite with
+ * the in-memory back end.
+ */
+public class MemoryDbManager {
+
+    private static final String ATTR_CREATE = ";create=true";
+
+    /** JDBC protocl prefix used for in-memory databases. */
+    private static final String JDBC_PREFIX = "jdbc:derby:memory:";
+    /** Shared manager instance. */
+    private static final MemoryDbManager DBM = new MemoryDbManager();
+
+    /**
+     * Returns a shared manager instance.
+     *
+     * @return The shared manager instance.
+     */
+    public static MemoryDbManager getSharedInstance() {
+        return DBM;
+    }
+
+    /** List of openend statements, closed at clean up. */
+    private final ArrayList STATEMENTS = new ArrayList();
+    /** List of openend connections, closed at clean up. */
+    private final ArrayList CONNECTIONS = new ArrayList();
+    /** List of created databases, deleted at clean up. */
+    private final ArrayList DATABASES = new ArrayList();
+
+    public MemoryDbManager() { }
+
+    /**
+     * Creates a new connection to the specified database (url).
+     * <p>
+     * Note that the specified URL will be appended to a fixed JDBC protcol
+     * prefix.
+     *
+     * @param dbNameAndAttributes database name and any JDBC url attributes
+     * @return A connection to the specified database.
+     * @throws SQLException if connecting to the database fails
+     */
+    public Connection getConnection(String dbNameAndAttributes)
+            throws SQLException {
+        final String url = JDBC_PREFIX + dbNameAndAttributes;
+        try {
+            DriverManager.getDriver(url);
+        } catch (SQLException sqle) {
+            // Rely on logic in the default method for obtaining a
+            // connection to load the driver.
+            new BaseJDBCTestCase("dummy") {}.getConnection();
+        }
+        Connection con = DriverManager.getConnection(url);
+        if (!CONNECTIONS.contains(con)) {
+            CONNECTIONS.add(con);
+        }
+        return con;
+    }
+
+    /**
+     * Creates a new statement from the given connection and keeps track of
+     * it and closes it when the clean up is invoked.
+     *
+     * @param con the connection to use for creation
+     * @return A new statement.
+     * @throws SQLException if creating the statement fails
+     * @see #cleanUp()
+     */
+    public Statement createStatement(Connection con)
+            throws SQLException {
+        Statement stmt = con.createStatement();
+        STATEMENTS.add(stmt);
+        if (!CONNECTIONS.contains(con)) {
+            CONNECTIONS.add(con);
+        }
+        return stmt;
+    }
+
+    /**
+     * Creates a new prepared statement from the given connection and keeps
+     * track of it and closes it when the clean up is invoked.
+     *
+     * @param con the connection to use for creation
+     * @param sql the sql text to prepare
+     * @return A new prepared statement.
+     * @throws SQLException if creating the statement fails
+     * @see #cleanUp()
+     */
+    public PreparedStatement prepareStatement(Connection con, String sql)
+            throws SQLException {
+        PreparedStatement pStmt = con.prepareStatement(sql);
+        STATEMENTS.add(pStmt);
+        if (!CONNECTIONS.contains(con)) {
+            CONNECTIONS.add(con);
+        }
+        return pStmt;
+    }
+
+    /**
+     * Drops the specified database.
+     * <p>
+     * Note that the specified URL will be appended to a fixed JDBC protcol
+     * prefix.
+     *
+     * @param dbNameAndAttributes the database name and any attributes
+     *      required to access the database (<em>excluding</em> the delete
+     *      attribute, which is added by this method)
+     * @throws SQLException if deleting the database fails
+     */
+    public void dropDatabase(String dbNameAndAttributes)
+            throws SQLException {
+        // DISABLED because the feature isn't implemented yet (see DERBY-4428)
+        if (true) {
+            return;
+        }
+        String url = JDBC_PREFIX + dbNameAndAttributes + ";drop=true";
+        try {
+            DriverManager.getConnection(url);
+            BaseJDBCTestCase.fail("Dropping database should raise exception.");
+        } catch (SQLException sqle) {
+            if (sqle.getSQLState().equals("08006")) {
+                // Database was deleted.
+            } else if (sqle.getSQLState().equals("XJ004")) {
+                // Database didn't exist. Already dropped?
+            } else {
+                BaseJDBCTestCase.assertSQLState("Dropping database failed: (" +
+                        sqle.getSQLState() + ") "+ sqle.getMessage(),
+                        "08006", sqle);
+            }
+        }
+    }
+
+    /**
+     * Creates a new database and keeps track of it to delete it when the
+     * clean up is invoked.
+     * <p>
+     * If the database already exists, a connection to the existing
+     * database is returned.
+     *
+     * @param dbName the database name
+     * @return A connection to the database.
+     * @throws SQLException if creating or connecting to the database fails
+     */
+    public Connection createDatabase(String dbName)
+            throws SQLException {
+        return createDatabase(dbName, null, null, null);
+    }
+
+    /**
+     * Creates a new database and keeps track of it to delete it when the
+     * clean up is invoked.
+     * <p>
+     * If the database already exists, a connection to the existing
+     * database is returned.
+     *
+     * @param dbName the database name
+     * @param dbAttributes database attributes (i.e. encryption)
+     * @param user user name
+     * @param password user password
+     * @return A connection to the database.
+     * @throws SQLException if creating or connecting to the database fails
+     */
+    public Connection createDatabase(String dbName, String dbAttributes,
+                                     String user, String password)
+            throws SQLException {
+        String userAttr = "";
+        if (user != null) {
+            userAttr = ";user=" + user;
+        }
+        if (password != null) {
+            userAttr += ";password=" + password;
+        }
+        String url = dbName;
+        if (dbAttributes != null) {
+            url += ";" + dbAttributes;
+        }
+        if (!userAttr.equals("")) {
+            url += userAttr;
+        }
+        if (url.indexOf(ATTR_CREATE) == -1) {
+            url += ATTR_CREATE;
+        }
+        Connection con = getConnection(url);
+        if (con.getWarnings() != null) {
+            // See if there are more than one warning.
+            SQLWarning w = con.getWarnings();
+            String warnings = w.getMessage();
+            while ((w = w.getNextWarning()) != null) {
+                warnings += " || " + w.getMessage();
+            }
+            BaseJDBCTestCase.fail(
+                    "Warning(s) when creating database: " + warnings);
+        }
+        // Keep track of the database we just created, so that we can
+        // delete it.
+        DATABASES.add(dbName + userAttr);
+        return con;
+
+    }
+
+    /**
+     * Cleans up database resources by closing known statements and
+     * connection, and deleting known in-memory databases.
+     * @throws SQLException
+     */
+    public void cleanUp()
+            throws SQLException {
+        // Close all known statements.
+        for (int i=STATEMENTS.size() -1; i >= 0; i--) {
+            Statement stmt = (Statement)STATEMENTS.remove(i);
+            stmt.close();
+        }
+        // Close all known connections.
+        for (int i=CONNECTIONS.size() -1; i >= 0; i--) {
+            Connection con = (Connection)CONNECTIONS.remove(i);
+            try {
+                con.rollback();
+            } catch (SQLException sqle) {
+                // Ignore this exception.
+            }
+            con.close();
+        }
+        // Delete all known databases.
+        for (int i=DATABASES.size() -1; i >= 0; i--) {
+            dropDatabase((String)DATABASES.remove(i));
+        }
+    }
+}

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MemoryDbManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MogTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MogTest.java?rev=835334&r1=835333&r2=835334&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MogTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/memorydb/MogTest.java
Thu Nov 12 11:15:52 2009
@@ -77,6 +77,8 @@
           } catch (SQLException sqle) {
               // Ignore exceptions during close.
           }
+          // DISABLED because the feature isn't implemented yet (see DERBY-4428)
+          //dropInMemoryDb();
       }
       println("duration-in-memory: " + (System.currentTimeMillis() - start));
   }
@@ -183,20 +185,46 @@
         sqle.initCause(e);
         throw sqle;
     }
-    StringBuffer sb = new StringBuffer("jdbc:derby:");
-    if (usingEmbedded()) {
-        sb.append("memory:");
-    } else {
-        // This is a hack. Change this when proper support for the in-memory
-        // back end has been implemented.
-        sb.append("//");
-        sb.append(TestConfiguration.getCurrent().getHostName());
-        sb.append(':');
-        sb.append(TestConfiguration.getCurrent().getPort());
-        sb.append('/');
-        sb.append("memory:");
-    }
-    sb.append("MogTestDb;create=true");
+    StringBuffer sb = constructUrl().append(";create=true");
     return DriverManager.getConnection(sb.toString());
   }
+
+  /**
+   * Drops the database used by the test.
+   *
+   * @throws SQLException if dropping the database fails
+   */
+    private void dropInMemoryDb()
+            throws SQLException {
+        StringBuffer sb = constructUrl().append(";drop=true");
+        try {
+            DriverManager.getConnection(sb.toString());
+            fail("Dropping database should have raised exception.");
+        } catch (SQLException sqle) {
+            assertSQLState("08006", sqle);
+        }
+    }
+
+    /**
+     * Constructs the default URL for the in-memory test database.
+     *
+     * @return A database URL (without any connection attributes).
+     */
+    private StringBuffer constructUrl() {
+        StringBuffer sb = new StringBuffer("jdbc:derby:");
+        if (usingEmbedded()) {
+            sb.append("memory:");
+        } else {
+            // This is a hack. Change this when proper support for the in-memory
+            // back end has been implemented.
+            sb.append("//");
+            sb.append(TestConfiguration.getCurrent().getHostName());
+            sb.append(':');
+            sb.append(TestConfiguration.getCurrent().getPort());
+            sb.append('/');
+            sb.append("memory:");
+        }
+        sb.append("MogTestDb");
+        return sb;
+    }
 }



Mime
View raw message