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 rh...@apache.org on 2009/08/18 17:25:03 UTC

svn commit: r805448 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/io/ testing/org/apache/derbyTesting/functionTests/tests/store/ testing/org/apache/derbyTesting/junit/

Author: rhillegas
Date: Tue Aug 18 15:25:03 2009
New Revision: 805448

URL: http://svn.apache.org/viewvc?rev=805448&view=rev
Log:
DERBY-700: Gracefully handle OverlappingFileLockException if someone attempts to boot a database twice.

Added:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/ClassLoaderBootTest.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/DirFile4.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/DirFile4.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/DirFile4.java?rev=805448&r1=805447&r2=805448&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/DirFile4.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/DirFile4.java Tue Aug 18 15:25:03 2009
@@ -33,6 +33,7 @@
 import java.io.RandomAccessFile;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
 
 /**
  * This class implements the StorageFile interface using features of Java 1.4 not available in earlier
@@ -186,7 +187,31 @@
 				SanityManager.THROWASSERT("Unable to Acquire Exclusive Lock on "
 										  + getPath(), ioe);
 			}
-		}
+		} catch (OverlappingFileLockException ofle)
+        {
+            //
+            // Under Java 6 and later, this exception is raised if the database
+            // has been opened by another Derby instance in a different
+            // ClassLoader in this VM. See DERBY-700.
+            //
+            // The OverlappingFileLockException is raised by the
+            // lockFileChannel.tryLock() call above.
+            //
+            try {
+                lockFileChannel.close();
+                lockFileOpen.close();
+            } catch (IOException e)
+            {
+                if (SanityManager.DEBUG)
+                {
+                    SanityManager.THROWASSERT("Error closing file channel "
+                                              + getPath(), e);
+                }
+            }
+            lockFileChannel=null;
+            lockFileOpen = null;
+            status = EXCLUSIVE_FILE_LOCK_NOT_AVAILABLE;
+        }
     
 		return status;
 	} // end of getExclusiveFileLock

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/ClassLoaderBootTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/ClassLoaderBootTest.java?rev=805448&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/ClassLoaderBootTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/ClassLoaderBootTest.java Tue Aug 18 15:25:03 2009
@@ -0,0 +1,375 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.functionTests.tests.store.ClassLoaderBootTest
+
+   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.store;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLStreamHandlerFactory;
+import java.sql.*;
+import java.security.AccessController;
+import java.security.AccessControlContext;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.security.CodeSource;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.extensions.TestSetup;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
+import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
+import org.apache.derbyTesting.junit.JDBC;
+import org.apache.derbyTesting.junit.JDBCDataSource;
+import org.apache.derbyTesting.junit.SecurityManagerSetup;
+import org.apache.derbyTesting.junit.SystemPropertyTestSetup;
+
+
+/*
+ * This class tests a database boots using  class loaders. Test cases in this
+ * class checks only one instance of a database can exist evenif database is 
+ * booted using different class loader instances.    
+ */
+public class ClassLoaderBootTest extends BaseJDBCTestCase {
+
+    private static URL derbyClassLocation; 
+	static {
+        // find the location of derby jar file or location 
+        // of classes. 
+        CodeSource cs;
+        try {
+            Class cls = Class.forName("org.apache.derby.database.Database");
+            cs = cls.getProtectionDomain().getCodeSource();
+        } catch (ClassNotFoundException e) {
+            cs = null;
+        }
+
+        if(cs == null )
+            derbyClassLocation = null;        
+        else 
+            derbyClassLocation = cs.getLocation();
+	}
+        
+
+    private ClassLoader loader_1;
+    private ClassLoader loader_2;
+    private ClassLoader mainLoader;
+
+
+    public ClassLoaderBootTest(String name ) {
+        super(name);
+    }
+
+    /**
+     * Runs the tests in the default embedded configuration and then
+     * the client server configuration.
+     */
+    public static Test suite()
+    {
+        TestSuite suite = new TestSuite(ClassLoaderBootTest.class);
+        Test test = suite;
+        TestSetup setup = 
+            new CleanDatabaseTestSetup(test) {
+                protected void decorateSQL(Statement s) throws SQLException {
+                    // table used to test  export.
+                    s.execute("CREATE TABLE BOOKS(id int," +
+                              "name varchar(30)," + 
+                              "content clob, " + 
+                              "pic blob )");
+                }
+                 protected void setUp() throws Exception {
+                     super.setUp();
+                     //shutdown the database. 
+                     DataSource ds = JDBCDataSource.getDataSource();
+                     JDBCDataSource.shutdownDatabase(ds);
+                 }
+            };
+            Properties p = new Properties();
+            p.setProperty("derby.infolog.append", "true");
+                                   
+            setup = new SystemPropertyTestSetup(setup,p);
+            return SecurityManagerSetup.noSecurityManager(setup);
+    }
+
+
+    /**
+     * Simple set up, just setup the loaders.
+     * @throws SQLException 
+     */
+    protected void setUp() throws Exception
+    {
+        URL[] urls = new URL[]{derbyClassLocation};
+        mainLoader = java.lang.Thread.currentThread().getContextClassLoader();
+
+        loader_1 = createDerbyClassLoader(urls);
+        loader_2 = createDerbyClassLoader(urls);
+    }
+
+
+    /**
+     * Given a loaded class, this
+     * routine asks the class's class loader for information about where the
+     * class was loaded from. Typically, this is a file, which might be
+     * either a class file or a jar file. The routine figures that out, and
+     * returns the name of the file. If it can't figure it out, it returns null
+     */
+    private DerbyURLClassLoader createDerbyClassLoader(final URL[] urls) 
+        throws Exception 
+    {
+        try {
+            return (DerbyURLClassLoader)AccessController.doPrivileged(
+            new java.security.PrivilegedExceptionAction(){   
+             public Object run()
+             {
+                 return new DerbyURLClassLoader(urls);
+             }
+         });
+        }catch(PrivilegedActionException pae) {
+            throw pae.getException();
+        }
+    }
+
+
+    /**
+     * Given a loaded class, this
+     * routine asks the class's class loader for information about where the
+     * class was loaded from. Typically, this is a file, which might be
+     * either a class file or a jar file. The routine figures that out, and
+     * returns the name of the file. If it can't figure it out, it returns null
+     */
+    private static URL getFileWhichLoadedClass(final Class cls) throws Exception 
+    {
+        try {
+         return (URL)AccessController.doPrivileged(
+         new java.security.PrivilegedExceptionAction(){   
+             public Object run()
+             {
+                 CodeSource cs = null;
+                 cs = cls.getProtectionDomain().getCodeSource ();
+                 if ( cs == null )
+                     return null;        
+                 return cs.getLocation ();
+                 }
+         });
+        }catch(PrivilegedActionException pae) {
+            throw pae.getException();
+        }
+    }
+    
+    private URL getURL(final File file) throws MalformedURLException
+    {
+        try {
+            return (URL) AccessController.doPrivileged
+            (new java.security.PrivilegedExceptionAction(){
+
+                public Object run() throws MalformedURLException{
+                return file.toURL();
+
+                }
+            }
+             );
+        } catch (PrivilegedActionException e) {
+            throw (MalformedURLException) e.getException();
+        } 
+    }
+
+    /* 
+     * Test booting a database, that was alreadt booted by another class loader.
+     */
+	public void testBootingAnAlreadyBootedDatabase() throws SQLException 
+    {
+        //
+        // This test relies on a bug fix in Java 6. Java 5 does not have this
+        // bug fix and will fail this test. See DERBY-700.
+        //
+        if (!JDBC.vmSupportsJDBC4())
+        {
+            println( "The dual boot test only runs on Java 6 and higher." );
+            return;
+        }
+
+        println( "The dual boot test is running." );
+        
+        // first boot the database using one loader and attempt 
+        // to boot it using another loader, it should fail to boot.
+        try {
+
+            setThreadLoader(loader_1);
+            DataSource ds_1 = JDBCDataSource.getDataSource();
+            Connection conn1 = ds_1.getConnection();
+            // now attemp to boot using another class loader.
+            setThreadLoader(loader_2);
+            try {
+                DataSource ds_2 = JDBCDataSource.getDataSource();
+                ds_2.getConnection();
+                fail("booted database that was already booted by another CLR");
+            } catch (SQLException e) {
+                SQLException ne = e.getNextException();
+                assertPreventDualBoot(ne);
+            }
+            
+            // shutdown the database.
+            setThreadLoader(loader_1);
+            JDBCDataSource.shutdownDatabase(ds_1);
+            
+        } catch (SQLException se) {
+            dumpSQLException(se);
+        }finally {
+            // set the thread context loader back to the generic one. 
+            setThreadLoader(mainLoader);
+        }
+    }
+
+    
+    /* 
+     * Test booting a database, that was  booted and shutdown 
+     * by another class loader.
+     */
+	public void testBootingDatabaseShutdownByAnotherCLR() throws SQLException 
+    {
+        // first boot the database using one loader and shutdown and then 
+        // attempt to boot it using another loader, it should boot.
+        try {
+
+            setThreadLoader(loader_1);
+            DataSource ds_1 = JDBCDataSource.getDataSource();
+            Connection conn1 = ds_1.getConnection();
+            //shutdown the database.
+            JDBCDataSource.shutdownDatabase(ds_1);
+            // now attemp to boot using another class loader.
+            setThreadLoader(loader_2);
+            DataSource ds_2 = JDBCDataSource.getDataSource();
+            ds_2.getConnection();
+            // shutdown the database.
+            JDBCDataSource.shutdownDatabase(ds_2);
+            
+        } catch (SQLException se) {
+            dumpSQLException(se);
+        }finally {
+            // set the thread context loader back to the generic one. 
+            setThreadLoader(mainLoader);
+        }
+    }
+
+
+
+    private void setThreadLoader(final ClassLoader which) {
+
+        AccessController.doPrivileged
+        (new java.security.PrivilegedAction(){
+            
+            public Object run()  { 
+                java.lang.Thread.currentThread().setContextClassLoader(which);
+              return null;
+            }
+        });
+    }
+
+
+    private static void dumpSQLException(SQLException se)
+    {
+		while (se != null)
+		{
+			se.printStackTrace();
+			se = se.getNextException();
+		}		
+	}	
+
+	private static void assertPreventDualBoot(SQLException ne) {
+		assertNotNull(ne);
+		String state = ne.getSQLState();
+		assertTrue("Unexpected SQLState:" + state, state.equals("XSDB6"));
+	}
+
+
+
+    /*
+     * Simple specialized URLClassLoader for Derby.  
+     * Filters all derby classes out of parent ClassLoader to ensure
+     * that Derby classes are loaded from the URL specified
+     */
+    public class DerbyURLClassLoader extends URLClassLoader {
+	
+        /**
+         * @see java.net.URLClassLoader#URLClassLoader(URL[] urls)
+         */
+        public DerbyURLClassLoader(URL[] urls) {
+            super(urls);
+        }
+
+
+        /**
+         * @see java.net.URLClassLoader#URLClassLoader(URL[] urls, 
+         *      ClassLoader parent)
+         */
+        public DerbyURLClassLoader(URL[] urls, ClassLoader parent) {
+            super(urls, parent);
+	
+        }
+	
+        /**
+         *@see java.net.URLClassLoader#URLClassLoader(java.net.URL[], 
+         *      java.lang.ClassLoader, java.net.URLStreamHandlerFactory)
+         */
+        public DerbyURLClassLoader(URL[] urls, ClassLoader parent,
+                                   URLStreamHandlerFactory factory) {
+            super(urls, parent, factory);
+		
+        }
+	
+        /* Override the parent class loader to filter out any derby
+         * jars in the classpath.  Any classes that start with 
+         * "org.apache.derby" will load  from the URLClassLoader
+         * 
+         * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
+         */
+        protected synchronized Class loadClass(String name, boolean resolve)
+            throws ClassNotFoundException
+        {
+
+            Class cl = findLoadedClass(name);
+            if (cl == null) {
+                // cut off delegation to parent for certain classes
+                // to ensure loading from the desired source
+                if (!name.startsWith("org.apache.derby")) {
+                    cl = getParent().loadClass(name);
+		    	}
+		    }
+            if (cl == null) cl = findClass(name);
+            if (cl == null) throw new ClassNotFoundException();
+            if (resolve) resolveClass(cl);
+            return cl;
+        }
+
+        /* 
+         * @see java.lang.ClassLoader#loadClass(java.lang.String)
+         */
+        public Class loadClass(String name) throws ClassNotFoundException {
+                return loadClass(name, false);
+        }
+
+    }
+}
+

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

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java?rev=805448&r1=805447&r2=805448&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/_Suite.java Tue Aug 18 15:25:03 2009
@@ -53,6 +53,7 @@
         TestSuite suite = new TestSuite("store");
         
         suite.addTest(BootAllTest.suite());
+        suite.addTest(ClassLoaderBootTest.suite());
         suite.addTest(StreamingColumnTest.suite());
         suite.addTest(Derby3625Test.suite());
         suite.addTest(PositionedStoreStreamTest.suite());

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java?rev=805448&r1=805447&r2=805448&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java Tue Aug 18 15:25:03 2009
@@ -1,6 +1,6 @@
 /*
  *
- * Derby - Class org.apache.derbyTesting.functionTests.util.JDBC
+ * Derby - Class org.apache.derbyTesting.junit.JDBC
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with