001: /*
002:
003: Derby - Class org.apache.derby.jdbc.XATransactionState
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.jdbc;
023:
024: import org.apache.derby.impl.jdbc.EmbedConnection;
025: import javax.transaction.xa.XAResource;
026: import org.apache.derby.iapi.services.context.ContextImpl;
027: import org.apache.derby.iapi.services.context.ContextManager;
028: import org.apache.derby.iapi.error.ExceptionSeverity;
029: import org.apache.derby.iapi.error.StandardException;
030: import org.apache.derby.iapi.store.access.xa.XAXactId;
031: import org.apache.derby.iapi.reference.SQLState;
032: import java.util.HashMap;
033: import javax.transaction.xa.XAException;
034:
035: /**
036: */
037: final class XATransactionState extends ContextImpl {
038:
039: /** Rollback-only due to deadlock */
040: final static int TRO_DEADLOCK = -2;
041: /** Rollback-only due to end(TMFAIL) */
042: final static int TRO_FAIL = -1;
043: final static int T0_NOT_ASSOCIATED = 0;
044: final static int T1_ASSOCIATED = 1;
045: // final static int T2_ASSOCIATION_SUSPENDED = 2;
046: final static int TC_COMPLETED = 3; // rollback/commit called
047:
048: final EmbedConnection conn;
049: final EmbedXAResource creatingResource;
050: // owning XAResource
051: private EmbedXAResource associatedResource;
052: final XAXactId xid;
053: /**
054: When an XAResource suspends a transaction (end(TMSUSPEND)) it must be resumed
055: using the same XAConnection. This has been the traditional Cloudscape behaviour,
056: though there does not seem to be a specific reference to this behaviour in
057: the JTA spec. Note that while the transaction is suspended by this XAResource,
058: another XAResource may join the transaction and suspend it after the join.
059: */
060: HashMap suspendedList;
061:
062: /**
063: Association state of the transaction.
064: */
065: int associationState;
066:
067: int rollbackOnlyCode;
068:
069: /**
070: has this transaction been prepared.
071: */
072: boolean isPrepared;
073:
074: XATransactionState(ContextManager cm, EmbedConnection conn,
075: EmbedXAResource resource, XAXactId xid) {
076:
077: super (cm, "XATransactionState");
078: this .conn = conn;
079: this .associatedResource = resource;
080: this .creatingResource = resource;
081: this .associationState = XATransactionState.T1_ASSOCIATED;
082: this .xid = xid;
083:
084: }
085:
086: public void cleanupOnError(Throwable t) {
087:
088: if (t instanceof StandardException) {
089:
090: StandardException se = (StandardException) t;
091:
092: if (se.getSeverity() >= ExceptionSeverity.SESSION_SEVERITY) {
093: popMe();
094: return;
095: }
096:
097: if (se.getSeverity() == ExceptionSeverity.TRANSACTION_SEVERITY) {
098:
099: synchronized (this ) {
100: // disable use of the connection until it is cleaned up.
101: conn.setApplicationConnection(null);
102: notifyAll();
103: associationState = TRO_FAIL;
104: if (SQLState.DEADLOCK.equals(se.getMessageId()))
105: rollbackOnlyCode = XAException.XA_RBDEADLOCK;
106: else if (SQLState.LOCK_TIMEOUT.equals(se
107: .getMessageId()))
108: rollbackOnlyCode = XAException.XA_RBTIMEOUT;
109: else
110: rollbackOnlyCode = XAException.XA_RBOTHER;
111: }
112: }
113: }
114: }
115:
116: void start(EmbedXAResource resource, int flags) throws XAException {
117:
118: synchronized (this ) {
119: if (associationState == XATransactionState.TRO_FAIL)
120: throw new XAException(rollbackOnlyCode);
121:
122: boolean isSuspendedByResource = (suspendedList != null)
123: && (suspendedList.get(resource) != null);
124:
125: if (flags == XAResource.TMRESUME) {
126: if (!isSuspendedByResource)
127: throw new XAException(XAException.XAER_PROTO);
128:
129: } else {
130: // cannot join a transaction we have suspended.
131: if (isSuspendedByResource)
132: throw new XAException(XAException.XAER_PROTO);
133: }
134:
135: while (associationState == XATransactionState.T1_ASSOCIATED) {
136:
137: try {
138: wait();
139: } catch (InterruptedException ie) {
140: throw new XAException(XAException.XA_RETRY);
141: }
142: }
143:
144: switch (associationState) {
145: case XATransactionState.T0_NOT_ASSOCIATED:
146: break;
147:
148: case XATransactionState.TRO_FAIL:
149: throw new XAException(rollbackOnlyCode);
150:
151: default:
152: throw new XAException(XAException.XAER_NOTA);
153: }
154:
155: if (isPrepared)
156: throw new XAException(XAException.XAER_PROTO);
157:
158: if (isSuspendedByResource) {
159: suspendedList.remove(resource);
160: }
161:
162: associationState = XATransactionState.T1_ASSOCIATED;
163: associatedResource = resource;
164: }
165: }
166:
167: boolean end(EmbedXAResource resource, int flags,
168: boolean endingCurrentXid) throws XAException {
169:
170: boolean rollbackOnly = false;
171: synchronized (this ) {
172:
173: boolean isSuspendedByResource = (suspendedList != null)
174: && (suspendedList.get(resource) != null);
175:
176: if (!endingCurrentXid) {
177: while (associationState == XATransactionState.T1_ASSOCIATED) {
178:
179: try {
180: wait();
181: } catch (InterruptedException ie) {
182: throw new XAException(XAException.XA_RETRY);
183: }
184: }
185: }
186:
187: switch (associationState) {
188: case XATransactionState.TC_COMPLETED:
189: throw new XAException(XAException.XAER_NOTA);
190: case XATransactionState.TRO_FAIL:
191: if (endingCurrentXid)
192: flags = XAResource.TMFAIL;
193: else
194: throw new XAException(rollbackOnlyCode);
195: }
196:
197: boolean notify = false;
198: switch (flags) {
199: case XAResource.TMSUCCESS:
200: if (isSuspendedByResource) {
201: suspendedList.remove(resource);
202: } else {
203: if (resource != associatedResource)
204: throw new XAException(XAException.XAER_PROTO);
205:
206: associationState = XATransactionState.T0_NOT_ASSOCIATED;
207: associatedResource = null;
208: notify = true;
209: }
210:
211: conn.setApplicationConnection(null);
212: break;
213:
214: case XAResource.TMFAIL:
215:
216: if (isSuspendedByResource) {
217: suspendedList.remove(resource);
218: } else {
219: if (resource != associatedResource)
220: throw new XAException(XAException.XAER_PROTO);
221: associatedResource = null;
222: }
223:
224: if (associationState != XATransactionState.TRO_FAIL) {
225: associationState = XATransactionState.TRO_FAIL;
226: rollbackOnlyCode = XAException.XA_RBROLLBACK;
227: }
228: conn.setApplicationConnection(null);
229: notify = true;
230: rollbackOnly = true;
231: break;
232:
233: case XAResource.TMSUSPEND:
234: if (isSuspendedByResource)
235: throw new XAException(XAException.XAER_PROTO);
236:
237: if (resource != associatedResource)
238: throw new XAException(XAException.XAER_PROTO);
239:
240: if (suspendedList == null)
241: suspendedList = new HashMap();
242: suspendedList.put(resource, this );
243:
244: associationState = XATransactionState.T0_NOT_ASSOCIATED;
245: associatedResource = null;
246: conn.setApplicationConnection(null);
247: notify = true;
248:
249: break;
250:
251: default:
252: throw new XAException(XAException.XAER_INVAL);
253: }
254:
255: if (notify)
256: notifyAll();
257:
258: return rollbackOnly;
259: }
260: }
261:
262: }
|