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

com.hazelcast.transaction.impl.XAResourceImpl Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * Copyright (c) 2008-2013, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.transaction.impl;

import com.hazelcast.logging.ILogger;
import com.hazelcast.spi.annotation.Beta;
import com.hazelcast.spi.impl.NodeEngineImpl;
import com.hazelcast.transaction.TransactionException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import java.util.concurrent.TimeUnit;

import static com.hazelcast.transaction.impl.Transaction.State;

@Beta
public class XAResourceImpl implements XAResource {

    private final TransactionManagerServiceImpl transactionManager;

    private final TransactionContextImpl transactionContext;
    private final ILogger logger;
    private boolean isTimeoutSet;

    private int transactionTimeoutSeconds;

    public XAResourceImpl(TransactionManagerServiceImpl transactionManager,
                          TransactionContextImpl transactionContext, NodeEngineImpl nodeEngine) {
        this.transactionManager = transactionManager;
        this.transactionContext = transactionContext;
        this.logger = nodeEngine.getLogger(XAResourceImpl.class);
    }

    //XAResource --START
    @Override
    public synchronized void start(Xid xid, int flags) throws XAException {
        nullCheck(xid);
        switch (flags) {
            case TMNOFLAGS:
                if (getTransaction(xid) != null) {
                    final XAException xaException = new XAException(XAException.XAER_DUPID);
                    logger.severe("Duplicate xid: " + xid, xaException);
                    throw xaException;
                }
                try {
                    final Transaction transaction = getTransaction();
                    transactionManager.addManagedTransaction(xid, transaction);
                    transaction.begin();
                } catch (IllegalStateException e) {
                    logger.severe(e);
                    throw new XAException(XAException.XAER_INVAL);
                }
                break;
            case TMRESUME:
            case TMJOIN:
                break;
            default:
                throw new XAException(XAException.XAER_INVAL);
        }
    }

    @Override
    public synchronized void end(Xid xid, int flags) throws XAException {
        nullCheck(xid);
        final TransactionImpl transaction = (TransactionImpl) getTransaction();
        final SerializableXID sXid = transaction.getXid();
        if (sXid == null || !sXid.equals(xid)) {
            logger.severe("started xid: " + sXid + " and given xid : " + xid + " not equal!!!");
        }
        validateTx(transaction, State.ACTIVE);

        switch (flags) {
            case XAResource.TMSUCCESS:
                //successfully end.
                break;
            case XAResource.TMFAIL:
                transaction.setRollbackOnly();
                throw new XAException(XAException.XA_RBROLLBACK);
//                break;
            case XAResource.TMSUSPEND:
                break;
            default:
                throw new XAException(XAException.XAER_INVAL);
        }
    }

    @Override
    public synchronized int prepare(Xid xid) throws XAException {
        nullCheck(xid);
        final TransactionImpl transaction = (TransactionImpl) getTransaction();
        final SerializableXID sXid = transaction.getXid();
        if (sXid == null || !sXid.equals(xid)) {
            logger.severe("started xid: " + sXid + " and given xid : " + xid + " not equal!!!");
        }
        validateTx(transaction, State.ACTIVE);

        try {
            transaction.prepare();
        } catch (TransactionException e) {
            throw new XAException(XAException.XAER_RMERR);
        }
        return XAResource.XA_OK;
    }

    @Override
    public synchronized void commit(Xid xid, boolean onePhase) throws XAException {
        nullCheck(xid);

        final Transaction transaction = getTransaction(xid);

        if (onePhase) {
            validateTx(transaction, State.ACTIVE);
            transaction.prepare();
        }

        validateTx(transaction, State.PREPARED);

        try {
            transaction.commit();
            transactionManager.removeManagedTransaction(xid);
        } catch (TransactionException e) {
            throw new XAException(XAException.XAER_RMERR);
        }
    }

    @Override
    public synchronized void rollback(Xid xid) throws XAException {
        nullCheck(xid);
        final Transaction transaction = getTransaction(xid);
        //NO_TXN means do not validate state
        validateTx(transaction, State.NO_TXN);
        try {
            transaction.rollback();
            transactionManager.removeManagedTransaction(xid);
        } catch (TransactionException e) {
            throw new XAException(XAException.XAER_RMERR);
        }
    }

    @Override
    public synchronized void forget(Xid xid) throws XAException {
        throw new XAException(XAException.XAER_PROTO);
    }

    @Override
    public synchronized boolean isSameRM(XAResource xaResource) throws XAException {
        if (xaResource instanceof XAResourceImpl) {
            XAResourceImpl other = (XAResourceImpl) xaResource;
            return transactionManager.getGroupName().equals(other.transactionManager.getGroupName());
        } else {
            // since XaResourceProxy is in client package and client package has dependency on ours,
            // we give our resource to them to check whether they are the same.
            return xaResource.isSameRM(this);
        }
    }

    @Override
    public synchronized Xid[] recover(int flag) throws XAException {
        return transactionManager.recover();
    }

    @Override
    public synchronized int getTransactionTimeout() throws XAException {
        long timeoutMillis = transactionContext.getTransaction().getTimeoutMillis();
        return (int) TimeUnit.MILLISECONDS.toSeconds(timeoutMillis);
    }

    @Override
    public synchronized boolean setTransactionTimeout(int seconds) throws XAException {
        if (transactionContext.setTransactionTimeout(seconds)) {
            this.transactionTimeoutSeconds = getTransactionTimeout();
            return true;
        }
        return false;
    }

    //XAResource --END

    public String getGroupName() {
        return transactionManager.getGroupName();
    }

    private void nullCheck(Xid xid) throws XAException {
        if (xid == null) {
            final XAException xaException = new XAException(XAException.XAER_INVAL);
            logger.severe("Xid cannot be null!!!", xaException);
            throw xaException;
        }
    }

    private void validateTx(Transaction tx, State state) throws XAException {
        if (tx == null) {
            final XAException xaException = new XAException(XAException.XAER_NOTA);
            logger.severe("Transaction is not available!!!", xaException);
            throw xaException;
        }
        final State txState = tx.getState();
        switch (state) {
            case ACTIVE:
                if (txState != State.ACTIVE) {
                    final XAException xaException = new XAException(XAException.XAER_NOTA);
                    logger.severe("Transaction is not active!!! state: " + txState, xaException);
                    throw xaException;
                }
                break;
            case PREPARED:
                if (txState != State.PREPARED) {
                    final XAException xaException = new XAException(XAException.XAER_INVAL);
                    logger.severe("Transaction is not prepared!!! state: " + txState, xaException);
                    throw xaException;
                }
                break;
            default:
                break;
        }
    }

    private Transaction getTransaction(Xid xid) {
        return transactionManager.getManagedTransaction(xid);
    }

    private Transaction getTransaction() {
        return transactionContext.getTransaction();
    }

    @Override
    public String toString() {
        final String txnId = transactionContext.getTxnId();
        final StringBuilder sb = new StringBuilder("XAResourceImpl{");
        sb.append("txdId=").append(txnId);
        sb.append(", transactionTimeoutSeconds=").append(transactionTimeoutSeconds);
        sb.append('}');
        return sb.toString();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy