// Copyright (c) 2001 Dustin Sallings <dustin@spy.net> package net.spy.db; import java.sql.Connection; import java.sql.SQLException; import java.util.Enumeration; import java.util.Map; import java.util.WeakHashMap; import net.spy.SpyObject; import net.spy.pool.JDBCPoolFiller; import net.spy.pool.ObjectPool; import net.spy.pool.PoolException; import net.spy.pool.PooledObject; import net.spy.util.SpyConfig; /** * Connection source to retrieve connections from an ObjectPool. * * The configuration passed into getConnection() requires the following * parameters: * * <ul> * <li>dbDriverName - Driver to load (i.e. org.postgresql.Driver)</li> * <li>dbSource - dbUser</li> * <li>dbUser - Database username</li> * <li>dbPass - Database password</li> * </ul> * * The following parameters are optional: * * <ul> * <li>dbPoolName - default: <i>db</i></li> * <li>dbMinConns - minimum number of connections - default 1</li> * <li>dbStartConns - minimum number of connections - default 1</li> * <li>dbYellowLine - the pool's ``yellow line'' percentage * - default 75</li> * <li>dbMaxConns - maximum number of connections - default 5</li> * <li>dbMaxLifeTime - maximum connection lifetime in milliseconds - * default 6 hours</li> * <li>dbPingOnCheckout - if true, ping on object pool checkout * default: true</li> * </ul> */ public class ObjectPoolConnectionSource extends SpyObject implements ConnectionSource { private static final int DEFAULT_MAX_CONNS=5; // This is the object pool from which connections will be retrieved private static ObjectPool pool=null; private final Map<Connection, PooledObject> objects; // The name of the pool referenced by this instance. private String poolName=null; /** * Get an instance of SpyPoolConnectionSource. */ public ObjectPoolConnectionSource() { super(); objects=new WeakHashMap<Connection, PooledObject>(); } /** * @see ConnectionSource */ public Connection getConnection(SpyConfig conf) throws SQLException { // Get the pool name poolName=conf.get("dbPoolName"); // Verify the pool is properly initialized initialize(conf); Connection conn=null; try { // Snatch the pebble from my hand. conn=getConn(poolName); } catch(PoolException pe) { getLogger().warn("Could not get a DB connection", pe); throw new DBInitException("Could not get a DB connection: " + pe); } return(conn); } // Do the pool work. private Connection getConn(String name) throws SQLException, PoolException { Connection rv=null; PooledObject object=pool.getObject(name); rv=(Connection)object.getObject(); objects.put(rv, object); return(rv); } /** * @see ConnectionSource */ public void returnConnection(Connection conn) { PooledObject object=objects.get(conn); if(object==null) { throw new NullPointerException("Object is null, already returned?"); } object.checkIn(); objects.remove(conn); } // Perform one-time initialization for a config. private void initialize(SpyConfig gConf) throws SQLException { synchronized(ObjectPoolConnectionSource.class) { // Get a copy of the config, we'll be mangling it a bit SpyConfig conf = null; if(pool==null) { // Clone the config conf=(SpyConfig)gConf.clone(); // Create the ObjectPool pool=new ObjectPool(conf); } // No pools exist // Make sure the ObjectPool has the pool we seek if(!pool.hasPool(poolName)) { // if we don't have a config yet, clone it if(conf==null) { conf=(SpyConfig)gConf.clone(); } try { createPool(conf); } catch(PoolException pe) { getLogger().error("Problem initializing pool", pe); throw new SQLException("Error initializing pool: " + pe); } // Catch the PoolException } // pool doesn't exist } // Synchronized block } // Create the pool described in this config. private void createPool(SpyConfig conf) throws SQLException, PoolException { // get the normalized config SpyConfig normConf=getNormalizedConfig(poolName, conf); // Get the pool filler. JDBCPoolFiller pf=new JDBCPoolFiller(poolName, normConf); // Create the pool. pool.createPool(poolName, pf); } // Get a config with all of the expected values for a DB pool private SpyConfig getNormalizedConfig(String name, SpyConfig conf) throws SQLException { // Will return a new instance of SpyConfig. SpyConfig rv=new SpyConfig(); // If a name is given, all properties should be prefixed with that name String prefix=""; if(name!=null) { prefix=name+"."; } // Minimum connections in the pool. int minConns=conf.getInt("dbMinConns", 1); rv.put(prefix + "min", "" + minConns); // Start connections int startConns=conf.getInt("dbStartCons", minConns); rv.put(prefix + "start", "" + startConns); // Yellow line percentage int yellowLine=conf.getInt("dbYellowLine", -1); if(yellowLine>0) { rv.put(prefix + "yellow_line", "" + yellowLine); } // Maximum number of connections in the pool. int maxConns=conf.getInt("dbMaxConns", DEFAULT_MAX_CONNS); rv.put(prefix + "max", "" + maxConns); // Maximum age of any item in the pool String tmp=conf.get("dbMaxLifeTime"); if(tmp==null) { tmp="86400"; } rv.put(prefix+"max_age", tmp); // ping on checkout flag tmp=conf.get("dbPingOnCheckout", "true"); rv.put(prefix + "pingOnCheckout", tmp); // Driver name tmp=conf.get("dbDriverName"); if(tmp==null) { throw new SQLException( "dbDriverName not given, invalid configuration."); } rv.put(prefix + "dbDriverName", tmp); // JDBC URL tmp=conf.get("dbSource"); if(tmp==null) { throw new SQLException( "dbSource not given, invalid configuration."); } rv.put(prefix + "dbSource", tmp); // DB username tmp=conf.get("dbUser"); if(tmp==null) { throw new SQLException( "dbUser not given, invalid configuration."); } rv.put(prefix + "dbUser", tmp); // DB username tmp=conf.get("dbPass"); if(tmp==null) { throw new SQLException( "dbPass not given, invalid configuration."); } rv.put(prefix + "dbPass", tmp); // Add the db options for(Enumeration<?> e=conf.propertyNames(); e.hasMoreElements();) { String pname=(String)e.nextElement(); if(pname.startsWith("dboption.")) { String ovalue=conf.get(pname); rv.put(prefix + pname, ovalue); } // found a dboption } // All properties return(rv); } }