001: /*
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * Initial developer(s): Eric HARDESTY
022: * --------------------------------------------------------------------------
023: * $Id: XAResourceImpl.java 9771 2006-10-19 23:08:18Z ehardesty $
024: * --------------------------------------------------------------------------
025: */
026: package org.objectweb.jonas.jdbc;
027:
028: import java.sql.SQLException;
029:
030: import javax.resource.spi.ConnectionEvent;
031: import javax.transaction.xa.XAException;
032: import javax.transaction.xa.XAResource;
033: import javax.transaction.xa.Xid;
034:
035: import org.objectweb.util.monolog.api.BasicLevel;
036:
037: /**
038: * This is an implementation of XAResource used for transaction mgmt
039: * with a JDBC driver. The standard JDBC drivers don't support real
040: * XA, so the prepare will just return OK and work will be done in the
041: * commit/rollback.
042: */
043: public class XAResourceImpl implements XAResource {
044:
045: // Current Xid
046: private Xid currentXid = null;
047:
048: // Is a xa transaction started ?
049: boolean started = false;
050: boolean ended = true;
051:
052: protected ManagedConnectionImpl mc = null;
053: private XAResource xares = null;
054:
055: /**
056: * XAResourceImpl constructor
057: * @param mc ManagedConnectionImpl used with this object
058: * @param xares XAResource from DB if supported
059: */
060: public XAResourceImpl(ManagedConnectionImpl mc, XAResource xares) {
061: this .mc = mc;
062: this .xares = xares;
063: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
064: mc.trace.log(BasicLevel.DEBUG, "" + mc + "," + xares);
065: }
066: }
067:
068: /**
069: * Commit the global transaction specified by xid.
070: */
071: public void commit(Xid xid, boolean onePhase) throws XAException {
072: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
073: mc.trace.log(BasicLevel.DEBUG, "" + xid + "," + onePhase);
074: }
075:
076: // Commit the transaction
077: try {
078: if (xares != null) {
079: xares.commit(xid, onePhase);
080: if (xid.equals(currentXid)) {
081: started = false;
082: }
083: } else {
084: // Make sure that this call is for the XID
085: if (currentXid == null || !currentXid.equals(xid)
086: || !started) {
087: mc.trace.log(BasicLevel.ERROR, "passed xid(" + xid
088: + "),current Xid(" + currentXid
089: + "),started(" + started + ")");
090: throw new XAException(
091: "Commit: Must call correct XAResource for its started XID");
092: }
093: mc.connection.commit();
094: started = false;
095: }
096: } catch (XAException xe) {
097: mc.trace.log(BasicLevel.ERROR, xe.getMessage());
098: throw xe;
099: } catch (SQLException e) {
100: mc.trace.log(BasicLevel.ERROR, e.getMessage());
101: try {
102: mc.signalEvent(
103: ConnectionEvent.CONNECTION_ERROR_OCCURRED,
104: null, e);
105: } catch (Exception ex) {
106: }
107: throw new XAException("Error on commit");
108: }
109:
110: try {
111: if (!mc.connection.getAutoCommit()) {
112: mc.connection.setAutoCommit(true);
113: }
114: } catch (Exception exc) {
115: if (xares == null) {
116: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
117: mc.trace.log(BasicLevel.DEBUG,
118: "Unable to set autoCommit to true:", exc);
119: }
120: }
121: }
122: }
123:
124: /**
125: * Ends the transaction
126: */
127: public void end(Xid xid, int flags) throws XAException {
128: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
129: mc.trace.log(BasicLevel.DEBUG, "" + xid + "," + flags);
130: }
131: // Make sure that the Xid has started
132: if (currentXid == null || !currentXid.equals(xid)) {
133: throw new XAException(XAException.XA_RBPROTO);
134: }
135: if (!started && ended) {
136: throw new XAException(XAException.XA_RBPROTO);
137: }
138: ended = true;
139: if (xares != null) {
140: xares.end(xid, flags);
141: }
142: }
143:
144: /**
145: * Need to forget the heuristically completed Xid
146: */
147: public void forget(Xid xid) throws XAException {
148: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
149: mc.trace.log(BasicLevel.DEBUG, "" + xid);
150: }
151: if (xares != null) {
152: xares.forget(xid);
153: }
154: }
155:
156: /**
157: * Get the current transaction timeout for this XAResource
158: */
159: public int getTransactionTimeout() throws XAException {
160: if (xares != null) {
161: return xares.getTransactionTimeout();
162: }
163: return 0;
164: }
165:
166: /**
167: * Determine if the resource manager instance is the same as the
168: * resouce manager being passed in
169: */
170: public boolean isSameRM(XAResource xaRes) throws XAException {
171:
172: boolean ret = false;
173: if (xaRes.equals(this )) {
174: ret = true;
175: } else if (!(xaRes instanceof XAResourceImpl)) {
176: ret = false;
177: } else {
178: XAResourceImpl xaResImpl = (XAResourceImpl) xaRes;
179: if (mc == xaResImpl.mc) {
180: ret = true;
181: }
182: }
183: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
184: mc.trace.log(BasicLevel.DEBUG, "" + xaRes + "," + this
185: + " is " + ret);
186: }
187: return ret;
188: }
189:
190: /**
191: * Prepare the Xid for a transaction commit
192: */
193: public int prepare(Xid xid) throws XAException {
194: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
195: mc.trace.log(BasicLevel.DEBUG, "" + xid);
196: }
197: if (xares != null) {
198: int ret = xares.prepare(xid);
199: // If read-only, then do what would have been done in the commit call
200: if (ret == XA_RDONLY) {
201: started = false;
202: try {
203: if (!mc.connection.getAutoCommit()) {
204: mc.connection.setAutoCommit(true);
205: }
206: } catch (Exception exc) {
207: }
208: }
209: return ret;
210: }
211: // Just return true, since we are just simulating XA with our wrapper
212: return XA_OK;
213: }
214:
215: /**
216: * Obtain a list of prepared transaction branches from a resource manager.
217: */
218: public Xid[] recover(int flag) throws XAException {
219: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
220: mc.trace.log(BasicLevel.DEBUG, "" + flag);
221: }
222: if (xares != null) {
223: return xares.recover(flag);
224: }
225: // Not a full RM, so just return null
226: return null;
227: }
228:
229: /**
230: * Roll back work done on the Xid
231: */
232: public void rollback(Xid xid) throws XAException {
233: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
234: mc.trace.log(BasicLevel.DEBUG, "" + xid);
235: }
236:
237: // Rollback the Xid
238: try {
239: if (xares != null) {
240: xares.rollback(xid);
241: if (xid.equals(currentXid)) {
242: started = false;
243: }
244: } else {
245: // Make sure that this call is for the XID
246: if (currentXid == null || !currentXid.equals(xid)
247: || !started) {
248: mc.trace.log(BasicLevel.ERROR, "passed xid(" + xid
249: + "),current Xid(" + currentXid
250: + "),started(" + started + ")");
251: throw new XAException(
252: "Rollback: Must call correct XAResource for its started XID");
253: }
254: mc.connection.rollback();
255: started = false;
256: }
257: } catch (XAException xe) {
258: mc.trace.log(BasicLevel.ERROR, xe.getMessage());
259: throw xe;
260: } catch (SQLException e) {
261: try {
262: mc.trace.log(BasicLevel.ERROR, e.getMessage());
263: mc.signalEvent(
264: ConnectionEvent.CONNECTION_ERROR_OCCURRED,
265: null, e);
266: } catch (Exception ex) {
267: }
268: throw (new XAException("Error on rollback"));
269: }
270: try {
271: if (!mc.connection.getAutoCommit()) {
272: mc.connection.setAutoCommit(true);
273: }
274: } catch (Exception exc) {
275: if (xares == null) {
276: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
277: mc.trace.log(BasicLevel.DEBUG,
278: "Unable to set autoCommit to true:", exc);
279: }
280: }
281: }
282: }
283:
284: /**
285: * Set the current transaction timeout value for this XAResource
286: */
287: public boolean setTransactionTimeout(int seconds)
288: throws XAException {
289: if (xares != null) {
290: return xares.setTransactionTimeout(seconds);
291: }
292: return false;
293: }
294:
295: /**
296: * Start work on this Xid. If TMJOIN is on, then join an existing transaction
297: * already started by the RM.
298: */
299: public void start(Xid xid, int flags) throws XAException {
300: if (mc.trace.isLoggable(BasicLevel.DEBUG)) {
301: mc.trace.log(BasicLevel.DEBUG, "" + xid + "," + flags);
302: }
303: try {
304: if (mc.connection.getAutoCommit()) {
305: mc.connection.setAutoCommit(false);
306: }
307: } catch (Exception ex) {
308: ex.printStackTrace();
309: mc.trace.log(BasicLevel.ERROR,
310: "Unable to set autoCommit to false:"
311: + ex.getMessage());
312: throw (new XAException(
313: "Error : Unable to set autoCommit to false in start"));
314: }
315:
316: if (xares != null) {
317: xares.start(xid, flags);
318: } else {
319: if (started
320: && (currentXid == null || !currentXid.equals(xid))) {
321: mc.trace
322: .log(BasicLevel.ERROR,
323: "Must call correct XAResource for its started XID");
324: throw new XAException(
325: "XAResourceImpl.start : Must call correct XAResource for its started XID");
326: }
327: }
328: currentXid = xid;
329: started = true;
330: ended = false;
331: }
332: }
|