1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.openejb.ri.sp; 18 19 import java.util.ArrayList; 20 import java.util.Collections; 21 import java.util.HashMap; 22 import java.util.List; 23 import java.util.Map; 24 import java.util.Properties; 25 import javax.transaction.InvalidTransactionException; 26 import javax.transaction.NotSupportedException; 27 import javax.transaction.RollbackException; 28 import javax.transaction.Status; 29 import javax.transaction.Synchronization; 30 import javax.transaction.Transaction; 31 import javax.transaction.TransactionManager; 32 import javax.transaction.TransactionSynchronizationRegistry; 33 import javax.transaction.xa.XAException; 34 import javax.transaction.xa.XAResource; 35 36 import org.apache.openejb.spi.TransactionService; 37 import org.apache.openejb.util.LogCategory; 38 import org.apache.openejb.util.Logger; 39 40 /** 41 * @org.apache.xbean.XBean element="pseudoTransactionService" 42 */ 43 public class PseudoTransactionService implements TransactionService, TransactionManager, TransactionSynchronizationRegistry { 44 private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.core.cmp"); 45 private final ThreadLocal<MyTransaction> threadTransaction = new ThreadLocal<MyTransaction>(); 46 47 public void init(Properties props) { 48 } 49 50 public TransactionManager getTransactionManager() { 51 return this; 52 } 53 54 public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { 55 return this; 56 } 57 58 public int getStatus() { 59 MyTransaction tx = threadTransaction.get(); 60 if (tx == null) { 61 return Status.STATUS_NO_TRANSACTION; 62 } 63 return tx.getStatus(); 64 } 65 66 public Transaction getTransaction() { 67 return threadTransaction.get(); 68 } 69 70 public boolean getRollbackOnly() { 71 MyTransaction tx = threadTransaction.get(); 72 if (tx == null) { 73 throw new IllegalStateException("No transaction active"); 74 } 75 return tx.getRollbackOnly(); 76 } 77 78 public void setRollbackOnly() { 79 MyTransaction tx = threadTransaction.get(); 80 if (tx == null) { 81 throw new IllegalStateException("No transaction active"); 82 } 83 tx.setRollbackOnly(); 84 } 85 86 public void begin() throws NotSupportedException { 87 if (threadTransaction.get() != null) { 88 throw new NotSupportedException("A transaction is already active"); 89 } 90 91 MyTransaction tx = new MyTransaction(); 92 threadTransaction.set(tx); 93 } 94 95 public void commit() throws RollbackException { 96 MyTransaction tx = threadTransaction.get(); 97 if (tx == null) { 98 throw new IllegalStateException("No transaction active"); 99 } 100 101 try { 102 tx.commit(); 103 } finally { 104 threadTransaction.set(null); 105 } 106 } 107 108 109 public void rollback() { 110 MyTransaction tx = threadTransaction.get(); 111 if (tx == null) { 112 throw new IllegalStateException("No transaction active"); 113 } 114 115 try { 116 tx.rollback(); 117 } finally { 118 threadTransaction.set(null); 119 } 120 } 121 122 public Transaction suspend() { 123 return threadTransaction.get(); 124 } 125 126 public void resume(Transaction tx) throws InvalidTransactionException { 127 if (tx == null) { 128 throw new InvalidTransactionException("Transaction is null"); 129 } 130 if (!(tx instanceof MyTransaction)) { 131 throw new InvalidTransactionException("Unknown transaction type " + tx.getClass().getName()); 132 } 133 MyTransaction myTransaction = (MyTransaction) tx; 134 135 if (threadTransaction.get() != null) { 136 throw new IllegalStateException("A transaction is already active"); 137 } 138 139 int status = myTransaction.getStatus(); 140 if (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK) { 141 throw new InvalidTransactionException("Expected transaction to be STATUS_ACTIVE or STATUS_MARKED_ROLLBACK, but was " + status); 142 } 143 144 threadTransaction.set(myTransaction); 145 } 146 147 public Object getTransactionKey() { 148 return getTransaction(); 149 } 150 151 public int getTransactionStatus() { 152 return getStatus(); 153 } 154 155 public Object getResource(Object key) { 156 MyTransaction tx = threadTransaction.get(); 157 if (tx == null) { 158 throw new IllegalStateException("No transaction active"); 159 } 160 161 Object value = tx.getResource(key); 162 return value; 163 } 164 165 public void putResource(Object key, Object value) { 166 MyTransaction tx = threadTransaction.get(); 167 if (tx == null) { 168 throw new IllegalStateException("No transaction active"); 169 } 170 171 tx.putResource(key, value); 172 } 173 174 public void registerInterposedSynchronization(Synchronization synchronization) { 175 MyTransaction tx = threadTransaction.get(); 176 if (tx == null) { 177 throw new IllegalStateException("No transaction active"); 178 } 179 180 tx.registerInterposedSynchronization(synchronization); 181 } 182 183 public void setTransactionTimeout(int seconds) { 184 } 185 186 public class MyTransaction implements Transaction { 187 private final List<Synchronization> registeredSynchronizations = Collections.synchronizedList(new ArrayList<Synchronization>()); 188 private final List<XAResource> xaResources = Collections.synchronizedList(new ArrayList<XAResource>()); 189 private final Map<Object, Object> resources = new HashMap<Object,Object>(); 190 private int status = Status.STATUS_ACTIVE; 191 192 public boolean delistResource(XAResource xaRes, int flag) { 193 xaResources.remove(xaRes); 194 return true; 195 } 196 197 public boolean enlistResource(XAResource xaRes) { 198 xaResources.add(xaRes); 199 return true; 200 } 201 202 public int getStatus() { 203 return status; 204 } 205 206 public void registerSynchronization(Synchronization synchronization) { 207 registeredSynchronizations.add(synchronization); 208 } 209 210 public void registerInterposedSynchronization(Synchronization synchronization) { 211 registeredSynchronizations.add(synchronization); 212 } 213 214 public boolean getRollbackOnly() { 215 return status == Status.STATUS_MARKED_ROLLBACK; 216 } 217 218 public void setRollbackOnly() { 219 status = Status.STATUS_MARKED_ROLLBACK; 220 } 221 222 public Object getResource(Object key) { 223 if (key == null) throw new NullPointerException("key is null"); 224 return resources.get(key); 225 } 226 227 public void putResource(Object key, Object value) { 228 if (key == null) throw new NullPointerException("key is null"); 229 if (value != null) { 230 resources.put(key, value); 231 } else { 232 resources.remove(key); 233 } 234 } 235 236 public void commit() throws RollbackException { 237 try { 238 if (status == Status.STATUS_MARKED_ROLLBACK) { 239 rollback(); 240 throw new RollbackException(); 241 } 242 try { 243 doBeforeCompletion(); 244 } catch (Exception e) { 245 rollback(); 246 throw (RollbackException) new RollbackException().initCause(e); 247 } 248 doXAResources(Status.STATUS_COMMITTED); 249 status = Status.STATUS_COMMITTED; 250 doAfterCompletion(Status.STATUS_COMMITTED); 251 } finally { 252 threadTransaction.set(null); 253 } 254 } 255 256 public void rollback() { 257 try { 258 doXAResources(Status.STATUS_ROLLEDBACK); 259 doAfterCompletion(Status.STATUS_ROLLEDBACK); 260 status = Status.STATUS_ROLLEDBACK; 261 registeredSynchronizations.clear(); 262 } finally { 263 threadTransaction.set(null); 264 } 265 } 266 267 private void doBeforeCompletion() { 268 for (Synchronization sync : new ArrayList<Synchronization>(registeredSynchronizations)) { 269 sync.beforeCompletion(); 270 } 271 } 272 273 private void doAfterCompletion(int status) { 274 for (Synchronization sync : new ArrayList<Synchronization>(registeredSynchronizations)) { 275 try { 276 sync.afterCompletion(status); 277 } catch (RuntimeException e) { 278 logger.warning("Synchronization afterCompletion threw a RuntimeException", e); 279 } 280 } 281 } 282 283 private void doXAResources(int status) { 284 for (XAResource xaRes : new ArrayList<XAResource>(xaResources)) { 285 if (status == Status.STATUS_COMMITTED) { 286 try { 287 xaRes.commit(null, true); 288 } catch (XAException e) { 289 290 } 291 try { 292 xaRes.end(null, XAResource.TMSUCCESS); 293 } catch (XAException e) { 294 295 } 296 } else { 297 try { 298 xaRes.rollback(null); 299 } catch (XAException e) { 300 301 } 302 try { 303 xaRes.end(null, XAResource.TMFAIL); 304 } catch (XAException e) { 305 } 306 } 307 } 308 xaResources.clear(); 309 } 310 } 311 } 312