/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Thierry Delprat * Florent Guillaume */ package org.eclipse.ecr.core.event.tx; import static javax.transaction.Status.STATUS_ACTIVE; import static javax.transaction.Status.STATUS_COMMITTED; import static javax.transaction.Status.STATUS_MARKED_ROLLBACK; import static javax.transaction.Status.STATUS_ROLLEDBACK; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.ecr.runtime.transaction.TransactionHelper; /** * Helper class to encapsulate Transaction management. * * @author Thierry Delprat * @author Florent Guillaume */ public class EventBundleTransactionHandler { private static final Log log = LogFactory.getLog(EventBundleTransactionHandler.class); protected UserTransaction tx; protected boolean disabled; public void beginNewTransaction() { beginNewTransaction(null); } public void beginNewTransaction(Integer transactionTimeout) { if (disabled) { return; } if (tx != null) { throw new UnsupportedOperationException( "There is already an uncommited transaction running"); } tx = createUT(transactionTimeout); if (tx == null) { log.debug("No TransactionManager"); disabled = true; return; } try { if (tx.getStatus() == STATUS_COMMITTED) { log.error("Transaction is already commited, try to begin anyway"); } tx.begin(); } catch (Exception e) { log.error("Unable to start transaction", e); } } protected UserTransaction createUT(Integer transactionTimeout) { return createUT(transactionTimeout, false); } protected UserTransaction createUT(Integer transactionTimeout, boolean retry) { try { new InitialContext(); } catch (Exception e) { disabled = true; return null; } UserTransaction ut; try { ut = TransactionHelper.lookupUserTransaction(); } catch (NamingException e) { disabled = true; return null; } try { int txStatus = ut.getStatus(); if (txStatus != Status.STATUS_NO_TRANSACTION && !retry) { // if previous tx in this thread aborted in TimeOut // Ajuna may have failed to dissociate tx from thread context // => force rollback to avoid reusing a dead tx log.warn("Transaction was not properly cleanup up from thread context, rolling back before getting a new tx"); try { ut.rollback(); } catch (Throwable t) { log.warn("error during tx rollback", t); } return createUT(transactionTimeout, true); } } catch (Exception se) { log.warn("Error while getting TX status", se); } if (transactionTimeout!=null) { try { ut.setTransactionTimeout(transactionTimeout); } catch (SystemException e) { log.error("Error while setting transaction timeout to " + transactionTimeout, e); } } return ut; } protected boolean isUTTransactionActive() { try { return tx.getStatus() == STATUS_ACTIVE; } catch (SystemException e) { log.error("Error while getting tx status", e); return false; } } private boolean isUTTransactionMarkedRollback() { try { int status = tx.getStatus(); return status == STATUS_MARKED_ROLLBACK || status == STATUS_ROLLEDBACK; } catch (SystemException e) { log.error("Error while getting tx status", e); return false; } } public void rollbackTransaction() { if (disabled || tx == null) { return; } try { if (!isUTTransactionMarkedRollback()) { tx.setRollbackOnly(); } commitOrRollbackTransaction(); } catch (Exception e) { log.error("Error while marking tx for rollback", e); } finally { tx = null; } } public void commitOrRollbackTransaction() { if (disabled || tx == null) { return; } try { if (isUTTransactionActive()) { try { tx.commit(); } catch (Exception e) { log.error("Error during commit", e); } } else { try { log.debug("Rolling back transaction"); tx.rollback(); } catch (Exception e) { // if the transaction was already rolledback due to // transaction timeout, we must still call tx.rollback but // this causes a spurious error message, so log at debug // level log.debug("Error during rollback", e); } } } finally { tx = null; } } }