KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > minerva > pool > jdbc > xa > wrapper > XAConnectionImpl


1 /*
2  * Licensed under the X license (see http://www.x.org/terms.htm)
3  */

4 package org.ofbiz.minerva.pool.jdbc.xa.wrapper;
5
6 import javax.sql.ConnectionEvent JavaDoc;
7 import javax.sql.ConnectionEventListener JavaDoc;
8 import javax.sql.XAConnection JavaDoc;
9 import javax.transaction.xa.XAResource JavaDoc;
10 import java.sql.Connection JavaDoc;
11 import java.sql.SQLException JavaDoc;
12 import java.util.ArrayList JavaDoc;
13 import java.util.Vector JavaDoc;
14
15 import org.ofbiz.minerva.pool.PoolEvent;
16 import org.ofbiz.minerva.pool.PoolEventListener;
17 import org.ofbiz.minerva.pool.PooledObject;
18 import org.ofbiz.minerva.pool.cache.ObjectCache;
19 import org.ofbiz.minerva.pool.jdbc.ConnectionInPool;
20
21 /**
22  * A transaction wrapper around a java.sql.Connection. This provides access to
23  * an XAResource (there is a one-to-one mapping between XAResource and
24  * XAConnection) and a java.sql.Connection (in this implementation, there is
25  * also a one-to-one mapping between XAConnection and java.sql.Connection).
26  * In order to pool java.sql.Connections in a transactional environment, this
27  * is the class that should be pooled - though you could pool the connections,
28  * there is no need to create and destroy these wrappers so frequently.
29  *
30  * <P>Note that there con only be one transaction at a time accessing one of
31  * these wrappers, and requests to a pool for multiple connections on behalf of
32  * one transaction should use the same wrapper. This is because there is no
33  * distinction between connections and transactions in a java.sql.Connection,
34  * and work done by one connection on behalf of a transaction would not be
35  * visible to another connection working on behalf of the same transaction - you
36  * would have effectively created two transactions.</P>
37  *
38  * <P>This also implies that an XAConnection should not be released to a
39  * connection pool until the work has been committed or rolled back. However,
40  * it must sent the close notification as usual in order to be delisted from
41  * the transaction. So the ConnectionEventListener must not release the
42  * XAConnection to a pool when it receives the close event. Instead, it should
43  * also register a TransactionListener that will be notified when the
44  * Transaction is finished, and release the XAConnection at that time.</P>
45  * @see org.ofbiz.minerva.pool.jdbc.xa.wrapper.TransactionListener
46  *
47  * @author Aaron Mulder (ammulder@alumni.princeton.edu)
48  * @author <a HREF="mailto:bill@burkecentral.com">Bill Burke</a>
49  *
50  * REVISIONS:
51  * 20010703 bill added code for transaction isolation
52  */

53 public class XAConnectionImpl implements XAConnection JavaDoc, PooledObject {
54
55     private final static String JavaDoc CLOSED = "Connection has been closed!";
56     private Connection JavaDoc con;
57     private XAResourceImpl resource;
58     private Vector JavaDoc listeners, poolListeners;
59     private ArrayList JavaDoc clientConnections;
60     private TransactionListener transListener;
61     private int preparedStatementCacheSize = 0;
62     private int clientConnectionCount = 0;
63     /** The JDBC user name used to open an underlying connection */
64     private String JavaDoc user;
65     /** The JDBC password used to open an underlying connection */
66     private String JavaDoc password;
67     private boolean saveStackTrace;
68
69     /**
70      * Creates a new transactional wrapper.
71      * @param con The underlying non-transactional Connection.
72      * @param resource The transaction resource used to enlist this
73      * connection in a transaction.
74      */

75     public XAConnectionImpl(Connection JavaDoc con, XAResourceImpl resource, boolean saveStackTrace) {
76         this.con = con;
77         this.resource = resource;
78         listeners = new Vector JavaDoc();
79         poolListeners = new Vector JavaDoc();
80         clientConnections = new ArrayList JavaDoc();
81         this.saveStackTrace = saveStackTrace;
82     }
83
84     /**
85      * Sets the transaction listener.
86      */

87     public void setTransactionListener(TransactionListener tl) {
88         transListener = tl;
89     }
90
91     /**
92      * Clears the transaction listener.
93      */

94     public void clearTransactionListener() {
95         transListener = null;
96     }
97
98     /**
99      * Sets the number of PreparedStatements to be cached for each
100      * Connection. Your DB product may impose a limit on the number
101      * of open PreparedStatements.
102      */

103     public void setPSCacheSize(int maxSize) {
104         preparedStatementCacheSize = maxSize;
105     }
106
107     /**
108      * Gets the number of PreparedStatements to be cached for each
109      * Connection.
110      */

111     public int getPSCacheSize() {
112         return preparedStatementCacheSize;
113     }
114
115
116     public void setTransactionIsolation(int iso) throws SQLException JavaDoc {
117         con.setTransactionIsolation(iso);
118     }
119
120     /**
121      * Shuts down this wrapper (and the underlying Connection) permanently.
122      */

123     public void close() {
124         try {
125             con.close();
126         } catch (SQLException JavaDoc e) {
127         }
128         ObjectCache cache = (ObjectCache) ConnectionInPool.psCaches.remove(con);
129         if (cache != null)
130             cache.close();
131         con = null;
132         resource = null;
133         listeners.clear();
134         listeners = null;
135     }
136
137     /**
138      * Indicates that the connection given to the client has been closed.
139      * If there is currently a transaction, this object should not be closed or
140      * returned to a pool. If not, it can be closed or returned immediately.
141      */

142     public void clientConnectionClosed(XAClientConnection clientCon) {
143         synchronized(clientConnections) {
144             clientConnections.remove(clientCon);
145         }
146         if (clientConnections.size() > 0)
147             return; // Only take action if the last connection referring to this is closed
148

149         boolean trans = resource.isTransaction(); // could be committed directly on notification? Seems unlikely, but let's not rule it out.
150
Vector JavaDoc local = (Vector JavaDoc) listeners.clone();
151         for (int i = local.size() - 1; i >= 0; i--)
152             ((ConnectionEventListener JavaDoc) local.elementAt(i)).connectionClosed(new ConnectionEvent JavaDoc(this));
153 // if(!trans)
154
// transactionFinished();
155
}
156
157     /**
158      * Indicates that the outstanding transaction has finished and this object
159      * can be closed or returned to a pool. This dispatches a close event to
160      * all listeners.
161      * @see #addConnectionEventListener
162      */

163     public void transactionFinished() {
164         if (transListener != null)
165             transListener.transactionFinished(this);
166     }
167
168     /**
169      * Indicates that the outstanding transaction has finished with a fatal
170      * error, and this object should be closed or permanently removed from a
171      * pool. This dispatches a close event to all listeners.
172      * @see #addConnectionEventListener
173      */

174     public void transactionFailed() {
175         if (transListener != null)
176             transListener.transactionFailed(this);
177     }
178
179     /**
180      * Indicates that the connection given to the client has had an error.
181      * If there is currently a transaction, this object should not be closed or
182      * returned to a pool. If not, it can be closed or returned immediately.
183      */

184     public void setConnectionError(SQLException JavaDoc e) {
185         Vector JavaDoc local = (Vector JavaDoc) listeners.clone();
186         for (int i = local.size() - 1; i >= 0; i--) {
187             try {
188                 ((ConnectionEventListener JavaDoc) local.elementAt(i)).connectionErrorOccurred(new ConnectionEvent JavaDoc(this, e));
189             } catch (RuntimeException JavaDoc ex) {
190                 // there can be thrown an induced exception,
191
// but we must report to client the original one, right?
192
ex.printStackTrace();
193             }
194         }
195     }
196
197     /**
198      * Rolls back the underlying connection. This is used when there is no
199      * current transaction and the connection is returned to the pool - since
200      * no transaction will be committed or rolled back but this connection
201      * will be reused, we must roll it back. This is only done if autocommit is
202      * false.
203      */

204     public void rollback() throws SQLException JavaDoc {
205         if (con.getAutoCommit() == false)
206             con.rollback();
207     }
208
209     // ---- Implementation of javax.sql.XAConnection ----
210

211     public XAResource JavaDoc getXAResource() {
212         return resource;
213     }
214
215     public void addConnectionEventListener(ConnectionEventListener JavaDoc listener) {
216         listeners.addElement(listener);
217     }
218
219     public void removeConnectionEventListener(ConnectionEventListener JavaDoc listener) {
220         if (!listeners.remove(listener))
221             throw new IllegalArgumentException JavaDoc();
222     }
223
224     public Connection JavaDoc getConnection() {
225         XAClientConnection xaCon;
226         synchronized (clientConnections) {
227             xaCon = new XAClientConnection(this, con, saveStackTrace);
228             xaCon.setPSCacheSize(preparedStatementCacheSize);
229             clientConnections.add(xaCon);
230         }
231         return xaCon;
232     }
233
234     // ---- Implementation of javax.sql.XAConnection ----
235

236     public void addPoolEventListener(PoolEventListener listener) {
237         poolListeners.addElement(listener);
238     }
239
240     public void removePoolEventListener(PoolEventListener listener) {
241         poolListeners.removeElement(listener);
242     }
243
244     /**
245      * Dispatches an event to the pool event listeners.
246      */

247     void firePoolEvent(PoolEvent evt) {
248         Vector JavaDoc local = (Vector JavaDoc) poolListeners.clone();
249         for (int i = local.size() - 1; i >= 0; i--)
250             if (evt.getType() == PoolEvent.OBJECT_CLOSED)
251                 ((PoolEventListener) local.elementAt(i)).objectClosed(evt);
252             else if (evt.getType() == PoolEvent.OBJECT_ERROR)
253                 ((PoolEventListener) local.elementAt(i)).objectError(evt);
254             else
255                 ((PoolEventListener) local.elementAt(i)).objectUsed(evt);
256     }
257
258     /** Getter for property password.
259      * @return Value of property password.
260      */

261     public java.lang.String JavaDoc getPassword() {
262         return password;
263     }
264
265     /** Setter for property password.
266      * @param password New value of property password.
267      */

268     public void setPassword(java.lang.String JavaDoc password) {
269         this.password = password;
270     }
271
272     /** Getter for property user.
273      * @return Value of property user.
274      */

275     public java.lang.String JavaDoc getUser() {
276         return user;
277     }
278
279     /** Setter for property user.
280      * @param user New value of property user.
281      */

282     public void setUser(java.lang.String JavaDoc user) {
283         this.user = user;
284     }
285
286     public void forceClientConnectionsClose() {
287         for (int i = 0; i < clientConnections.size(); i++) {
288             XAClientConnection client = (XAClientConnection) clientConnections.get(i);
289             try {
290                 client.forcedClose();
291             } catch (SQLException JavaDoc ignored) {
292             }
293         }
294         clientConnections.clear();
295     }
296 }
297
Popular Tags