SQLiteConnectionPoolDataSourceTest.java
/*--------------------------------------------------------------------------
* 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 org.sqlite;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.sqlite.javax.SQLiteConnectionPoolDataSource;
public class SQLiteConnectionPoolDataSourceTest {
@Test
public void connectionTest() throws SQLException {
ConnectionPoolDataSource ds = new SQLiteConnectionPoolDataSource();
PooledConnection pooledConn = ds.getPooledConnection();
Connection handle = pooledConn.getConnection();
assertThat(handle.isClosed()).isFalse();
assertThat(handle.createStatement().execute("select 1")).isTrue();
Connection handle2 = pooledConn.getConnection();
assertThat(handle.isClosed()).isTrue();
Connection finalHandle = handle;
assertThatThrownBy(() -> finalHandle.createStatement().execute("select 1"))
.isInstanceOf(SQLException.class)
.hasMessage("Connection is closed");
assertThat(handle2.createStatement().execute("select 1")).isTrue();
handle2.close();
handle = pooledConn.getConnection();
assertThat(handle.createStatement().execute("select 1")).isTrue();
pooledConn.close();
assertThat(handle.isClosed()).isTrue();
}
/**
* When a handle is closed the physical connection must be reset (rollback + auto-commit) before
* the pool is notified, otherwise a concurrent borrower can reuse the physical connection while
* the reset is still running. See issue #821.
*/
@Test
public void concurrentReuseDoesNotRaceOnClose() throws Exception {
SQLiteConnectionPoolDataSource ds = new SQLiteConnectionPoolDataSource();
ds.setUrl("jdbc:sqlite::memory:");
DummyPool pool = new DummyPool(ds);
AtomicReference<Throwable> failure = new AtomicReference<>();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 8; i++) {
Thread t =
new Thread(
() -> {
for (int j = 0; j < 2000 && failure.get() == null; j++) {
try (Connection c = pool.getConnection()) {
c.setAutoCommit(false);
c.createStatement().execute("select 1");
} catch (Throwable e) {
failure.compareAndSet(null, e);
}
}
});
threads.add(t);
t.start();
}
for (Thread t : threads) {
t.join();
}
assertThat(failure.get()).isNull();
}
/** Minimal pool that hands out and takes back pooled connections, like a real pool would. */
private static class DummyPool implements ConnectionEventListener {
private final List<PooledConnection> available = new ArrayList<>();
private final ConnectionPoolDataSource dataSource;
DummyPool(ConnectionPoolDataSource dataSource) {
this.dataSource = dataSource;
}
synchronized Connection getConnection() throws SQLException {
Iterator<PooledConnection> it = available.iterator();
PooledConnection pooled;
if (it.hasNext()) {
pooled = it.next();
it.remove();
} else {
pooled = dataSource.getPooledConnection();
pooled.addConnectionEventListener(this);
}
return pooled.getConnection();
}
@Override
public synchronized void connectionClosed(ConnectionEvent event) {
available.add((PooledConnection) event.getSource());
}
@Override
public void connectionErrorOccurred(ConnectionEvent event) {}
}
@Disabled
@Test
public void proxyConnectionCloseTest() throws SQLException {
ConnectionPoolDataSource ds = new SQLiteConnectionPoolDataSource();
PooledConnection pooledConn = ds.getPooledConnection();
System.out.println("pooledConn: " + pooledConn.getClass());
Connection handle = pooledConn.getConnection();
System.out.println("pooledConn.getConnection: " + handle.getClass());
Statement st = handle.createStatement();
System.out.println("statement: " + st.getClass());
Connection stConn = handle.createStatement().getConnection();
System.out.println("statement connection:" + stConn.getClass());
stConn.close(); // This closes the physical connection, not the proxy
Connection handle2 = pooledConn.getConnection();
}
}