001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.ri.sp;
017:
018: import java.util.ArrayList;
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.Properties;
024: import javax.transaction.InvalidTransactionException;
025: import javax.transaction.NotSupportedException;
026: import javax.transaction.RollbackException;
027: import javax.transaction.Status;
028: import javax.transaction.Synchronization;
029: import javax.transaction.Transaction;
030: import javax.transaction.TransactionManager;
031: import javax.transaction.TransactionSynchronizationRegistry;
032: import javax.transaction.xa.XAException;
033: import javax.transaction.xa.XAResource;
034:
035: import org.apache.openejb.spi.TransactionService;
036: import org.apache.openejb.util.LogCategory;
037: import org.apache.openejb.util.Logger;
038:
039: /**
040: * @org.apache.xbean.XBean element="pseudoTransactionService"
041: */
042: public class PseudoTransactionService implements TransactionService,
043: TransactionManager, TransactionSynchronizationRegistry {
044: private static final Logger logger = Logger.getInstance(
045: LogCategory.OPENEJB, "org.apache.openejb.core.cmp");
046: private final ThreadLocal<MyTransaction> threadTransaction = new ThreadLocal<MyTransaction>();
047:
048: public void init(Properties props) {
049: }
050:
051: public TransactionManager getTransactionManager() {
052: return this ;
053: }
054:
055: public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
056: return this ;
057: }
058:
059: public int getStatus() {
060: MyTransaction tx = threadTransaction.get();
061: if (tx == null) {
062: return Status.STATUS_NO_TRANSACTION;
063: }
064: return tx.getStatus();
065: }
066:
067: public Transaction getTransaction() {
068: return threadTransaction.get();
069: }
070:
071: public boolean getRollbackOnly() {
072: MyTransaction tx = threadTransaction.get();
073: if (tx == null) {
074: throw new IllegalStateException("No transaction active");
075: }
076: return tx.getRollbackOnly();
077: }
078:
079: public void setRollbackOnly() {
080: MyTransaction tx = threadTransaction.get();
081: if (tx == null) {
082: throw new IllegalStateException("No transaction active");
083: }
084: tx.setRollbackOnly();
085: }
086:
087: public void begin() throws NotSupportedException {
088: if (threadTransaction.get() != null) {
089: throw new NotSupportedException(
090: "A transaction is already active");
091: }
092:
093: MyTransaction tx = new MyTransaction();
094: threadTransaction.set(tx);
095: }
096:
097: public void commit() throws RollbackException {
098: MyTransaction tx = threadTransaction.get();
099: if (tx == null) {
100: throw new IllegalStateException("No transaction active");
101: }
102:
103: try {
104: tx.commit();
105: } finally {
106: threadTransaction.set(null);
107: }
108: }
109:
110: public void rollback() {
111: MyTransaction tx = threadTransaction.get();
112: if (tx == null) {
113: throw new IllegalStateException("No transaction active");
114: }
115:
116: try {
117: tx.rollback();
118: } finally {
119: threadTransaction.set(null);
120: }
121: }
122:
123: public Transaction suspend() {
124: return threadTransaction.get();
125: }
126:
127: public void resume(Transaction tx)
128: throws InvalidTransactionException {
129: if (tx == null) {
130: throw new InvalidTransactionException("Transaction is null");
131: }
132: if (!(tx instanceof MyTransaction)) {
133: throw new InvalidTransactionException(
134: "Unknown transaction type "
135: + tx.getClass().getName());
136: }
137: MyTransaction myTransaction = (MyTransaction) tx;
138:
139: if (threadTransaction.get() != null) {
140: throw new IllegalStateException(
141: "A transaction is already active");
142: }
143:
144: int status = myTransaction.getStatus();
145: if (status != Status.STATUS_ACTIVE
146: && status != Status.STATUS_MARKED_ROLLBACK) {
147: throw new InvalidTransactionException(
148: "Expected transaction to be STATUS_ACTIVE or STATUS_MARKED_ROLLBACK, but was "
149: + status);
150: }
151:
152: threadTransaction.set(myTransaction);
153: }
154:
155: public Object getTransactionKey() {
156: return getTransaction();
157: }
158:
159: public int getTransactionStatus() {
160: return getStatus();
161: }
162:
163: public Object getResource(Object key) {
164: MyTransaction tx = threadTransaction.get();
165: if (tx == null) {
166: throw new IllegalStateException("No transaction active");
167: }
168:
169: Object value = tx.getResource(key);
170: return value;
171: }
172:
173: public void putResource(Object key, Object value) {
174: MyTransaction tx = threadTransaction.get();
175: if (tx == null) {
176: throw new IllegalStateException("No transaction active");
177: }
178:
179: tx.putResource(key, value);
180: }
181:
182: public void registerInterposedSynchronization(
183: Synchronization synchronization) {
184: MyTransaction tx = threadTransaction.get();
185: if (tx == null) {
186: throw new IllegalStateException("No transaction active");
187: }
188:
189: tx.registerInterposedSynchronization(synchronization);
190: }
191:
192: public void setTransactionTimeout(int seconds) {
193: }
194:
195: public class MyTransaction implements Transaction {
196: private final List<Synchronization> registeredSynchronizations = Collections
197: .synchronizedList(new ArrayList<Synchronization>());
198: private final List<XAResource> xaResources = Collections
199: .synchronizedList(new ArrayList<XAResource>());
200: private final Map<Object, Object> resources = new HashMap<Object, Object>();
201: private int status = Status.STATUS_ACTIVE;
202:
203: public boolean delistResource(XAResource xaRes, int flag) {
204: xaResources.remove(xaRes);
205: return true;
206: }
207:
208: public boolean enlistResource(XAResource xaRes) {
209: xaResources.add(xaRes);
210: return true;
211: }
212:
213: public int getStatus() {
214: return status;
215: }
216:
217: public void registerSynchronization(
218: Synchronization synchronization) {
219: registeredSynchronizations.add(synchronization);
220: }
221:
222: public void registerInterposedSynchronization(
223: Synchronization synchronization) {
224: registeredSynchronizations.add(synchronization);
225: }
226:
227: public boolean getRollbackOnly() {
228: return status == Status.STATUS_MARKED_ROLLBACK;
229: }
230:
231: public void setRollbackOnly() {
232: status = Status.STATUS_MARKED_ROLLBACK;
233: }
234:
235: public Object getResource(Object key) {
236: if (key == null)
237: throw new NullPointerException("key is null");
238: return resources.get(key);
239: }
240:
241: public void putResource(Object key, Object value) {
242: if (key == null)
243: throw new NullPointerException("key is null");
244: if (value != null) {
245: resources.put(key, value);
246: } else {
247: resources.remove(key);
248: }
249: }
250:
251: public void commit() throws RollbackException {
252: try {
253: if (status == Status.STATUS_MARKED_ROLLBACK) {
254: rollback();
255: throw new RollbackException();
256: }
257: try {
258: doBeforeCompletion();
259: } catch (Exception e) {
260: rollback();
261: throw (RollbackException) new RollbackException()
262: .initCause(e);
263: }
264: doXAResources(Status.STATUS_COMMITTED);
265: status = Status.STATUS_COMMITTED;
266: doAfterCompletion(Status.STATUS_COMMITTED);
267: } finally {
268: threadTransaction.set(null);
269: }
270: }
271:
272: public void rollback() {
273: try {
274: doXAResources(Status.STATUS_ROLLEDBACK);
275: doAfterCompletion(Status.STATUS_ROLLEDBACK);
276: status = Status.STATUS_ROLLEDBACK;
277: registeredSynchronizations.clear();
278: } finally {
279: threadTransaction.set(null);
280: }
281: }
282:
283: private void doBeforeCompletion() {
284: for (Synchronization sync : new ArrayList<Synchronization>(
285: registeredSynchronizations)) {
286: sync.beforeCompletion();
287: }
288: }
289:
290: private void doAfterCompletion(int status) {
291: for (Synchronization sync : new ArrayList<Synchronization>(
292: registeredSynchronizations)) {
293: try {
294: sync.afterCompletion(status);
295: } catch (RuntimeException e) {
296: logger
297: .warning(
298: "Synchronization afterCompletion threw a RuntimeException",
299: e);
300: }
301: }
302: }
303:
304: private void doXAResources(int status) {
305: for (XAResource xaRes : new ArrayList<XAResource>(
306: xaResources)) {
307: if (status == Status.STATUS_COMMITTED) {
308: try {
309: xaRes.commit(null, true);
310: } catch (XAException e) {
311:
312: }
313: try {
314: xaRes.end(null, XAResource.TMSUCCESS);
315: } catch (XAException e) {
316:
317: }
318: } else {
319: try {
320: xaRes.rollback(null);
321: } catch (XAException e) {
322:
323: }
324: try {
325: xaRes.end(null, XAResource.TMFAIL);
326: } catch (XAException e) {
327: }
328: }
329: }
330: xaResources.clear();
331: }
332: }
333: }
|