001 package org.apache.ojb.odmg;
002
003 /* Copyright 2002-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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 */
017
018 import org.apache.ojb.broker.OJBRuntimeException;
019 import org.apache.ojb.broker.util.configuration.Configuration;
020 import org.apache.ojb.broker.util.logging.Logger;
021 import org.apache.ojb.broker.util.logging.LoggerFactory;
022 import org.apache.ojb.broker.transaction.tm.TransactionManagerFactoryException;
023 import org.apache.ojb.broker.transaction.tm.TransactionManagerFactoryFactory;
024 import org.apache.commons.lang.SystemUtils;
025 import org.odmg.TransactionNotInProgressException;
026
027 import javax.transaction.Status;
028 import javax.transaction.SystemException;
029 import javax.transaction.Transaction;
030 import javax.transaction.TransactionManager;
031 import java.lang.ref.WeakReference;
032
033 /**
034 * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
035 *
036 * In an app server environment, where we have a transaction manager, we
037 * use the transactionmanager and it's associated JTA transaction to associate
038 * with the ODMG transaction. So the key is retrieved by calling getTransaction
039 * on the transactionManager
040 */
041 public class JTATxManager implements OJBTxManager
042 {
043 private Logger log = LoggerFactory.getLogger(JTATxManager.class);
044 private static ThreadLocal txRepository = new ThreadLocal();
045
046 /**
047 * Remove the ODMG transaction from the transaction buffer
048 * ODMG transactions are associated with JTA transactions via a map
049 */
050 public void deregisterTx(Object transaction)
051 {
052 // TxBuffer buf = (TxBuffer) txRepository.get();
053 // if (buf != null)
054 // {
055 // buf.setInternTx(null);
056 // }
057 txRepository.set(null);
058 }
059
060 public void registerTx(TransactionImpl odmgTrans)
061 {
062 if (log.isDebugEnabled()) log.debug("registerSynchronization was called");
063 Transaction transaction = null;
064 try
065 {
066 transaction = getJTATransaction();
067 }
068 catch (SystemException e)
069 {
070 log.error("Obtain current transaction from container failed", e);
071 }
072 if (transaction == null)
073 {
074 log.error("Cannot get the external transaction from the external TM");
075 throw new TransactionNotInProgressException("No external transaction found");
076 }
077 if (log.isDebugEnabled())
078 {
079 log.debug("registerSynchronization was called with parameters"
080 + SystemUtils.LINE_SEPARATOR +"J2EETransactionImpl: " + odmgTrans
081 + SystemUtils.LINE_SEPARATOR + "Transaction: " + transaction);
082 }
083 registerSynchronization(odmgTrans, transaction);
084 }
085
086 /**
087 * Do synchronization of the given J2EE ODMG Transaction
088 */
089 private void registerSynchronization(TransactionImpl odmgTrans, Transaction transaction)
090 {
091 // todo only need for development
092 if (odmgTrans == null || transaction == null)
093 {
094 log.error("One of the given parameters was null --> cannot do synchronization!" +
095 " omdg transaction was null: " + (odmgTrans == null) +
096 ", external transaction was null: " + (transaction == null));
097 return;
098 }
099
100 int status = -1; // default status.
101 try
102 {
103 status = transaction.getStatus();
104 if (status != Status.STATUS_ACTIVE)
105 {
106 throw new OJBRuntimeException(
107 "Transaction synchronization failed - wrong status of external container tx: " +
108 getStatusString(status));
109 }
110 }
111 catch (SystemException e)
112 {
113 throw new OJBRuntimeException("Can't read status of external tx", e);
114 }
115
116 try
117 {
118 //Sequence of the following method calls is significant
119 // 1. register the synchronization with the ODMG notion of a transaction.
120 transaction.registerSynchronization((J2EETransactionImpl) odmgTrans);
121 // 2. mark the ODMG transaction as being in a JTA Transaction
122 // Associate external transaction with the odmg transaction.
123 txRepository.set(new TxBuffer(odmgTrans, transaction));
124 }
125 catch (Exception e)
126 {
127 log.error("Cannot associate PersistenceBroker with running Transaction", e);
128 throw new OJBRuntimeException(
129 "Transaction synchronization failed - wrong status of external container tx", e);
130 }
131 }
132
133 private static String getStatusString(int status)
134 {
135 switch (status)
136 {
137 case Status.STATUS_ACTIVE:
138 return "STATUS_ACTIVE";
139 case Status.STATUS_COMMITTED:
140 return "STATUS_COMMITTED";
141 case Status.STATUS_COMMITTING:
142 return "STATUS_COMMITTING";
143 case Status.STATUS_MARKED_ROLLBACK:
144 return "STATUS_MARKED_ROLLBACK";
145 case Status.STATUS_NO_TRANSACTION:
146 return "STATUS_NO_TRANSACTION";
147 case Status.STATUS_PREPARED:
148 return "STATUS_PREPARED";
149 case Status.STATUS_PREPARING:
150 return "STATUS_PREPARING";
151 case Status.STATUS_ROLLEDBACK:
152 return "STATUS_ROLLEDBACK";
153 case Status.STATUS_ROLLING_BACK:
154 return "STATUS_ROLLING_BACK";
155 case Status.STATUS_UNKNOWN:
156 return "STATUS_UNKNOWN";
157 default:
158 return "NO STATUS FOUND";
159 }
160 }
161
162 /**
163 * Return the TransactionManager of the external app
164 */
165 private TransactionManager getTransactionManager()
166 {
167 TransactionManager retval = null;
168 try
169 {
170 if (log.isDebugEnabled()) log.debug("getTransactionManager called");
171 retval = TransactionManagerFactoryFactory.instance().getTransactionManager();
172 }
173 catch (TransactionManagerFactoryException e)
174 {
175 log.warn("Exception trying to obtain TransactionManager from Factory", e);
176 e.printStackTrace();
177 }
178 return retval;
179 }
180
181 public Transaction getJTATransaction() throws SystemException
182 {
183 if (log.isDebugEnabled()) log.debug("getTransaction called");
184 if (getTransactionManager() == null)
185 {
186 log.warn("TransactionManager was null");
187 return null;
188 }
189 return getTransactionManager().getTransaction();
190 }
191
192 /**
193 * Returns the current transaction based on the JTA Transaction.
194 * @throws org.odmg.TransactionNotInProgressException if no transaction was found.
195 */
196 public TransactionImpl getCurrentTransaction()
197 {
198 TransactionImpl retval = getTransaction();
199 if (null == retval)
200 {
201 throw new TransactionNotInProgressException(
202 "Calling method needed transaction, but no transaction found via TransactionManager");
203 }
204 return retval;
205 }
206
207 /**
208 * Returns the current transaction based on the JTA Transaction or <code>null</code>
209 * if no transaction was found.
210 */
211 public TransactionImpl getTransaction()
212 {
213 TxBuffer buf = (TxBuffer) txRepository.get();
214 return buf != null ? buf.getInternTx() : null;
215 }
216
217 /**
218 * Abort an active extern transaction associated with the given PB.
219 */
220 public void abortExternalTx(TransactionImpl odmgTrans)
221 {
222 if (log.isDebugEnabled()) log.debug("abortExternTransaction was called");
223 if (odmgTrans == null) return;
224 TxBuffer buf = (TxBuffer) txRepository.get();
225 Transaction extTx = buf != null ? buf.getExternTx() : null;
226 try
227 {
228 if (extTx != null && extTx.getStatus() == Status.STATUS_ACTIVE)
229 {
230 if(log.isDebugEnabled())
231 {
232 log.debug("Set extern transaction to rollback");
233 }
234 extTx.setRollbackOnly();
235 }
236 }
237 catch (Exception ignore)
238 {
239 }
240 txRepository.set(null);
241 }
242
243 public void configure(Configuration config)
244 {
245 /**
246 * no-op
247 */
248 }
249
250
251 //************************************************************************
252 // inner class
253 //************************************************************************
254 private static final class TxBuffer
255 {
256 private WeakReference externTx = null;
257 private WeakReference internTx = null;
258
259 public TxBuffer()
260 {
261 }
262
263 /*
264 arminw:
265 use WeakReference to make sure that closed Transaction objects can be
266 immediately reclaimed by the garbage collector.
267 */
268
269 public TxBuffer(TransactionImpl internTx, Transaction externTx)
270 {
271 this.internTx = new WeakReference(internTx);
272 this.externTx = new WeakReference(externTx);
273 }
274
275 public Transaction getExternTx()
276 {
277 return (Transaction) externTx.get();
278 }
279
280 public void setExternTx(Transaction externTx)
281 {
282 this.externTx = new WeakReference(externTx);
283 }
284
285 public TransactionImpl getInternTx()
286 {
287 return (TransactionImpl) internTx.get();
288 }
289
290 public void setInternTx(TransactionImpl internTx)
291 {
292 this.internTx = new WeakReference(internTx);
293 }
294 }
295 }