/**
* Copyright 2010 Wallace Wadge
*
* Licensed 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 com.jolbox.bonecp;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import com.google.common.base.FinalizableReferenceQueue;
import com.jolbox.bonecp.proxy.ConnectionProxy;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
/**
* @author wwadge
*
*/
public class TestConnectionPartition {
/** mock handle. */
private BoneCP mockPool = createNiceMock(BoneCP.class);
/** mock handle. */
private Logger mockLogger;
/** mock handle. */
private BoneCPConfig mockConfig;
/** mock handle. */
private ConnectionPartition testClass;
@Before
public void setUp() throws NoSuchFieldException, IllegalAccessException
{
mockPool = createNiceMock(BoneCP.class);
mockConfig = createNiceMock(BoneCPConfig.class);
expect(mockConfig.getAcquireIncrement()).andReturn(1).anyTimes();
expect(mockConfig.getMaxConnectionsPerPartition()).andReturn(1).anyTimes();
expect(mockConfig.getMinConnectionsPerPartition()).andReturn(1).anyTimes();
expect(mockConfig.getUsername()).andReturn("testuser").anyTimes();
expect(mockConfig.getPassword()).andReturn("testpass").anyTimes();
expect(mockConfig.getJdbcUrl()).andReturn("testurl").anyTimes();
expect(mockConfig.getPoolName()).andReturn("Junit test").anyTimes();
expect(mockConfig.isDisableConnectionTracking()).andReturn(false).anyTimes();
Map<Connection, Reference<ConnectionHandle>> refs = new HashMap<Connection, Reference<ConnectionHandle>>();
expect(this.mockPool.getFinalizableRefs()).andReturn(refs).anyTimes();
expect(this.mockPool.getConfig()).andReturn(mockConfig).anyTimes();
replay(mockPool, mockConfig);
testClass = new ConnectionPartition(this.mockPool);
mockLogger = TestUtils.mockLogger(testClass.getClass());
makeThreadSafe(mockLogger, true);
mockLogger.error((String)anyObject());
expectLastCall().anyTimes();
replay(mockLogger);
}
/**
* Tests the constructor. Makes sure release helper threads are launched (+ setup other config items).
* @throws NoSuchFieldException
* @throws SecurityException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
@Test
public void testConstructor() {
verify(this.mockPool, mockConfig);
reset(this.mockPool, mockConfig);
}
/**
* Test method for created connections.
* @throws NoSuchFieldException
* @throws SecurityException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
@Test
public void testUpdateCreatedConnections() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
int count = testClass.getCreatedConnections();
testClass.updateCreatedConnections(5);
assertEquals(count+5, testClass.getCreatedConnections());
}
/**
* Test method for created connections.
* @throws NoSuchFieldException
* @throws SecurityException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
@Test
public void testUpdateCreatedConnectionsWithException() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
// Test #2: Same test but fake an exception
ReentrantReadWriteLock mockLock = createNiceMock(ReentrantReadWriteLock.class);
WriteLock mockWriteLock = createNiceMock(WriteLock.class);
Field field = testClass.getClass().getDeclaredField("statsLock");
field.setAccessible(true);
ReentrantReadWriteLock oldLock = (ReentrantReadWriteLock) field.get(testClass);
field.set(testClass, mockLock);
expect(mockLock.writeLock()).andThrow(new RuntimeException()).once().andReturn(mockWriteLock).once();
mockWriteLock.lock();
expectLastCall().once();
replay(mockLock, mockWriteLock);
try{
testClass.updateCreatedConnections(5);
fail("Should have thrown an exception");
} catch (Throwable t){
//do nothing
}
verify(mockLock);
field.set(testClass, oldLock);
}
/**
* Test method for freeConnections
* @throws SQLException
*/
@SuppressWarnings("unchecked")
@Test
public void testFreeConnection() throws SQLException {
int count = testClass.getCreatedConnections();
LinkedBlockingQueue<ConnectionHandle> freeConnections = createNiceMock(LinkedBlockingQueue.class);
makeThreadSafe(freeConnections, true);
testClass.setFreeConnections(freeConnections);
assertEquals(freeConnections, testClass.getFreeConnections());
reset(this.mockPool);
Map<Connection, Reference<ConnectionHandle>> refs = new HashMap<Connection, Reference<ConnectionHandle>>();
expect(this.mockPool.getFinalizableRefs()).andReturn(refs).anyTimes();
FinalizableReferenceQueue finalizableRefQueue = new FinalizableReferenceQueue();
expect(this.mockPool.getFinalizableRefQueue()).andReturn(finalizableRefQueue).anyTimes();
ConnectionHandle mockConnectionHandle = createNiceMock(ConnectionHandle.class);
expect(mockConnectionHandle.getPool()).andReturn(this.mockPool).anyTimes();
expect(freeConnections.offer(mockConnectionHandle)).andReturn(true).anyTimes();
replay(mockConnectionHandle, freeConnections, this.mockPool);
testClass.addFreeConnection(mockConnectionHandle);
verify(mockConnectionHandle, freeConnections);
assertEquals(count+1, testClass.getCreatedConnections());
assertEquals(0, testClass.getRemainingCapacity());
}
/** fail to offer a new connection.
* @throws SQLException
*/
@SuppressWarnings("unchecked")
@Test
public void testFreeConnectionFailing() throws SQLException {
int count = testClass.getCreatedConnections();
LinkedBlockingQueue<ConnectionHandle> freeConnections = createNiceMock(LinkedBlockingQueue.class);
makeThreadSafe(freeConnections, true);
testClass.setFreeConnections(freeConnections);
assertEquals(freeConnections, testClass.getFreeConnections());
reset(this.mockPool);
Map<Connection, Reference<ConnectionHandle>> refs = new HashMap<Connection, Reference<ConnectionHandle>>();
expect(this.mockPool.getFinalizableRefs()).andReturn(refs).anyTimes();
FinalizableReferenceQueue finalizableRefQueue = new FinalizableReferenceQueue();
expect(this.mockPool.getFinalizableRefQueue()).andReturn(finalizableRefQueue).anyTimes();
ConnectionHandle mockConnectionHandle = createNiceMock(ConnectionHandle.class);
expect(mockConnectionHandle.getPool()).andReturn(this.mockPool).anyTimes();
expect(freeConnections.offer(mockConnectionHandle)).andReturn(false);
mockConnectionHandle.internalClose();
expectLastCall().once();
expect(freeConnections.remainingCapacity()).andReturn(1).anyTimes();
Connection mockRealConnection = createNiceMock(Connection.class);
expect(mockConnectionHandle.getInternalConnection()).andReturn(mockRealConnection).anyTimes();
testClass.pool = this.mockPool;
replay(mockConnectionHandle, mockRealConnection, freeConnections, this.mockPool);
testClass.addFreeConnection(mockConnectionHandle);
verify(mockConnectionHandle, freeConnections);
assertEquals(count, testClass.getCreatedConnections());
assertEquals(1, testClass.getRemainingCapacity());
}
/**
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
@Test
public void testGetCreatedConnections() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException{
ReentrantReadWriteLock mockLock = createNiceMock(ReentrantReadWriteLock.class);
ReadLock mockReadLock = createNiceMock(ReadLock.class);
Field field = testClass.getClass().getDeclaredField("statsLock");
field.setAccessible(true);
ReentrantReadWriteLock oldLock = (ReentrantReadWriteLock) field.get(testClass);
field.set(testClass, mockLock);
expect(mockLock.readLock()).andThrow(new RuntimeException()).once().andReturn(mockReadLock).once();
mockReadLock.lock();
expectLastCall().once();
replay(mockLock, mockReadLock);
try{
testClass.getCreatedConnections();
fail("Should have thrown an exception");
} catch (Throwable t){
//do nothing
}
verify(mockLock);
field.set(testClass, oldLock);
}
/**
* Test method for config related stuff.
*/
@Test
public void testConfigStuff() {
assertEquals("testurl", testClass.getUrl());
assertEquals("testuser", testClass.getUsername());
assertEquals("testpass", testClass.getPassword());
assertEquals(1, testClass.getMaxConnections());
assertEquals(1, testClass.getMinConnections());
assertEquals(1, testClass.getAcquireIncrement());
assertNotNull(testClass.toString());
}
/**
* Test method for unable to create more transactions.
*/
@Test
public void testUnableToCreateMoreTransactionsFlag() {
testClass.setUnableToCreateMoreTransactions(true);
assertEquals(testClass.isUnableToCreateMoreTransactions(), true);
}
/** Test finalizer.
* @throws SQLException
* @throws InterruptedException
*/
@Test
public void testFinalizer() throws SQLException, InterruptedException{
ConnectionHandle mockConnectionHandle = createNiceMock(ConnectionHandle.class);
expect(mockConnectionHandle.isInReplayMode()).andReturn(true).anyTimes();
Connection mockConnection = createNiceMock(Connection.class);
Connection connection = MemorizeTransactionProxy.memorize(mockConnection, mockConnectionHandle);
expect(mockConnectionHandle.getInternalConnection()).andReturn(connection).anyTimes();
mockConnection.close();
expectLastCall().once();
reset(this.mockPool, this.mockConfig);
Map<Connection, Reference<ConnectionHandle>> refs = new HashMap<Connection, Reference<ConnectionHandle>>();
expect(this.mockPool.getFinalizableRefs()).andReturn(refs).anyTimes();
FinalizableReferenceQueue finalizableRefQueue = new FinalizableReferenceQueue();
expect(this.mockPool.getFinalizableRefQueue()).andReturn(finalizableRefQueue).anyTimes();
expect(mockConnectionHandle.getPool()).andReturn(this.mockPool).anyTimes();
expect(this.mockPool.getConfig()).andReturn(mockConfig).anyTimes();
expect(mockConfig.getPoolName()).andReturn("foo").once();
makeThreadSafe(this.mockPool, true);
replay(mockConnection, mockConnectionHandle, this.mockPool, mockConfig);
testClass.trackConnectionFinalizer(mockConnectionHandle);
reset(mockConnectionHandle);
mockConnectionHandle = null; // prompt GC to kick in
for (int i=0; i < 500; i++){
System.gc();System.gc();System.gc();
Thread.sleep(20);
try{
verify(mockConnection);
break; // we succeeded
} catch (Throwable t){
// t.printStackTrace();
// do nothing, try again
Thread.sleep(20);
}
}
}
/** Test finalizer with error.
* @throws SQLException
* @throws InterruptedException
*/
@Test
public void testFinalizerCoverageException() throws SQLException, InterruptedException{
ConnectionHandle mockConnectionHandle = createNiceMock(ConnectionHandle.class);
Connection mockConnection = createNiceMock(Connection.class);
expect(mockConnectionHandle.getInternalConnection()).andReturn(mockConnection).anyTimes();
mockConnection.close();
expectLastCall().andThrow(new SQLException("fake reason")).once();
reset(this.mockPool);
Map<Connection, Reference<ConnectionHandle>> refs = new HashMap<Connection, Reference<ConnectionHandle>>();
expect(this.mockPool.getFinalizableRefs()).andReturn(refs).anyTimes();
FinalizableReferenceQueue finalizableRefQueue = new FinalizableReferenceQueue();
expect(this.mockPool.getFinalizableRefQueue()).andReturn(finalizableRefQueue).anyTimes();
expect(mockConnectionHandle.getPool()).andReturn(this.mockPool).anyTimes();
replay(mockConnectionHandle, mockConnection, this.mockPool);
testClass.trackConnectionFinalizer(mockConnectionHandle);
testClass.statsLock = null; // this makes it blow up.
reset(mockLogger);
mockLogger.error((String)anyObject());
expectLastCall().anyTimes();
replay(mockLogger);
reset(mockConnectionHandle);
mockConnectionHandle = null; // prompt GC to kick in
for (int i=0; i < 100; i++){
System.gc();System.gc();System.gc();
Thread.sleep(20);
try{
verify(mockLogger);
break; // we succeeded
} catch (Throwable t){
// do nothing, try again
Thread.sleep(20);
}
}
}
/** Test finalizer.
* @throws SQLException
* @throws InterruptedException
* @throws NoSuchFieldException
* @throws SecurityException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
@Test
public void testFinalizerException2() throws SQLException, InterruptedException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException{
ConnectionHandle mockConnectionHandle = createNiceMock(ConnectionHandle.class);
expect(mockConnectionHandle.isInReplayMode()).andReturn(true).anyTimes();
Connection mockConnection = createNiceMock(Connection.class);
Connection connection = new MemorizeTransactionProxyDummy(null,null).memorizeDummy(mockConnection, mockConnectionHandle);
expect(mockConnectionHandle.getInternalConnection()).andReturn(connection).anyTimes();
makeThreadSafe(mockConnectionHandle, true);
makeThreadSafe(mockConnection, true);
mockLogger = TestUtils.mockLogger(testClass.getClass());
reset(mockLogger);
makeThreadSafe(mockLogger, true);
reset(this.mockPool);
Map<Connection, Reference<ConnectionHandle>> refs = new HashMap<Connection, Reference<ConnectionHandle>>();
expect(this.mockPool.getFinalizableRefs()).andReturn(refs).anyTimes();
FinalizableReferenceQueue finalizableRefQueue = new FinalizableReferenceQueue();
expect(this.mockPool.getFinalizableRefQueue()).andReturn(finalizableRefQueue).anyTimes();
expect(mockConnectionHandle.getPool()).andReturn(this.mockPool).anyTimes();
replay(mockConnection, mockConnectionHandle, this.mockPool);
testClass.trackConnectionFinalizer(mockConnectionHandle);
reset(mockConnectionHandle);
mockConnectionHandle = null; // prompt GC to kick in
for (int i=0; i < 100; i++){
System.gc();System.gc();System.gc();
Thread.sleep(20);
try{
verify(mockConnection);
break; // we succeeded
} catch (Throwable t){
t.printStackTrace();
// do nothing, try again
Thread.sleep(20);
}
}
}
/** Fake proxy
* @author wallacew
*
*/
class MemorizeTransactionProxyDummy extends MemorizeTransactionProxy {
/** Fake constructor
* @param target
* @param connectionHandle
*/
public MemorizeTransactionProxyDummy(Connection target,
ConnectionHandle connectionHandle) {
// do nothing
}
/** Just throw errors for test
* @param target
* @param connectionHandle
* @return proxy
*/
protected Connection memorizeDummy(final Connection target, final ConnectionHandle connectionHandle) {
return (Connection) Proxy.newProxyInstance(
ConnectionProxy.class.getClassLoader(),
new Class[] {ConnectionProxy.class},
new MemorizeTransactionProxyDummy(target, connectionHandle));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName() != "hashCode"){
throw new RuntimeException("fake error");
}
return 1;
}
}
}