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 006 package fc.jdbc; 007 008 import java.sql.*; 009 import java.util.*; 010 011 import fc.io.*; 012 import fc.util.*; 013 014 /** 015 A poolable {@link java.sql.Connection} that works with the {@link 016 PooledConnectionMgr}. Closing this connection marks it as closed and 017 returns it to the pool. 018 <p> 019 The connection can be retrieved and closed as many times as needed. 020 It is very important that a connection be closed after use (this 021 is true for all connections, pooled or regular). 022 <p> 023 Typically one does so by saying: 024 <blockquote> 025 <pre> 026 Connection con; 027 try { 028 con = [..get either a pooled or regular connection..] 029 ..use.. 030 <b>con.commit();</b> //if transactional 031 } 032 catch (SQLException e) { 033 <b>con.rollback();</b> //if transactional 034 } 035 finally { 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 041 invoked. You must either commit or rollback the transaction if in transaction mode (ie you 042 have called <code>setAutoCommit(false)</code> on this connection). Returning the connection (via close) 043 back to the pool without committing/rolling-back will keep the prior statements around and those will 044 be committed/rolledback at a later date if commit/rollback is invoked upon retrieving/using the connection 045 again. 046 047 @author hursh jain 048 **/ 049 public class PooledConnection implements Connection 050 { 051 static 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 056 int id; 057 PooledConnectionMgr pool; //the parent pool 058 Connection connection; 059 volatile boolean closed; 060 //statistical data 061 int commitCount; 062 int rollbackCount; 063 int warningsCount; 064 long transaction_start_time = 0; 065 066 /** 067 Creates a initially open pooled connection that wraps the 068 specified jdbc connection. 069 **/ 070 public 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 080 private static final synchronized int makeID() { 081 return idCounter++; 082 } 083 084 protected int getID() { 085 return id; 086 } 087 088 /** 089 <tt>true</tt> to mark this connection as closed, <tt>false</tt> 090 to mark it as open. 091 **/ 092 void setClosed(boolean val) 093 { 094 closed = val; 095 } 096 097 void checkOpen() throws SQLException 098 { 099 if (closed) { 100 throw new SQLException("This connection has been closed"); 101 } 102 } 103 104 //== pooled ========================================= 105 106 /** 107 transactions are not rolled back or committed when <code>close</code> is invoked. You must either commit or 108 rollback the transaction if in transaction mode (ie you have called <code>setAutoCommit(false)</code> on this 109 connection). 110 <p> 111 eturning the connection (via close) back to the pool without committing/rolling-back will keep 112 the prior statements around and those will be committed/rolledback at a later date if commit/rollback is 113 invoked upon retrieving/using the connection again. 114 */ 115 public 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 127 void closeReally() throws SQLException 128 { 129 connection.close(); 130 } 131 132 133 public boolean isClosed() { 134 return closed; 135 } 136 137 //we must use interned strings with id based hashmaps; 138 private final Map pstmtMap = new IdentityHashMap(256); 139 public PreparedStatement getCachedPreparedStatement(String sql) 140 throws 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 163 public 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 208 public void clearWarnings() throws SQLException { 209 connection.clearWarnings(); 210 } 211 212 public void commit() throws SQLException 213 { 214 commitCount++; 215 transaction_start_time = 0; 216 connection.commit(); 217 } 218 219 public Statement createStatement() throws SQLException { 220 return connection.createStatement(); 221 } 222 223 public Statement createStatement(int resultSetType, 224 int resultSetConcurrency) throws SQLException { 225 226 return connection.createStatement(resultSetType, resultSetConcurrency); 227 } 228 229 public boolean getAutoCommit() throws SQLException { 230 return connection.getAutoCommit(); 231 } 232 233 public String getCatalog() throws SQLException { 234 return connection.getCatalog(); 235 } 236 237 public DatabaseMetaData getMetaData() throws SQLException { 238 return connection.getMetaData(); 239 } 240 241 public int getTransactionIsolation() throws SQLException { 242 return connection.getTransactionIsolation(); 243 } 244 245 public Map getTypeMap() throws SQLException { 246 return connection.getTypeMap(); 247 } 248 249 public SQLWarning getWarnings() throws SQLException { 250 warningsCount++; 251 return connection.getWarnings(); 252 } 253 254 255 public boolean isReadOnly() throws SQLException { 256 return connection.isReadOnly(); 257 } 258 259 public String nativeSQL(String sql) throws SQLException { 260 return connection.nativeSQL(sql); 261 } 262 263 public CallableStatement prepareCall(String sql) throws SQLException { 264 return connection.prepareCall(sql); 265 } 266 267 public CallableStatement prepareCall(String sql, int resultSetType, 268 int resultSetConcurrency) throws SQLException { 269 return connection.prepareCall(sql, resultSetType, resultSetConcurrency); 270 } 271 272 public PreparedStatement prepareStatement(String sql) throws SQLException { 273 return connection.prepareStatement(sql); 274 } 275 276 public PreparedStatement prepareStatement(String sql, int resultSetType, 277 int resultSetConcurrency) throws SQLException { 278 return connection.prepareStatement(sql, resultSetType, resultSetConcurrency); 279 } 280 281 public void rollback() throws SQLException { 282 connection.rollback(); 283 } 284 285 public 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 295 public void setCatalog(String catalog) throws SQLException { 296 connection.setCatalog(catalog); 297 } 298 299 public void setReadOnly(boolean readOnly) throws SQLException { 300 connection.setReadOnly(readOnly); 301 } 302 303 public void setTransactionIsolation(int level) throws SQLException { 304 connection.setTransactionIsolation(level); 305 } 306 307 public void setTypeMap(Map map) throws SQLException { 308 connection.setTypeMap(map); 309 } 310 311 public int getHoldability() throws SQLException { 312 return connection.getHoldability(); 313 } 314 315 public void setHoldability(int holdability) throws SQLException { 316 connection.setHoldability(holdability); 317 } 318 319 public java.sql.Savepoint setSavepoint() throws SQLException { 320 return connection.setSavepoint(); 321 } 322 323 public java.sql.Savepoint setSavepoint(String name) throws SQLException { 324 return connection.setSavepoint(name); 325 } 326 327 public void rollback(java.sql.Savepoint savepoint) throws SQLException { 328 rollbackCount++; 329 connection.rollback(savepoint); 330 } 331 332 public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException { 333 connection.releaseSavepoint(savepoint); 334 } 335 336 public Statement createStatement(int resultSetType, 337 int resultSetConcurrency, 338 int resultSetHoldability) throws SQLException { 339 return connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); 340 } 341 342 public Struct createStruct(String str, Object[] arr) throws SQLException { 343 return connection.createStruct(str, arr); 344 } 345 346 public java.util.Properties getClientInfo() throws SQLException { 347 return connection.getClientInfo(); 348 } 349 350 public String getClientInfo(String name) throws SQLException { 351 return connection.getClientInfo(name); 352 } 353 354 public void setClientInfo(String key, String val) throws SQLClientInfoException { 355 connection.setClientInfo(key, val); 356 } 357 358 public void setClientInfo(java.util.Properties props) throws SQLClientInfoException { 359 connection.setClientInfo(props); 360 } 361 362 public java.sql.Array createArrayOf(String str, Object[] arr) throws SQLException { 363 return connection.createArrayOf(str, arr); 364 } 365 366 public Blob createBlob() throws SQLException { 367 return connection.createBlob(); 368 } 369 370 public Clob createClob() throws SQLException { 371 return connection.createClob(); 372 } 373 374 public NClob createNClob() throws SQLException { 375 return connection.createNClob(); 376 } 377 378 public SQLXML createSQLXML() throws SQLException { 379 return connection.createSQLXML(); 380 } 381 382 public boolean isValid(int timeout) throws SQLException { 383 return connection.isValid(timeout); 384 } 385 386 public boolean isWrapperFor(Class iface) throws SQLException { 387 return connection.isWrapperFor(iface); 388 } 389 390 public Object unwrap(Class iface) throws SQLException { 391 return connection.unwrap(iface); 392 } 393 394 public PreparedStatement prepareStatement(String sql, int resultSetType, 395 int resultSetConcurrency, 396 int resultSetHoldability) throws SQLException { 397 return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); 398 } 399 400 public CallableStatement prepareCall(String sql, int resultSetType, 401 int resultSetConcurrency, 402 int resultSetHoldability) throws SQLException { 403 return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); 404 } 405 406 public 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 414 public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException { 415 return connection.prepareStatement(sql, columnNames); 416 } 417 418 //====================== 1.7 + =============================== 419 /* enable this when building for jdk7+ */ 420 /* 421 public int getNetworkTimeout() 422 throws SQLException { 423 return connection.getNetworkTimeout(); 424 } 425 426 public void setNetworkTimeout(java.util.concurrent.Executor executor, int milliseconds) 427 throws SQLException { 428 connection.setNetworkTimeout(executor, milliseconds); 429 } 430 431 public void abort(java.util.concurrent.Executor executor) 432 throws SQLException { 433 connection.abort(executor); 434 } 435 436 public String getSchema() 437 throws SQLException { 438 return connection.getSchema(); 439 } 440 441 public void setSchema(String schema) 442 throws SQLException { 443 connection.setSchema(schema); 444 } 445 */ 446 } //~PooledConnection