/* * 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: * Florent Guillaume */ package org.eclipse.ecr.runtime.transaction; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Utilities to work with transactions. */ public class TransactionHelper { private static final Log log = LogFactory.getLog(TransactionHelper.class); private TransactionHelper() { // utility class } /** * Various binding names for the UserTransaction. They depend on the * application server used and how the configuration is done. */ public static final String[] UT_NAMES = { "java:comp/UserTransaction", // standard "java:comp/env/UserTransaction", // manual binding outside appserver "UserTransaction" // jboss }; /** * Various binding names for the TransactionManager. They depend on the * application server used and how the configuration is done. */ public static final String[] TM_NAMES = { "java:comp/TransactionManager", // common "java:comp/env/TransactionManager", // manual binding "java:TransactionManager" // }; /** * Looks up the User Transaction in JNDI. * * @return the User Transaction * @throws NamingException if not found */ public static UserTransaction lookupUserTransaction() throws NamingException { InitialContext context = new InitialContext(); int i = 0; for (String name : UT_NAMES) { try { UserTransaction userTransaction = (UserTransaction) context.lookup(name); if (userTransaction != null) { if (i != 0) { // put successful name first for next time UT_NAMES[i] = UT_NAMES[0]; UT_NAMES[0] = name; } return userTransaction; } } catch (NamingException e) { // try next one } i++; } throw new NamingException("UserTransaction not found in JNDI"); } /** * Returns the UserTransaction JNDI binding name. * <p> * Assumes {@link #lookupUserTransaction} has been called once before. */ public static String getUserTransactionJNDIName() { return UT_NAMES[0]; } /** * Looks up the TransactionManager in JNDI. * * @return the TransactionManager * @throws NamingException if not found */ public static TransactionManager lookupTransactionManager() throws NamingException { InitialContext context = new InitialContext(); int i = 0; for (String name : TM_NAMES) { try { TransactionManager transactionManager = (TransactionManager) context.lookup(name); if (transactionManager != null) { if (i != 0) { // put successful name first for next time TM_NAMES[i] = TM_NAMES[0]; TM_NAMES[0] = name; } return transactionManager; } } catch (NamingException e) { // try next one } i++; } throw new NamingException("TransactionManager not found in JNDI"); } /** * Checks if the current User Transaction is active. */ public static boolean isTransactionActive() { try { return lookupUserTransaction().getStatus() == Status.STATUS_ACTIVE; } catch (Exception e) { return false; } } /** * Checks if the current User Transaction is marked rollback only. */ public static boolean isTransactionMarkedRollback() { try { return lookupUserTransaction().getStatus() == Status.STATUS_MARKED_ROLLBACK; } catch (Exception e) { return false; } } /** * Checks if the current User Transaction is active or marked rollback only. */ public static boolean isTransactionActiveOrMarkedRollback() { try { int status = lookupUserTransaction().getStatus(); return status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK; } catch (Exception e) { return false; } } /** * Starts a new User Transaction. * * @return {@code true} if the transaction was successfully started, {@code * false} otherwise */ public static boolean startTransaction() { try { lookupUserTransaction().begin(); return true; } catch (NamingException e) { // no transaction } catch (Exception e) { log.error("Unable to start transaction", e); } return false; } /** * Commits or rolls back the User Transaction depending on the transaction * status. * * @throws SystemException * @throws HeuristicRollbackException * @throws HeuristicMixedException * @throws RollbackException * @throws IllegalStateException * @throws SecurityException */ public static void commitOrRollbackTransaction() { UserTransaction ut; try { ut = lookupUserTransaction(); } catch (NamingException e) { log.warn("No user transaction", e); return; } try { int status = ut.getStatus(); if (status == Status.STATUS_ACTIVE) { if (log.isDebugEnabled()) { log.debug("Commiting transaction"); } ut.commit(); } else if (status == Status.STATUS_MARKED_ROLLBACK) { if (log.isDebugEnabled()) { log.debug("Cannot commit transaction because it is marked rollback only"); } ut.rollback(); } } catch (Exception e) { String msg = "Unable to commit/rollback " + ut; if (e instanceof RollbackException && "Unable to commit: transaction marked for rollback".equals(e.getMessage())) { // don't log as error, this happens if there's a // ConcurrentModificationException at transaction end inside VCS log.debug(msg, e); } else { log.error(msg, e); } throw new TransactionRuntimeException(msg, e); } } /** * Sets the current User Transaction as rollback only. * * @return {@code true} if the transaction was successfully marked rollback * only, {@code false} otherwise */ public static boolean setTransactionRollbackOnly() { try { lookupUserTransaction().setRollbackOnly(); return true; } catch (NamingException e) { // no transaction } catch (Exception e) { log.error("Could not mark transaction as rollback only", e); } return false; } }