1 /* $Id: ClusterConnectionPool.java 222 2010-09-06 19:20:24Z dobashi $
\r
3 * (c)2004 Lavans Networks Inc. All Rights Reserved.
\r
5 package com.lavans.luz2.sql.cluster;
\r
7 import java.sql.Connection;
\r
8 import java.sql.SQLException;
\r
9 import java.util.ArrayList;
\r
10 import java.util.HashMap;
\r
11 import java.util.List;
\r
12 import java.util.Map;
\r
13 import java.util.Properties;
\r
14 import java.util.Random;
\r
16 import org.apache.commons.logging.Log;
\r
17 import org.apache.commons.logging.LogFactory;
\r
19 import com.lavans.luz2.sql.ConnectionPool;
\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.StatsConnection;
\r
30 public class ClusterConnectionPool extends ConnectionPool {
\r
31 /** 接続先がない場合のエラーメッセージ */
\r
32 private static final String MSG_ERR_NOCONNECT="有効な接続先がありません。";
\r
35 private static Log logger = LogFactory.getLog(ClusterConnectionPool.class);
\r
38 private List<String> urlList = null;
\r
43 public ClusterConnectionPool(String driver, String url,String user,String pass){
\r
44 super(driver,url,user,pass);
\r
48 private static Random rnd = new Random();
\r
50 /** 接続先コネクションとurlのマッピング。 */
\r
51 private Map<Connection, String> urlMap = null;
\r
56 public List<String> getUrlList() {
\r
63 public void setUrlList(List<String> list) {
\r
65 urlMap = new HashMap<Connection, String>(list.size());
\r
70 * @see com.lavans.util.jdbc.ConnectionPool#createConnection()
\r
73 protected PooledConnection createConnection() throws SQLException {
\r
75 if((getPoolList().size() + getUseList().size()) >= getMaxConnections()){
\r
76 throw new SQLException(MSG_ERR_TOOMANYCONNECTIONS, SQLSTATE_CONNECTION_EXCEPTION, ERR_CONNECTION_OVERFLOW);
\r
78 Connection conn = createNativeConnection();
\r
80 conn = new ClusterConnection(conn,this);
\r
81 logger.debug(conn.getMetaData().getURL());
\r
82 urlMap.put(conn,conn.getMetaData().getURL()); // コネクションと新しいurlのマッピングを行う。
\r
84 // StatsConnection,LoggingConnectionともにLoggableインターフェースの
\r
85 // 実装とした。LoggableなConnectionの作成順序は入れ替え可能。
\r
86 // Loggableを実装しないラッパークラスを作る場合はこれらの前にnewすること。
\r
90 conn = new StatsConnection(conn);
\r
93 // BindConnection型を返すので、BindConnectionでラップするのは
\r
95 BindConnection bcon = new BindConnectionImpl(conn);
\r
97 // さらにConnection#close()で物理的に閉じずにDBManagerに
\r
98 // 返却するためPooledConnectionでラップする。
\r
99 // ConnectionPoolではなくDBManagerを通すのは
\r
100 // 統計情報取得時に貸し出し管理を行うため。
\r
101 PooledConnection pcon = new PooledConnection(this,bcon);
\r
107 * Native接続作成。Clusterの中から選択して接続テストを行い、
\r
109 * @see createNativeConnection(String)
\r
112 private Connection createNativeConnection() throws SQLException{
\r
113 // ワーク用にURLリストをコピーして有効なurlを探索。
\r
114 return createNativeConnection(new ArrayList<String>(urlList));
\r
118 * Native接続作成。Clusterの中から選択して接続テストを行い、
\r
119 * 失敗したら他の接続先にする。DBに障害があった時に特定の
\r
120 * urlをあらかじめ排除するため、接続先一覧を指定できるようにする。
\r
123 private Connection createNativeConnection(List<String> workList) throws SQLException{
\r
124 // from java.sql.DriverManager
\r
125 Properties info = new Properties();
\r
126 if (getUser() != null) {
\r
127 info.put("user", getUser());
\r
129 if (getPass() != null) {
\r
130 info.put("password", getPass());
\r
133 // 接続に失敗した場合はそのurlをワークリストから外して、
\r
134 // 再度ランダムにurlを選択し直す。
\r
135 while(workList.size()>0){
\r
136 String url = getRandomSelectUrl(workList);
\r
138 Connection conn = getDriver().connect(url, info);
\r
139 if(checkConnection(conn)){ // 接続に成功したら
\r
142 // 接続に失敗したら今のurlを除外して、無くなるまで繰り返す。
\r
143 workList.remove(url);
\r
144 }catch (SQLException e) { // getConnectionに失敗した場合も
\r
145 workList.remove(url); // 今のurlを除外して、無くなるまで繰り返す。
\r
149 // 有効なConnectionが見つからないままworkListが0になってしまったらエラー
\r
150 throw new SQLException(MSG_ERR_NOCONNECT);
\r
154 * Clusterの接続先からランダムに選択する。
\r
155 * @return 選択されたJDBC Driver URL
\r
157 private String getRandomSelectUrl(List<String> list){
\r
158 int i = (int)Math.ceil(rnd.nextInt(list.size()));
\r
159 logger.info("★ "+ i +"番目が選択された。"+list.get(i));
\r
160 return list.get(i);
\r
165 * このメソッドが呼ばれるのはClusterConnectionが接続している先のDBに
\r
166 * 障害があったとき。したがって、エラーが発生していないurlの中から
\r
167 * 有効なurlを探索し、Connectionを生成して返してやる。
\r
168 * BindやLogginはClusterConnectionの上にラップされているので、
\r
172 public Connection getAnotherConnection(ClusterConnection target) throws SQLException{
\r
173 String url = urlMap.remove(target);
\r
174 List<String> workList = new ArrayList<String>(urlList); // urlListをコピーして
\r
175 workList.remove(url); // 障害が起きたurlを除外する。
\r
176 Connection conn = createNativeConnection(workList);
\r
178 logger.debug(conn.getMetaData().getURL());
\r
179 urlMap.put(target,conn.getMetaData().getURL()); // コネクションと新しいurlのマッピングを行う。
\r
186 * コネクション管理はスーパークラスであるConnectionPoolで行うが、
\r
187 * クラスタコネクションの場合だけは初期化処理(過去の更新履歴を削除)する。
\r
189 * 初期化の呼び出しを行う適切な場所が思いつかなかったので、
\r
190 * ここでrollback()することにした。setAutoCommit(false)で
\r
191 * 使われるときは別に呼ばなくても構わないのだが、setAutoCommit(true)で
\r
192 * 使われるときにClusterConnection#set○○系の処理がキューに溜まっていってしまうので、
\r
193 * コネクション取得時にmethodListがクリアされるようにした。
\r
195 * @see com.lavans.luz.sql.ConnectionPool#getConnection()
\r
198 public PooledConnection getConnection() throws SQLException {
\r
199 PooledConnection con = super.getConnection();
\r