You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jg...@apache.org on 2011/06/27 21:21:18 UTC

svn commit: r1140285 - in /karaf/branches/karaf-2.1.x/main/src: main/java/org/apache/karaf/main/ test/java/org/apache/karaf/main/

Author: jgoodyear
Date: Mon Jun 27 19:21:18 2011
New Revision: 1140285

URL: http://svn.apache.org/viewvc?rev=1140285&view=rev
Log:
[KARAF-689] Karaf instance can wrongly obtain a lock when the locking table is empty on a Oracle DB

Applying patch from Ton Swieb, with thanks :)


Modified:
    karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/OracleJDBCLock.java
    karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/Statements.java
    karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockIntegrationTest.java
    karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockTest.java
    karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/OracleJDBCLockTest.java

Modified: karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/OracleJDBCLock.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/OracleJDBCLock.java?rev=1140285&r1=1140284&r2=1140285&view=diff
==============================================================================
--- karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/OracleJDBCLock.java (original)
+++ karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/OracleJDBCLock.java Mon Jun 27 19:21:18 2011
@@ -18,6 +18,8 @@
  */
 package org.apache.karaf.main;
 
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
 import java.util.Properties;
 
 /**
@@ -67,4 +69,37 @@ public class OracleJDBCLock extends Defa
     boolean updateLock() {
         return aquireLock();
     }
+    
+    /**
+     * A SELECT FOR UPDATE does not create a database lock when the SELECT FOR UPDATE is performed
+     * on an empty selection. So a succesfull call to {@link DefaultJDBCLock#aquireLock()} is not sufficient to 
+     * ensure that we are the only one who have acquired the lock.
+     */
+    @Override
+    boolean aquireLock() {
+    	return super.aquireLock() && lockAcquiredOnNonEmptySelection();
+    }
+    
+    //Verify that we have a non empty record set.
+    private boolean lockAcquiredOnNonEmptySelection() {
+        String verifySelectionNotEmpytStatement = statements.getLockVerifySelectionNotEmptyStatement();
+        PreparedStatement preparedStatement = null;
+        boolean lockAquired = false;
+        
+        try {
+            preparedStatement = getConnection().prepareStatement(verifySelectionNotEmpytStatement);
+            preparedStatement.setQueryTimeout(timeout);
+            ResultSet rs = preparedStatement.executeQuery();
+            if (rs.next()) {
+            	lockAquired = rs.getInt(1) > 0;
+            } else {
+            	LOG.warning("Failed to acquire database lock. Missing database lock record.");
+            }
+        } catch (Exception e) {
+            LOG.warning("Failed to acquire database lock: " + e);
+        }finally {
+            closeSafely(preparedStatement);
+        }        
+        return lockAquired;
+    }
 }

Modified: karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/Statements.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/Statements.java?rev=1140285&r1=1140284&r2=1140285&view=diff
==============================================================================
--- karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/Statements.java (original)
+++ karaf/branches/karaf-2.1.x/main/src/main/java/org/apache/karaf/main/Statements.java Mon Jun 27 19:21:18 2011
@@ -30,6 +30,7 @@ public class Statements {
     private String[] lockCreateSchemaStatements;
     private String lockCreateStatement;
     private String lockUpdateStatement;
+    private String lockVerifySelectionNotEmptyStatement;
     
     public String[] getLockCreateSchemaStatements(long moment) {
         if (lockCreateSchemaStatements == null) {
@@ -67,6 +68,20 @@ public class Statements {
         this.lockUpdateStatement = lockUpdateStatement;
     }
 
+	public void setLockVerifySelectionNotEmptyStatement(String lockVerifySelectionNotEmptyStatement) {
+		this.lockVerifySelectionNotEmptyStatement = lockVerifySelectionNotEmptyStatement;
+	}
+
+	public String getLockVerifySelectionNotEmptyStatement() {
+		if (lockVerifySelectionNotEmptyStatement == null) {
+			 //The lock create and lock update are perfomed on the whole table instead of 
+			 //the cluster. So not taking the node into account for now.
+			lockVerifySelectionNotEmptyStatement = "SELECT COUNT(*) FROM " + getFullLockTableName();
+		}
+		return lockVerifySelectionNotEmptyStatement;
+	}
+    
+    
     long getCurrentTimeMillis() {
         return System.currentTimeMillis();
     }

Modified: karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockIntegrationTest.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockIntegrationTest.java?rev=1140285&r1=1140284&r2=1140285&view=diff
==============================================================================
--- karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockIntegrationTest.java (original)
+++ karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockIntegrationTest.java Mon Jun 27 19:21:18 2011
@@ -167,6 +167,20 @@ public abstract class BaseJDBCLockIntegr
     }
     
     @Test
+    public void lockShouldReturnFalseIfTheTableIsEmpty() throws Exception {
+        Connection connection = null;
+        try {
+            lock = createLock(props);
+            truncateTable(); //Empty the table
+            connection = lock(tableName, clustername);
+            
+            assertFalse(lock.lock());
+        } finally {
+            close(connection);
+        }
+    }
+    
+    @Test
     public void release() throws Exception {
         lock = createLock(props);
         
@@ -309,6 +323,11 @@ public abstract class BaseJDBCLockIntegr
         executeStatement("UPDATE " + tableName + " SET MOMENT = " + System.currentTimeMillis());
     }
 
+    void truncateTable() throws SQLException, ClassNotFoundException {
+	    executeStatement("TRUNCATE TABLE " + tableName);
+    }
+    
+    
     Connection lock(String table, String node) throws ClassNotFoundException, SQLException {
         Connection connection = null;
         Statement statement = null;

Modified: karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockTest.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockTest.java?rev=1140285&r1=1140284&r2=1140285&view=diff
==============================================================================
--- karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockTest.java (original)
+++ karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/BaseJDBCLockTest.java Mon Jun 27 19:21:18 2011
@@ -307,4 +307,28 @@ public abstract class BaseJDBCLockTest {
         verify(connection, metaData, statement, preparedStatement, resultSet);
         assertFalse(alive);
     }
+    
+    @Test
+    public void lockShouldReturnFalseIfTableIsEmpty() throws Exception {
+        initShouldNotCreateTheSchemaIfItAlreadyExists();
+        reset(connection, metaData, statement, preparedStatement, resultSet);
+        
+        expect(connection.isClosed()).andReturn(false);
+        expect(connection.prepareStatement("SELECT * FROM " + tableName + " FOR UPDATE")).andReturn(preparedStatement);
+        preparedStatement.setQueryTimeout(10);
+        expect(preparedStatement.execute()).andReturn(true);
+        preparedStatement.close();
+        expect(connection.isClosed()).andReturn(false);
+        expect(connection.prepareStatement("UPDATE " + tableName + " SET MOMENT = 1")).andReturn(preparedStatement);
+        preparedStatement.setQueryTimeout(10);
+        expect(preparedStatement.executeUpdate()).andReturn(0);
+        preparedStatement.close();
+        
+        replay(connection, metaData, statement, preparedStatement, resultSet);
+        
+        boolean lockAquired = lock.lock();
+        
+        verify(connection, metaData, statement, preparedStatement, resultSet);
+        assertFalse(lockAquired);
+    }
 }

Modified: karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/OracleJDBCLockTest.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/OracleJDBCLockTest.java?rev=1140285&r1=1140284&r2=1140285&view=diff
==============================================================================
--- karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/OracleJDBCLockTest.java (original)
+++ karaf/branches/karaf-2.1.x/main/src/test/java/org/apache/karaf/main/OracleJDBCLockTest.java Mon Jun 27 19:21:18 2011
@@ -74,6 +74,13 @@ public class OracleJDBCLockTest extends 
         preparedStatement.setQueryTimeout(10);
         expect(preparedStatement.execute()).andReturn(true);
         preparedStatement.close();
+        expect(connection.isClosed()).andReturn(false);
+        expect(connection.prepareStatement("SELECT COUNT(*) FROM " + tableName)).andReturn(preparedStatement);
+        preparedStatement.setQueryTimeout(10);
+        expect(preparedStatement.executeQuery()).andReturn(resultSet);
+        expect(resultSet.next()).andReturn(Boolean.TRUE);
+        expect(resultSet.getInt(1)).andReturn(1);
+        preparedStatement.close();
         
         replay(connection, metaData, statement, preparedStatement, resultSet);
         
@@ -134,6 +141,13 @@ public class OracleJDBCLockTest extends 
         preparedStatement.setQueryTimeout(10);
         expect(preparedStatement.execute()).andReturn(true);
         preparedStatement.close();
+        expect(connection.isClosed()).andReturn(false);
+        expect(connection.prepareStatement("SELECT COUNT(*) FROM " + tableName)).andReturn(preparedStatement);
+        preparedStatement.setQueryTimeout(10);
+        expect(preparedStatement.executeQuery()).andReturn(resultSet);
+        expect(resultSet.next()).andReturn(Boolean.TRUE);
+        expect(resultSet.getInt(1)).andReturn(1);
+        preparedStatement.close();
         
         replay(connection, metaData, statement, preparedStatement, resultSet);
         
@@ -162,4 +176,30 @@ public class OracleJDBCLockTest extends 
         verify(connection, metaData, statement, preparedStatement, resultSet);
         assertFalse(alive);
     }
+    
+    @Test
+    public void lockShouldReturnFalseIfTableIsEmpty() throws Exception {
+        initShouldNotCreateTheSchemaIfItAlreadyExists();
+        reset(connection, metaData, statement, preparedStatement, resultSet);
+        
+        expect(connection.isClosed()).andReturn(false);
+        expect(connection.prepareStatement("SELECT * FROM " + tableName + " FOR UPDATE")).andReturn(preparedStatement);
+        preparedStatement.setQueryTimeout(10);
+        expect(preparedStatement.execute()).andReturn(true);
+        preparedStatement.close();
+        expect(connection.isClosed()).andReturn(false);
+        expect(connection.prepareStatement("SELECT COUNT(*) FROM " + tableName)).andReturn(preparedStatement);
+        preparedStatement.setQueryTimeout(10);
+        expect(preparedStatement.executeQuery()).andReturn(resultSet);
+        expect(resultSet.next()).andReturn(Boolean.TRUE);
+        expect(resultSet.getInt(1)).andReturn(0);
+        preparedStatement.close();
+        
+        replay(connection, metaData, statement, preparedStatement, resultSet);
+        
+        boolean lockAquired = lock.lock();
+        
+        verify(connection, metaData, statement, preparedStatement, resultSet);
+        assertFalse(lockAquired);
+    }
 }