I have built this very simple Connection Pool in Java Using Factory pattern. My thought here was to try on 2 connection Types e.g. SQL and NoSQL. Please review and suggest if my approach is fine. I am trying my hands on Java design patterns, so your review will help me to see where I need to improve in my design process.
Connection class
// very basic definition
public interface Connection {
UUID getMarker();
boolean commit();
void close();
}
JdbcConnection class
public class JdbcConnection implements Connection {
private Credentials credentials;
private HostInfo hostInfo;
private UUID marker;
public JdbcConnection(UUID marker, Credentials credentials, HostInfo hostInfo) {
this.credentials = credentials;
this.hostInfo = hostInfo;
this.marker = marker;
}
public UUID getMarker() {
return marker;
}
public boolean commit() {
return true;
}
public void close() {
System.out.println("["+ this.marker+ "] is renewed for next connection");
this.marker = UUID.randomUUID();
}
}
ConnectionFactory class
public final class ConnectionFactory {
private ConnectionFactory() {
}
public static Connection get(ConnectionType type, DbPoolconfig dbPoolconfig) {
if (type.equals(ConnectionType.SQL)) {
return new JdbcConnection(UUID.randomUUID(), dbPoolconfig.getCredentials(), dbPoolconfig.getHostInfo());
}
return new CassandraConnection(UUID.randomUUID(), dbPoolconfig.getCredentials(), dbPoolconfig.getHostInfo());
}
}
Pool class
public interface Pool<T> {
T get() throws InterruptedException;
void release(T object);
void shutdown();
}
DbConnectionPool class
public interface DbConnectionPool<T> extends Pool {
int getAvailableConnections();
int getBusyConnectionsCount();
}
DbConnectionPoolManager class
public final class DbConnectionPoolManager implements DbConnectionPool {
private static DbConnectionPoolManager instance;
private DbPoolconfig dbPoolconfig;
private BlockingQueue<Connection> pool;
private Set<Connection> busy;
public static DbConnectionPoolManager get(DbPoolconfig dbPoolconfig) {
if (instance == null) {
synchronized (DbConnectionPoolManager.class) {
if (instance == null) {
return new DbConnectionPoolManager(dbPoolconfig);
}
}
}
return instance;
}
private DbConnectionPoolManager(DbPoolconfig dbPoolconfig) {
this.dbPoolconfig = dbPoolconfig;
this.pool = new LinkedBlockingQueue<Connection>(dbPoolconfig.getMaxSize());
this.busy = new HashSet<Connection>();
init();
}
private void init() {
int initial = dbPoolconfig.getIntialSize();
for (int i = 0; i < initial; i++) {
pool.offer(ConnectionFactory.get(dbPoolconfig.getType(), dbPoolconfig));
}
}
public Connection createNew() {
if (busy.size() == dbPoolconfig.getMaxSize()) {
throw new RuntimeException("Pool is full.. cannot issue connection");
}
Connection connection = ConnectionFactory.get(dbPoolconfig.getType(), dbPoolconfig);
pool.offer(connection);
return connection;
}
public Connection get() throws InterruptedException {
Connection connection = null;
if (pool.peek() == null && busy.size() < dbPoolconfig.getMaxSize()) {
connection = createNew();
} else {
connection = pool.take();
}
this.busy.add(connection);
return connection;
}
public void release(Object object) {
if (object != null) {
Connection connection = (Connection) object;
connection.close();
pool.offer(connection);
this.busy.remove(connection);
}
}
public void shutdown() {
// clear connection and remove from queu
while (!pool.isEmpty()) {
Connection connection = null;
try {
connection = pool.take();
connection.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public int getAvailableConnections() {
return (dbPoolconfig.getMaxSize() - busy.size());
}
public int getBusyConnectionsCount() {
return busy.size();
}
}
Test
public class DbConnectionPooMangerTest {
DbConnectionPoolManager mgr;
@Before
public void before() {
Credentials credentials = new Credentials("1", "2");
HostInfo info = new HostInfo("sss", "sss");
DbPoolconfig config = new DbPoolconfig(info, credentials, ConnectionType.SQL, 15, 2);
mgr = DbConnectionPoolManager.get(config);
}
@Test
public void testConnection() {
int m = 40;
ExecutorService service = Executors.newFixedThreadPool(m);
final List<Future<String>> conns = new ArrayList();
Callable<String> callable = new Callable<String>() {
public String call() throws Exception {
try {
Connection con = mgr.get();
TimeUnit.SECONDS.sleep((long) (Math.random() * 2));
mgr.release(con);
System.out.println("Available "+ mgr.getAvailableConnections());
System.out.println("Busy "+ mgr.getBusyConnectionsCount());
return (con.getMarker()) +"";
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
};
for (int i = 0; i < m; i++) {
Future<String> f = service.submit(callable);
conns.add(f);
}
int count = 0;
for(Future<String> f : conns) {
try {
String s = f.get();
System.out.println(s);
if(s != null) {
count++;
}
}catch (Exception e) {
e.printStackTrace();;
}
}
Assert.assertEquals(m, count);
service.shutdown();
}
}
```