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

com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2015 Oracle and/or its affiliates. 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_1_1.html
 * or packager/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 packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [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.
 */
// Portions Copyright [2016-2021] [Payara Foundation]
package com.sun.enterprise.transaction.jts;

import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Properties;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.TimeUnit;

import jakarta.transaction.*;
import javax.transaction.xa.*;
import jakarta.resource.spi.XATerminator;
import jakarta.resource.spi.work.WorkException;

import com.sun.enterprise.transaction.config.TransactionService;
import com.sun.jts.jta.TransactionManagerImpl;
import com.sun.jts.jta.TransactionServiceProperties;
import com.sun.jts.CosTransactions.Configuration;
import com.sun.jts.CosTransactions.DefaultTransactionService;
import com.sun.jts.CosTransactions.RecoveryManager;
import com.sun.jts.CosTransactions.DelegatedRecoveryManager;
import com.sun.jts.CosTransactions.RWLock;

import com.sun.enterprise.config.serverbeans.ServerTags;

import com.sun.enterprise.transaction.api.JavaEETransaction;
import com.sun.enterprise.transaction.api.JavaEETransactionManager;
import com.sun.enterprise.transaction.api.TransactionAdminBean;
import com.sun.enterprise.transaction.api.XAResourceWrapper;
import com.sun.enterprise.transaction.spi.JavaEETransactionManagerDelegate;
import com.sun.enterprise.transaction.spi.TransactionalResource;
import com.sun.enterprise.transaction.spi.TransactionInternal;

import com.sun.enterprise.transaction.jts.recovery.OracleXAResource;
import com.sun.enterprise.transaction.jts.recovery.SybaseXAResource;

import com.sun.enterprise.transaction.JavaEETransactionManagerSimplified;
import com.sun.enterprise.transaction.JavaEETransactionImpl;

import com.sun.enterprise.util.i18n.StringManager;
import com.sun.logging.LogDomains;
import org.glassfish.api.admin.ServerEnvironment;

import org.jvnet.hk2.annotations.Service;

import jakarta.inject.Inject;
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.ServiceLocator;

/**
 ** Implementation of JavaEETransactionManagerDelegate that supports XA
 * transactions with JTS.
 *
 * @author Marina Vatkina
 */
@Service
public class JavaEETransactionManagerJTSDelegate 
            implements JavaEETransactionManagerDelegate, PostConstruct {

    @Inject private ServiceLocator serviceLocator;

    // an implementation of the JavaEETransactionManager that calls
    // this object.
    // @Inject 
    private JavaEETransactionManager javaEETM;

    // an implementation of the JTA TransactionManager provided by JTS.
    private ThreadLocal tmLocal = new ThreadLocal();

    private Hashtable globalTransactions;
    private Hashtable xaresourcewrappers =
            new Hashtable();

    private Logger _logger;

    // Use JavaEETransactionManagerSimplified logger and Sting Manager for Localization
    private static StringManager sm
           = StringManager.getManager(JavaEETransactionManagerSimplified.class);

    private boolean lao = true;
    private final static ReadWriteLock lock = new ReadWriteLock();
    private static JavaEETransactionManagerJTSDelegate instance = null;
    private volatile TransactionManager transactionManagerImpl = null;
    private TransactionService txnService = null;

    public JavaEETransactionManagerJTSDelegate() {
        globalTransactions = new Hashtable();
    }

    public void postConstruct() {
        if (javaEETM != null) {
            // JavaEETransactionManager has been already initialized
            javaEETM.setDelegate(this);
        }

        _logger = LogDomains.getLogger(JavaEETransactionManagerSimplified.class, LogDomains.JTA_LOGGER);
        initTransactionProperties();

        setInstance(this);
    }

    public boolean useLAO() {
         return lao;
    }

    public void setUseLAO(boolean b) {
        lao = b;
    }

    /** An XA transaction commit
     */
    public void commitDistributedTransaction() throws 
            RollbackException, HeuristicMixedException, 
            HeuristicRollbackException, SecurityException, 
            IllegalStateException, SystemException {

        if (_logger.isLoggable(Level.FINE))
                _logger.log(Level.FINE,"TM: commit");
        validateTransactionManager();
        TransactionManager tm = tmLocal.get();
        Object obj = tm.getTransaction(); // monitoring object

        JavaEETransactionManagerSimplified javaEETMS = 
                (JavaEETransactionManagerSimplified)javaEETM;
        
        boolean success = false;
        if (javaEETMS.isInvocationStackEmpty()) {
            try{
                tm.commit();
                success = true;
            }catch(HeuristicMixedException e){
                success = true;
                throw e;
            } finally {
                javaEETMS.monitorTxCompleted(obj, success);
            }
        } else {
            try {
                javaEETMS.setTransactionCompeting(true);
                tm.commit();
                success = true;
/**
            } catch (InvocationException ex) {
                assert false;
**/
            }catch(HeuristicMixedException e){
                success = true;
                throw e;
            } finally {
                javaEETMS.monitorTxCompleted(obj, success);
                javaEETMS.setTransactionCompeting(false);
            }
        }
    }

    /** An XA transaction rollback
    */
    public void rollbackDistributedTransaction() throws IllegalStateException, 
            SecurityException, SystemException {

        if (_logger.isLoggable(Level.FINE))
                _logger.log(Level.FINE,"TM: rollback");
        validateTransactionManager();

        TransactionManager tm = tmLocal.get();
        Object obj = tm.getTransaction(); // monitoring object
        
        JavaEETransactionManagerSimplified javaEETMS = 
                (JavaEETransactionManagerSimplified)javaEETM;
        
        try {
            if (javaEETMS.isInvocationStackEmpty()) {
                tm.rollback();
            } else {
                try {
                    javaEETMS.setTransactionCompeting(true);
                    tm.rollback();
/**
                } catch (InvocationException ex) {
                    assert false;
**/
                } finally {
                    javaEETMS.setTransactionCompeting(false);
                }
            }
        } finally {
            javaEETMS.monitorTxCompleted(obj, false);
        }
    }

    public int getStatus() throws SystemException {

        JavaEETransaction tx = javaEETM.getCurrentTransaction();
        int status = jakarta.transaction.Status.STATUS_NO_TRANSACTION;

        TransactionManager tm = tmLocal.get();
        if ( tx != null) 
            status = tx.getStatus();
        else if (tm != null) 
            status = tm.getStatus();

        if (_logger.isLoggable(Level.FINE))
            _logger.log(Level.FINE,"TM: status: " + JavaEETransactionManagerSimplified.getStatusAsString(status));

        return status;
    }

    public Transaction getTransaction() 
            throws SystemException {
        JavaEETransaction tx = javaEETM.getCurrentTransaction();
        if (_logger.isLoggable(Level.FINE))
            _logger.log(Level.FINE,"TM: getTransaction: tx=" + tx + ", tm=" + tmLocal.get());

        if ( tx != null )
            return tx;

        // Check for a JTS imported tx
        TransactionInternal jtsTx = null;
        TransactionManager tm = tmLocal.get();
        if (tm != null) {
            jtsTx = (TransactionInternal)tm.getTransaction();
        }

        if ( jtsTx == null )
            return null;
        else {
            // check if this JTS Transaction was previously active
            // in this JVM (possible for distributed loopbacks).
            tx = (JavaEETransaction)globalTransactions.get(jtsTx);
            if (_logger.isLoggable(Level.FINE))
                _logger.log(Level.FINE,"TM: getTransaction: tx=" + tx + ", jtsTx=" + jtsTx);

            if ( tx == null ) {
                tx = ((JavaEETransactionManagerSimplified)javaEETM).createImportedTransaction(jtsTx);
                globalTransactions.put(jtsTx, tx);
            }
            javaEETM.setCurrentTransaction(tx); // associate tx with thread
            return tx;
        }
    }

    public JavaEETransaction getJavaEETransaction(Transaction t) {
        if(t instanceof JavaEETransaction){
            return  (JavaEETransaction)t;
        }

        return (JavaEETransaction)globalTransactions.get(t);

    }
    public boolean enlistDistributedNonXAResource(Transaction tx, TransactionalResource h)
           throws RollbackException, IllegalStateException, SystemException {
        if(useLAO()) {
            if (((JavaEETransactionManagerSimplified)javaEETM).resourceEnlistable(h)) {
                XAResource res = h.getXAResource();
                boolean result = tx.enlistResource(res);
                if (!h.isEnlisted())
                    h.enlistedInTransaction(tx);
                    return result;
                } else {
                    return true;
            }
        } else {
            throw new IllegalStateException(
                    sm.getString("enterprise_distributedtx.nonxa_usein_jts"));
        }
    }

    public boolean enlistLAOResource(Transaction tran, TransactionalResource h)
           throws RollbackException, IllegalStateException, SystemException {

        if (tran instanceof JavaEETransaction) {
            JavaEETransaction tx = (JavaEETransaction)tran;
            ((JavaEETransactionManagerSimplified) javaEETM).startJTSTx(tx);

            //If transaction conatains a NonXA and no LAO, convert the existing
            //Non XA to LAO
            if(useLAO()) {
                if(h != null && (tx.getLAOResource() == null) ) {
                    tx.setLAOResource(h);
                    if (h.isTransactional()) {
                        XAResource res = h.getXAResource();
                        return tran.enlistResource(res);
                    }
                }
            }
            return true;
        } else {
            // Should not be called
            return false;
        }

    }

    public void setRollbackOnlyDistributedTransaction()
            throws IllegalStateException, SystemException {
        if (_logger.isLoggable(Level.FINE))
                _logger.log(Level.FINE,"TM: setRollbackOnly");

        validateTransactionManager();
        tmLocal.get().setRollbackOnly();
    }

    public Transaction suspend(JavaEETransaction tx) throws SystemException {
        if ( tx != null ) {
            if ( !tx.isLocalTx() )
                suspendXA();

            javaEETM.setCurrentTransaction(null);
            return tx;
        } else if (tmLocal.get() != null) {
            return suspendXA(); // probably a JTS imported tx
        }

        return null;
    }

    public void resume(Transaction tx)
        throws InvalidTransactionException, IllegalStateException,
        SystemException {
        if (_logger.isLoggable(Level.FINE))
            _logger.log(Level.FINE,"TM: resume");

        if (transactionManagerImpl != null) {
            setTransactionManager();
            tmLocal.get().resume(tx);
        }
    }

    public void removeTransaction(Transaction tx) {
        globalTransactions.remove(tx);
    }

    public int getOrder() {
        return 3;
    }

    public void setTransactionManager(JavaEETransactionManager tm) {
        javaEETM = tm;
        _logger = ((JavaEETransactionManagerSimplified)javaEETM).getLogger();
    }

    public TransactionInternal startJTSTx(JavaEETransaction tran, boolean isAssociatedTimeout) 
            throws RollbackException, IllegalStateException, SystemException {
        setTransactionManager();

        JavaEETransactionImpl tx = (JavaEETransactionImpl)tran;
        try {
            if (isAssociatedTimeout) {
                // calculate the timeout for the transaction, this is required as the local tx
                // is getting converted to a global transaction
                int timeout = tx.cancelTimerTask();
                int newtimeout = (int) ((System.currentTimeMillis() - tx.getStartTime()) / 1000);
                newtimeout = (timeout -   newtimeout);
                beginJTS(newtimeout);
            } else {
                beginJTS(((JavaEETransactionManagerSimplified)javaEETM).getEffectiveTimeout());
            }
        } catch ( NotSupportedException ex ) {
            throw new RuntimeException(sm.getString("enterprise_distributedtx.lazy_transaction_notstarted"),ex);
        }

        TransactionInternal jtsTx = (TransactionInternal)tmLocal.get().getTransaction();
        globalTransactions.put(jtsTx, tx);

        return jtsTx;
    }

    public void initRecovery(boolean force) {
        TransactionServiceProperties.initRecovery(force);
    }

    public void recover(XAResource[] resourceList) {
        setTransactionManager();
        TransactionManagerImpl.recover(
                Collections.enumeration(Arrays.asList(resourceList)));
    }

    public void release(Xid xid) throws WorkException {
        setTransactionManager();
        TransactionManagerImpl.release(xid);
    }

    public void recreate(Xid xid, long timeout) throws WorkException {
        setTransactionManager();
        TransactionManagerImpl.recreate(xid, timeout);
    }

    public XATerminator getXATerminator() {
        setTransactionManager();
        return TransactionManagerImpl.getXATerminator();
    }

    private Transaction suspendXA() throws SystemException {
        if (_logger.isLoggable(Level.FINE))
            _logger.log(Level.FINE,"TM: suspend");
        validateTransactionManager();
        return tmLocal.get().suspend();
    }

    private void validateTransactionManager() throws IllegalStateException {
        if (tmLocal.get() == null) {
            throw new IllegalStateException
            (sm.getString("enterprise_distributedtx.transaction_notactive"));
        }
    }

    private void setTransactionManager() {
        if (_logger.isLoggable(Level.FINE))
            _logger.log(Level.FINE,"TM: setTransactionManager: tm=" + tmLocal.get());

        if (transactionManagerImpl == null) {
           transactionManagerImpl = TransactionManagerImpl.getTransactionManagerImpl();
        }

        if (tmLocal.get() == null)
            tmLocal.set(transactionManagerImpl);
    }

    public XAResourceWrapper getXAResourceWrapper(String clName) {
        XAResourceWrapper rc = xaresourcewrappers.get(clName);

        if (rc != null)
            return rc.getInstance();

        return null;
    }

    public void handlePropertyUpdate(String name, Object value) {
        if (name.equals(ServerTags.KEYPOINT_INTERVAL)) {
            Configuration.setKeypointTrigger(Integer.parseInt((String)value,10));

        } else if (name.equals(ServerTags.RETRY_TIMEOUT_IN_SECONDS)) {
            Configuration.setCommitRetryVar((String)value);

        }
    }

    public boolean recoverIncompleteTx(boolean delegated, String logPath, 
            XAResource[] xaresArray) throws Exception {
        boolean result = false;
        if (!delegated) {
            RecoveryManager.recoverIncompleteTx(xaresArray);
            result = true;
        }
        else
            result = DelegatedRecoveryManager.delegated_recover(logPath, xaresArray);

        return result;
    }

    public void beginJTS(int timeout) throws NotSupportedException, SystemException {
        TransactionManagerImpl tm = (TransactionManagerImpl)tmLocal.get();
        tm.begin(timeout);
        ((JavaEETransactionManagerSimplified)javaEETM).monitorTxBegin(tm.getTransaction());
    }

    public boolean supportsXAResource() {
        return true;
    }

    public void initTransactionProperties() {
        if (serviceLocator != null) {
            txnService = serviceLocator.getService(TransactionService.class,
                    ServerEnvironment.DEFAULT_INSTANCE_NAME);

            if (txnService != null) {
                String value = txnService.getPropertyValue("use-last-agent-optimization");
                if (value != null && "false".equals(value)) {
                    setUseLAO(false);
                    if (_logger.isLoggable(Level.FINE))
                        _logger.log(Level.FINE,"TM: LAO is disabled");
                }
        
                value = txnService.getPropertyValue("oracle-xa-recovery-workaround");
                if (value == null || "true".equals(value)) {
                    xaresourcewrappers.put(
                        "oracle.jdbc.xa.client.OracleXADataSource",
                        new OracleXAResource());
                }
        
                if (Boolean.parseBoolean(txnService.getPropertyValue("sybase-xa-recovery-workaround"))) {
                    xaresourcewrappers.put(
                        "com.sybase.jdbc2.jdbc.SybXADataSource",
                        new SybaseXAResource());
                }
        
                if (Boolean.parseBoolean(txnService.getAutomaticRecovery())) {
                    // If recovery on server startup is set, initialize other properties as well
                    Properties props = TransactionServiceProperties.getJTSProperties(serviceLocator, false);
                    DefaultTransactionService.setServerName(props);

                    if (Boolean.parseBoolean(txnService.getPropertyValue("delegated-recovery"))) {
                        _logger.warning("delegated-recovery is no longer supported!!");
                    }
                }
            }
        }
    }

    /**
     * Return true if a "null transaction context" was received
     * from the client. See EJB2.0 spec section 19.6.2.1.
     * A null tx context has no Coordinator objref. It indicates
     * that the client had an active
     * tx but the client container did not support tx interop.
     */
    public boolean isNullTransaction() {
        try {
            return com.sun.jts.pi.InterceptorImpl.isTxCtxtNull();
        } catch ( Exception ex ) {
            // sometimes JTS throws an EmptyStackException if isTxCtxtNull
            // is called outside of any CORBA invocation.
            return false;
        }
    }

    public TransactionAdminBean getTransactionAdminBean(Transaction t) 
            throws jakarta.transaction.SystemException {
        TransactionAdminBean tBean = null;
        if(t instanceof com.sun.jts.jta.TransactionImpl) {
            String id = ((com.sun.jts.jta.TransactionImpl)t).getTransactionId();
            long startTime = ((com.sun.jts.jta.TransactionImpl)t).getStartTime();
            long elapsedTime = System.currentTimeMillis() - startTime;
            String status = JavaEETransactionManagerSimplified.getStatusAsString(t.getStatus());

            JavaEETransactionImpl tran = (JavaEETransactionImpl)globalTransactions.get(t);
            if(tran != null) {
                tBean = ((JavaEETransactionManagerSimplified)javaEETM).getTransactionAdminBean(tran);

                // Override with JTS values
                tBean.setIdentifier(t);
                tBean.setId(id);
                tBean.setStatus(status);
                tBean.setElapsedTime(elapsedTime);
                if (tBean.getComponentName() == null) {
                    tBean.setComponentName("unknown");
                }
            } else {
                tBean = new TransactionAdminBean(t, id, status, elapsedTime,
                                             "unknown", null);
            }
        } else {
            tBean = ((JavaEETransactionManagerSimplified)javaEETM).getTransactionAdminBean(t);
        }
        return tBean;
    }

    /** {@inheritDoc}
    */
    public String getTxLogLocation() {
        if (Configuration.getServerName() == null) {
            // If server name is null, the properties were not fully initialized
            Properties props = TransactionServiceProperties.getJTSProperties(serviceLocator, false);
            DefaultTransactionService.setServerName(props);
        }

        return Configuration.getDirectory(Configuration.LOG_DIRECTORY,
                                                 Configuration.JTS_SUBDIRECTORY,
                                                 new int[1]);
    }

    /** {@inheritDoc}
    */
    public void registerRecoveryResourceHandler(XAResource xaResource) {
            ResourceRecoveryManagerImpl.registerRecoveryResourceHandler(xaResource);
    }

    public Lock getReadLock() {
        return lock;
    }

    public void acquireWriteLock() {
        if(com.sun.jts.CosTransactions.AdminUtil.isFrozenAll()){
            //multiple freezes will hang this thread, therefore just return
            return;
        }
        com.sun.jts.CosTransactions.AdminUtil.freezeAll();

/** XXX Do we need to check twice? XXX **
        if(lock.isWriteLocked()){
            //multiple freezes will hang this thread, therefore just return
            return;
        }
** XXX Do we need to check twice? XXX **/

        lock.acquireWriteLock();
    }

    public void releaseWriteLock() {
        if(com.sun.jts.CosTransactions.AdminUtil.isFrozenAll()){
            com.sun.jts.CosTransactions.AdminUtil.unfreezeAll();
        }

/** XXX Do we need to check twice? XXX **
        if(lock.isWriteLocked()){
            lock.releaseWriteLock();
        }
** XXX Do we need to check twice? XXX **/

        lock.releaseWriteLock();
    }

    public boolean isWriteLocked() {
        return com.sun.jts.CosTransactions.AdminUtil.isFrozenAll();
    }

    public static JavaEETransactionManagerJTSDelegate getInstance() {
        return instance;
    }

    private static void setInstance(JavaEETransactionManagerJTSDelegate new_instance) {
        if (instance == null) {
            instance = new_instance;
        }
    }

    public void initXA() {
        setTransactionManager();
    }

    private static class ReadWriteLock implements Lock {
        private static final RWLock freezeLock = new RWLock();

        public void lock() { 
            freezeLock.acquireReadLock(); 
        }
        
        public void unlock() { 
            freezeLock.releaseReadLock(); 
        }

        private void acquireWriteLock() { 
            freezeLock.acquireWriteLock();
        }

        private void releaseWriteLock() { 
            freezeLock.releaseWriteLock();
        }

        public void lockInterruptibly() throws InterruptedException {
            throw new UnsupportedOperationException();
        }

        public  boolean tryLock() {
            throw new UnsupportedOperationException();
        }

        public boolean tryLock(long timeout, TimeUnit unit) 
                throws InterruptedException {
            throw new UnsupportedOperationException();
        }

        public Condition newCondition() {
            throw new UnsupportedOperationException();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy