KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > easybeans > component > jdbcpool > JManagedConnection


1 /**
2  * EasyBeans
3  * Copyright (C) 2006 Bull S.A.S.
4  * Contact: easybeans@objectweb.org
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA
20  *
21  * --------------------------------------------------------------------------
22  * $Id: JManagedConnection.java 1022 2006-08-04 11:02:28Z benoitf $
23  * --------------------------------------------------------------------------
24  */

25
26 package org.objectweb.easybeans.component.jdbcpool;
27
28 import java.sql.Connection JavaDoc;
29 import java.sql.PreparedStatement JavaDoc;
30 import java.sql.ResultSet JavaDoc;
31 import java.sql.SQLException JavaDoc;
32 import java.util.Collections JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.Vector JavaDoc;
37
38 import javax.sql.ConnectionEvent JavaDoc;
39 import javax.sql.ConnectionEventListener JavaDoc;
40 import javax.sql.XAConnection JavaDoc;
41 import javax.transaction.Synchronization JavaDoc;
42 import javax.transaction.Transaction JavaDoc;
43 import javax.transaction.xa.XAException JavaDoc;
44 import javax.transaction.xa.XAResource JavaDoc;
45 import javax.transaction.xa.Xid JavaDoc;
46
47 import org.objectweb.easybeans.log.JLog;
48 import org.objectweb.easybeans.log.JLogFactory;
49
50 /**
51  * This class represents the connection managed by the pool. This connection is
52  * a managed connection and is notified of the transaction events.
53  * @author Philippe Durieux
54  * @author Florent Benoit
55  */

56 public class JManagedConnection implements Comparable JavaDoc, XAConnection JavaDoc, XAResource JavaDoc, Synchronization JavaDoc {
57
58     /**
59      * Logger.
60      */

61     private static JLog logger = JLogFactory.getLog(JManagedConnection.class);
62
63     /**
64      * Connection to the database.
65      */

66     private Connection JavaDoc physicalConnection = null;
67
68     /**
69      * Connection returned to the user.
70      */

71     private Connection JavaDoc implConn = null;
72
73     /**
74      * Maximum of prepared statements.
75      */

76     private int pstmtmax = 0;
77
78     /**
79      * Current number of opened prepared statements.
80      */

81     private int psOpenNb = 0;
82
83     /**
84      * Event listeners (of PooledConnection).
85      */

86     private Vector JavaDoc<ConnectionEventListener JavaDoc> eventListeners = new Vector JavaDoc<ConnectionEventListener JavaDoc>();
87
88     /**
89      * count of opening this connection. >0 if open.
90      */

91     private int open = 0;
92
93     /**
94      * Transaction timeout value.
95      */

96     private int timeout = 0;
97
98     /**
99      * Transaction the connection is involved with.
100      */

101     private Transaction JavaDoc tx = null;
102
103     /**
104      * Counter of all managed connections created.
105      */

106     private static int objcount = 0;
107
108     /**
109      * Identifier of this connection.
110      */

111     private final int identifier;
112
113     /**
114      * Prepared statements that were reused.
115      */

116     private int reUsedPreparedStatements = 0;
117
118     /**
119      * List of PreparedStatement in the pool.
120      */

121     private Map JavaDoc<String JavaDoc, JStatement> psList = null;
122
123     /**
124      * Link to the connection manager.
125      */

126     private ConnectionManager ds = null;
127
128     /**
129      * Time of the death for this connection.
130      */

131     private long deathTime = 0;
132
133     /**
134      * Time for closing this connection.
135      */

136     private long closeTime = 0;
137
138
139     /**
140      * Builds a new managed connection on a JDBC connection.
141      * @param physicalConnection the physical JDBC Connection.
142      * @param ds the connection manager
143      */

144     public JManagedConnection(final Connection JavaDoc physicalConnection, final ConnectionManager ds) {
145         this.physicalConnection = physicalConnection;
146         this.ds = ds;
147
148         // An XAConnection holds 2 objects: 1 Connection + 1 XAResource
149
this.implConn = new JConnection(this, physicalConnection);
150
151         open = 0;
152         deathTime = System.currentTimeMillis() + ds.getMaxAgeMilli();
153
154         identifier = objcount++;
155
156         // Prepared statement.
157
pstmtmax = ds.getPstmtMax();
158         psOpenNb = 0;
159         psList = Collections.synchronizedMap(new HashMap JavaDoc<String JavaDoc, JStatement>());
160
161     }
162
163     /**
164      * @return The identifier of this JManagedConnection
165      */

166     public int getIdentifier() {
167         return identifier;
168     }
169
170     /**
171      * Dynamically change the prepared statement pool size.
172      * @param max the maximum of prepared statement.
173      */

174     public void setPstmtMax(final int max) {
175         pstmtmax = max;
176         if (psList == null) {
177             psList = Collections.synchronizedMap(new HashMap JavaDoc<String JavaDoc, JStatement>(pstmtmax));
178         }
179     }
180
181     /**
182      * Commit the global transaction specified by xid.
183      * @param xid transaction xid
184      * @param onePhase true if one phase commit
185      * @throws XAException XA protocol error
186      */

187     public void commit(final Xid JavaDoc xid, final boolean onePhase) throws XAException JavaDoc {
188         logger.debug("XA-COMMIT for {0}", xid);
189
190         // Commit the transaction
191
try {
192             physicalConnection.commit();
193         } catch (SQLException JavaDoc e) {
194             logger.error("Cannot commit transaction", e);
195             notifyError(e);
196             throw new XAException JavaDoc("Error on commit");
197         }
198     }
199
200     /**
201      * Ends the work performed on behalf of a transaction branch.
202      * @param xid transaction xid
203      * @param flags currently unused
204      * @throws XAException XA protocol error
205      */

206     public void end(final Xid JavaDoc xid, final int flags) throws XAException JavaDoc {
207         logger.debug("XA-END for {0}", xid);
208     }
209
210     /**
211      * Tell the resource manager to forget about a heuristically completed
212      * transaction branch.
213      * @param xid transaction xid
214      * @throws XAException XA protocol error
215      */

216     public void forget(final Xid JavaDoc xid) throws XAException JavaDoc {
217         logger.debug("XA-FORGET for {0}", xid);
218     }
219
220     /**
221      * Obtain the current transaction timeout value set for this XAResource
222      * instance.
223      * @return the current transaction timeout in seconds
224      * @throws XAException XA protocol error
225      */

226     public int getTransactionTimeout() throws XAException JavaDoc {
227         logger.debug("getTransactionTimeout for {0}", this);
228         return timeout;
229     }
230
231     /**
232      * Determine if the resource manager instance represented by the target
233      * object is the same as the resource manager instance represented by the
234      * parameter xares.
235      * @param xares An XAResource object
236      * @return True if same RM instance, otherwise false.
237      * @throws XAException XA protocol error
238      */

239     public boolean isSameRM(final XAResource JavaDoc xares) throws XAException JavaDoc {
240
241         // In this pseudo-driver, we must return true only if
242
// both objects refer to the same XAResource, and not
243
// the same Resource Manager, because actually, we must
244
// send commit/rollback on each XAResource involved in
245
// the transaction.
246
if (xares.equals(this)) {
247             logger.debug("isSameRM = true {0}", this);
248             return true;
249         }
250         logger.debug("isSameRM = false {0}", this);
251         return false;
252     }
253
254     /**
255      * Ask the resource manager to prepare for a transaction commit of the
256      * transaction specified in xid.
257      * @param xid transaction xid
258      * @throws XAException XA protocol error
259      * @return always OK
260      */

261     public int prepare(final Xid JavaDoc xid) throws XAException JavaDoc {
262         logger.debug("XA-PREPARE for {0}", xid);
263         // No 2PC on standard JDBC drivers
264
return XA_OK;
265     }
266
267     /**
268      * Obtain a list of prepared transaction branches from a resource manager.
269      * @param flag unused parameter.
270      * @return an array of transaction Xids
271      * @throws XAException XA protocol error
272      */

273     public Xid JavaDoc[] recover(final int flag) throws XAException JavaDoc {
274         logger.debug("XA-RECOVER for {0}", this);
275         // Not implemented
276
return null;
277     }
278
279     /**
280      * Inform the resource manager to roll back work done on behalf of a
281      * transaction branch.
282      * @param xid transaction xid
283      * @throws XAException XA protocol error
284      */

285     public void rollback(final Xid JavaDoc xid) throws XAException JavaDoc {
286         logger.debug("XA-ROLLBACK for {0}", xid);
287
288         // Make sure that we are not in AutoCommit mode
289
try {
290             if (physicalConnection.getAutoCommit()) {
291                 logger.error("Rollback called on XAResource with AutoCommit set");
292                 throw (new XAException JavaDoc(XAException.XA_HEURCOM));
293             }
294         } catch (SQLException JavaDoc e) {
295             logger.error("Cannot getAutoCommit", e);
296             notifyError(e);
297             throw (new XAException JavaDoc("Error on getAutoCommit"));
298         }
299
300         // Rollback the transaction
301
try {
302             physicalConnection.rollback();
303         } catch (SQLException JavaDoc e) {
304             logger.error("Cannot rollback transaction", e);
305             notifyError(e);
306             throw (new XAException JavaDoc("Error on rollback"));
307         }
308     }
309
310     /**
311      * Set the current transaction timeout value for this XAResource instance.
312      * @param seconds timeout value, in seconds.
313      * @return always true
314      * @throws XAException XA protocol error
315      */

316     @SuppressWarnings JavaDoc("boxing")
317     public boolean setTransactionTimeout(final int seconds) throws XAException JavaDoc {
318         logger.debug("setTransactionTimeout to {0} for {1}", seconds, this);
319         timeout = seconds;
320         return true;
321     }
322
323     /**
324      * Start work on behalf of a transaction branch specified in xid.
325      * @param xid transaction xid
326      * @param flags unused parameter
327      * @throws XAException XA protocol error
328      */

329     public void start(final Xid JavaDoc xid, final int flags) throws XAException JavaDoc {
330         logger.debug("XA-START for {0}", xid);
331     }
332
333     /**
334      * Return an XA resource to the caller.
335      * @return The XAResource
336      * @exception SQLException - if a database-access error occurs
337      */

338     public XAResource JavaDoc getXAResource() throws SQLException JavaDoc {
339         return this;
340     }
341
342     /**
343      * Compares this object with another specified object.
344      * @param o the object to compare
345      * @return a value detecting if these objects are matching or not.
346      */

347     public int compareTo(final Object JavaDoc o) {
348         JManagedConnection other = (JManagedConnection) o;
349         int diff = getReUsedPreparedStatements() - other.getReUsedPreparedStatements();
350         if (diff == 0) {
351             return getIdentifier() - other.getIdentifier();
352         }
353         return diff;
354     }
355
356     /**
357      * @return value of reused prepared statement.
358      */

359     public int getReUsedPreparedStatements() {
360         return reUsedPreparedStatements;
361     }
362
363     /**
364      * Create an object handle for a database connection.
365      * @exception SQLException - if a database-access error occurs
366      * @return connection used by this managed connection
367      */

368     public Connection JavaDoc getConnection() throws SQLException JavaDoc {
369         // Just return the already created object.
370
return implConn;
371     }
372
373     /**
374      * Close the database connection.
375      * @exception SQLException - if a database-access error occurs
376      */

377     public void close() throws SQLException JavaDoc {
378
379         // Close the actual Connection here.
380
if (physicalConnection != null) {
381             physicalConnection.close();
382         } else {
383             logger.error("Connection already closed. Stack of this new close()", new Exception JavaDoc());
384         }
385         physicalConnection = null;
386         implConn = null;
387     }
388
389     /**
390      * Add an event listener.
391      * @param listener event listener
392      */

393     public void addConnectionEventListener(final ConnectionEventListener JavaDoc listener) {
394         eventListeners.addElement(listener);
395     }
396
397     /**
398      * Remove an event listener.
399      * @param listener event listener
400      */

401     public void removeConnectionEventListener(final ConnectionEventListener JavaDoc listener) {
402         eventListeners.removeElement(listener);
403     }
404
405
406
407     /**
408      * synchronization implementation. {@inheritDoc}
409      */

410     public void beforeCompletion() {
411         // nothing to do
412
}
413
414     /**
415      * synchronization implementation. {@inheritDoc}
416      */

417     public void afterCompletion(final int status) {
418         if (tx != null) {
419             ds.freeConnections(tx);
420         } else {
421             logger.error("NO TX!");
422         }
423     }
424
425     /**
426      * @return true if connection max age has expired
427      */

428     public boolean isAged() {
429         return (deathTime < System.currentTimeMillis());
430     }
431
432     /**
433      * @return true if connection is still open
434      */

435     public boolean isOpen() {
436         return (open > 0);
437     }
438
439     /**
440      * @return open count
441      */

442     public int getOpenCount() {
443         return open;
444     }
445
446     /**
447      * Check if the connection has been unused for too long time. This occurs
448      * usually when the caller forgot to call close().
449      * @return true if open time has been reached, and not involved in a tx.
450      */

451     public boolean inactive() {
452         return (open > 0 && tx == null && closeTime < System.currentTimeMillis());
453     }
454
455     /**
456      * @return true if connection is closed
457      */

458     public boolean isClosed() {
459         return (open <= 0);
460     }
461
462     /**
463      * Notify as opened.
464      */

465     public void hold() {
466         open++;
467         closeTime = System.currentTimeMillis() + ds.getMaxOpenTimeMilli();
468     }
469
470     /**
471      * notify as closed.
472      * @return true if normal close.
473      */

474     public boolean release() {
475         open--;
476         if (open < 0) {
477             logger.warn("connection was already closed");
478             open = 0;
479             return false;
480         }
481         if (tx == null && open > 0) {
482             logger.error("connection-open counter overflow");
483             open = 0;
484         }
485         return true;
486     }
487
488     /**
489      * Set the associated transaction.
490      * @param tx Transaction
491      */

492     public void setTx(final Transaction JavaDoc tx) {
493         this.tx = tx;
494     }
495
496     /**
497      * @return the Transaction
498      */

499     public Transaction JavaDoc getTx() {
500         return tx;
501     }
502
503     /**
504      * remove this item, ignoring exception on close.
505      */

506     public void remove() {
507         // Close the physical connection
508
try {
509             close();
510         } catch (java.sql.SQLException JavaDoc ign) {
511             logger.error("Could not close Connection: ", ign);
512         }
513
514         // remove all references (for GC)
515
tx = null;
516
517     }
518
519     // -----------------------------------------------------------------
520
// Other methods
521
// -----------------------------------------------------------------
522

523     /**
524      * Try to find a PreparedStatement in the pool for the given options.
525      * @param sql the sql of the prepared statement
526      * @param resultSetType the type of resultset
527      * @param resultSetConcurrency the concurrency of this resultset
528      * @return a preparestatement object
529      * @throws SQLException if an errors occurs on the database.
530      */

531     public PreparedStatement JavaDoc prepareStatement(final String JavaDoc sql, final int resultSetType, final int resultSetConcurrency)
532             throws SQLException JavaDoc {
533
534         logger.debug("sql = {0}", sql);
535         if (pstmtmax == 0) {
536             return physicalConnection.prepareStatement(sql, resultSetType, resultSetConcurrency);
537         }
538         JStatement ps = null;
539         synchronized (psList) {
540             ps = psList.get(sql);
541             if (ps != null) {
542                 if (!ps.isClosed()) {
543                     logger.warn("reuse an open pstmt");
544                 }
545                 ps.reuse();
546                 reUsedPreparedStatements++;
547             } else {
548                 // Not found in cache. Create a new one.
549
PreparedStatement JavaDoc aps = physicalConnection.prepareStatement(sql, resultSetType, resultSetConcurrency);
550                 ps = new JStatement(aps, this, sql);
551                 psList.put(sql, ps);
552             }
553             psOpenNb++;
554         }
555         return ps;
556     }
557
558     /**
559      * Try to find a PreparedStatement in the pool.
560      * @param sql the given sql query.
561      * @throws SQLException if an error in the database occurs.
562      * @return a given prepared statement.
563      */

564     public PreparedStatement JavaDoc prepareStatement(final String JavaDoc sql) throws SQLException JavaDoc {
565         return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
566     }
567
568     /**
569      * A PreparedStatement has been logically closed.
570      * @param ps a prepared statement.
571      */

572     public void notifyPsClose(final JStatement ps) {
573         logger.debug(ps.getSql());
574         synchronized (psList) {
575             psOpenNb--;
576             if (psList.size() >= pstmtmax) {
577                 // Choose a closed element to remove.
578
JStatement lru = null;
579                 Iterator JavaDoc i = psList.values().iterator();
580                 while (i.hasNext()) {
581                     lru = (JStatement) i.next();
582                     if (lru.isClosed()) {
583                         // actually, remove the first closed element.
584
i.remove();
585                         lru.forget();
586                         break;
587                     }
588                 }
589             }
590         }
591     }
592
593     /**
594      * Notify a Close event on Connection.
595      */

596     @SuppressWarnings JavaDoc("boxing")
597     public void notifyClose() {
598
599         // Close all PreparedStatement not already closed
600
// When a Connection has been closed, no PreparedStatement should
601
// remain open. This can avoids lack of cursor on some databases.
602
synchronized (psList) {
603             if (psOpenNb > 0) {
604                 JStatement jst = null;
605                 Iterator JavaDoc i = psList.values().iterator();
606                 while (i.hasNext()) {
607                     jst = (JStatement) i.next();
608                     if (jst.forceClose()) {
609                         psOpenNb--;
610                     }
611                 }
612                 if (psOpenNb != 0) {
613                     logger.warn("Bad psOpenNb value = {0}", psOpenNb);
614                     psOpenNb = 0;
615                 }
616             }
617         }
618
619         // Notify event to listeners
620
for (int i = 0; i < eventListeners.size(); i++) {
621             ConnectionEventListener JavaDoc l = eventListeners.elementAt(i);
622             l.connectionClosed(new ConnectionEvent JavaDoc(this));
623         }
624     }
625
626     /**
627      * Notify an Error event on Connection.
628      * @param ex the given exception
629      */

630     public void notifyError(final SQLException JavaDoc ex) {
631         // Notify event to listeners
632
for (int i = 0; i < eventListeners.size(); i++) {
633             ConnectionEventListener JavaDoc l = eventListeners.elementAt(i);
634             l.connectionErrorOccurred(new ConnectionEvent JavaDoc(this, ex));
635         }
636     }
637
638 }
639
Popular Tags