001: // You can redistribute this software and/or modify it under the terms of
002: // the Ozone Library License version 1 published by ozone-db.org.
003: //
004: // The original code and portions created by SMB are
005: // Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
006: //
007: // $Id: OzoneXAResource.java,v 1.1 2001/12/18 10:31:31 per_nyfelt Exp $
008:
009: package org.ozoneDB.xa;
010:
011: import java.io.*;
012: import org.ozoneDB.*;
013: import org.ozoneDB.core.*;
014: import org.ozoneDB.DxLib.*;
015: import javax.transaction.*;
016: import javax.transaction.xa.*;
017:
018: /**
019: * This is the XA adapter (XAResource) for an ozone database connection. It is
020: * created using the getXAResource method of an ExternalDatabase. It allows an
021: * TransactionManager to control transaction demarcation in an ozone database.
022: *
023: * @author <a href="http://www.softwarebuero.de/">SMB</a>
024: * @version $Revision: 1.1 $Date: 2001/12/18 10:31:31 $
025: */
026: public class OzoneXAResource implements XAResource {
027:
028: private ExternalDatabase db;
029:
030: private DxHashMap xids;
031:
032: private PrintStream log;
033:
034: public OzoneXAResource(ExternalDatabase _db) {
035: db = _db;
036: xids = new DxHashMap();
037: log = System.out;
038: }
039:
040: public String toString() {
041: return "OzoneXAResource [" + db + "]";
042: }
043:
044: protected void debug(String msg) {
045: log.println("OzoneXAResource: " + msg);
046: }
047:
048: /**
049: * Start work on behalf of a transaction branch specified in xid. If TMJOIN
050: * is specified, the start is for joining a transaction previously seen by
051: * the resource manager. If TMRESUME is specified, the start is to resume a
052: * suspended transaction specified in the parameter xid. If neither TMJOIN
053: * nor TMRESUME is specified and the transaction specified by xid has
054: * previously been seen by the resource manager, the resource manager throws
055: * the XAException exception with XAER_DUPID error code.
056: */
057: public void start(Xid xid, int flags) throws XAException {
058: debug("start(): xid:" + xid.hashCode() + ", flags:" + flags);
059:
060: if (xid == null) {
061: throw new XAException(XAException.XAER_INVAL);
062: }
063: if (db == null) {
064: throw new XAException(XAException.XAER_OUTSIDE);
065: }
066:
067: try {
068: switch (flags) {
069: case TMNOFLAGS: {
070: debug("start(): TMNOFLAGS...");
071: XATransaction tx = new XATransaction(db);
072: if (!xids.addForKey(tx, xid)) {
073: throw new XAException(XAException.XAER_DUPID);
074: }
075: db.beginTX(tx);
076: break;
077: }
078: case TMRESUME: {
079: debug("start(): TMRESUME...");
080: XATransaction tx = (XATransaction) xids
081: .elementForKey(xid);
082: if (tx == null) {
083: throw new XAException("Specified XID not found.");
084: }
085: db.joinTX(tx);
086: break;
087: }
088: case TMJOIN: {
089: debug("start(): TMJOIN...");
090: throw new RuntimeException("Not implemented yet.");
091: }
092: default: {
093: debug("start(): unknown flag...");
094:
095: // No other flags are supported in start().
096: throw new XAException(XAException.XAER_INVAL);
097: }
098: }
099: } catch (XAException e) {
100: throw e;
101: } catch (Exception e) {
102: log.println(e);
103: throw new XAException(XAException.XAER_RMERR);
104: }
105: }
106:
107: /**
108: * Ends the work performed on behalf of a transaction branch. The resource
109: * manager disassociates the XA resource from the transaction branch
110: * specified and let the transaction be completed.
111: *
112: * If TMSUSPEND is specified in flags, the transaction branch is temporarily
113: * suspended in incomplete state. The transaction context is in suspened
114: * state and must be resumed via start with TMRESUME specified.
115: *
116: *
117: * If TMFAIL is specified, the portion of work has failed. The resource
118: * manager may mark the transaction as rollback-only If TMSUCCESS is
119: * specified, the portion of work has completed successfully.
120: */
121: public void end(Xid xid, int flags) throws XAException {
122: debug("end(): xid:" + xid.hashCode() + ", flags:" + flags);
123:
124: if (xid == null) {
125: throw new XAException(XAException.XAER_INVAL);
126: }
127:
128: try {
129: XATransaction tx = (XATransaction) xids.elementForKey(xid);
130: if (xid == null) {
131: throw new XAException(XAException.XAER_NOTA);
132: }
133:
134: switch (flags) {
135: case TMSUCCESS: {
136: debug("end(): TMSUCCESS...");
137: db.leaveTX(tx);
138: break;
139: }
140: case TMFAIL: {
141: debug("end(): TMFAIL...");
142: db.rollbackTX(tx);
143: break;
144: }
145: case TMSUSPEND: {
146: debug("end(): TMSUSPEND...");
147: db.leaveTX(tx);
148: break;
149: }
150: default: {
151: debug("end(): unknown flag...");
152:
153: // No other flags are supported in end().
154: throw new XAException(XAException.XAER_INVAL);
155: }
156: }
157: } catch (XAException e) {
158: throw e;
159: } catch (Exception e) {
160: log.println(e);
161: throw new XAException(XAException.XAER_RMERR);
162: }
163: }
164:
165: /**
166: * Ask the resource manager to prepare for a transaction commit of the
167: * transaction specified in xid.
168: */
169: public int prepare(Xid xid) throws XAException {
170: debug("prepare(): xid:" + xid.hashCode());
171:
172: if (xid == null) {
173: throw new XAException(XAException.XAER_INVAL);
174: }
175:
176: try {
177: XATransaction tx = (XATransaction) xids.elementForKey(xid);
178: if (xid == null) {
179: throw new XAException(XAException.XAER_NOTA);
180: }
181: db.prepareTX(tx);
182: return XA_OK;
183: } catch (XAException e) {
184: throw e;
185: } catch (TransactionExc e) {
186: log.println(e);
187: throw new XAException(XAException.XA_RBROLLBACK);
188: } catch (Exception e) {
189: log.println(e);
190: throw new XAException(XAException.XAER_RMERR);
191: }
192: }
193:
194: /**
195: * Commit the global transaction specified by xid.
196: */
197: public void commit(Xid xid, boolean onePhase) throws XAException {
198: debug("commit(): xid:" + xid.hashCode() + ", onePhase:"
199: + onePhase);
200:
201: if (xid == null) {
202: throw new XAException(XAException.XAER_INVAL);
203: }
204:
205: try {
206: XATransaction tx = (XATransaction) xids.elementForKey(xid);
207: if (xid == null) {
208: throw new XAException(XAException.XAER_NOTA);
209: }
210: db.commitTX(tx, onePhase);
211: } catch (XAException e) {
212: throw e;
213: } catch (TransactionExc e) {
214: log.println(e);
215: throw new XAException(XAException.XA_RBROLLBACK);
216: } catch (Exception e) {
217: log.println(e);
218: throw new XAException(XAException.XAER_RMERR);
219: }
220: }
221:
222: /**
223: * Inform the resource manager to roll back work done on behalf of a
224: * transaction branch.
225: */
226: public void rollback(Xid xid) throws XAException {
227: debug("rollback(): xid:" + xid.hashCode());
228:
229: if (xid == null) {
230: throw new XAException(XAException.XAER_INVAL);
231: }
232:
233: try {
234: XATransaction tx = (XATransaction) xids.elementForKey(xid);
235: if (xid == null) {
236: throw new XAException(XAException.XAER_NOTA);
237: }
238: db.rollbackTX(tx);
239: } catch (XAException e) {
240: throw e;
241: } catch (Exception e) {
242: log.println(e);
243: throw new XAException(XAException.XAER_RMERR);
244: }
245: }
246:
247: /**
248: * Tell the resource manager to forget about a heuristically completed
249: * transaction branch.
250: */
251: public void forget(Xid xid) throws XAException {
252: debug("forget(): xid:" + xid.hashCode());
253:
254: xids.removeForKey(xid);
255: }
256:
257: /**
258: * Set the current transaction timeout value for this XAResource instance.
259: * Once set, this timeout value is effective until setTransactionTimeout is
260: * invoked again with a different value. To reset the timeout value to the
261: * default value used by the resource manager, set the value to zero. If the
262: * timeout operation is performed successfully, the method returns true;
263: * otherwise false. If a resource manager does not support transaction
264: * timeout value to be set explicitly, this method returns false.
265: */
266: public boolean setTransactionTimeout(int seconds)
267: throws XAException {
268: debug("setTransactionTimeout(): seconds:" + seconds);
269: return false;
270: }
271:
272: /**
273: * Obtain the current transaction timeout value set for this XAResource
274: * instance. If XAResource.setTransactionTimeout was not use prior to
275: * invoking this method, the return value is the default timeout set for the
276: * resource manager; otherwise, the value used in the previous
277: * setTransactionTimeout call is returned.
278: */
279: public int getTransactionTimeout() throws XAException {
280: debug("getTransactionTimeout():");
281: return -1;
282: }
283:
284: /**
285: * This method is called to determine if the resource manager instance
286: * represented by the target object is the same as the resouce manager
287: * instance represented by the parameter xares.
288: */
289: public boolean isSameRM(XAResource xares) throws XAException {
290: debug("isSameRM(): xares:" + xares);
291:
292: if (xares != null && xares instanceof OzoneXAResource) {
293: return db == ((OzoneXAResource) xares).db;
294: } else {
295: return false;
296: }
297: }
298:
299: /**
300: * Obtain a list of prepared transaction branches from a resource manager.
301: * The transaction manager calls this method during recovery to obtain the
302: * list of transaction branches that are currently in prepared or
303: * heuristically completed states.
304: */
305: public Xid[] recover(int flag) throws XAException {
306: debug("recover(): flags:" + flag);
307:
308: try {
309: DxArrayBag result = new DxArrayBag();
310: DxIterator it = xids.iterator();
311: while (it.next() != null) {
312: XATransaction tx = (XATransaction) it.object();
313: if (db.getStatusTX(tx) == Status.STATUS_PREPARED) {
314: result.add(tx);
315: }
316: }
317:
318: return (Xid[]) result.toArray();
319: } catch (Exception e) {
320: throw new XAException(e.toString());
321: }
322: }
323:
324: }
|