직접 만들어서 사용하고 있는 Connection Pool입니다.
Connection Pool 을 직접 만들어서 사용하고 있습니다만, 제가 짠 코드에 문제가 없는 것인지 문득 확신이 들지 않더군요...
그래서 공개 합니다. 혹시 문제가 있으면 알려 주세요~~~
import java.io.InputStream;
import java.sql.DriverManager;
import java.util.Properties;
import java.util.Vector;
import java.sql.SQLException;
import java.sql.Connection;
import java.util.Enumeration;
/**
* 데이터베이스 연결 관리를 위한 클래스<br>
* <br>
* 설정파일은 클래스패스에 database.properties 로 존재해야 한다.<br>
* ex)WEB-INF/classes/database.properties<br>
* <br>
* database.properties는 다음과 같은 형식을 따른다.<br>
* driver = oracle.jdbc.driver.OracleDriver<br>
* url = jdbc:oracle:thin:@DBIP:PORT:DBNAME<br>
* user = DB접속용ID<br>
* password = DB접속용 PASSWORD<br>
* initialCons = 최초 생성 커넥션 갯수<br>
* maxCons = 최대 생성 커넥션 갯수<br>
* block = 커넥션이 다 찼을경우 대기 여부<br>
* timeout = 대기만료 시간<br>
* <br>
* @author Jeewon, Lee.
* @version 1.2
* @created 2005년 12월 12일
* @modify 2006년 1월 11일. 싱글톤 패턴으로 수정.
* @modify 2006년 1월 20일. DB정보 프로퍼티 파일에서 읽도록 수정.
*/
public final class ConnectionPool
{
private static ConnectionPool instance;
private Vector free;
private Vector used;
private String jdbcclass;
private String url;
private String user;
private String password;
private int initialCons = 0;
private int maxCons = 0;
private int numCons = 0;
private boolean block;
private long timeout;
private boolean reuseCons = true;
/**
* 데이터베이스 연결관리를 위해 커넥션풀을 생성한다.
*/
protected ConnectionPool()
{
try
{
loadProperties();
Class.forName(jdbcclass);
}
catch (Exception e)
{
e.printStackTrace();
}
if ((maxCons > 0) && (maxCons < initialCons))
{
initialCons = maxCons;
}
free = new Vector(initialCons);
used = new Vector(initialCons);
while (numCons < initialCons)
{
try
{
addConnection();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
/**
* 커넥션풀을 반환한다.
* @return ConnectionPool
*/
public static ConnectionPool getInstance() throws Exception
{
if (instance == null)
{
synchronized (ConnectionPool.class)
{
instance = new ConnectionPool();
}
}
return instance;
}
/**
* 데이터베이스 접속을 위한 설정 파일을 읽는다.
*
* @throws Exception
*/
private void loadProperties() throws Exception
{
InputStream is = this.getClass().getResourceAsStream("/database.properties");
Properties dbProp = new Properties();
try
{
dbProp.load(is);
jdbcclass = dbProp.getProperty("driver");
url = dbProp.getProperty("url");
user = dbProp.getProperty("user");
password = dbProp.getProperty("password");
initialCons = Integer.parseInt(dbProp.getProperty("initialCons"));
maxCons = Integer.parseInt(dbProp.getProperty("maxCons"));
block = Boolean.getBoolean(dbProp.getProperty("block"));
timeout = Integer.parseInt(dbProp.getProperty("timeout"));
}
catch (Exception e)
{
e.printStackTrace();
throw new Exception(e.getMessage());
}
/**
* 신규 커넥션 객체를 pool에 추가 한다.
* @throws SQLException
*/
}
/**
* 신규 커넥션 객체를 pool에 추가 한다.
* @throws SQLException
*/
private void addConnection() throws SQLException
{
free.addElement(getNewConnection());
}
/**
* 신규 커넥션 객체를 생성한다.
* @return Connection
* @throws SQLException
*/
private Connection getNewConnection() throws SQLException
{
Connection con = null;
try
{
con = DriverManager.getConnection(url, user, password);
}
catch (Exception e)
{
e.printStackTrace();
}
++numCons;
return con;
}
/**
* 커넥션 객체를 반환한다.
* @return Connection
* @throws SQLException
*/
public Connection getConnection() throws SQLException
{
return getConnection(this.block, this.timeout);
}
/**
* 사용가능한 커넥션 객체를 찾아서 반환한다.
* 사용가능한 커넥션 객체가 pool에 없다면, 신규 커넥션 객체를 pool에 추가한다.
* @param block
* @param timeout
* @return
* @throws SQLException
*/
private synchronized Connection getConnection(boolean block, long timeout) throws SQLException
{
if (free.isEmpty())
{
if ((maxCons <= 0) || (numCons < maxCons))
{
addConnection();
}
else if (block)
{
try
{
long start = System.currentTimeMillis();
do
{
wait(timeout);
if (timeout > 0)
{
timeout -= (System.currentTimeMillis() - start);
if (timeout == 0)
{
timeout = -1;
}
}
}
while ((timeout >= 0) && free.isEmpty() && (maxCons > 0) && (numCons >= maxCons));
}
catch (InterruptedException e)
{
e.printStackTrace();
}
if (free.isEmpty())
{
if ((maxCons <= 0) || (numCons < maxCons))
{
addConnection();
}
else
{
throw new SQLException("ConnectionPoolTimeOut");
}
}
}
//block 되지 않겠다고 했을 때
else
{
throw new SQLException("MaxConnection");
}
}
Connection con;
synchronized (used)
{
con = (Connection)free.lastElement();
free.removeElement(con);
used.addElement(con);
}
return con;
}
/**
* 사용한 커넥션 객체를 반환한다.
* 반환된 객체는 재사용 여부를 판단하여 pool에 반환하거나 제거한다.
* @param con 사용된 커넥션 객체
* @throws SQLException
*/
public synchronized void releaseConnection(Connection con) throws SQLException
{
boolean reuseThisCon;
reuseThisCon = reuseCons;
if (used.contains(con))
{
used.removeElement(con);
numCons--;
}
else
{
throw new SQLException("Connection객체인 " + con + " 은 ConnectionPool에 반환되지 못하였습니다.");
}
try
{
if (reuseThisCon)
{
free.addElement(con);
numCons++;
}
else
{
con.close();
}
notify();
}
catch (SQLException e)
{
try
{
con.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
notify();
}
}
/**
* 모든 연결 객체를 닫는다.
*/
public synchronized void closeAll()
{
Enumeration cons = ((Vector)free.clone()).elements();
while (cons.hasMoreElements())
{
Connection con = (Connection)cons.nextElement();
free.removeElement(con);
numCons--;
try
{
con.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
}
cons = ((Vector)used.clone()).elements();
while (cons.hasMoreElements())
{
Connection con = (Connection)cons.nextElement();
used.removeElement(con);
}
}
}