2
\$\begingroup\$

I have read the code reviews on this website: this one and this one

So creating the pool in itself might not be that difficult, I'm more worried about the client closing the connection or the connection not being given back to the pool.

So I have tried to implement this answer.

public interface Connection {
    Object read();
    void close();
    void rollback();
}

public class ConnectionImpl implements Connection{
    public ConnectionImpl(String connectionString) {
    }

    @Override
    public Object read() {
        return null;
    }

    @Override
    public void close() {
        // do stuff to close the connection
    }

    @Override
    public void rollback() {
        // do stuff to go back to a valid state
    }
}

/**
 * This will wrap the connectionImpl and make sure the client doesn't close the connection himself, it will instead release it
 */
public class ConnectionWrapper implements Connection {
    private Connection m_connection;
    private ConnectionPool connectionPool;

    public ConnectionWrapper(Connection connection, ConnectionPool connectionPool) {
        m_connection = connection;
    }

    @Override
    public Object read() {
        return m_connection.read();
    }

    @Override
    public void close() {
        connectionPool.release(m_connection);
    }

    @Override
    public void rollback() {
        m_connection.rollback();
    }

    @Override
    protected void finalize() throws Throwable {
        try{
            close();
        }finally {
            super.finalize();
        }
    }
}

public class ConnectionPool {
    private BlockingQueue<Connection> m_connections;
    private List<Connection> m_usedConnections;

    private int m_maxConnectionNumber;
    private String m_connectionString;

    private AtomicInteger m_currentConnectionNumber;

    public ConnectionPool(int maxConnectionNumber, String connectionString) {
        m_connections = new ArrayBlockingQueue<>(maxConnectionNumber);
        m_usedConnections = new ArrayList<>();
        m_maxConnectionNumber = maxConnectionNumber;
        m_connectionString = connectionString;
        m_currentConnectionNumber = new AtomicInteger(0);
    }

    public Connection get() throws InterruptedException {
        createNewConnectionIfNecessary();

        Connection connection = m_connections.take();
        m_usedConnections.add(connection);

        return connection;
    }

    public void release(Connection connection) {
        int usedConnectionIndex = m_usedConnections.indexOf(connection);
        if (usedConnectionIndex == -1) {
            return;
        }

        connection.rollback();
        m_usedConnections.remove(usedConnectionIndex);
        m_connections.offer(connection);
    }

    private void createNewConnectionIfNecessary() {
        synchronized(m_connections) {
            if (m_connections.isEmpty() && m_currentConnectionNumber.get() < m_maxConnectionNumber) {
                Connection connection = new ConnectionImpl(m_connectionString);
                ConnectionWrapper connectionWrapper = new ConnectionWrapper(connection,this);
                m_connections.add(connectionWrapper);
                m_currentConnectionNumber.getAndIncrement();
            }
        }
    }
}

So, few questions:

  1. is my implementation thread-safe?
  2. Is there any other way my implementation could result in not releasing the connection?
  3. I know that the finalize method might not be called, resulting in a Connection leaking. Is there any way to prevent this?
  4. In general, any other advice?
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.