2 * $Id: ConnectionPool.java 222 2010-09-06 19:20:24Z dobashi $
\r
4 * Copyright Lavans Networks Inc.
\r
6 package com.lavans.luz2.sql;
\r
8 import java.sql.Connection;
\r
9 import java.sql.Driver;
\r
10 import java.sql.SQLException;
\r
11 import java.sql.Statement;
\r
12 import java.util.ArrayList;
\r
13 import java.util.Collections;
\r
14 import java.util.List;
\r
15 import java.util.Properties;
\r
17 import org.apache.commons.logging.Log;
\r
18 import org.apache.commons.logging.LogFactory;
\r
20 import com.lavans.luz2.sql.bind.BindConnection;
\r
21 import com.lavans.luz2.sql.bind.BindConnectionImpl;
\r
22 import com.lavans.luz2.sql.pool.PooledConnection;
\r
23 import com.lavans.luz2.sql.stats.Statistics;
\r
24 import com.lavans.luz2.sql.stats.StatsConnection;
\r
28 * DBManagerからのみ利用される。
\r
32 * @version 1.1 dobashi20040707 接続の有効性はcreateStatement()だけでチェックできるはず
\r
34 public class ConnectionPool{
\r
36 private static Log logger = LogFactory.getLog(ConnectionPool.class);
\r
41 private String driverName = null;
\r
42 private String url = null;
\r
43 private String user = null;
\r
44 private String pass = null;
\r
48 * ConnectionPoolは接続先ごとにインスタンス化されるので、
\r
49 * DriverManagerに管理させる必要はなく、ConnectionPool毎に
\r
52 private Driver driver = null;
\r
56 * trueならcreateConnectionしたときにStatsConnectionでラップする。
\r
58 private boolean statistics = false;
\r
63 private boolean isLogging = true;
\r
67 * デフォルトは10。lavansutil.xmlのinit_connectionsで変更可能。
\r
69 private int max_connections = 10;
\r
73 * デフォルトは2。lavansutil.xmlのinit_connectionsで変更可能。
\r
75 private int init_connections = 2;
\r
80 private final List<PooledConnection> poolList = Collections.synchronizedList(new ArrayList<PooledConnection>());
\r
84 private final List<PooledConnection> useList = Collections.synchronizedList(new ArrayList<PooledConnection>());
\r
89 private final ThreadLocal<PooledConnection> transactionList = new ThreadLocal<PooledConnection>();
\r
91 protected static final String MSG_ERR_TOOMANYCONNECTIONS = "接続数が最大値を超えています。";
\r
92 protected static final String SQLSTATE_CONNECTION_EXCEPTION = "08000";
\r
93 public static final int ERR_CONNECTION_OVERFLOW = 1;
\r
98 private boolean isForceCheck=false;
\r
102 private String validSql=null;
\r
108 public ConnectionPool(String aDriver, String aUrl,String aUser,String aPass){
\r
109 driverName = aDriver;
\r
118 public void setMaxConnections(int value){
\r
119 max_connections = value;
\r
125 public void setInitConnections(int value){
\r
126 init_connections = value;
\r
133 throws ClassNotFoundException,IllegalAccessException,InstantiationException, SQLException
\r
135 driver = (Driver)Class.forName(driverName).newInstance();
\r
136 for(int i=0; i<init_connections; i++){
\r
137 poolList.add(createConnection());
\r
143 * 設定ファイルが間違っていてDriverManager#getConnection()の戻りが遅いときに、
\r
144 * 何度もcreateConnectionするのを防ぐためsynchronizedとする。
\r
145 * ただし、DoSアタックによるスタックオーバーフローは避けられないので
\r
146 * 間違った設定ファイルのまま運用すべきでない。
\r
148 protected synchronized PooledConnection createConnection()
\r
149 throws SQLException
\r
151 // from java.sql.DriverManager
\r
152 Properties info = new Properties();
\r
153 if (user != null) {
\r
154 info.put("user", user);
\r
156 if (pass != null) {
\r
157 info.put("password", pass);
\r
161 if((poolList.size() + useList.size()) >= max_connections){
\r
162 throw new SQLException(MSG_ERR_TOOMANYCONNECTIONS, SQLSTATE_CONNECTION_EXCEPTION, ERR_CONNECTION_OVERFLOW);
\r
164 Connection conn = null;
\r
165 logger.info(url+":"+user+":"+pass);
\r
167 conn = driver.connect(url, info);
\r
168 } catch (SQLException e) {
\r
172 // Loggableを実装しないラッパークラスを作る場合はこれらの前にnewすること。
\r
176 conn = new StatsConnection(conn);
\r
179 // BindConnection型を返すので、BindConnectionでラップするのは
\r
181 BindConnection bcon = new BindConnectionImpl(conn);
\r
183 // さらにConnection#close()で物理的に閉じずにDBManagerに
\r
184 // 返却するためPooledConnectionでラップする。
\r
185 // ConnectionPoolではなくDBManagerを通すのは
\r
186 // 統計情報取得時に貸し出し管理を行うため。
\r
187 PooledConnection pcon = new PooledConnection(this,bcon);
\r
194 * force_checkを再導入することでDB(postgres)を再起動したときにも
\r
197 protected boolean checkConnection(Connection conn)
\r
199 boolean result = false;
\r
200 Statement st = null;
\r
201 //ResultSet rs = null;
\r
204 st = conn.createStatement();
\r
206 st.executeQuery(validSql);
\r
209 result = true; // 例外がなければOK
\r
210 }catch (SQLException e) {
\r
211 // ここでキャッチしておかないとgetConnection()自身が
\r
217 }catch(Exception e){
\r
227 public PooledConnection getConnection() throws SQLException{
\r
228 return getConnection(true);
\r
233 * @param countStats StatsでgetConnection()をカウントするかどうか。startTransaction()からはカウントしない。
\r
235 * @throws SQLException
\r
237 private PooledConnection getConnection(boolean countStats) throws SQLException {
\r
238 PooledConnection conn=null;
\r
239 // トランザクション中なら該当するコネクションを返す
\r
240 conn = transactionList.get();
\r
244 Statistics.getInstance().getConnectionTran();
\r
246 // 接続チェックはしない。トランザクション中にエラーになるようであれば
\r
247 // 利用側がエラーハンドリングする必要がある。
\r
252 synchronized(poolList){
\r
253 if(poolList.size()>0){ // プールがあれば
\r
254 conn=poolList.remove(0); //
\r
255 if(!checkConnection(conn)){ // 接続失敗したら
\r
257 conn.getRealConnection().close();
\r
258 }catch(Exception e){
\r
266 conn=createConnection();
\r
268 if(logger.isDebugEnabled()) logger.debug("□□ "+conn.toString());
\r
270 if(logger.isDebugEnabled()) logger.debug(useList.toString());
\r
272 // 接続状態管理をする場合 ----------------
\r
273 if(statistics && countStats){
\r
274 Statistics.getInstance().getConnection();
\r
283 public void releaseConnection(Connection conn) throws SQLException{
\r
284 releaseConnection(conn, true);
\r
290 * @param countStats StatsでgetConnection()をカウントするかどうか。commit/rollback()からはカウントしない。
\r
291 * @throws SQLException
\r
293 private void releaseConnection(Connection conn, boolean countStats) throws SQLException{
\r
294 // トランザクション中かどうかチェック
\r
295 PooledConnection connTran = transactionList.get();
\r
296 if(connTran==conn){
\r
299 Statistics.getInstance().releaseConnectionTran();
\r
300 } // トランザクション中の場合はなにもしない
\r
304 conn.setAutoCommit(true); // 返却時は必ずcommit()されている状態にする。
\r
305 // statement.close()を行ってないものがいたら閉じておく
\r
306 if(conn instanceof PooledConnection){
\r
307 ((PooledConnection)conn).clearStatementList();
\r
309 if(logger.isDebugEnabled()) logger.debug("□□ "+conn.toString());
\r
310 if(logger.isDebugEnabled()) logger.debug(useList.toString());
\r
311 if(!useList.remove(conn)){ // 使用中リストに存在しなければ
\r
312 throw new SQLException("This is not my connection.");
\r
315 // 接続状態管理をする場合 ----------------
\r
316 if(statistics && countStats){
\r
317 Statistics.getInstance().releaseConnection();
\r
320 poolList.add((PooledConnection)conn);
\r
325 * すでにトランザクションがスタートしていてもOK。
\r
327 * @throws SQLException
\r
329 public void startTransaction() throws SQLException{
\r
331 if(isTransaction()){
\r
333 Statistics.getInstance().startTransactionTran();
\r
338 PooledConnection conn = getConnection(false);
\r
339 conn.setAutoCommit(false);
\r
340 transactionList.set(conn);
\r
341 // 接続状態管理をする場合 ----------------
\r
343 Statistics.getInstance().startTransaction();
\r
350 * @throws SQLException
\r
352 public void commit() throws SQLException{
\r
353 PooledConnection conn = transactionList.get();
\r
355 // 接続状態管理をする場合 ----------------
\r
357 Statistics.getInstance().commitNoTran();
\r
364 transactionList.remove();
\r
365 releaseConnection(conn, false);
\r
366 // 接続状態管理をする場合 ----------------
\r
368 Statistics.getInstance().commit();
\r
374 * @throws SQLException
\r
376 public void rollback() throws SQLException{
\r
377 PooledConnection conn = transactionList.get();
\r
379 // 接続状態管理をする場合 ----------------
\r
381 Statistics.getInstance().rollbackNoTran();
\r
388 transactionList.remove();
\r
389 releaseConnection(conn, false);
\r
390 // 接続状態管理をする場合 ----------------
\r
392 Statistics.getInstance().rollback();
\r
397 * トランザクション実行中かどうかを返す。
\r
400 public boolean isTransaction(){
\r
401 return transactionList.get()!=null;
\r
407 public void setStatistics(boolean b) {
\r
414 public Log getLogger() {
\r
421 public void setLogger(Log logger) {
\r
422 ConnectionPool.logger = logger;
\r
428 protected int getMaxConnections() {
\r
429 return max_connections;
\r
435 protected Driver getDriver() {
\r
442 protected String getUser() {
\r
449 protected String getPass() {
\r
456 protected List<PooledConnection> getPoolList() {
\r
463 protected boolean isStatistics() {
\r
470 protected List<PooledConnection> getUseList() {
\r
475 * @param isForceCheck isForceCheck を設定。
\r
477 public void setForceCheck(boolean isForceCheck) {
\r
478 this.isForceCheck = isForceCheck;
\r
481 * @param validSql validSql を設定。
\r
483 public void setValidSql(String validSql) {
\r
484 this.validSql = validSql;
\r
487 * @return isLogging を戻します。
\r
489 public boolean isLogging() {
\r
493 * @param isLogging isLogging を設定。
\r
495 public void setLogging(boolean isLogging) {
\r
496 this.isLogging = isLogging;
\r
500 * 現在使用中のコネクション数を返す。
\r
503 public int getUseCount(){
\r
504 return useList.size();
\r
508 * 現在待機中のコネクション数を返す。
\r
511 public int getPoolCount(){
\r
512 return poolList.size();
\r