/* * 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.karaf.main.lock; import static org.easymock.EasyMock.*; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.apache.felix.utils.properties.Properties; import org.apache.karaf.main.util.BootstrapLogManager; import org.easymock.EasyMock; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public abstract class BaseJDBCLockTest { DefaultJDBCLock lock; Properties props; String user = "root"; String password = ""; String driver; String url; String tableName = "LOCK_TABLE"; String clustername = "karaf_cluster"; int timeout = 10; String momentDatatype = "BIGINT"; String nodeDatatype = "VARCHAR(20)"; String createTableStmtSuffix = ""; Connection connection; DatabaseMetaData metaData; ResultSet resultSet; PreparedStatement preparedStatement; Statement statement; abstract DefaultJDBCLock createLock(Properties props); @BeforeClass public static void setUpTestSuite() { Properties properties = new Properties(); properties.put("karaf.bootstrap.log", "target/karaf.log"); BootstrapLogManager.setProperties(properties); } @Before public void setUp() throws Exception { connection = EasyMock.createNiceMock(Connection.class); metaData = EasyMock.createMock(DatabaseMetaData.class); resultSet = EasyMock.createMock(ResultSet.class); preparedStatement = EasyMock.createMock(PreparedStatement.class); statement = EasyMock.createMock(Statement.class); props = new Properties(); props.put("karaf.lock.jdbc.url", url); props.put("karaf.lock.jdbc.driver", driver); props.put("karaf.lock.jdbc.user", user); props.put("karaf.lock.jdbc.password", password); props.put("karaf.lock.jdbc.table", tableName); props.put("karaf.lock.jdbc.clustername", clustername); props.put("karaf.lock.jdbc.timeout", Integer.toString(timeout)); } @Test public void initShouldCreateTheSchemaIfItNotExists() throws Exception { expect(connection.isClosed()).andReturn(false); connection.setAutoCommit(false); expect(connection.getMetaData()).andReturn(metaData).anyTimes(); expect(metaData.getTables((String) isNull(), (String) isNull(), anyString(), aryEq(new String[]{"TABLE"}))).andReturn(resultSet); expect(resultSet.next()).andReturn(false); resultSet.close(); expectLastCall().atLeastOnce(); expect(connection.isClosed()).andReturn(false); expect(connection.createStatement()).andReturn(statement); expect(statement.execute("CREATE TABLE " + tableName + " (MOMENT " + momentDatatype + ", NODE " + nodeDatatype + ")" + createTableStmtSuffix)).andReturn(false); expect(statement.execute("INSERT INTO " + tableName + " (MOMENT, NODE) VALUES (1, '" + clustername + "')")).andReturn(false); statement.close(); connection.commit(); replay(connection, metaData, statement, preparedStatement, resultSet); lock = createLock(props); verify(connection, metaData, statement, preparedStatement, resultSet); } @Test public void initShouldNotCreateTheSchemaIfItAlreadyExists() throws Exception { connection.setAutoCommit(false); expect(connection.getMetaData()).andReturn(metaData); expect(metaData.getTables((String) isNull(), (String) isNull(), anyString(), aryEq(new String[]{"TABLE"}))).andReturn(resultSet); expect(resultSet.next()).andReturn(true); resultSet.close(); expectLastCall().atLeastOnce(); replay(connection, metaData, statement, preparedStatement, resultSet); lock = createLock(props); verify(connection, metaData, statement, preparedStatement, resultSet); } @Test public void lockShouldReturnTrueItTheTableIsNotLocked() 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(1); preparedStatement.close(); replay(connection, metaData, statement, preparedStatement, resultSet); boolean lockAquired = lock.lock(); verify(connection, metaData, statement, preparedStatement, resultSet); assertTrue(lockAquired); } @Test public void lockShouldReturnFalseIfAnotherRowIsLocked() 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()).andThrow(new SQLException()); preparedStatement.close(); replay(connection, metaData, statement, preparedStatement, resultSet); boolean lockAquired = lock.lock(); verify(connection, metaData, statement, preparedStatement, resultSet); assertFalse(lockAquired); } @Test public void lockShouldReturnFalseIfTheRowIsAlreadyLocked() 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()).andThrow(new SQLException()); preparedStatement.close(); replay(connection, metaData, statement, preparedStatement, resultSet); boolean lockAquired = lock.lock(); verify(connection, metaData, statement, preparedStatement, resultSet); assertFalse(lockAquired); } @Test public void release() throws Exception { initShouldNotCreateTheSchemaIfItAlreadyExists(); reset(connection, metaData, statement, preparedStatement, resultSet); expect(connection.isClosed()).andReturn(false); expect(connection.isClosed()).andReturn(false); expect(connection.isClosed()).andReturn(false); connection.rollback(); connection.close(); replay(connection, metaData, statement, preparedStatement, resultSet); lock.release(); verify(connection, metaData, statement, preparedStatement, resultSet); } @Test public void releaseShouldSucceedForAnAlreadyClosedConnection() throws Exception { initShouldNotCreateTheSchemaIfItAlreadyExists(); reset(connection, metaData, statement, preparedStatement, resultSet); expect(connection.isClosed()).andReturn(true); replay(connection, metaData, statement, preparedStatement, resultSet); lock.release(); verify(connection, metaData, statement, preparedStatement, resultSet); } @Test public void releaseShouldSucceedForANullConnectionReference() throws Exception { initShouldNotCreateTheSchemaIfItAlreadyExists(); reset(connection, metaData, statement, preparedStatement, resultSet); lock.lockConnection = null; replay(connection, metaData, statement, preparedStatement, resultSet); lock.release(); verify(connection, metaData, statement, preparedStatement, resultSet); } @Test public void isAliveShouldReturnTrueIfItHoldsTheLock() throws Exception { initShouldNotCreateTheSchemaIfItAlreadyExists(); reset(connection, metaData, statement, preparedStatement, resultSet); expect(connection.isClosed()).andReturn(false); expect(connection.isClosed()).andReturn(false); expect(connection.prepareStatement("UPDATE " + tableName + " SET MOMENT = 1")).andReturn(preparedStatement); preparedStatement.setQueryTimeout(10); expect(preparedStatement.executeUpdate()).andReturn(1); preparedStatement.close(); replay(connection, metaData, statement, preparedStatement, resultSet); boolean alive = lock.isAlive(); verify(connection, metaData, statement, preparedStatement, resultSet); assertTrue(alive); } @Test public void isAliveShouldReturnFalseIfTheConnectionIsClosed() throws Exception { initShouldNotCreateTheSchemaIfItAlreadyExists(); reset(connection, metaData, statement, preparedStatement, resultSet); expect(connection.isClosed()).andReturn(true); replay(connection, metaData, statement, preparedStatement, resultSet); boolean alive = lock.isAlive(); verify(connection, metaData, statement, preparedStatement, resultSet); assertFalse(alive); } @Test public void isAliveShouldReturnFalseIfTheConnectionIsNull() throws Exception { initShouldNotCreateTheSchemaIfItAlreadyExists(); reset(connection, metaData, statement, preparedStatement, resultSet); lock.lockConnection = null; replay(connection, metaData, statement, preparedStatement, resultSet); boolean alive = lock.isAlive(); verify(connection, metaData, statement, preparedStatement, resultSet); assertFalse(alive); } @Test public void isAliveShouldReturnFalseIfItNotHoldsTheLock() throws Exception { initShouldNotCreateTheSchemaIfItAlreadyExists(); reset(connection, metaData, statement, preparedStatement, resultSet); expect(connection.isClosed()).andReturn(false); expect(connection.isClosed()).andReturn(false); expect(connection.prepareStatement("UPDATE " + tableName + " SET MOMENT = 1")).andReturn(preparedStatement); preparedStatement.setQueryTimeout(10); expect(preparedStatement.executeUpdate()).andThrow(new SQLException()); preparedStatement.close(); replay(connection, metaData, statement, preparedStatement, resultSet); boolean alive = lock.isAlive(); 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); } }