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 javax.transaction.Status;
019 import javax.transaction.Synchronization;
020
021 import org.apache.ojb.broker.PersistenceBroker;
022 import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
023 import org.apache.ojb.broker.util.logging.Logger;
024 import org.apache.ojb.broker.util.logging.LoggerFactory;
025 import org.odmg.LockNotGrantedException;
026 import org.odmg.ODMGRuntimeException;
027 import org.odmg.TransactionAbortedException;
028
029 /**
030 * Implementation for use in managed environments.
031 *
032 * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
033 * @version $Id: J2EETransactionImpl.java,v 1.1 2007-08-24 22:17:37 ewestfal Exp $
034 */
035 public class J2EETransactionImpl extends TransactionImpl implements Synchronization
036 {
037 private Logger log = LoggerFactory.getLogger(J2EETransactionImpl.class);
038 private boolean isInExternTransaction;
039
040 /**
041 * beforeCompletion is being called twice in JBoss, so this
042 * isPrepared flag prevents code from executing twice.
043 * todo: find out why it's being called twice and fix it.
044 */
045 private boolean beforeCompletionCall = false;
046 private boolean afterCompletionCall = false;
047
048 public J2EETransactionImpl(ImplementationImpl implementation)
049 {
050 super(implementation);
051 isInExternTransaction = false;
052 }
053
054 public void setInExternTransaction(boolean mode)
055 {
056 isInExternTransaction = mode;
057 }
058
059 public boolean isInExternTransaction()
060 {
061 return isInExternTransaction;
062 }
063
064 public void join()
065 {
066 throw new UnsupportedOperationException("Not supported in managed enviroment");
067 }
068
069 public void leave()
070 {
071 throw new UnsupportedOperationException("Not supported in managed enviroment");
072 }
073
074 public void checkpoint()
075 {
076 throw new UnsupportedOperationException("Not supported in managed enviroment");
077 }
078
079 /**
080 * FOR internal use. This method was called after the external transaction was completed.
081 *
082 * @see javax.transaction.Synchronization
083 */
084 public void afterCompletion(int status)
085 {
086 if(afterCompletionCall) return;
087
088 log.info("Method afterCompletion was called");
089 try
090 {
091 switch(status)
092 {
093 case Status.STATUS_COMMITTED:
094 if(log.isDebugEnabled())
095 {
096 log.debug("Method afterCompletion: Do commit internal odmg-tx, status of JTA-tx is " + TxUtil.getStatusString(status));
097 }
098 commit();
099 break;
100 default:
101 log.error("Method afterCompletion: Do abort call on internal odmg-tx, status of JTA-tx is " + TxUtil.getStatusString(status));
102 abort();
103 }
104 }
105 finally
106 {
107 afterCompletionCall = true;
108 log.info("Method afterCompletion finished");
109 }
110 }
111
112 /**
113 * FOR internal use. This method was called before the external transaction was completed.
114 *
115 * This method was called by the JTA-TxManager before the JTA-tx prepare call. Within this method
116 * we prepare odmg for commit and pass all modified persistent objects to DB and release/close the used
117 * connection. We have to close the connection in this method, because the TxManager does prepare for commit
118 * after this method and all used DataSource-connections have to be closed before.
119 *
120 * @see javax.transaction.Synchronization
121 */
122 public void beforeCompletion()
123 {
124 // avoid redundant calls
125 if(beforeCompletionCall) return;
126
127 log.info("Method beforeCompletion was called");
128 int status = Status.STATUS_UNKNOWN;
129 try
130 {
131 JTATxManager mgr = (JTATxManager) getImplementation().getTxManager();
132 status = mgr.getJTATransaction().getStatus();
133 // ensure proper work, check all possible status
134 // normally only check for 'STATUS_MARKED_ROLLBACK' is necessary
135 if(status == Status.STATUS_MARKED_ROLLBACK
136 || status == Status.STATUS_ROLLEDBACK
137 || status == Status.STATUS_ROLLING_BACK
138 || status == Status.STATUS_UNKNOWN
139 || status == Status.STATUS_NO_TRANSACTION)
140 {
141 log.error("Synchronization#beforeCompletion: Can't prepare for commit, because tx status was "
142 + TxUtil.getStatusString(status) + ". Do internal cleanup only.");
143 }
144 else
145 {
146 if(log.isDebugEnabled())
147 {
148 log.debug("Synchronization#beforeCompletion: Prepare for commit");
149 }
150 // write objects to database
151 prepareCommit();
152 }
153 }
154 catch(Exception e)
155 {
156 log.error("Synchronization#beforeCompletion: Error while prepare for commit", e);
157 if(e instanceof LockNotGrantedException)
158 {
159 throw (LockNotGrantedException) e;
160 }
161 else if(e instanceof TransactionAbortedException)
162 {
163 throw (TransactionAbortedException) e;
164 }
165 else if(e instanceof ODMGRuntimeException)
166 {
167 throw (ODMGRuntimeException) e;
168 }
169 else
170 {
171 throw new ODMGRuntimeException("Method beforeCompletion() fails, status of JTA-tx was "
172 + TxUtil.getStatusString(status) + ", message: " + e.getMessage());
173 }
174
175 }
176 finally
177 {
178 beforeCompletionCall = true;
179 setInExternTransaction(false);
180 internalCleanup();
181 }
182 }
183
184 /**
185 * In managed environment do internal close the used connection
186 */
187 private void internalCleanup()
188 {
189 if(hasBroker())
190 {
191 PersistenceBroker broker = getBroker();
192 if(log.isDebugEnabled())
193 {
194 log.debug("Do internal cleanup and close the internal used connection without" +
195 " closing the used broker");
196 }
197 ConnectionManagerIF cm = broker.serviceConnectionManager();
198 if(cm.isInLocalTransaction())
199 {
200 /*
201 arminw:
202 in managed environment this call will be ignored because, the JTA transaction
203 manager control the connection status. But to make connectionManager happy we
204 have to complete the "local tx" of the connectionManager before release the
205 connection
206 */
207 cm.localCommit();
208 }
209 cm.releaseConnection();
210 }
211 }
212
213 public void commit()
214 {
215 try
216 {
217 // prepare for commit was done before on 'beforeCompleation' call
218 if(log.isDebugEnabled()) log.debug("Commit transaction " + this + ", commit on broker " + broker);
219 if(hasBroker())
220 {
221 getBroker().commitTransaction();
222 doClose();
223 }
224 setStatus(Status.STATUS_COMMITTED);
225 // Now, we notify everything the commit is done.
226 performTransactionAwareAfterCommit();
227 }
228 catch(Exception ex)
229 {
230 // We should not reach this block
231 log.error("Unexpected error while do commit on used PB-Instance and close resources", ex);
232 abort();
233 }
234 }
235
236 public void abort()
237 {
238 if(getStatus() == Status.STATUS_ROLLEDBACK) return;
239
240 try
241 {
242 try
243 {
244 doAbort();
245 }
246 catch(Exception ignore)
247 {
248 log.error("Failure while do abort call", ignore);
249 }
250
251 getImplementation().getTxManager().abortExternalTx(this);
252
253 try
254 {
255 doClose();
256 }
257 catch(Exception e)
258 {
259 log.error("Failure while do abort call", e);
260 }
261 setStatus(Status.STATUS_ROLLEDBACK);
262 }
263 finally
264 {
265 setInExternTransaction(false);
266 }
267 }
268 }