OSDN Git Service

i
[luz/luz.git] / luz2 / src / com / lavans / luz2 / sql / cluster / ClusterConnectionPool.java
1 /* $Id: ClusterConnectionPool.java 222 2010-09-06 19:20:24Z dobashi $\r
2  * create: 2004/10/22\r
3  * (c)2004 Lavans Networks Inc. All Rights Reserved.\r
4  */\r
5 package com.lavans.luz2.sql.cluster;\r
6 \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
15 \r
16 import org.apache.commons.logging.Log;\r
17 import org.apache.commons.logging.LogFactory;\r
18 \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
24 \r
25 \r
26 /**\r
27  * @author dobashi\r
28  * @version 1.00\r
29  */\r
30 public class ClusterConnectionPool extends ConnectionPool {\r
31         /** 接続先がない場合のエラーメッセージ */\r
32         private static final String MSG_ERR_NOCONNECT="有効な接続先がありません。";\r
33 \r
34         /** ロガー。debug用 */\r
35         private static Log logger = LogFactory.getLog(ClusterConnectionPool.class);\r
36 \r
37         /** 接続先URL一覧 */\r
38         private List<String> urlList = null;\r
39 \r
40         /**\r
41          * コンストラクタ。\r
42          **/\r
43         public ClusterConnectionPool(String driver, String url,String user,String pass){\r
44                 super(driver,url,user,pass);\r
45         }\r
46 \r
47         /** 乱数生成用 */\r
48         private static Random rnd = new Random();\r
49 \r
50         /** 接続先コネクションとurlのマッピング。 */\r
51         private Map<Connection, String> urlMap = null;\r
52 \r
53         /**\r
54          * @return\r
55          */\r
56         public List<String> getUrlList() {\r
57                 return urlList;\r
58         }\r
59 \r
60         /**\r
61          * @param list\r
62          */\r
63         public void setUrlList(List<String> list) {\r
64                 urlList = list;\r
65                 urlMap = new HashMap<Connection, String>(list.size());\r
66         }\r
67 \r
68 \r
69         /* (非 Javadoc)\r
70          * @see com.lavans.util.jdbc.ConnectionPool#createConnection()\r
71          */\r
72         @Override\r
73         protected PooledConnection createConnection() throws SQLException {\r
74                 // 最大数チェック\r
75                 if((getPoolList().size() + getUseList().size()) >= getMaxConnections()){\r
76                         throw new SQLException(MSG_ERR_TOOMANYCONNECTIONS, SQLSTATE_CONNECTION_EXCEPTION, ERR_CONNECTION_OVERFLOW);\r
77                 }\r
78                 Connection conn = createNativeConnection();\r
79 \r
80                 conn = new ClusterConnection(conn,this);\r
81                 logger.debug(conn.getMetaData().getURL());\r
82                 urlMap.put(conn,conn.getMetaData().getURL());   // コネクションと新しいurlのマッピングを行う。\r
83 \r
84                 // StatsConnection,LoggingConnectionともにLoggableインターフェースの\r
85                 // 実装とした。LoggableなConnectionの作成順序は入れ替え可能。\r
86                 // Loggableを実装しないラッパークラスを作る場合はこれらの前にnewすること。\r
87 \r
88                 // 統計情報を収集するなら\r
89                 if(isStatistics()){\r
90                         conn = new StatsConnection(conn);\r
91                 }\r
92 \r
93                 // BindConnection型を返すので、BindConnectionでラップするのは\r
94                 // 一番最後でないといけない。\r
95                 BindConnection bcon = new BindConnectionImpl(conn);\r
96 \r
97                 // さらにConnection#close()で物理的に閉じずにDBManagerに\r
98                 // 返却するためPooledConnectionでラップする。\r
99                 // ConnectionPoolではなくDBManagerを通すのは\r
100                 // 統計情報取得時に貸し出し管理を行うため。\r
101                 PooledConnection pcon = new PooledConnection(this,bcon);\r
102 \r
103                 return pcon;\r
104         }\r
105 \r
106         /**\r
107          * Native接続作成。Clusterの中から選択して接続テストを行い、\r
108          * 失敗したら他の接続先にする。\r
109          * @see createNativeConnection(String)\r
110          * @return\r
111          */\r
112         private Connection createNativeConnection() throws SQLException{\r
113                 // ワーク用にURLリストをコピーして有効なurlを探索。\r
114                 return createNativeConnection(new ArrayList<String>(urlList));\r
115         }\r
116 \r
117         /**\r
118          * Native接続作成。Clusterの中から選択して接続テストを行い、\r
119          * 失敗したら他の接続先にする。DBに障害があった時に特定の\r
120          * urlをあらかじめ排除するため、接続先一覧を指定できるようにする。\r
121          * @return\r
122          */\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
128                 }\r
129                 if (getPass() != null) {\r
130                     info.put("password", getPass());\r
131                 }\r
132 \r
133                 // 接続に失敗した場合はそのurlをワークリストから外して、\r
134                 // 再度ランダムにurlを選択し直す。\r
135                 while(workList.size()>0){\r
136                         String url = getRandomSelectUrl(workList);\r
137                         try{\r
138                                 Connection conn = getDriver().connect(url, info);\r
139                                 if(checkConnection(conn)){                              // 接続に成功したら\r
140                                         return conn;\r
141                                 }\r
142                                 // 接続に失敗したら今のurlを除外して、無くなるまで繰り返す。\r
143                                 workList.remove(url);\r
144                         }catch (SQLException e) {                               // getConnectionに失敗した場合も\r
145                                 workList.remove(url);                           // 今のurlを除外して、無くなるまで繰り返す。\r
146                         }\r
147                 }\r
148 \r
149                 // 有効なConnectionが見つからないままworkListが0になってしまったらエラー\r
150                 throw new SQLException(MSG_ERR_NOCONNECT);\r
151         }\r
152 \r
153         /**\r
154          * Clusterの接続先からランダムに選択する。\r
155          * @return 選択されたJDBC Driver URL\r
156          */\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
161         }\r
162 \r
163         /**\r
164          * 別の接続先を取得する。\r
165          * このメソッドが呼ばれるのはClusterConnectionが接続している先のDBに\r
166          * 障害があったとき。したがって、エラーが発生していないurlの中から\r
167          * 有効なurlを探索し、Connectionを生成して返してやる。\r
168          * BindやLogginはClusterConnectionの上にラップされているので、\r
169          * ここでは意識する必要はない。\r
170          * @return\r
171          */\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
177 \r
178                 logger.debug(conn.getMetaData().getURL());\r
179                 urlMap.put(target,conn.getMetaData().getURL()); // コネクションと新しいurlのマッピングを行う。\r
180 \r
181                 return conn;\r
182         }\r
183 \r
184         /**\r
185          * コネクションの取得。\r
186          * コネクション管理はスーパークラスであるConnectionPoolで行うが、\r
187          * クラスタコネクションの場合だけは初期化処理(過去の更新履歴を削除)する。\r
188          *\r
189          * 初期化の呼び出しを行う適切な場所が思いつかなかったので、\r
190          * ここでrollback()することにした。setAutoCommit(false)で\r
191          * 使われるときは別に呼ばなくても構わないのだが、setAutoCommit(true)で\r
192          * 使われるときにClusterConnection#set○○系の処理がキューに溜まっていってしまうので、\r
193          * コネクション取得時にmethodListがクリアされるようにした。\r
194          *\r
195          * @see com.lavans.luz.sql.ConnectionPool#getConnection()\r
196          */\r
197         @Override\r
198         public PooledConnection getConnection() throws SQLException {\r
199                 PooledConnection con = super.getConnection();\r
200                 con.rollback();\r
201                 return con;\r
202         }\r
203 \r
204 }\r