You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ka...@apache.org on 2014/03/31 13:42:31 UTC

svn commit: r1583304 - in /db/derby/code/trunk/java: engine/org/apache/derby/jdbc/ testing/org/apache/derbyTesting/functionTests/tests/lang/ testing/org/apache/derbyTesting/junit/

Author: kahatlen
Date: Mon Mar 31 11:42:30 2014
New Revision: 1583304

URL: http://svn.apache.org/r1583304
Log:
DERBY-6107: Connection to classpath database fails when login timeout is set

Don't cache threads used for connecting to the database when a login
timeout is set, to prevent that the connection attempt uses the wrong
context class loader.

Added:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/LoginTimeoutTestSetup.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/jdbc/InternalDriver.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/DatabaseClassLoadingTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/jdbc/InternalDriver.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/jdbc/InternalDriver.java?rev=1583304&r1=1583303&r2=1583304&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/jdbc/InternalDriver.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/jdbc/InternalDriver.java Mon Mar 31 11:42:30 2014
@@ -39,9 +39,10 @@ import java.util.StringTokenizer;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.logging.Logger;
@@ -94,8 +95,26 @@ public class InternalDriver implements M
      */
     private static boolean deregister = true;
 
+    /**
+     * <p>
+     * An executor service used for executing connection attempts when a
+     * login timeout has been specified.
+     * </p>
+     *
+     * <p>
+     * DERBY-6107: Core pool size and keep alive timeout should be zero so
+     * that no threads are cached. By creating a fresh thread each time a
+     * task is submitted, we make sure that the task will run in a thread
+     * with the same context class loader as the thread that submitted the
+     * task. This is important for example when connecting to a database
+     * using the classpath subsubprotocol, and the database lives in the
+     * context class loader. If threads are cached, a task may execute in
+     * a thread that has a different context class loader.
+     * </p>
+     */
     private static final ExecutorService _executorPool =
-            Executors.newCachedThreadPool(new DaemonThreadFactory());
+            new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0L, TimeUnit.SECONDS,
+                new SynchronousQueue<Runnable>(), new DaemonThreadFactory());
 
 	public static final InternalDriver activeDriver()
 	{

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/DatabaseClassLoadingTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/DatabaseClassLoadingTest.java?rev=1583304&r1=1583303&r2=1583304&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/DatabaseClassLoadingTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/DatabaseClassLoadingTest.java Mon Mar 31 11:42:30 2014
@@ -50,6 +50,7 @@ import org.apache.derbyTesting.junit.Cla
 import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
 import org.apache.derbyTesting.junit.JDBC;
 import org.apache.derbyTesting.junit.JDBCDataSource;
+import org.apache.derbyTesting.junit.LoginTimeoutTestSetup;
 import org.apache.derbyTesting.junit.SupportFilesSetup;
 import org.apache.derbyTesting.junit.SecurityManagerSetup;
 
@@ -131,7 +132,10 @@ public class DatabaseClassLoadingTest ex
            // specific classes is correct. This operation is not allowed in general.
            suite.addTest(SecurityManagerSetup.noSecurityManager(
                    new DatabaseClassLoadingTest("testClassLoadOrdering")));
-           
+
+           // Add test cases accessing a classpath database when a login
+           // timeout has been specified.
+           suite.addTest(loginTimeoutSuite());
 
            test = new SupportFilesSetup(suite,
                    new String[] {
@@ -185,7 +189,46 @@ public class DatabaseClassLoadingTest ex
                 }
         };
     }
-    
+
+    /**
+     * Create a test suite that verifies the fix for DERBY-6107. Connection
+     * attempts used to fail when trying to access a classpath database that
+     * lived in the context class loader, if a login timeout was used and a
+     * previous connection attempt had been made from a thread that did not
+     * have the database in its context class loader.
+     */
+    private static Test loginTimeoutSuite() throws Exception {
+        TestSuite suite = new TestSuite("Class loading with login timeout");
+
+        // First run a test when the database is not in the classpath.
+        // Expect the connection attempt to fail.
+        suite.addTest(
+            new DatabaseClassLoadingTest("testLoginTimeoutNotInClasspath"));
+
+        // Then try again with the database in the classpath. Should succeed.
+        // Failed before DERBY-6107.
+        //
+        // Only add this test case if we can close the URLClassLoader when
+        // we're done. Otherwise, we won't be able to delete the jar file
+        // afterwards. (DERBY-2162)
+        if (ClasspathSetup.supportsClose()) {
+            suite.addTest(
+                new ClasspathSetup(
+                    new DatabaseClassLoadingTest("testLoginTimeoutInClasspath"),
+                    SupportFilesSetup.getReadOnlyURL("dclt.jar")));
+        }
+
+        // Finally, check that the database cannot be found anymore after
+        // it has been removed from the classpath.
+        suite.addTest(
+            new DatabaseClassLoadingTest("testLoginTimeoutNotInClasspath"));
+
+        // All of this should be done with a login timeout. Set the timeout
+        // to a high value, so that the connection attempts don't actually
+        // time out.
+        return new LoginTimeoutTestSetup(suite, 100);
+    }
+
     /**
      * Test the routines fail before the jars that contain their
      * code have been installed and/or set in the classpath.
@@ -1187,9 +1230,58 @@ public class DatabaseClassLoadingTest ex
         
         s.close();
     }
-    
-            
-  
+
+    /**
+     * Test that a classpath database is not found when it's not in the
+     * classpath and there is a login timeout.
+     * @see #loginTimeoutSuite()
+     */
+    public void testLoginTimeoutNotInClasspath() throws SQLException {
+        checkConnectionToClasspathDB(false);
+    }
+
+    /**
+     * Test that a classpath database is found when it's in the
+     * classpath and there is a login timeout.
+     * @see #loginTimeoutSuite()
+     */
+    public void testLoginTimeoutInClasspath() throws SQLException {
+        checkConnectionToClasspathDB(true);
+    }
+
+    /**
+     * Check if it is possible to connect to a classpath database.
+     *
+     * @param databaseInClasspath if {@code true}, expect that the database
+     * can be connected to; otherwise, expect that the database cannot be
+     * found.
+     */
+    private void checkConnectionToClasspathDB(boolean databaseInClasspath) {
+        String dbName = "classpath:dbro";
+        DataSource ds = JDBCDataSource.getDataSource(dbName);
+        try {
+            ds.getConnection().close();
+            // We should only be able to get a connection if the database is
+            // in the classpath.
+            assertTrue(
+                "Could connect to database when it was not in the classpath",
+                databaseInClasspath);
+        } catch (SQLException sqle) {
+            // If the database is not in the classpath, we expect
+            // ERROR XJ004: Database 'classpath:dbro' not found.
+            if (databaseInClasspath) {
+                fail("Could not connect to the database", sqle);
+            } else {
+                assertSQLState("XJ004", sqle);
+            }
+        }
+
+        // If we managed to boot the database, shut it down again.
+        if (databaseInClasspath) {
+            JDBCDataSource.shutdownDatabase(ds);
+        }
+    }
+
     private void installJar(String resource, String jarName) throws SQLException, MalformedURLException
     {        
         URL jar = SupportFilesSetup.getReadOnlyURL(resource);

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/LoginTimeoutTestSetup.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/LoginTimeoutTestSetup.java?rev=1583304&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/LoginTimeoutTestSetup.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/LoginTimeoutTestSetup.java Mon Mar 31 11:42:30 2014
@@ -0,0 +1,57 @@
+/*
+ * Derby - Class org.apache.derbyTesting.junit.LoginTimeoutTestSetup
+ *
+ * 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.junit;
+
+import junit.framework.Test;
+
+/**
+ * A decorator that changes the login timeout for the current configuration
+ * and resets it afterwards.
+ */
+public class LoginTimeoutTestSetup extends BaseJDBCTestSetup {
+
+    private int originalLoginTimeout;
+    private final int newLoginTimeout;
+
+    /**
+     * Create a decorator that makes {@code test} run with a login timeout.
+     *
+     * @param test the test to decorate
+     * @param timeout the login timeout in seconds
+     */
+    public LoginTimeoutTestSetup(Test test, int timeout) {
+        super(test);
+        newLoginTimeout = timeout;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        TestConfiguration config = getTestConfiguration();
+        originalLoginTimeout = config.getLoginTimeout();
+        config.setLoginTimeout(newLoginTimeout);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        getTestConfiguration().setLoginTimeout(originalLoginTimeout);
+        super.tearDown();
+    }
+
+}

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/LoginTimeoutTestSetup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java?rev=1583304&r1=1583303&r2=1583304&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java Mon Mar 31 11:42:30 2014
@@ -1824,6 +1824,15 @@ public final class TestConfiguration {
         return connector.getLoginTimeout();
     }
 
+    /**
+     * Set the login timeout for the connector.
+     * @param seconds the login timeout in seconds
+     * @throws SQLException if the timeout cannot be set
+     */
+    public void setLoginTimeout(int seconds) throws SQLException {
+        connector.setLoginTimeout(seconds);
+    }
+
     public void waitForShutdownComplete(String physicalDatabaseName) {
         String path = getDatabasePath(physicalDatabaseName);
         boolean lockfilepresent = true;