Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 3
synchronized Object checkOut()
{
long now = System.currentTimeMillis();
Object o;
if( unlocked.size() > 0 )
{
Enumeration e = unlocked.keys();
while( e.hasMoreElements() )
{
o = e.nextElement();
if( ( now - ( ( Long ) unlocked.get( o ) ).longValue() ) >
expirationTime )
{
// object has expired
unlocked.remove( o );
expire( o );
o = null;
}
else
{
if( validate( o ) )
{
unlocked.remove( o );
locked.put( o, new Long( now ) );
return( o );
}
else
{
// object failed validation
unlocked.remove( o );
expire( o );
o = null;
}
}
}
}
// no objects available, create a new one
o = create();
locked.put( o, new Long( now ) );
return( o );
}
That's the most complex method in the ObjectPool class, it's all downhill from here. The checkIn() method simply moves the passed-in object from the locked hashtable into the unlocked hashtable.
synchronized void checkIn( Object o )
{
locked.remove( o );
unlocked.put( o, new Long( System.currentTimeMillis() ) );
}
The three remaining methods are abstract and therefore must be implemented by the subclass. For the sake of this article, I am going to create a database connection pool called JDBCConnectionPool. Here's the skeleton:
public class JDBCConnectionPool extends ObjectPool
{
private String dsn, usr, pwd;
public JDBCConnectionPool(){...}
create(){...}
validate(){...}
expire(){...}
public Connection borrowConnection(){...}
public void returnConnection(){...}
}
The JDBCConnectionPool will require the application to specify the database driver, DSN, username, and password upon instantiation (via the constructor). If this is Greek to you, don't worry, JDBC is another topic. Just bear with me until we get back to the pooling.
public JDBCConnectionPool( String driver, String dsn, String usr, String pwd )
{
try
{
Class.forName( driver ).newInstance();
}
catch( Exception e )
{
e.printStackTrace();
}
this.dsn = dsn;
this.usr = usr;
this.pwd = pwd;
}
Now we can dive into implementation of the abstract methods. As you saw in the checkOut() method, ObjectPool will call create() from its subclass when it needs to instantiate a new object. For JDBCConnectionPool, all we have to do is create a new Connection object and pass it back. Again, for the sake of keeping this article simple, I am throwing caution to the wind and ignoring any exceptions and null-pointer conditions.
Object create()
{
try
{
return( DriverManager.getConnection( dsn, usr, pwd ) );
}
catch( SQLException e )
{
e.printStackTrace();
return( null );
}
}
Before the ObjectPool frees an expired (or invalid) object for garbage collection, it passes it to its subclassed expire() method for any necessary last-minute clean-up (very similar to the finalize() method called by the garbage collector). In the case of JDBCConnectionPool, all we need to do is close the connection.
void expire( Object o )
{
try
{
( ( Connection ) o ).close();
}
catch( SQLException e )
{
e.printStackTrace();
}
}
And finally, we need to implement the validate() method that ObjectPool calls to make sure an object is still valid for use. This is also the place that any re-initialization should take place. For JDBCConnectionPool, we just check to see that the connection is still open.