KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > minerva > pool > jdbc > xa > XAConnectionFactory


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

4 package org.ofbiz.minerva.pool.jdbc.xa;
5
6 import javax.sql.ConnectionEvent JavaDoc;
7 import javax.sql.ConnectionEventListener JavaDoc;
8 import javax.sql.XAConnection JavaDoc;
9 import javax.sql.XADataSource JavaDoc;
10 import javax.transaction.Status JavaDoc;
11 import javax.transaction.Transaction JavaDoc;
12 import javax.transaction.TransactionManager JavaDoc;
13 import javax.transaction.xa.XAResource JavaDoc;
14 import java.sql.Connection JavaDoc;
15 import java.sql.SQLException JavaDoc;
16 import java.util.Collections JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.Map JavaDoc;
19
20 import org.apache.log4j.Logger;
21 import org.ofbiz.minerva.pool.ObjectPool;
22 import org.ofbiz.minerva.pool.PoolObjectFactory;
23 import org.ofbiz.minerva.pool.jdbc.xa.wrapper.TransactionListener;
24 import org.ofbiz.minerva.pool.jdbc.xa.wrapper.XAConnectionImpl;
25 import org.ofbiz.minerva.pool.jdbc.xa.wrapper.XADataSourceImpl;
26 import org.ofbiz.minerva.pool.jdbc.xa.wrapper.XAResourceImpl;
27
28 /**
29  * Object factory for JDBC 2.0 standard extension XAConnections. You pool the
30  * XAConnections instead of the java.sql.Connections since with vendor
31  * conformant drivers, you don't have direct access to the java.sql.Connection,
32  * and any work done isn't associated with the java.sql.Connection anyway.
33  * <P><B>Note:</B> This implementation requires that the TransactionManager
34  * be bound to a JNDI name.</P>
35  * <P><B>Note:</B> This implementation has special handling for Minerva JDBC
36  * 1/2 XA Wrappers. Namely, when a request comes in, if it is for a wrapper
37  * connection and it has the same current transaction as a previous active
38  * connection, the same previous connection will be returned. Otherwise,
39  * you won't be able to share changes across connections like you can with
40  * the native JDBC 2 Standard Extension implementations.</P>
41  *
42  * @author Aaron Mulder (ammulder@alumni.princeton.edu)
43  * @author <a HREF="mailto:bill@burkecentral.com">Bill Burke</a>
44  *
45  * REVISIONS:
46  * 20010703 bill added code for transaction isolation
47  @version $Rev: 5462 $
48  */

49 public class XAConnectionFactory extends PoolObjectFactory {
50
51     public static final int DEFAULT_ISOLATION = -1;
52
53     private XADataSource JavaDoc source;
54     private String JavaDoc userName;
55     private String JavaDoc password;
56     private int psCacheSize = 10;
57     private boolean releaseOnCommit = false;
58     private boolean saveStackTrace = false;
59     private int transactionIsolation = DEFAULT_ISOLATION;
60     private ConnectionEventListener JavaDoc listener, errorListener;
61     private TransactionListener transListener;
62     private ObjectPool pool;
63     private final Map JavaDoc wrapperTx = Collections.synchronizedMap(new HashMap JavaDoc());
64     private final Map JavaDoc rms = Collections.synchronizedMap(new HashMap JavaDoc());
65     private TransactionManager JavaDoc tm;
66
67     private static Logger log = Logger.getLogger(XAConnectionFactory.class);
68
69     /**
70      * Creates a new factory. You must set the XADataSource and
71      * TransactionManager JNDI name before the factory can be used.
72      */

73     public XAConnectionFactory() {
74         final boolean trace = log.isDebugEnabled();
75         //wrapperTx = new HashMap();
76
//rms = new HashMap();
77
errorListener = new ConnectionEventListener JavaDoc() {
78             public void connectionErrorOccurred(ConnectionEvent JavaDoc evt) {
79                 if (pool.isInvalidateOnError()) {
80                     pool.markObjectAsInvalid(evt.getSource());
81                 }
82             }
83
84             public void connectionClosed(ConnectionEvent JavaDoc evt) {
85             }
86         };
87
88         listener = new ConnectionEventListener JavaDoc() {
89
90             public void connectionErrorOccurred(ConnectionEvent JavaDoc evt) {
91                 if (pool.isInvalidateOnError()) {
92                     pool.markObjectAsInvalid(evt.getSource());
93                 }
94 // closeConnection(evt, XAResource.TMFAIL);
95
}
96
97             public void connectionClosed(ConnectionEvent JavaDoc evt) {
98                 closeConnection(evt, XAResource.TMSUCCESS);
99             }
100
101             private void closeConnection(ConnectionEvent JavaDoc evt, int status) {
102                 XAConnection JavaDoc con = (XAConnection JavaDoc) evt.getSource();
103                 try {
104                     con.removeConnectionEventListener(listener);
105                 } catch (IllegalArgumentException JavaDoc e) {
106                     return; // Removed twice somehow?
107
}
108                 Transaction JavaDoc trans = null;
109                 try {
110                     if (tm.getStatus() != Status.STATUS_NO_TRANSACTION) {
111                         trans = tm.getTransaction();
112                         XAResource JavaDoc res = (XAResource JavaDoc) rms.remove(con);
113                         if (res != null) {
114                             trans.delistResource(res, status);
115                             if (trace)
116                                 log.debug("delisted resource from TM - " + res);
117                         } // end of if ()
118
else {
119                             log.warn("no xares in rms for con " + con);
120                         } // end of else
121
}
122                 } catch (Exception JavaDoc e) {
123                     log.error("Unable to deregister with TransactionManager", e);
124                     throw new RuntimeException JavaDoc("Unable to deregister with TransactionManager: " + e);
125                 }
126
127                 if (!(con instanceof XAConnectionImpl)) {
128                     // Real XAConnection -> not associated w/ transaction
129
pool.releaseObject(con);
130                 } else {
131                     XAConnectionImpl xaCon = (XAConnectionImpl) con;
132                     if (!((XAResourceImpl) xaCon.getXAResource()).isTransaction()) {
133                         // Wrapper - we can only release it if there's no current transaction
134
// Can't just check TM because con may have been committed but left open
135
// so if there's a current transaction it may not apply to the con.
136
if (trace)
137                             log.debug("XAConnectionImpl: " + xaCon + " has no current tx!");
138                         try {
139                             xaCon.rollback();
140                         } catch (SQLException JavaDoc e) {
141                             pool.markObjectAsInvalid(con);
142                         }
143                         pool.releaseObject(con);
144                     } else {
145                         // Still track errors, but don't try to close again.
146
con.addConnectionEventListener(errorListener);
147                     }
148                 }
149             }
150         };
151         transListener = new TransactionListener() {
152             public void transactionFinished(XAConnectionImpl con) {
153                 con.clearTransactionListener();
154                 Object JavaDoc tx = wrapperTx.remove(con);
155                 //System.out.println("removing con: " + con + "from wrapperTx, tx: " + tx);
156
if (tx != null)
157                     wrapperTx.remove(tx);
158                 try {
159                     con.removeConnectionEventListener(errorListener);
160                 } catch (IllegalArgumentException JavaDoc e) {
161                     // connection was not closed, but transaction ended
162
if (!releaseOnCommit) {
163                         return;
164                     } else {
165                         rms.remove(con);
166                         pool.markObjectAsInvalid(con);
167                         con.forceClientConnectionsClose();
168                     }
169                 }
170
171                 pool.releaseObject(con);
172             }
173
174             public void transactionFailed(XAConnectionImpl con) {
175                 con.clearTransactionListener();
176                 Object JavaDoc tx = wrapperTx.remove(con);
177                 if (tx != null)
178                     wrapperTx.remove(tx);
179                 //System.out.println("removing con: " + con + "from wrapperTx, tx: " + tx);
180
pool.markObjectAsInvalid(con);
181                 try {
182                     con.removeConnectionEventListener(errorListener);
183                 } catch (IllegalArgumentException JavaDoc e) {
184                     if (!releaseOnCommit) {
185                         return;
186                     } else {
187                         rms.remove(con);
188                         con.forceClientConnectionsClose();
189                     }
190                 }
191                 pool.releaseObject(con);
192             }
193         };
194     }
195
196     /**
197      * Sets the user name used to generate XAConnections. This is optional,
198      * and will only be used if present.
199      */

200     public void setUser(String JavaDoc userName) {
201         this.userName = userName;
202     }
203
204     /**
205      * Gets the user name used to generate XAConnections.
206      */

207     public String JavaDoc getUser() {
208         return userName;
209     }
210
211     /**
212      * Sets the password used to generate XAConnections. This is optional,
213      * and will only be used if present.
214      */

215     public void setPassword(String JavaDoc password) {
216         this.password = password;
217     }
218
219     /**
220      * Gets the password used to generate XAConnections.
221      */

222     public String JavaDoc getPassword() {
223         return password;
224     }
225
226     public boolean getReleaseOnCommit() {
227         return releaseOnCommit;
228     }
229
230     public void setReleaseOnCommit(boolean rel) {
231         releaseOnCommit = rel;
232     }
233
234     /**
235      * Sets the number of PreparedStatements to be cached for each
236      * Connection. Your DB product may impose a limit on the number
237      * of open PreparedStatements. The default value is 10.
238      */

239     public void setPSCacheSize(int size) {
240         psCacheSize = size;
241     }
242
243     /**
244      * Gets the number of PreparedStatements to be cached for each
245      * Connection. The default value is 10.
246      */

247     public int getPSCacheSize() {
248         return psCacheSize;
249     }
250
251
252     /**
253      * Gets the transaction isolation level of connections. This defaults to
254      * whatever the connection's default iso level is.
255      */

256     public int getTransactionIsolation() {
257         return transactionIsolation;
258     }
259
260     public void setTransactionIsolation(int iso) {
261         this.transactionIsolation = iso;
262     }
263
264     public void setTransactionIsolation(String JavaDoc iso) {
265         if (iso.equals("TRANSACTION_NONE")) {
266             this.transactionIsolation = Connection.TRANSACTION_NONE;
267         } else if (iso.equals("TRANSACTION_READ_COMMITTED")) {
268             this.transactionIsolation = Connection.TRANSACTION_READ_COMMITTED;
269         } else if (iso.equals("TRANSACTION_READ_UNCOMMITTED")) {
270             this.transactionIsolation = Connection.TRANSACTION_READ_UNCOMMITTED;
271         } else if (iso.equals("TRANSACTION_REPEATABLE_READ")) {
272             this.transactionIsolation = Connection.TRANSACTION_REPEATABLE_READ;
273         } else if (iso.equals("TRANSACTION_SERIALIZABLE")) {
274             this.transactionIsolation = Connection.TRANSACTION_SERIALIZABLE;
275         } else {
276             throw new IllegalArgumentException JavaDoc("Setting Isolation level to unknown state: " + iso);
277         }
278     }
279
280     /**
281      * Sets the XADataSource used to generate XAConnections. This may be
282      * supplied by the vendor, or it may use the wrappers for non-compliant
283      * drivers (see XADataSourceImpl).
284      * @see org.ofbiz.minerva.pool.jdbc.xa.wrapper.XADataSourceImpl
285      */

286     public void setDataSource(XADataSource JavaDoc dataSource) {
287         source = dataSource;
288     }
289
290     /**
291      * Gets the XADataSource used to generate XAConnections.
292      */

293     public XADataSource JavaDoc getDataSource() {
294         return source;
295     }
296
297     /**
298      * Sets the TransactionManager.
299      */

300     public void setTransactionManager(TransactionManager JavaDoc tm) {
301         this.tm = tm;
302     }
303
304     /**
305      * Gets the TransactionManager.
306      */

307     public TransactionManager JavaDoc getTransactionManager() {
308         return this.tm;
309     }
310
311
312     /**
313      * Have XAClientConnections save a stack trace on creation
314      * This is useful for debugging non-closed connections.
315      * It must be used with ReleaseOnCommit option
316      */

317     public boolean getSaveStackTrace() {
318         return saveStackTrace;
319     }
320
321     public void setSaveStackTrace(boolean save) {
322         saveStackTrace = save;
323     }
324
325     /**
326      * Verifies that the data source and transaction manager are accessible.
327      */

328     public void poolStarted(ObjectPool pool) {
329         if (log.isDebugEnabled())
330             log.debug("Starting");
331
332         super.poolStarted(pool);
333         this.pool = pool;
334         if (source == null)
335             throw new IllegalStateException JavaDoc("Must specify XADataSource to " + getClass().getName());
336         if (source instanceof XADataSourceImpl) {
337             ((XADataSourceImpl) source).setSaveStackTrace(saveStackTrace);
338         }
339
340         /*
341         if(tmJndiName == null)
342             throw new IllegalStateException("Must specify TransactionManager JNDI Name to "+getClass().getName());
343         if(ctx == null)
344             throw new IllegalStateException("Must specify InitialContext to "+getClass().getName());
345         try {
346             tm = (TransactionManager)ctx.lookup(tmJndiName);
347         } catch(NamingException e) {
348             throw new IllegalStateException("Cannot lookup TransactionManager using specified context and name!");
349         }
350         */

351     }
352
353     /**
354      * Creates a new XAConnection from the provided XADataSource.
355      */

356     public Object JavaDoc createObject(Object JavaDoc parameters) throws Exception JavaDoc {
357
358         log.debug("Opening new XAConnection");
359
360         Object JavaDoc obj = null;
361         try {
362             if (parameters != null) {
363                 String JavaDoc credentials[] = (String JavaDoc[]) parameters;
364                 if (credentials.length == 2)
365                     obj = source.getXAConnection(credentials[0], credentials[1]);
366             } else if (userName != null && userName.length() > 0)
367                 obj = source.getXAConnection(userName, password);
368             else
369                 obj = source.getXAConnection();
370         } catch (SQLException JavaDoc e) {
371             log.error("Can't get an XAConnection", e);
372             throw e;
373         }
374         return obj;
375     }
376
377     /**
378      * Registers the XAConnection's XAResource with the current transaction (if
379      * there is one). Sets listeners that will handle deregistering and
380      * returning the XAConnection to the pool via callbacks.
381      */

382     public Object JavaDoc prepareObject(Object JavaDoc pooledObject) {
383         boolean trace = log.isDebugEnabled();
384         XAConnection JavaDoc con = (XAConnection JavaDoc) pooledObject;
385         con.addConnectionEventListener(listener);
386         Transaction JavaDoc trans = null;
387         try {
388             if (tm.getStatus() != Status.STATUS_NO_TRANSACTION) {
389                 trans = tm.getTransaction();
390                 XAResource JavaDoc res = con.getXAResource();
391                 rms.put(con, res);
392                 trans.enlistResource(res);
393                 if (trace)
394                     log.debug("Resource '" + res + "' enlisted for '" + con + "'.");
395             } else {
396                 if (trace)
397                     log.debug("No transaction right now.");
398             }
399         } catch (Exception JavaDoc e) {
400             //System.out.println("error in prepareObject!!!!!");
401
e.printStackTrace();
402             log.error("Unable to register with TransactionManager", e);
403             con.removeConnectionEventListener(listener);
404             throw new RuntimeException JavaDoc("Unable to register with TransactionManager: " + e);
405         }
406
407         if (con instanceof XAConnectionImpl) {
408             ((XAConnectionImpl) con).setTransactionListener(transListener);
409             ((XAConnectionImpl) con).setPSCacheSize(psCacheSize);
410             if (transactionIsolation != DEFAULT_ISOLATION) {
411                 try {
412                     ((XAConnectionImpl) con).setTransactionIsolation(transactionIsolation);
413                 } catch (SQLException JavaDoc sex) {
414                     throw new RuntimeException JavaDoc("Unable to setTransactionIsolation: " + sex.getMessage());
415                 }
416             }
417
418             if (trans != null) {
419                 //System.out.println("inserting con: " + con + "into wrapperTx, tx: " + trans);
420
wrapperTx.put(con, trans); // For JDBC 1/2 wrappers, remember which
421
wrapperTx.put(trans, con); // connection goes with a given transaction
422
}
423         }
424         return con;
425     }
426
427     /**
428      * Closes a connection.
429      */

430     public void deleteObject(Object JavaDoc pooledObject) {
431         XAConnection JavaDoc con = (XAConnection JavaDoc) pooledObject;
432         try {
433             con.close();
434         } catch (SQLException JavaDoc e) {
435         }
436     }
437
438     /**
439      * If a new object is requested and it is a JDBC 1/2 wrapper connection
440      * in the same Transaction as an existing connection, return that same
441      * connection.
442      */

443     public Object JavaDoc isUniqueRequest() {
444         try {
445             if (tm.getStatus() != Status.STATUS_NO_TRANSACTION) {
446                 Transaction JavaDoc trans = tm.getTransaction();
447                 //System.out.println("isUniqueRequest returning conn: " + wrapperTx.get(trans) + " attached to tx: " + trans);
448
return wrapperTx.get(trans);
449             }
450         } catch (Exception JavaDoc e) {
451         }
452         return null;
453     }
454
455     /** For XAConnectionImpl check that parameters = String[2]{username, password}
456      and that these match the the source connection user and password. Return
457      true for non-XAConnectionImpl sources
458      */

459     public boolean checkValidObject(Object JavaDoc source, Object JavaDoc parameters) {
460         boolean validObject = true;
461         if (parameters != null && source instanceof XAConnectionImpl) {
462             XAConnectionImpl con = (XAConnectionImpl) source;
463             String JavaDoc credentials[] = (String JavaDoc[]) parameters;
464             if (credentials.length == 2) {
465                 String JavaDoc user = con.getUser();
466                 String JavaDoc password = con.getPassword();
467                 boolean validUser = ((user == null) && (credentials[0] == null)) || ((user != null) && user.equals(credentials[0]));
468                 boolean validPassword = ((password == null) && (credentials[1] == null)) || ((password != null) && password.equals(credentials[1]));
469                 validObject = validUser && validPassword;
470             }
471         }
472         return validObject;
473     }
474
475 }
476
Popular Tags