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

org.eclipse.persistence.transaction.AbstractSynchronizationListener Maven / Gradle / Ivy

There is a newer version: 4.0.2
Show newest version
/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.transaction;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.persistence.exceptions.EclipseLinkException;
import org.eclipse.persistence.exceptions.TransactionException;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.internal.sequencing.SequencingCallback;
import org.eclipse.persistence.internal.sequencing.SequencingCallbackFactory;
import org.eclipse.persistence.logging.*;
import org.eclipse.persistence.sessions.broker.SessionBroker;
import org.eclipse.persistence.sessions.SessionProfiler;
import org.eclipse.persistence.sessions.DatabaseSession;

/**
 * 

* Purpose: Abstract Synchronization Listener class * * Description: This abstract class is paired with the * AbstractTransactionController class. It contains most of the implementation * logic to handle callback notifications from an external transaction * manager to ensure consistency between the global transaction and the * EclipseLink unit of work. It does not assume any particular specification * or interface, but can be called by any implementation subclass. * * @see AbstractTransactionController */ public abstract class AbstractSynchronizationListener { /** * The external txn controller that is intimate with the transaction manager * and knows how to do things like rolling back transactions, etc. */ protected AbstractTransactionController controller; /** * The parent of the uow. */ protected AbstractSession session; /** * The unit of work associated with the global txn that this listener is * bound to. * Note that unitOfWork is null in case it's a purely sequencing listener. */ protected UnitOfWorkImpl unitOfWork; /** * The global transaction object. */ protected Object transaction; /** * The global transaction key. */ protected Object transactionKey; /** * sequencingCallback used in case listener has a single callback. */ protected SequencingCallback sequencingCallback; /** * sequencingCallbackMap used in case listener has more than one callback: * SessionBroker with at least two members requiring callbacks. */ protected Map sequencingCallbackMap; /** * INTERNAL: */ public AbstractSynchronizationListener() { super(); } /** * INTERNAL: */ protected AbstractSynchronizationListener(UnitOfWorkImpl unitOfWork, AbstractSession session, Object transaction, AbstractTransactionController controller) { this.session = session; this.unitOfWork = unitOfWork; this.transaction = transaction; this.controller = controller; this.transactionKey = controller.getTransactionKey(transaction); } /** * INTERNAL: * This method performs the logic that occurs at transaction * completion time. This includes issuing the SQL, etc. * This method executes within the transaction context of the caller of * transaction.commit(), or in the case of container-managed transactions, * in the context of the method for which the Container started the transaction. */ public void beforeCompletion() { UnitOfWorkImpl uow = getUnitOfWork(); // it's a purely sequencing listener - nothing to do in beforeCompletion. if(unitOfWork == null) { return; } try { Object status = getTransactionController().getTransactionStatus(); getTransactionController().logTxStateTrace(uow, "TX_beforeCompletion", status); //CR# 3452053 session.startOperationProfile(SessionProfiler.JtsBeforeCompletion); // In case jts transaction was internally started but completed // directly by TransactionManager this flag is still set to true. getSession().setWasJTSTransactionInternallyStarted(false); // If the uow is not active then somebody somewhere messed up if (!uow.isActive()) { throw TransactionException.inactiveUnitOfWork(uow); } // Bail out if we don't think we should actually issue the SQL if (!getTransactionController().canIssueSQLToDatabase_impl(status)) { // Must force concurrency mgrs active thread if in nested transaction if (getSession().isInTransaction()) { getSession().getTransactionMutex().setActiveThread(Thread.currentThread()); if(getUnitOfWork().wasTransactionBegunPrematurely()) { getUnitOfWork().setWasTransactionBegunPrematurely(false); } getSession().rollbackTransaction(); } getSession().releaseJTSConnection(); return; } // Must force concurrency mgrs active thread if in nested transaction if (getSession().isInTransaction()) { getSession().getTransactionMutex().setActiveThread(Thread.currentThread()); } // If sequencing callback for this transaction will be required // in case it doesn't already exist it will be created on this very listener // avoiding adding more listeners while processing a listener. if(getTransactionController().isSequencingCallbackRequired()) { getTransactionController().currentlyProcessedListeners.put(getTransactionKey(), this); } // Send the SQL to the DB uow.issueSQLbeforeCompletion(); // Fix up our merge state in the unit of work and the session uow.setPendingMerge(); } catch (RuntimeException exception) { // Log the exception if it has not already been logged, or is a non-EclipseLink exception if (!(exception instanceof EclipseLinkException && ((EclipseLinkException)exception).hasBeenLogged())) { uow.logThrowable(SessionLog.WARNING, SessionLog.TRANSACTION, exception); } // Handle the exception according to transaction manager requirements handleException(exception); } finally { if(getTransactionController().isSequencingCallbackRequired()) { getTransactionController().currentlyProcessedListeners.remove(getTransactionKey()); } getSession().releaseJTSConnection(); session.endOperationProfile(SessionProfiler.JtsBeforeCompletion); } } /** * INTERNAL: * The method performs the logic that should be executed after the transaction * has been completed. The status passed in indicates whether the transaction * was committed or rolled back. This status flag may be different for different * implementations. * This method executes without a transaction context. * * @param status The status code of the transaction completion. */ public void afterCompletion(Object status) { UnitOfWorkImpl uow = getUnitOfWork(); // it's a purely sequencing listener - call sequencing callback if the transaction has committed. if(uow == null) { if(getTransactionController().isSequencingCallbackRequired()) { if(getTransactionController().canMergeUnitOfWork_impl(status)) { callSequencingCallback(); } } } else { try { // Log the fact that we got invoked getTransactionController().logTxStateTrace(uow, "TX_afterCompletion", status); //Cr#3452053 this.session.startOperationProfile(SessionProfiler.JtsAfterCompletion); // The uow should still be active even in rollback case if (!uow.isActive()) { throw TransactionException.inactiveUnitOfWork(uow); } // Only do merge if txn was committed if (getTransactionController().canMergeUnitOfWork_impl(status)) { if(getTransactionController().isSequencingCallbackRequired()) { callSequencingCallback(); } if (uow.isMergePending()) { // uow in PENDING_MERGE state, merge clones uow.mergeClonesAfterCompletion(); } } else { // call this method again because there may have been no beforeCompletion call // if case transaction is to be rolled back. getSession().releaseJTSConnection(); uow.afterExternalTransactionRollback(); } // Clean up by releasing the uow and client session if (uow.shouldResumeUnitOfWorkOnTransactionCompletion() && getTransactionController().canMergeUnitOfWork_impl(status)){ uow.synchronizeAndResume(); uow.setSynchronized(false); } else { uow.release(); // Release the session explicitly if (getSession().isClientSession() || (getSession().isSessionBroker() && ((SessionBroker)getSession()).isClientSessionBroker())) { getSession().release(); } } } catch (RuntimeException exception) { // Log the exception if it has not already been logged, or is a non-EclipseLink exception if (!(exception instanceof EclipseLinkException && ((EclipseLinkException)exception).hasBeenLogged())) { uow.logThrowable(SessionLog.WARNING, SessionLog.TRANSACTION, exception); } handleException(exception); } finally { getTransactionController().removeUnitOfWork(getTransactionKey()); this.session.endOperationProfile(SessionProfiler.JtsAfterCompletion); setUnitOfWork(null); setSession(null); } } if(getTransactionController().isSequencingCallbackRequired()) { getTransactionController().removeSequencingListener(getTransactionKey()); this.sequencingCallback = null; this.sequencingCallbackMap = null; } setTransaction(null); setTransactionKey(null); } /** * INTERNAL: * Do the appropriate thing for when an exception occurs during SQL issuance. * The default thing to do is to simply mark the transaction to be rolled back, * for those transaction managers that support this, and rethrow the exception. * We hope that the exception will do the trick for those that do not allow * marking rollback. * * This method may optionally be overridden by concrete subclass implementations. * Different transaction manager vendors may have different reactions to exceptions * that get signalled during the commit phase of synchronization. */ public void handleException(RuntimeException exception) { // Don't do this just yet, since some may not be able to handle it // getTransactionController().markTransactionForRollback(); if (this.controller.getExceptionHandler() != null) { this.controller.getExceptionHandler().handleException(exception); return; } throw exception; } protected AbstractTransactionController getTransactionController() { return controller; } protected void setTransactionController(AbstractTransactionController newController) { controller = newController; } protected Object getTransaction() { return transaction; } protected void setTransaction(Object transaction) { this.transaction = transaction; } protected Object getTransactionKey() { return transactionKey; } protected void setTransactionKey(Object transactionKey) { this.transactionKey = transactionKey; } protected AbstractSession getSession() { return session; } protected void setSession(AbstractSession session) { this.session = session; } protected UnitOfWorkImpl getUnitOfWork() { return unitOfWork; } protected void setUnitOfWork(UnitOfWorkImpl unitOfWork) { this.unitOfWork = unitOfWork; } protected void callSequencingCallback() { if(sequencingCallback != null) { sequencingCallback.afterCommit(null); } else if (sequencingCallbackMap != null) { Iterator itCallback = sequencingCallbackMap.values().iterator(); while(itCallback.hasNext()) { itCallback.next().afterCommit(null); } } } /** * Return sequencingCallback corresponding to the passed session. */ public SequencingCallback getSequencingCallback(DatabaseSession dbSession, SequencingCallbackFactory sequencingCallbackFactory) { if(getTransactionController().numSessionsRequiringSequencingCallback() == 1) { if(sequencingCallback == null) { sequencingCallback = sequencingCallbackFactory.createSequencingCallback(); } return sequencingCallback; } else if(getTransactionController().numSessionsRequiringSequencingCallback() > 1) { SequencingCallback callback = null; if(sequencingCallbackMap == null) { sequencingCallbackMap = new HashMap(getTransactionController().numSessionsRequiringSequencingCallback()); } else { callback = sequencingCallbackMap.get(dbSession); } if(callback == null) { callback = sequencingCallbackFactory.createSequencingCallback(); sequencingCallbackMap.put(dbSession, callback); } return callback; } else { // should never happen. return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy