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.core.transaction;
18
19 import org.apache.openejb.ApplicationException;
20 import org.apache.openejb.InvalidateReferenceException;
21 import org.apache.openejb.SystemException;
22 import org.apache.openejb.core.ThreadContext;
23 import org.apache.openejb.core.Operation;
24 import org.apache.openejb.util.LogCategory;
25 import org.apache.openejb.util.Logger;
26
27 import javax.transaction;
28 import javax.ejb.EJBException;
29 import javax.ejb.EJBTransactionRolledbackException;
30
31 import java.rmi.RemoteException;
32
33 public abstract class TransactionPolicy {
34 public Type getPolicyType() {
35 return policyType;
36 }
37
38 public static enum Type {
39 Mandatory,
40 Never,
41 NotSupported,
42 Required,
43 RequiresNew,
44 Supports,
45 BeanManaged;
46 }
47
48
49 private final Type policyType;
50 protected final TransactionContainer container;
51 private TransactionManager manager;
52
53 protected final static Logger logger = Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");
54 protected final static Logger txLogger = Logger.getInstance(LogCategory.TRANSACTION, "org.apache.openejb.util.resources");
55
56 public TransactionPolicy(Type policyType, TransactionContainer container) {
57 this.policyType = policyType;
58 this.container = container;
59 }
60
61 public TransactionContainer getContainer() {
62 return container;
63 }
64
65 public String policyToString() {
66 return policyType.toString();
67 }
68
69 public abstract void handleApplicationException(Throwable appException, boolean rollback, TransactionContext context) throws ApplicationException, SystemException;
70
71 public abstract void handleSystemException(Throwable sysException, Object instance, TransactionContext context) throws ApplicationException, SystemException;
72
73 public abstract void beforeInvoke(Object bean, TransactionContext context) throws SystemException, ApplicationException;
74
75 public abstract void afterInvoke(Object bean, TransactionContext context) throws ApplicationException, SystemException;
76
77 protected void markTxRollbackOnly(Transaction tx) throws SystemException {
78 try {
79 if (tx != null) {
80 tx.setRollbackOnly();
81 if (txLogger.isInfoEnabled()) {
82 txLogger.info("TX " + policyToString() + ": setRollbackOnly() on transaction " + tx);
83 }
84 }
85 } catch (javax.transaction.SystemException se) {
86 logger.error("Exception during setRollbackOnly()", se);
87 throw new SystemException(se);
88 }
89 }
90
91 protected Transaction suspendTransaction(TransactionContext context) throws SystemException {
92 try {
93 Transaction tx = context.getTransactionManager().suspend();
94 if (txLogger.isInfoEnabled()) {
95 txLogger.info("TX " + policyToString() + ": Suspended transaction " + tx);
96 }
97 return tx;
98 } catch (javax.transaction.SystemException se) {
99 logger.error("Exception during suspend()", se);
100 throw new SystemException(se);
101 }
102 }
103
104 protected void resumeTransaction(TransactionContext context, Transaction tx) throws SystemException {
105 try {
106 if (tx == null) {
107 if (txLogger.isInfoEnabled()) {
108 txLogger.info("TX " + policyToString() + ": No transaction to resume");
109 }
110 } else {
111 if (txLogger.isInfoEnabled()) {
112 txLogger.info("TX " + policyToString() + ": Resuming transaction " + tx);
113 }
114 context.getTransactionManager().resume(tx);
115 }
116 } catch (InvalidTransactionException ite) {
117
118 txLogger.error("Could not resume the client's transaction, the transaction is no longer valid: " + ite.getMessage());
119 throw new SystemException(ite);
120 } catch (IllegalStateException e) {
121
122 txLogger.error("Could not resume the client's transaction: " + e.getMessage());
123 throw new SystemException(e);
124 } catch (javax.transaction.SystemException e) {
125
126 txLogger.error("Could not resume the client's transaction: The transaction reported a system exception: " + e.getMessage());
127 throw new SystemException(e);
128 }
129 }
130
131 protected void commitTransaction(TransactionContext context, Transaction tx) throws SystemException, ApplicationException {
132 try {
133 if (txLogger.isInfoEnabled()) {
134 txLogger.info("TX " + policyToString() + ": Committing transaction " + tx);
135 }
136 if (tx.equals(context.getTransactionManager().getTransaction())) {
137
138 context.getTransactionManager().commit();
139 } else {
140 tx.commit();
141 }
142 } catch (RollbackException e) {
143
144 txLogger.info("The transaction has been rolled back rather than commited: " + e.getMessage());
145 // TODO can't set initCause on a TransactionRolledbackException, update the convertException and related code to handle something else
146 Throwable txe = new javax.transaction.TransactionRolledbackException("Transaction was rolled back, presumably because setRollbackOnly was called during a synchronization: "+e.getMessage());
147 throw new ApplicationException(txe);
148
149 } catch (HeuristicMixedException e) {
150
151 txLogger.info("A heuristic decision was made, some relevant updates have been committed while others have been rolled back: " + e.getMessage());
152 throw new ApplicationException(new RemoteException("A heuristic decision was made, some relevant updates have been committed while others have been rolled back").initCause(e));
153
154 } catch (HeuristicRollbackException e) {
155
156 txLogger.info("A heuristic decision was made while commiting the transaction, some relevant updates have been rolled back: " + e.getMessage());
157 throw new ApplicationException(new RemoteException("A heuristic decision was made while commiting the transaction, some relevant updates have been rolled back").initCause(e));
158
159 } catch (SecurityException e) {
160
161 txLogger.error("The current thread is not allowed to commit the transaction: " + e.getMessage());
162 throw new SystemException(e);
163
164 } catch (IllegalStateException e) {
165
166 txLogger.error("The current thread is not associated with a transaction: " + e.getMessage());
167 throw new SystemException(e);
168
169 } catch (javax.transaction.SystemException e) {
170 txLogger.error("The Transaction Manager has encountered an unexpected error condition while attempting to commit the transaction: " + e.getMessage());
171
172 throw new SystemException(e);
173 }
174 }
175
176 protected void rollbackTransaction(TransactionContext context, Transaction tx) throws SystemException {
177 try {
178 if (txLogger.isInfoEnabled()) {
179 txLogger.info("TX " + policyToString() + ": Rolling back transaction " + tx);
180 }
181 if (tx.equals(context.getTransactionManager().getTransaction())) {
182
183 context.getTransactionManager().rollback();
184 } else {
185 tx.rollback();
186 }
187 } catch (IllegalStateException e) {
188
189 logger.error("The TransactionManager reported an exception while attempting to rollback the transaction: " + e.getMessage());
190 throw new SystemException(e);
191
192 } catch (javax.transaction.SystemException e) {
193
194 logger.error("The TransactionManager reported an exception while attempting to rollback the transaction: " + e.getMessage());
195 throw new SystemException(e);
196 }
197 }
198
199 protected void throwAppExceptionToServer(Throwable appException) throws ApplicationException {
200 throw new ApplicationException(appException);
201 }
202
203 protected void throwTxExceptionToServer(Throwable sysException) throws ApplicationException {
204 /* Throw javax.transaction.TransactionRolledbackException to remote client */
205
206 String message = "The transaction has been marked rollback only because the bean encountered a non-application exception :" + sysException.getClass().getName() + " : " + sysException.getMessage();
207 TransactionRolledbackException txException = new TransactionRolledbackException(message, sysException);
208
209 throw new InvalidateReferenceException(txException);
210
211 }
212
213 protected void throwExceptionToServer(Throwable sysException) throws ApplicationException {
214
215 RemoteException re = new RemoteException("The bean encountered a non-application exception.", sysException);
216
217 throw new InvalidateReferenceException(re);
218
219 }
220
221 protected void logSystemException(Throwable sysException, TransactionContext context) {
222 Operation operation = context.callContext.getCurrentOperation();
223 if (operation != null && operation.isCallback()){
224 logger.error("startup.beanInstanceSystemExceptionThrown", sysException, sysException.getMessage());
225 } else {
226 logger.debug("startup.beanInstanceSystemExceptionThrown", sysException, sysException.getMessage());
227 }
228 }
229
230 protected void discardBeanInstance(Object instance, ThreadContext callContext) {
231 container.discardInstance(instance, callContext);
232 }
233
234 protected void beginTransaction(TransactionContext context) throws javax.transaction.SystemException {
235 try {
236 context.getTransactionManager().begin();
237 if (txLogger.isInfoEnabled()) {
238 txLogger.info("TX " + policyToString() + ": Started transaction " + context.getTransactionManager().getTransaction());
239 }
240 } catch (NotSupportedException nse) {
241 logger.error("", nse);
242 }
243 }
244
245 protected void handleCallbackException() {
246 }
247 }
248