All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.toplink.essentials.transaction.AbstractTransactionController Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
 * 
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package oracle.toplink.essentials.transaction;

import java.util.*;
import javax.naming.*;
import oracle.toplink.essentials.sessions.ExternalTransactionController;
import oracle.toplink.essentials.logging.SessionLog;
import oracle.toplink.essentials.exceptions.TransactionException;
import oracle.toplink.essentials.exceptions.DatabaseException;
import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
import oracle.toplink.essentials.internal.sessions.AbstractSession;

/**
 * 

* Purpose: Abstract implementation of an ExternalTransactionController. *

* Description: This class implements the ExternalTransactionController * interface. Concrete implementations of this class are responsible for performing * the coordination with the external transaction manager through whatever means * available to that manager. Different transaction services may do this in slightly * different ways, but most common ones (JTA and JTS/OTS) will use a synchronization * interface. *

* @see AbstractSynchronizationListener * @see oracle.toplink.essentials.sessions.ExternalTransactionController */ public abstract class AbstractTransactionController implements ExternalTransactionController { /** Table of external transaction object keys and unit of work values */ protected Hashtable unitsOfWork; /** The session this controller is responsible for controlling */ protected AbstractSession session; /** Generates listener instances for synchronization */ protected SynchronizationListenerFactory listenerFactory; /** PERF: Cache the active uow in a thread local. */ protected ThreadLocal activeUnitOfWorkThreadLocal; /** * INTERNAL: * Return a new controller. */ public AbstractTransactionController() { this.unitsOfWork = new Hashtable(); this.activeUnitOfWorkThreadLocal = new ThreadLocal(); } /** * INTERNAL: * Associate the given unit of work and TopLink session with the active external * transaction. This may be done by whatever means supported by the transaction * manager (i.e. through a synchronization protocol). * * @param unitOfWork The unit of work that is to be bound to the active transaction * @param session The session ancestor of the unit of work */ public void bindToCurrentTransaction(UnitOfWorkImpl unitOfWork, AbstractSession session) { Object status = getTransactionStatus(); logTxStateTrace(unitOfWork, "TX_bind", status); try { Object txn = getTransaction(); if (txn == null) { // If no external transaction is active then start one through the uow unitOfWork.beginTransaction(); txn = getTransaction(); } // If there still is no active txn then something is wrong if (txn == null) { throw TransactionException.externalTransactionNotActive(); } // Create and register the synchronization listener AbstractSynchronizationListener listener = getListenerFactory().newSynchronizationListener(unitOfWork, session, txn, this); registerSynchronization_impl(listener, txn); unitOfWork.setSynchronized(true); } catch (Exception exception) { throw TransactionException.errorBindingToExternalTransaction(exception); } } /** * INTERNAL: * Begin an external transaction. * * @param session The session for which the transaction is being begun. */ public void beginTransaction(AbstractSession session) { try { Object status = getTransactionStatus(); logTxStateTrace(session, "TX_begin", status); // Make sure that we are in a state that we can actually start // a transaction (e.g. ensure one is not already in progress) if (canBeginTransaction_impl(status)) { logTxTrace(session, "TX_beginningTxn", null); beginTransaction_impl(); session.setWasJTSTransactionInternallyStarted(true); } } catch (Exception exception) { throw TransactionException.errorBeginningExternalTransaction(exception); } } /** * INTERNAL: * Commit the external transaction. * * @param session The session for which the transaction is being committed. */ public void commitTransaction(AbstractSession session) { try { Object status = getTransactionStatus(); logTxStateTrace(session, "TX_commit", status); if (canCommitTransaction_impl(status)) { logTxTrace(session, "TX_committingTxn", null); session.setWasJTSTransactionInternallyStarted(false); commitTransaction_impl(); } } catch (Exception exception) { throw TransactionException.errorCommittingExternalTransaction(exception); } } /** * INTERNAL: * Roll back the external transaction. * * @param session The session for which the transaction is being rolled back. */ public void rollbackTransaction(AbstractSession session) { try { Object status = getTransactionStatus(); logTxStateTrace(session, "TX_rollback", status); session.setWasJTSTransactionInternallyStarted(false); // Only roll back if there is a transaction to roll back if ((canRollbackTransaction_impl(status)) && (getTransaction() != null)) { logTxTrace(session, "TX_rollingBackTxn", null); rollbackTransaction_impl(); } } catch (Exception exception) { throw TransactionException.errorRollingBackExternalTransaction(exception); } } /** * INTERNAL: * Mark the external transaction for rollback. */ public void markTransactionForRollback() { try { markTransactionForRollback_impl(); } catch (Exception exception) { throw TransactionException.errorMarkingTransactionForRollback(exception); } } /** * INTERNAL: * Return the active external transaction object, or null if * none is active. This may be in whatever form the transaction system uses. */ public Object getTransaction() { try { return getTransaction_impl(); } catch (Exception exception) { throw TransactionException.errorGettingExternalTransaction(exception); } } /** * INTERNAL: * Return a key for the specified external transaction object. * The key is just something that can be inserted into a hashtable (must support * hashCode() and equals() methods). */ public Object getTransactionKey(Object transaction) { try { return getTransactionKey_impl(transaction); } catch (Exception exception) { throw TransactionException.errorGettingExternalTransaction(exception); } } /** * INTERNAL: * Return the transaction status. This may be any type of status or value, * depending upon the transaction system. */ public Object getTransactionStatus() { try { return getTransactionStatus_impl(); } catch (Exception exception) { throw TransactionException.errorGettingExternalTransactionStatus(exception); } } /** * INTERNAL: * Used the EJB 3.0 to determine if a transaction is in a state where an EntityManager can * be closed */ public boolean noTransactionOrRolledBackOrCommited() { try { Object status = getTransactionStatus(); return canBeginTransaction_impl(status) || canMergeUnitOfWork_impl(status) || isRolledBack_impl(status); } catch (Exception exception) { throw TransactionException.errorGettingExternalTransactionStatus(exception); } } /** * INTERNAL: * Return true if the transaction is in the roleld back state. */ public abstract boolean isRolledBack_impl(Object status); /** * INTERNAL: * Return true if there is a unit of work associated with the active external * transaction. Return false if no transaction is current, or if no uow has * been associated with the active transaction yet. */ public boolean hasActiveUnitOfWork() { return this.lookupActiveUnitOfWork() != null; } /** * INTERNAL: * Return the active unit of work for the current external transaction. * If no transaction is active then return null. If a transaction is active * but no unit of work has been bound to it then create and return one. */ public UnitOfWorkImpl getActiveUnitOfWork() { Object transaction = getTransaction(); if (transaction == null) { return null; } UnitOfWorkImpl activeUnitOfWork = lookupActiveUnitOfWork(transaction); if (activeUnitOfWork == null) { // Txn is active but no UoW has been associated with it yet, so create one. activeUnitOfWork = (UnitOfWorkImpl) getSession().acquireUnitOfWork(); Object transactionKey = getTransactionKey(transaction); addUnitOfWork(transactionKey, activeUnitOfWork); activeUnitOfWork.setTransaction(transaction); this.activeUnitOfWorkThreadLocal.set(activeUnitOfWork); } return activeUnitOfWork; } /** * INTERNAL: * Return the unit of work associated with the active external transaction. * Return null if no transaction is active, or if no uow has been associated with * the active transaction yet. */ public UnitOfWorkImpl lookupActiveUnitOfWork() { return lookupActiveUnitOfWork(getTransaction()); } /** * INTERNAL: * Return the unit of work associated with the active external transaction. * Return null if no transaction is active, or if no uow has been associated with * the active transaction yet. */ public UnitOfWorkImpl lookupActiveUnitOfWork(Object transaction) { if (transaction == null) { return null; } Object transactionKey = getTransactionKey(transaction); // PERF: Cache the active unit of work in a thread local. // This is just a heuristic, so uses == and no tx-key and direct access as extreamly high throughput. UnitOfWorkImpl activeUnitOfWork = (UnitOfWorkImpl)this.activeUnitOfWorkThreadLocal.get(); if (activeUnitOfWork != null) { if (transaction == activeUnitOfWork.getTransaction()) { return activeUnitOfWork; } } activeUnitOfWork = (UnitOfWorkImpl)getUnitsOfWork().get(transactionKey); if (activeUnitOfWork != null) { activeUnitOfWork.setTransaction(transaction); } this.activeUnitOfWorkThreadLocal.set(activeUnitOfWork); return activeUnitOfWork; } /** * INTERNAL: * Add a UnitOfWork object to the Hashtable keyed on the external transaction object. */ public void addUnitOfWork(Object transaction, UnitOfWorkImpl activeUnitOfWork) { this.activeUnitOfWorkThreadLocal.set(null); getUnitsOfWork().put(transaction, activeUnitOfWork); } /** * INTERNAL: * Remove the unit of work associated with the transaction passed in. */ public void removeUnitOfWork(Object transaction) { if (transaction != null) { getUnitsOfWork().remove(transaction); } this.activeUnitOfWorkThreadLocal.set(null); } /** * INTERNAL: * Return the manager's session. */ public AbstractSession getSession() { return session; } /** * INTERNAL: * Set the manager's session. */ public void setSession(AbstractSession session) { this.session = session; } /** * INTERNAL: * Return the hashtable keyed on the external transaction objectss with values * that are the associated units of work. */ public Hashtable getUnitsOfWork() { return unitsOfWork; } /** * INTERNAL: * Set the table of transactions to units of work. */ protected void setUnitsOfWork(Hashtable unitsOfWork) { this.unitsOfWork = unitsOfWork; } /** * INTERNAL: * Get the factory used to generate synchronization listeners. */ public SynchronizationListenerFactory getListenerFactory() { return listenerFactory; } /** * INTERNAL: * Set the factory used to generate synchronization listeners. This should be * set if a listener other than the default one is being used. */ public void setListenerFactory(SynchronizationListenerFactory factory) { listenerFactory = factory; } /** * INTERNAL: * Associate the given unit of work and TopLink session with the current external * transaction. This method is offered only for backwards compatibility. */ public void registerSynchronizationListener(UnitOfWorkImpl uow, AbstractSession session) throws DatabaseException { this.bindToCurrentTransaction(uow, session); } /** * PUBLIC: * Look up a given name in JNDI. This can be used by a subclass or even an * application to look up transaction artifacts that are required for the * implementation. *

* The lookup assumes that it is taking place on the server side, and that the * InitialContext can be used without requiring any special properties. * * @param jndiName The name to look up * @return The object bound in JNDI to the specified name * @exception TransactionException Thrown in case of lookup failure */ public Object jndiLookup(String jndiName) { Context context = null; Object jndiObject = null; try { context = new InitialContext(); jndiObject = context.lookup(jndiName); } catch (NamingException ex) { throw TransactionException.jndiLookupException(jndiName, ex); } finally { try { context.close(); } catch (Exception ex2) {/* ignore */ } } return jndiObject; } /* * INTERNAL: * Helper method to log trace statements to the transaction channel. */ public void logTxTrace(AbstractSession session, String msgInd, Object[] args) { session.log(SessionLog.FINER, SessionLog.TRANSACTION, msgInd, args); } /* * INTERNAL: * Helper method to log transaction state to the transaction channel. */ public void logTxStateTrace(AbstractSession session, String msgInd, Object status) { if (session.shouldLog(SessionLog.FINER, SessionLog.TRANSACTION)) { String statusString = statusToString_impl(status); Object[] args = { statusString }; session.log(SessionLog.FINER, SessionLog.TRANSACTION, msgInd, args); } } //--------------------------------------------------------------------------------- // The following methods must be implemented by subclass implementations. //--------------------------------------------------------------------------------- /** * INTERNAL: * Register the specified synchronization listener with the given active * transaction. * * @param listener The synchronization listener created for this transaction * @param txn The active transaction for which notification is being requested */ protected abstract void registerSynchronization_impl(AbstractSynchronizationListener listener, Object txn) throws Exception; /** * INTERNAL: * Return the active external transaction for the calling thread, or null if * none is currently active for this thread. * * @return The active transaction object, or null if no transaction is active */ protected abstract Object getTransaction_impl() throws Exception; /** * INTERNAL: * Return a key for the specified external transaction object. * The key is just something that can be inserted into a hashtable (must support * hashCode() and equals() methods). * * @param transaction The transaction to which the returned key applies (may be null) * @return A key for the passed in transaction, or null if no transaction specified */ protected abstract Object getTransactionKey_impl(Object transaction) throws Exception; /** * INTERNAL: * Return the transaction status. This may be any type of status or value, * depending upon the transaction system. * * @return The current transaction status */ protected abstract Object getTransactionStatus_impl() throws Exception; /** * INTERNAL: * Begin an external transaction. Do this in a way appropriate to the * transaction subsystem. */ protected abstract void beginTransaction_impl() throws Exception; /** * INTERNAL: * Commit the external transaction. Do this in a way appropriate to the * transaction subsystem. */ protected abstract void commitTransaction_impl() throws Exception; /** * INTERNAL: * Roll back the external transaction. Do this in a way appropriate to the * transaction subsystem. */ protected abstract void rollbackTransaction_impl() throws Exception; /** * INTERNAL: * Mark the external transaction for rollback. Do this in a way appropriate to the * transaction subsystem. */ protected abstract void markTransactionForRollback_impl() throws Exception; /** * INTERNAL: * Return true if the status indicates that a transaction can be started. This * would normally mean that no transaction is currently active. * The status is interpreted by the transaction subsystem. * * @param status The current transaction status * @return true if the current state allows for a transaction to be started */ protected abstract boolean canBeginTransaction_impl(Object status); /** * INTERNAL: * Return true if the status indicates that a transaction can be committed. This * would normally mean that a transaction is currently active. * The status is interpreted by the transaction subsystem. * * @param status The current transaction status * @return true if the current state allows for a transaction to be committed */ protected abstract boolean canCommitTransaction_impl(Object status); /** * INTERNAL: * Return true if the status indicates that a transaction can be rolled back. This * would normally mean that a transaction is currently active. * The status is interpreted by the transaction subsystem. * * @param status The current transaction status * @return true if the current state allows for a transaction to be rolled back */ protected abstract boolean canRollbackTransaction_impl(Object status); /** * INTERNAL: * Return true if the status indicates that the SQL should be issued to the db. * This would normally mean that a transaction was active and not being rolled * back or marked for rollback. * The status is interpreted by the transaction subsystem. * * @param status The current transaction status * @return true if the current state allows for the SQL to be sent to the database */ protected abstract boolean canIssueSQLToDatabase_impl(Object status); /** * INTERNAL: * Return true if the status indicates that the unit of work should be merged * into the shared cache. This would normally mean that the transaction was * committed successfully. * The status is interpreted by the transaction subsystem. * * @param status The current transaction status * @return true if the current state dictates that the unit of work should be merged */ protected abstract boolean canMergeUnitOfWork_impl(Object status); /** * INTERNAL: * Convert the status to a string for tracing. */ protected abstract String statusToString_impl(Object status); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy