001: /*
002: * Copyright 2004-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.compass.gps.device.hibernate;
018:
019: import javax.transaction.Status;
020: import javax.transaction.Synchronization;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.compass.core.CompassException;
025: import org.compass.core.CompassSession;
026: import org.compass.core.spi.InternalCompassSession;
027: import org.compass.core.transaction.AbstractTransaction;
028: import org.compass.core.transaction.TransactionException;
029: import org.compass.core.transaction.TransactionFactory;
030: import org.hibernate.SessionFactory;
031: import org.hibernate.Transaction;
032: import org.hibernate.engine.SessionImplementor;
033:
034: /**
035: * @author kimchy
036: */
037: public class HibernateSyncTransaction extends AbstractTransaction {
038:
039: private static final Log log = LogFactory
040: .getLog(HibernateSyncTransaction.class);
041:
042: private SessionFactory sessionFactory;
043:
044: /**
045: * Did we start the Hibernate transaction
046: */
047: private boolean newTransaction;
048:
049: /**
050: * Is this the up most level controlling the Compass transaction
051: */
052: private boolean controllingNewTransaction = false;
053:
054: private InternalCompassSession session;
055:
056: private boolean commitFailed;
057:
058: private boolean commitBeforeCompletion;
059:
060: private Transaction transaction;
061:
062: public HibernateSyncTransaction(SessionFactory sessionFactory,
063: boolean commitBeforeCompletion,
064: TransactionFactory transactionFactory) {
065: super (transactionFactory);
066: this .sessionFactory = sessionFactory;
067: this .commitBeforeCompletion = commitBeforeCompletion;
068: }
069:
070: public void begin(InternalCompassSession session,
071: TransactionIsolation transactionIsolation)
072: throws CompassException {
073: this .session = session;
074: try {
075: controllingNewTransaction = true;
076: newTransaction = !((SessionImplementor) sessionFactory
077: .getCurrentSession()).isTransactionInProgress();
078: transaction = sessionFactory.getCurrentSession()
079: .getTransaction();
080: if (newTransaction) {
081: if (log.isDebugEnabled()) {
082: log
083: .debug("Beginning new Hibernate transaction, and a new compass transaction on thread ["
084: + Thread.currentThread().getName()
085: + "] with isolation ["
086: + transactionIsolation + "]");
087: }
088: session.getSearchEngine().begin(transactionIsolation);
089: transaction.begin();
090: } else {
091: // joining an exisiting transaction
092: session.getSearchEngine().begin(transactionIsolation);
093: if (log.isDebugEnabled()) {
094: log
095: .debug("Joining an existing Hibernate transaction, starting a new compass transaction on thread ["
096: + Thread.currentThread().getName()
097: + "] with isolation ["
098: + transactionIsolation + "]");
099: }
100: }
101: transaction
102: .registerSynchronization(new HibernateTransactionSynchronization(
103: session, transaction, newTransaction,
104: commitBeforeCompletion, transactionFactory));
105: } catch (Exception e) {
106: throw new TransactionException(
107: "Begin failed with exception", e);
108: }
109: setBegun(true);
110: }
111:
112: /**
113: * Called by the factory when joining an already running compass transaction
114: */
115: public void join(InternalCompassSession session)
116: throws CompassException {
117: this .session = session;
118: controllingNewTransaction = false;
119: if (log.isDebugEnabled()) {
120: log
121: .debug("Joining an existing compass transcation on thread ["
122: + Thread.currentThread().getName() + "]");
123: }
124: }
125:
126: protected void doCommit() throws CompassException {
127:
128: if (!controllingNewTransaction) {
129: if (log.isDebugEnabled()) {
130: log
131: .debug("Not committing Hibernate transaction since compass does not control it on thread ["
132: + Thread.currentThread().getName()
133: + "]");
134: }
135: return;
136: }
137:
138: if (newTransaction) {
139: if (log.isDebugEnabled()) {
140: log
141: .debug("Committing Hibernate transaction controlled by compass on thread ["
142: + Thread.currentThread().getName()
143: + "]");
144: }
145: try {
146: transaction.commit();
147: } catch (Exception e) {
148: commitFailed = true;
149: // so the transaction is already rolled back, by Hibernate spec
150: throw new TransactionException("Commit failed", e);
151: }
152: } else {
153: if (log.isDebugEnabled()) {
154: log
155: .debug("Commit called, let Hibernate synchronization commit the transaciton on thread ["
156: + Thread.currentThread().getName()
157: + "]");
158: }
159: }
160: }
161:
162: protected void doRollback() throws CompassException {
163:
164: try {
165: if (newTransaction) {
166: if (log.isDebugEnabled()) {
167: log
168: .debug("Rolling back Hibernate transaction controlled by compass on thread ["
169: + Thread.currentThread().getName()
170: + "]");
171: }
172: if (!commitFailed)
173: transaction.rollback();
174: } else {
175: if (log.isDebugEnabled()) {
176: log
177: .debug("Marking Hibernate transaction as rolled back since compass controlls it on thread ["
178: + Thread.currentThread().getName()
179: + "]");
180: }
181: // no way to mark a transaction as rolled back, assume throwing the exception will make it
182: // transaction.setRollbackOnly();
183: }
184: } catch (Exception e) {
185: throw new TransactionException(
186: "Rollback failed with exception", e);
187: }
188: }
189:
190: public boolean wasRolledBack() throws TransactionException {
191:
192: if (!isBegun())
193: return false;
194: if (commitFailed)
195: return true;
196:
197: return transaction.wasRolledBack();
198: }
199:
200: public boolean wasCommitted() throws TransactionException {
201:
202: if (!isBegun() || commitFailed)
203: return false;
204:
205: return transaction.wasCommitted();
206: }
207:
208: public CompassSession getSession() {
209: return this .session;
210: }
211:
212: private static class HibernateTransactionSynchronization implements
213: Synchronization {
214:
215: private static final Log log = LogFactory
216: .getLog(HibernateTransactionSynchronization.class);
217:
218: private InternalCompassSession session;
219:
220: private Transaction tx;
221:
222: private boolean compassControlledHibernateTransaction;
223:
224: private boolean commitBeforeCompletion;
225:
226: private TransactionFactory transactionFactory;
227:
228: public HibernateTransactionSynchronization(
229: InternalCompassSession session, Transaction tx,
230: boolean compassControlledHibernateTransaction,
231: boolean commitBeforeCompletion,
232: TransactionFactory transactionFactory) {
233: this .transactionFactory = transactionFactory;
234: this .session = session;
235: this .tx = tx;
236: this .compassControlledHibernateTransaction = compassControlledHibernateTransaction;
237: this .commitBeforeCompletion = commitBeforeCompletion;
238: }
239:
240: public void beforeCompletion() {
241: if (!commitBeforeCompletion) {
242: return;
243: }
244: if (log.isDebugEnabled()) {
245: log
246: .debug("Committing compass transaction using Hibernate synchronization beforeCompletion on thread ["
247: + Thread.currentThread().getName()
248: + "]");
249: }
250: session.getSearchEngine().commit(true);
251: }
252:
253: public void afterCompletion(int status) {
254: try {
255: if (!commitBeforeCompletion) {
256: if (status == Status.STATUS_COMMITTED) {
257: if (log.isDebugEnabled()) {
258: log
259: .debug("Committing compass transaction using Hibernate synchronization afterCompletion on thread ["
260: + Thread.currentThread()
261: .getName() + "]");
262: }
263: session.getSearchEngine().commit(true);
264: } else {
265: if (log.isDebugEnabled()) {
266: log
267: .debug("Rolling back compass transaction using Hibernate synchronization afterCompletion on thread ["
268: + Thread.currentThread()
269: .getName() + "]");
270: }
271: session.getSearchEngine().rollback();
272: }
273: }
274: } catch (Exception e) {
275: // TODO swallow??????
276: log.error(
277: "Exception occured when sync with transaction",
278: e);
279: } finally {
280: session.evictAll();
281: ((HibernateSyncTransactionFactory) transactionFactory)
282: .unbindSessionFromTransaction(tx);
283: // close the session AFTER we cleared it from the transaction,
284: // so it will be actually closed (and only if we are not
285: // controlling the trnasction)
286: if (!compassControlledHibernateTransaction) {
287: session.close();
288: }
289: }
290: }
291: }
292:
293: }
|