001// Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 002// The Molly framework is freely distributable under the terms of an 003// MIT-style license. For details, see the molly pages web site at: 004// http://www.mollypages.org/. Use, modify, have fun ! 005 006package fc.jdbc; 007 008import java.sql.*; 009import java.util.*; 010 011import fc.io.*; 012import fc.util.*; 013 014/** 015A poolable {@link java.sql.Connection} that works with the {@link 016PooledConnectionMgr}. Closing this connection marks it as closed and 017returns it to the pool. 018<p> 019The connection can be retrieved and closed as many times as needed. 020It is very important that a connection be closed after use (this 021is true for all connections, pooled or regular). 022<p> 023Typically one does so by saying: 024<blockquote> 025<pre> 026Connection con; 027try { 028 con = [..get either a pooled or regular connection..] 029 ..use.. 030 <b>con.commit();</b> //if transactional 031 } 032catch (SQLException e) { 033 <b>con.rollback();</b> //if transactional 034 } 035finally { 036 <b>con.close();</b> //always (transactional or not) 037 } 038</pre> 039</blockquote> 040<b>Super Important</b>: transactions are not rolled back or committed when <code>close</code> is 041invoked. You must either commit or rollback the transaction if in transaction mode (ie you 042have called <code>setAutoCommit(false)</code> on this connection). Returning the connection (via close) 043back to the pool without committing/rolling-back will keep the prior statements around and those will 044be committed/rolledback at a later date if commit/rollback is invoked upon retrieving/using the connection 045again. 046 047@author hursh jain 048**/ 049public final class PooledConnection implements Connection 050{ 051static int idCounter = 1; 052 053//each pooled connection has an ID which is useful to track 054//check in/check outs from the pool. id's are assigned from 055//the idCount variable 056int id; 057PooledConnectionMgr pool; //the parent pool 058Connection connection; 059volatile boolean closed; 060//statistical data 061int commitCount; 062int rollbackCount; 063int warningsCount; 064long transaction_start_time = 0; 065 066/** 067Creates a initially open pooled connection that wraps the 068specified jdbc connection. 069**/ 070public PooledConnection(PooledConnectionMgr pool, Connection con) 071 { 072 Argcheck.notnull(pool, "specified 'pool' arg was null"); 073 Argcheck.notnull(con, "specifed 'con' arg was null"); 074 this.pool = pool; 075 this.connection = con; 076 this.closed = false; 077 id = makeID(); 078 } 079 080private static final synchronized int makeID() { 081 return idCounter++; 082 } 083 084protected int getID() { 085 return id; 086 } 087 088/** 089<tt>true</tt> to mark this connection as closed, <tt>false</tt> 090to mark it as open. 091**/ 092void setClosed(boolean val) 093 { 094 closed = val; 095 } 096 097void checkOpen() throws SQLException 098 { 099 if (closed) { 100 throw new SQLException("This connection has been closed"); 101 } 102 } 103 104//== pooled ========================================= 105 106/** 107transactions are not rolled back or committed when <code>close</code> is invoked. You must either commit or 108rollback the transaction if in transaction mode (ie you have called <code>setAutoCommit(false)</code> on this 109connection). 110<p> 111eturning the connection (via close) back to the pool without committing/rolling-back will keep 112the prior statements around and those will be committed/rolledback at a later date if commit/rollback is 113invoked upon retrieving/using the connection again. 114*/ 115public void close() throws SQLException 116 { 117 if (! isClosed() ) { 118 //pool.log.bug("closing connection", IOUtil.throwableToString(new Exception("Debug Stack Trace"))); 119 setClosed(true); 120 pool.put(this); 121 } 122 else { 123 pool.log.bug("Tried to close already closed connection (#", id, ") =>", IOUtil.throwableToString(new Exception("Debug Stack Trace"))); 124 } 125 } 126 127void closeReally() throws SQLException 128 { 129 connection.close(); 130 } 131 132 133public boolean isClosed() { 134 return closed; 135 } 136 137//we must use interned strings with id based hashmaps; 138private final Map pstmtMap = new IdentityHashMap(256); 139public PreparedStatement getCachedPreparedStatement(String sql) 140throws SQLException 141 { 142 sql = sql.intern(); 143 synchronized (this) 144 { 145 //intern can be tricky. we must use the string 146 //*returned* by the intern() method 147 if (pstmtMap.containsKey(sql)) { 148 final PreparedStatement ps = (PreparedStatement) pstmtMap.get(sql); 149 ps.clearParameters(); 150 return ps; 151 } 152 else{ 153 final PreparedStatement pstmt = this.prepareStatement( 154 sql, 155 ResultSet.TYPE_SCROLL_INSENSITIVE, 156 ResultSet.CONCUR_READ_ONLY); 157 pstmtMap.put(sql, pstmt); 158 return pstmt; 159 } 160 } 161 } 162 163public String toString() { 164 StringBuilder buf = new StringBuilder(); 165 buf.append(super.toString()); 166 buf.append("/PoolID:"); 167 buf.append(id); 168 try { 169 buf.append("/currentIsolation:"); 170 int i = getTransactionIsolation(); 171 switch (i) 172 { 173 case Connection.TRANSACTION_NONE: 174 buf.append("NONE"); break; 175 case Connection.TRANSACTION_READ_UNCOMMITTED : 176 buf.append("READ_UNCOMMITTED"); break; 177 case Connection.TRANSACTION_READ_COMMITTED: 178 buf.append("READ_COMMITTED"); break; 179 case Connection.TRANSACTION_REPEATABLE_READ : 180 buf.append("REPEATABLE_READ"); break; 181 case Connection.TRANSACTION_SERIALIZABLE : 182 buf.append("SERIALIZABLE"); break; 183 default: 184 buf.append("Unknown: "); 185 buf.append(i); 186 } 187 buf.append("/autoCommit:"); 188 buf.append(String.valueOf(getAutoCommit()).toUpperCase()); 189 if (transaction_start_time == 0) { 190 buf.append("/[no open transaction]"); 191 } 192 else{ 193 buf.append("/[in open transaction,"); 194 buf.append(System.currentTimeMillis() - transaction_start_time); 195 buf.append(" ms]"); 196 } 197 } 198 catch (SQLException e) { 199 } 200 return buf.toString(); 201 } 202 203 204 205 206//== wrapped methods ==================================== 207 208public void clearWarnings() throws SQLException { 209 connection.clearWarnings(); 210 } 211 212public void commit() throws SQLException 213 { 214 commitCount++; 215 transaction_start_time = 0; 216 connection.commit(); 217 } 218 219public Statement createStatement() throws SQLException { 220 return connection.createStatement(); 221 } 222 223public Statement createStatement(int resultSetType, 224 int resultSetConcurrency) throws SQLException { 225 226 return connection.createStatement(resultSetType, resultSetConcurrency); 227 } 228 229public boolean getAutoCommit() throws SQLException { 230 return connection.getAutoCommit(); 231 } 232 233public String getCatalog() throws SQLException { 234 return connection.getCatalog(); 235 } 236 237public DatabaseMetaData getMetaData() throws SQLException { 238 return connection.getMetaData(); 239 } 240 241public int getTransactionIsolation() throws SQLException { 242 return connection.getTransactionIsolation(); 243 } 244 245public Map getTypeMap() throws SQLException { 246 return connection.getTypeMap(); 247 } 248 249public SQLWarning getWarnings() throws SQLException { 250 warningsCount++; 251 return connection.getWarnings(); 252 } 253 254 255public boolean isReadOnly() throws SQLException { 256 return connection.isReadOnly(); 257 } 258 259public String nativeSQL(String sql) throws SQLException { 260 return connection.nativeSQL(sql); 261 } 262 263public CallableStatement prepareCall(String sql) throws SQLException { 264 return connection.prepareCall(sql); 265 } 266 267public CallableStatement prepareCall(String sql, int resultSetType, 268 int resultSetConcurrency) throws SQLException { 269 return connection.prepareCall(sql, resultSetType, resultSetConcurrency); 270 } 271 272public PreparedStatement prepareStatement(String sql) throws SQLException { 273 return connection.prepareStatement(sql); 274 } 275 276public PreparedStatement prepareStatement(String sql, int resultSetType, 277 int resultSetConcurrency) throws SQLException { 278 return connection.prepareStatement(sql, resultSetType, resultSetConcurrency); 279 } 280 281public void rollback() throws SQLException { 282 connection.rollback(); 283 } 284 285public void setAutoCommit(boolean b) throws SQLException 286 { 287 if (b) 288 transaction_start_time = 0; 289 else 290 transaction_start_time = System.currentTimeMillis(); 291 292 connection.setAutoCommit(b); 293 } 294 295public void setCatalog(String catalog) throws SQLException { 296 connection.setCatalog(catalog); 297 } 298 299public void setReadOnly(boolean readOnly) throws SQLException { 300 connection.setReadOnly(readOnly); 301 } 302 303public void setTransactionIsolation(int level) throws SQLException { 304 connection.setTransactionIsolation(level); 305 } 306 307public void setTypeMap(Map map) throws SQLException { 308 connection.setTypeMap(map); 309 } 310 311public int getHoldability() throws SQLException { 312 return connection.getHoldability(); 313 } 314 315public void setHoldability(int holdability) throws SQLException { 316 connection.setHoldability(holdability); 317 } 318 319public java.sql.Savepoint setSavepoint() throws SQLException { 320 return connection.setSavepoint(); 321 } 322 323public java.sql.Savepoint setSavepoint(String name) throws SQLException { 324 return connection.setSavepoint(name); 325 } 326 327public void rollback(java.sql.Savepoint savepoint) throws SQLException { 328 rollbackCount++; 329 connection.rollback(savepoint); 330 } 331 332public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException { 333 connection.releaseSavepoint(savepoint); 334 } 335 336public Statement createStatement(int resultSetType, 337 int resultSetConcurrency, 338 int resultSetHoldability) throws SQLException { 339 return connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); 340 } 341 342public Struct createStruct(String str, Object[] arr) throws SQLException { 343 return connection.createStruct(str, arr); 344 } 345 346public java.util.Properties getClientInfo() throws SQLException { 347 return connection.getClientInfo(); 348 } 349 350public String getClientInfo(String name) throws SQLException { 351 return connection.getClientInfo(name); 352 } 353 354public void setClientInfo(String key, String val) throws SQLClientInfoException { 355 connection.setClientInfo(key, val); 356 } 357 358public void setClientInfo(java.util.Properties props) throws SQLClientInfoException { 359 connection.setClientInfo(props); 360 } 361 362public java.sql.Array createArrayOf(String str, Object[] arr) throws SQLException { 363 return connection.createArrayOf(str, arr); 364 } 365 366public Blob createBlob() throws SQLException { 367 return connection.createBlob(); 368 } 369 370public Clob createClob() throws SQLException { 371 return connection.createClob(); 372 } 373 374public NClob createNClob() throws SQLException { 375 return connection.createNClob(); 376 } 377 378public SQLXML createSQLXML() throws SQLException { 379 return connection.createSQLXML(); 380 } 381 382public boolean isValid(int timeout) throws SQLException { 383 return connection.isValid(timeout); 384 } 385 386public boolean isWrapperFor(Class iface) throws SQLException { 387 return connection.isWrapperFor(iface); 388 } 389 390public Object unwrap(Class iface) throws SQLException { 391 return connection.unwrap(iface); 392 } 393 394public PreparedStatement prepareStatement(String sql, int resultSetType, 395 int resultSetConcurrency, 396 int resultSetHoldability) throws SQLException { 397 return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); 398 } 399 400public CallableStatement prepareCall(String sql, int resultSetType, 401 int resultSetConcurrency, 402 int resultSetHoldability) throws SQLException { 403 return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); 404 } 405 406public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { 407 return connection.prepareStatement(sql, autoGeneratedKeys); 408 } 409 410 public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException { 411 return connection.prepareStatement(sql, columnIndexes); 412 } 413 414public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException { 415 return connection.prepareStatement(sql, columnNames); 416 } 417 418//====================== 1.7 + =============================== 419public int getNetworkTimeout() 420throws SQLException { 421 return connection.getNetworkTimeout(); 422 } 423 424public void setNetworkTimeout(java.util.concurrent.Executor executor, int milliseconds) 425throws SQLException { 426 connection.setNetworkTimeout(executor, milliseconds); 427 } 428 429public void abort(java.util.concurrent.Executor executor) 430throws SQLException { 431 connection.abort(executor); 432 } 433 434public String getSchema() 435throws SQLException { 436 return connection.getSchema(); 437 } 438 439public void setSchema(String schema) 440throws SQLException { 441 connection.setSchema(schema); 442 } 443 444} //~PooledConnection