Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package com.j256.ormlite.jdbc;
  
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
 
Implementation of the ConnectionSource interface that supports basic pooled connections. New connections are created on demand only if there are no dormant connections otherwise released connections will be reused. This class is reentrant and can handle requests from multiple threads.

WARNING: As of 10/2010 this is one of the newer parts of ORMLite meaning it may still have bugs. Additional review of the code and any feedback would be appreciated.

NOTE: If you are using the Spring type wiring in Java, initialize() should be called after all of the set methods. In Spring XML, init-method="initialize" should be used.

NOTE: This class spawns a thread to test the pooled connections that are in the free-list as a keep-alive mechanism. It will test any dormant connections every so often to see if they are still valid. If this is not the behavior that you want then call setCheckConnectionsEveryMillis(long) with 0 to disable the thread. You can also call setTestBeforeGet(boolean) and set it to true to test the connection before it is handed back to you.

Author(s):
graywatson
 
 public class JdbcPooledConnectionSource extends JdbcConnectionSource implements ConnectionSource {
 
 	private static Logger logger = LoggerFactory.getLogger(JdbcPooledConnectionSource.class);
 	private final static int DEFAULT_MAX_CONNECTIONS_FREE = 5;
 	// maximum age that a connection can be before being closed
 	private final static int DEFAULT_MAX_CONNECTION_AGE_MILLIS = 60 * 60 * 1000;
 	private final static int CHECK_CONNECTIONS_EVERY_MILLIS = 30 * 1000;
 
 	private final Object lock = new Object();
 	private ConnectionTester tester = null;
 	private String pingStatment;
 
 	private int openCount = 0;
 	private int releaseCount = 0;
 	private int closeCount = 0;
 	private int maxEverUsed = 0;
 	private boolean testBeforeGetFromPool = false;
 	private volatile boolean isOpen = true;
 
 		// for spring type wiring
 	}
 
 	public JdbcPooledConnectionSource(String urlthrows SQLException {
 		this(urlnullnullnull);
 	}
 
 	public JdbcPooledConnectionSource(String urlDatabaseType databaseTypethrows SQLException {
 		this(urlnullnulldatabaseType);
 	}
 
 	public JdbcPooledConnectionSource(String urlString usernameString passwordthrows SQLException {
 		this(urlusernamepasswordnull);
 	}
 
 	public JdbcPooledConnectionSource(String urlString usernameString passwordDatabaseType databaseType)
 			throws SQLException {
 		super(urlusernamepassworddatabaseType);
 	}
 
 	public void initialize() throws SQLException {
 		super.initialize();
 	}
 
 	public void close() throws SQLException {
 		.debug("closing");
 		synchronized () {
			// close the outstanding connections in the list
			for (ConnectionMetaData connMetaData : ) {
			}
			 = null;
			// NOTE: We can't close the ones left in the connectionMap because they may still be in use.
			 = false;
		}
	}
		// set the connection to be read-only in JDBC-land? would need to set read-only or read-write
	}
		if (conn != null) {
			return conn;
		}
		synchronized () {
			while (.size() > 0) {
				// take the first one off of the list
				if (connMetaData == null) {
					// need to create a new one
else if ( && !testConnection(connMetaData)) {
					// close expired connection
else {
					.debug("reusing connection {}"connMetaData);
					return connMetaData.connection;
				}
			}
			// if none in the free list then make a new one
			// add it to our connection map
			int maxInUse = .size();
			if (maxInUse > ) {
				 = maxInUse;
			}
			return connection;
		}
	}
		synchronized () {
			long now = System.currentTimeMillis();
			while (.size() > 0) {
				// take the first one off of the list
				// is it already expired
				if (connMetaData.isExpired(now)) {
					// close expired connection
else {
					return connMetaData;
				}
			}
		}
		return null;
	}
	public void releaseConnection(DatabaseConnection connectionthrows SQLException {
		if (isSavedConnection(connection)) {
			// ignore the release when we are in a transaction
			return;
		}
		synchronized () {
			if (connection.isClosed()) {
				// it's already closed so just drop it
				if (meta == null) {
					.debug("dropping already closed unknown connection {}"connection);
else {
					.debug("dropping already closed connection {}"meta);
				}
				return;
			}
			if ( == null) {
				// if we've already closed the pool then just close the connection
				closeConnection(connection);
				return;
			}
			ConnectionMetaData meta = .get(connection);
			if (meta == null) {
				.error("should have found connection {} in the map"connection);
				closeConnection(connection);
else {
				.debug("cache released connection {}"meta);
					// close the first connection in the queue
					meta = .remove(0);
					.debug("cache too full, closing connection {}"meta);
					closeConnection(meta.connection);
				}
				if ( > 0 &&  == null) {
					.setName(getClass().getSimpleName() + " connection tester");
				}
			}
		}
	}
	public boolean saveSpecialConnection(DatabaseConnection connectionthrows SQLException {
		boolean saved = saveSpecial(connection);
			ConnectionMetaData meta = .get(connection);
			.debug("saved special connection {}"meta);
		}
		return saved;
	}
	public void clearSpecialConnection(DatabaseConnection connection) {
		boolean cleared = clearSpecial(connection);
			ConnectionMetaData meta = .get(connection);
			if (cleared) {
				.debug("cleared special connection {}"meta);
else {
				.debug("special connection {} not saved"meta);
			}
		}
		// release should then called after the clear
	}
	public boolean isOpen() {
		return ;
	}
	public void setUsesTransactions(boolean usesTransactions) {
		this. = usesTransactions;
	}

Set the number of connections that can be unused in the available list.
	public void setMaxConnectionsFree(int maxConnectionsFree) {
		this. = maxConnectionsFree;
	}

Set the number of milliseconds that a connection can stay open before being closed. Set to Long.MAX_VALUE to have the connections never expire.
	public void setMaxConnectionAgeMillis(long maxConnectionAgeMillis) {
		this. = maxConnectionAgeMillis;
	}

Return the approximate number of connections opened over the life of the pool.
	public int getOpenCount() {
		return ;
	}

Return the approximate number of connections released over the life of the pool.
	public int getReleaseCount() {
		return ;
	}

Return the approximate number of connections closed over the life of the pool.
	public int getCloseCount() {
		return ;
	}

Return the approximate maximum number of connections in use at one time.
	public int getMaxConnectionsEverUsed() {
		return ;
	}

Return the number of currently freed connectionsin the free list.
	public int getCurrentConnectionsFree() {
		synchronized () {
			return .size();
		}
	}

Return the number of current connections that we are tracking.
		synchronized () {
		}
	}

There is an internal thread which checks each of the database connections as a keep-alive mechanism. This set the number of milliseconds it sleeps between checks -- default is 30000. To disable the checking thread, set this to 0 before you start using the connection source.
	public void setCheckConnectionsEveryMillis(long checkConnectionsEveryMillis) {
		this. = checkConnectionsEveryMillis;
	}
	public void setTestBeforeGet(boolean testBeforeGetFromPool) {
		this. = testBeforeGetFromPool;
	}
	private void checkInitializedSqlException() throws SQLException {
		if (!) {
			throw new SQLException(getClass().getSimpleName() + " was not initialized properly");
		}
	}
		if (!) {
			throw new IllegalStateException(getClass().getSimpleName() + " was not initialized properly");
		}
	}

This should be inside of synchronized (lock) stanza.
	private void closeConnection(DatabaseConnection connectionthrows SQLException {
		// this can return null if we are closing the pool
		connection.close();
		.debug("closed connection {}"meta);
	}

Must be called inside of synchronized(lock)
	private void closeConnectionQuietly(ConnectionMetaData connMetaData) {
		try {
			// close expired connection
			closeConnection(connMetaData.connection);
catch (SQLException e) {
			// we ignore this
		}
	}
	private boolean testConnection(ConnectionMetaData connMetaData) {
		try {
			// issue our ping statement
			long result = connMetaData.connection.queryForLong();
			.debug("tested connection {}, got {}"connMetaDataresult);
			return true;
catch (Exception e) {
			.debug(e"testing connection {} threw exception: {}"connMetaDatae.getMessage());
			return false;
		}
	}

Class to hold the connection and its meta data.
	private static class ConnectionMetaData {
		private final long expiresMillis;
		public ConnectionMetaData(DatabaseConnection connectionlong maxConnectionAgeMillis) {
			this. = connection;
			long now = System.currentTimeMillis();
			if (maxConnectionAgeMillis > . - now) {
else {
				this. = now + maxConnectionAgeMillis;
			}
		}
		public boolean isExpired(long now) {
			return ( <= now);
		}
		public String toString() {
			return "#" + hashCode();
		}
	}

Tester thread that checks the connections that we have queued to make sure they are still good.
	private class ConnectionTester extends Thread {
		public void run() {
				try {
					if (!testConnections()) {
						return;
					}
catch (InterruptedException e) {
					// quit if we've been interrupted
					return;
				}
			}
		}

Test the connections, returning true if we should continue.
		private boolean testConnections() {
			// clear our tested set
			long now = System.currentTimeMillis();
			ConnectionMetaData connMetaData = null;
			while (true) {
				synchronized () {
					if ( == null) {
						if (connMetaData != null) {
						}
						// we're closed
						return false;
					}
					if (connMetaData != null) {
						// we do this so we don't have to double lock in the loop
						.add(connMetaData);
					}
						// nothing to do
						continue;
					}
					connMetaData = .get(0);
					if (.contains(connMetaData)) {
						// we are done if we've tested it before on this pass
						return true;
					}
					// otherwise, take the first one off the list
					connMetaData = .remove(0);
					if (connMetaData.isExpired(now)) {
						// close expired connection
						// don't return the connection to the free list
						connMetaData = null;
						continue;
					}
				}
				if (testConnection(connMetaData)) {
					.add(connMetaData);
else {
					synchronized () {
						connMetaData = null;
					}
				}
			}
		}
	}
New to GrepCode? Check out our FAQ X