/**
* this method hands out the connections in round-robin order.
* this prevents a faulty connection from locking
* up an application entirely. a browser 'refresh' will
* get the next connection while the faulty
* connection is cleaned up by the housekeeping thread.
*
* if the min number of threads are ever exhausted, new
* threads are added up the the max thread count.
* finally, if all threads are in use, this method waits
* 2 seconds and tries again, up to ten times. after that, it
* returns a null.
*/
public connection getconnection() {
connection conn=null;
if(available){
boolean gotone = false;
for(int outerloop=1; outerloop<=10; outerloop++) {
try {
int loop=0;
int roundrobin = connlast + 1;
if(roundrobin >= currconnections) roundrobin=0;
do {
synchronized(connstatus) {
if((connstatus[roundrobin] < 1) &&
(! connpool[roundrobin].isclosed()))
{
conn = connpool[roundrobin];
connstatus[roundrobin]=1;
connlocktime[roundrobin] =
system.currenttimemillis();
connlast = roundrobin;
gotone = true;
break;
}
else {
loop++;
roundrobin++;
if(roundrobin >= currconnections) roundrobin=0;
}
}
}
while((gotone==false)&&(loop < currconnections));
}
catch (sqlexception e1) {}
if(gotone) {
break;
}
else {
synchronized(this) { // add new connections to the pool
if(currconnections < maxconns) {
try {
createconn(currconnections);
currconnections++;
}
catch(sqlexception e) {
log.println("unable to create new connection: " + e);
}
}
}
try { thread.sleep(2000); }
catch(interruptedexception e) {}
log.println("-----> connections exhausted! will wait and try " +
"again in loop " +
string.valueof(outerloop));
}
} // end of try 10 times loop
}
else {
log.println("unsuccessful getconnection() request during destroy()");
} // end if(available)
return conn;
}
/**
* returns the local jdbc id for a connection.
*/
public int idofconnection(connection conn) {
int match;
string tag;
try {
tag = conn.tostring();
}
catch (nullpointerexception e1) {
tag = "none";
}
match=-1;
for(int i=0; i< currconnections; i++) {
if(connid[i].equals(tag)) {
match = i;
break;
}
}
return match;
}
/**
* frees a connection. replaces connection back into the main pool for
* reuse.
*/
public string freeconnection(connection conn) {
string res="";
int thisconn = idofconnection(conn);
if(thisconn >= 0) {
connstatus[thisconn]=0;
res = "freed " + conn.tostring();
//log.println("freed connection " + string.valueof(thisconn) +
// " normal exit: ");
}
else {
log.println("----> could not free connection!!!");
}
return res;
}
//文件:dbconnectiondefaultpool.java的第三部分
/**
* returns the age of a connection -- the time since it was handed out to
* an application.
*/
public long getage(connection conn) { // returns the age of the connection in millisec.
int thisconn = idofconnection(conn);
return system.currenttimemillis() - connlocktime[thisconn];
}
private void createconn(int i) throws sqlexception {
date now = new date();
try {
class.forname (dbdriver);
properties dbprop = new properties();
//log.println("creating.....");
dbprop.put("user", dblogin);
dbprop.put("password", dbpassword);
dbprop.put("characterencoding","gb2112");
//dbprop.put("useunicode", "true");
connpool[i] = drivermanager.getconnection
(dbserver,dbprop);
//log.println("created ok...");
connstatus[i]=0;
connid[i]=connpool[i].tostring();
connlocktime[i]=0;
conncreatedate[i] = now.gettime();
}
catch (classnotfoundexception e2) {}
log.println(now.tostring() + " opening connection " + string.valueof(i) +
" " + connpool[i].tostring() + ":");
}
/**
* shuts down the housekeeping thread and closes all connections
* in the pool. call this method from the destroy() method of the servlet.
*/
/**
* multi-phase shutdown. having following sequence:
* <ol>
* <li><code>getconnection()</code> will refuse to return connections.
* <li>the housekeeping thread is shut down.<br>
* up to the time of <code>millis</code> milliseconds after shutdown of
* the housekeeping thread, <code>freeconnection()</code> can still be
* called to return used connections.
* <li>after <code>millis</code> milliseconds after the shutdown of the
* housekeeping thread, all connections in the pool are closed.
* <li>if any connections were in use while being closed then a
* <code>sqlexception</code> is thrown.
* <li>the log is closed.
* </ol><br>
* call this method from a servlet destroy() method.
*
* @param millis the time to wait in milliseconds.
* @exception sqlexception if connections were in use after
* <code>millis</code>.
*/
public void destroy(int millis) throws sqlexception {
// checking for invalid negative arguments is not necessary,
// thread.join() does this already in runner.join().
// stop issuing connections
available=false;
// shut down the background housekeeping thread
runner.interrupt();
// wait until the housekeeping thread has died.
try { runner.join(millis); }
catch(interruptedexception e){} // ignore
// the housekeeping thread could still be running
// (e.g. if millis is too small). this case is ignored.
// at worst, this method will throw an exception with the
// clear indication that the timeout was too short.
long starttime=system.currenttimemillis();
// wait for freeconnection() to return any connections
// that are still used at this time.
int usecount;
while((usecount=getusecount())>0 && system.currenttimemillis() - starttime <= millis) {
try { thread.sleep(500); }
catch(interruptedexception e) {} // ignore
}
// close all connections, whether safe or not
for(int i=0; i < currconnections; i++) {
try {
connpool[i].close();
}
catch (sqlexception e1)
{
log.println("cannot close connections on destroy");
}
}
if(usecount > 0) {
//bt-test successful
string msg="unsafe shutdown: had to close "+usecount+
" active db connections after "+millis+"ms";
log.println(msg);
// close all open files
log.close();
// throwing following exception is essential because servlet authors
// are likely to have their own error logging requirements.
throw new sqlexception(msg);
}
// close all open files
log.close();
}//end destroy()
/**
* less safe shutdown. uses default timeout value.
* this method simply calls the <code>destroy()</code> method
* with a <code>millis</code>
* value of 10000 (10 seconds) and ignores <code>sqlexception</code>
* thrown by that method.
* @see #destroy(int)
*/
public void destroy() {
try {
destroy(10000);
}
catch(sqlexception e) {}
}
/**
* returns the number of connections in use.
*/
// this method could be reduced to return a counter that is
// maintained by all methods that update connstatus.
// however, it is more efficient to do it this way because:
// updating the counter would put an additional burden on the most
// frequently used methods; in comparison, this method is
// rarely used (although essential).
public int getusecount() {
int usecount=0;
synchronized(connstatus) {
for(int i=0; i < currconnections; i++) {
if(connstatus[i] > 0) { // in use
usecount++;
}
}
}
return usecount;
}//end getusecount()
/**
* returns the number of connections in the dynamic pool.
*/
public int getsize() {
return currconnections;
}//end getsize()
}
/**
* an implementation of the connection interface that wraps an underlying
* connection object. it releases the connection back to a connection pool
* when connection.close() is called.
*/
public class connectionwrapper implements connection {
private connection connection;
private connectionpool connectionpool;
public connectionwrapper(connection connection, connectionpool connectionpool) {
this.connection = connection;
this.connectionpool = connectionpool;
}
/**
* instead of closing the underlying connection, we simply release
* it back into the pool.
*/
public void close() throws sqlexception {
connectionpool.freeconnection(this.connection);
//release object references. any further method calls on the
//connection will fail.
connection = null;
connectionpool = null;
}
public statement createstatement() throws sqlexception {
return connection.createstatement();
}
public preparedstatement preparestatement(string sql) throws sqlexception {
return connection.preparestatement(sql);
}
public callablestatement preparecall(string sql) throws sqlexception {
return connection.preparecall(sql);
}
public string nativesql(string sql) throws sqlexception {
return connection.nativesql(sql);
}
public void setautocommit(boolean autocommit) throws sqlexception {
connection.setautocommit(autocommit);
}
public boolean getautocommit() throws sqlexception {
return connection.getautocommit();
}
public void commit() throws sqlexception {
connection.commit();
}
public void rollback() throws sqlexception {
connection.rollback();
}
public boolean isclosed() throws sqlexception {
return connection.isclosed();
}
public databasemetadata getmetadata() throws sqlexception {
return connection.getmetadata();
}
public void setreadonly(boolean readonly) throws sqlexception {
connection.setreadonly(readonly);
}
public boolean isreadonly() throws sqlexception {
return connection.isreadonly();
}
public void setcatalog(string catalog) throws sqlexception {
connection.setcatalog(catalog);
}
public string getcatalog() throws sqlexception {
return connection.getcatalog();
}
public void settransactionisolation(int level) throws sqlexception {
connection.settransactionisolation(level);
}
public int gettransactionisolation() throws sqlexception {
return connection.gettransactionisolation();
}
public sqlwarning getwarnings() throws sqlexception {
return connection.getwarnings();
}
public void clearwarnings() throws sqlexception {
connection.clearwarnings();
}
public statement createstatement(int resultsettype, int resultsetconcurrency)
throws sqlexception
{
return connection.createstatement(resultsettype, resultsetconcurrency);
}
public preparedstatement preparestatement(string sql, int resultsettype,
int resultsetconcurrency) throws sqlexception
{
return connection.preparestatement(sql, resultsettype, resultsetconcurrency);
}
public callablestatement preparecall(string sql, int resultsettype,
int resultsetconcurrency) throws sqlexception
{
return preparecall(sql, resultsettype, resultsetconcurrency);
}
public map gettypemap() throws sqlexception {
return connection.gettypemap();
}
public void settypemap(map map) throws sqlexception {
connection.settypemap(map);
}
}
}
//文件:dbconnectionmanager.java
package com.qingtuo.db.pool;
import java.sql.*;
import java.io.*;
import java.util.*;
/**
* central manager of database connections.
*/
public class dbconnectionmanager {
private static dbconnectionprovider connectionprovider;
private static object providerlock = new object();
/**
* returns a database connection from the currently active connection
* provider.
*/
public static connection getconnection() {
if (connectionprovider == null) {
synchronized (providerlock) {
if (connectionprovider == null) {
//create the connection provider -- for now, this is hardcoded. for
//the next beta, i'll change this to load up the provider dynamically.
connectionprovider = new dbconnectiondefaultpool();
connectionprovider.start();
}
}
}
connection con = connectionprovider.getconnection();
if (con == null) {
system.err.println("warning: dbconnectionmanager.getconnection() failed to obtain a connection.");
}
return con;
}
/**
* returns the current connection provider. the only case in which this
* method should be called is if more information about the current
* connection provider is needed. database connections should always be
* obtained by calling the getconnection method of this class.
*/
public static dbconnectionprovider getdbconnectionprovider() {
return connectionprovider;
}
/**
* sets the connection provider. the old provider (if it exists) is shut
* down before the new one is started. a connection provider <b>should
* not</b> be started before being passed to the connection manager.
*/
public static void setdbconnectionprovider(dbconnectionprovider provider) {
synchronized (providerlock) {
if (connectionprovider != null) {
connectionprovider.destroy();
connectionprovider = null;
}
connectionprovider = provider;
provider.start();
}
}
}