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