001: /*
002: * XAPool: Open Source XA JDBC Pool
003: * Copyright (C) 2003 Objectweb.org
004: * Initial Developer: Lutris Technologies Inc.
005: * Contact: xapool-public@lists.debian-sf.objectweb.org
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
020: * USA
021: */
022: package org.enhydra.jdbc.pool;
023:
024: import java.sql.SQLException;
025: import java.util.Hashtable;
026:
027: import javax.naming.Context;
028: import javax.naming.InitialContext;
029: import javax.naming.Name;
030: import javax.naming.NamingException;
031: import javax.naming.Reference;
032: import javax.naming.StringRefAddr;
033: import javax.sql.ConnectionEvent;
034: import javax.sql.XAConnection;
035: import javax.sql.XADataSource;
036: import javax.transaction.TransactionManager;
037: import javax.transaction.Transaction;
038: import javax.transaction.SystemException;
039: import javax.transaction.xa.XAResource;
040: import org.enhydra.jdbc.standard.StandardXADataSource;
041: import org.enhydra.jdbc.standard.StandardXAConnection;
042: import org.apache.commons.logging.Log;
043: import org.apache.commons.logging.LogFactory;
044: import java.sql.Connection;
045: import java.sql.Statement;
046: import javax.sql.PooledConnection;
047: import javax.transaction.Status;
048:
049: /**
050: * StandardXAPoolDataSource class allows to make some operations on
051: * XAConnection. It implements PoolHelper for the 3 methods :<p>
052: * create : create an XAConnection<p>
053: * create(user,password) : create a PooledConnection with an other user/password<p>
054: * testThisObject : check if the object is still valid<p>
055: * checkThisObject : check if the object is closed<p>
056: * expire : kill the object<p>
057: */
058: public class StandardXAPoolDataSource extends StandardPoolDataSource {
059:
060: public XADataSource xads; // object to build XAConnection
061: public TransactionManager transactionManager;
062: public String transactionManagerName;
063:
064: // the current Transaction Manager
065: public Log glog = LogFactory.getLog("org.enhydra.jdbc.xapool");
066:
067: /**
068: * Constructor
069: */
070: public StandardXAPoolDataSource() {
071: super ();
072: }
073:
074: /**
075: * Constructor
076: */
077: public StandardXAPoolDataSource(int initSize) {
078: super (initSize);
079: }
080:
081: /**
082: * Constructor
083: */
084: public StandardXAPoolDataSource(StandardXADataSource ds) {
085: super (ds);
086: setDataSource(ds);
087: }
088:
089: /**
090: * Constructor
091: */
092: public StandardXAPoolDataSource(StandardXADataSource ds,
093: int initSize) {
094: super (ds, initSize);
095: setDataSource(ds);
096: }
097:
098: public void setTransactionManager(TransactionManager tm) {
099: log.debug("StandardXAPoolDataSource:setTransactionManager");
100: transactionManager = tm;
101: }
102:
103: /**
104: * Invoked when the application calls close()
105: * on its representation of the connection
106: */
107: public void connectionClosed(ConnectionEvent event) {
108: Object obj = event.getSource();
109: log.debug("StandardXAPoolDataSource:connectionClosed");
110: XAConnection xac = (XAConnection) obj; // cast it into an xaConnection
111:
112: Transaction tx = null;
113: try {
114: if (transactionManager == null) {
115: TransactionManager tm = ((StandardXADataSource) xads)
116: .getTransactionManager();
117: if (tm == null) {
118: throw new NullPointerException("TM is null");
119: } else
120: // here we use tm instead to setup transactionManager = tm
121: // if the current transactionManager property is null, it stays
122: // there, and we continue to use the TM from the XADataSource
123: tx = tm.getTransaction();
124: } else {
125: tx = transactionManager.getTransaction();
126: }
127: log
128: .debug("StandardXAPoolDataSource:connectionClosed get a transaction");
129: } catch (NullPointerException n) {
130: // current is null: we are not in EJBServer.
131: log
132: .error("StandardXAPoolDataSource:connectionClosed should not be used outside an EJBServer");
133: } catch (SystemException e) {
134: log
135: .error("StandardXAPoolDataSource:connectionClosed getTransaction failed:"
136: + e);
137: }
138:
139: // delist Resource if in transaction
140: // We must keep the connection till commit or rollback
141: if ((tx != null)
142: && (((StandardXAConnection) xac).connectionHandle.isReallyUsed)) {
143: try {
144: tx.delistResource(xac.getXAResource(),
145: XAResource.TMSUCCESS);
146: // delist the xaResource
147: log
148: .debug("StandardXAPoolDataSource:connectionClosed the resourse is delisted");
149: } catch (Exception e) {
150: log
151: .error("StandardXAPoolDataSource:connectionClosed Exception in connectionClosed:"
152: + e);
153: }
154: }
155: log
156: .debug("StandardXAPoolDataSource:connectionClosed checkIn an object to the pool");
157: pool.checkIn(obj); // return the connection to the pool
158: }
159:
160: public GenerationObject create(String _user, String _password)
161: throws SQLException {
162: GenerationObject genObject;
163: XAConnection xaCon = xads.getXAConnection(_user, _password);
164: // get the xa connection
165: xaCon.addConnectionEventListener(this ); // add it to the event listener
166: log
167: .debug("StandardXAPoolDataSource:create create a object for the pool");
168: genObject = new GenerationObject(xaCon, pool.getGeneration(),
169: _user, _password);
170:
171: return genObject;
172: }
173:
174: /**
175: * Retrieves the Reference of this object. Used at binding time by JNDI
176: * to build a reference on this object.
177: *
178: * @return The non-null Reference of this object.
179: * @exception NamingException If a naming exception was encountered while
180: * retrieving the reference.
181: */
182: public Reference getReference() throws NamingException {
183: log
184: .debug("StandardXAPoolDataSource:getReference return a reference of the object");
185: Reference ref = super .getReference();
186: ref.add(new StringRefAddr("transactionManagerName",
187: this .transactionManagerName));
188:
189: return ref;
190: }
191:
192: /* (non-Javadoc)
193: * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
194: */
195: public Object getObjectInstance(Object refObj, Name name,
196: Context nameCtx, Hashtable env) throws Exception {
197:
198: super .getObjectInstance(refObj, name, nameCtx, env);
199: Reference ref = (Reference) refObj;
200: InitialContext ictx = new InitialContext(env);
201: this .setTransactionManagerName((String) ref.get(
202: "transactionManagerName").getContent());
203: if (this .transactionManagerName != null) {
204: try {
205: this .setTransactionManager((TransactionManager) ictx
206: .lookup(this .transactionManagerName));
207: } catch (NamingException e) {
208: // ignore, TransactionManager might be set later enlisting the XAResouce on the Transaction
209: }
210: }
211:
212: this .setDataSource((XADataSource) ictx
213: .lookup(this .dataSourceName));
214: log
215: .debug("StandardXAPoolDataSource:getObjectInstance: instance created");
216: return this ;
217: }
218:
219: /** Getter for property dataSource.
220: * @return Value of property dataSource.
221: */
222: public XADataSource getDataSource() {
223: return xads;
224: }
225:
226: /** Setter for property dataSource.
227: * @param dataSource New value of property dataSource.
228: */
229: public void setDataSource(XADataSource dataSource) {
230: this .xads = dataSource;
231: if (transactionManager != null)
232: ((StandardXADataSource) dataSource)
233: .setTransactionManager(transactionManager);
234: if (transactionManagerName != null)
235: ((StandardXADataSource) dataSource)
236: .setTransactionManagerName(transactionManagerName);
237: }
238:
239: public String toString() {
240: StringBuffer sb = new StringBuffer();
241: sb.append("StandardXAPoolDataSource:\n");
242: if (this .transactionManager != null)
243: sb.append(" transaction manager=<"
244: + this .transactionManager.toString() + ">\n");
245: if (this .xads != null)
246: sb.append(this .xads.toString());
247: sb.append(super .toString());
248: return sb.toString();
249: }
250:
251: /**
252: * This method tests if a connection is valid or not. It overrides the
253: * method in StandardPoolDataSource to take into account global transactions:
254: * if global transaction is in progress - suspend it so that
255: * connection testing happens ouside of transaction.
256: * If connection testing fails - it will not affect transaction
257: * and next good connection can join the transaction
258: */
259: public boolean testThisObject(Object o) {
260: Connection ret = null;
261: log
262: .debug("StandardXAPoolDataSource:testThisObject verify the current object");
263: Transaction suspended = null;
264: try {
265: Transaction tx = transactionManager == null ? null
266: : transactionManager.getTransaction();
267: boolean isActive = tx == null ? false
268: : tx.getStatus() == Status.STATUS_ACTIVE;
269: if (isActive) {
270: suspended = transactionManager.suspend();
271: }
272:
273: PooledConnection con = (PooledConnection) o;
274: ret = con.getConnection();
275:
276: Statement s = ret.createStatement();
277: s.execute(jdbcTestStmt);
278: s.close();
279: try {
280: ret.close();
281: } catch (Exception e) {
282: log
283: .error("StandardXAPoolDataSource:checkThisObject can't closed the connection: "
284: + e);
285: }
286: return true;
287: } catch (SQLException e) {
288: log
289: .error("StandardXAPoolDataSource:checkThisObject Error java.sql.SQLException in StandardXAPoolDataSource:testThisObject");
290: return false;
291: } catch (SystemException e) {
292: log
293: .error("StandardXAPoolDataSource:checkThisObject Error java.sql.SystemException in StandardXAPoolDataSource:testThisObject");
294: return false;
295: } finally {
296: if (suspended != null) {
297: try {
298: transactionManager.resume(suspended);
299: } catch (Exception ex) {
300: log
301: .error("StandardXAPoolDataSource:checkThisObject Error Exception in StandardXAPoolDataSource:testThisObject");
302: return false;
303: }
304: }
305: }
306: }
307:
308: public void setTransactionManagerName(String tmName) {
309: log.debug("StandardXAPoolDataSource:setTransactionManagerName");
310: transactionManagerName = tmName;
311: }
312:
313: }
|