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