org.nuxeo.ecm.core.blob.storage.impl.XASession.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.core.blob.storage.impl.XASession.java

Source

/*
 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     bstefanescu
 *
 * $Id$
 */

package org.nuxeo.ecm.core.blob.storage.impl;

import java.io.InputStream;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.resource.ResourceException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.blob.storage.BlobResource;
import org.nuxeo.ecm.core.blob.storage.BlobStorageException;
import org.nuxeo.ecm.core.blob.storage.BlobStorageSession;

/**
 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
 *
 */
public class XASession implements XAResource, BlobStorageSession {

    private final static Log log = LogFactory.getLog(XASession.class);

    protected final static int DEAULT_TIMEOUT = 10;

    protected int timeout = DEAULT_TIMEOUT;
    protected DefaultBlobStorage storage;

    protected ConcurrentMap<Xid, TransactionContext> transactions;

    protected TransactionContext transaction;

    /**
     *
     */
    public XASession(DefaultBlobStorage storage) {
        this.storage = storage;
        this.transactions = new ConcurrentHashMap<Xid, TransactionContext>();
    }

    /**
     * @return the storage.
     */
    public DefaultBlobStorage getStorage() {
        return storage;
    }

    public BlobResource get(String hash) throws BlobStorageException {
        return storage.get(hash);
    }

    public BlobResource put(InputStream in) throws BlobStorageException {
        if (transaction != null) {
            return transaction.put(in);
        }
        PutOperation op = new PutOperation(storage, in);
        op.execute();
        return op.getResult();
    }

    public void remove(String hash) throws BlobStorageException {
        if (transaction != null) {
            transaction.remove(hash);
        } else {
            new RemoveOperation(storage, hash).execute();
        }
    }

    public int getTransactionTimeout() throws XAException {
        return timeout;
    }

    public boolean setTransactionTimeout(int arg0) throws XAException {
        timeout = arg0 == 0 ? DEAULT_TIMEOUT : arg0;
        return true;
    }

    public boolean isSameRM(XAResource xares) throws XAException {
        return xares instanceof XASession;
    }

    protected boolean isAssociated() {
        return transaction != null;
    }

    protected void associate(TransactionContext transaction) {
        this.transaction = transaction;
    }

    protected TransactionContext createTransaction(Xid xid) {
        TransactionContext tr = new TransactionContext(storage, xid);
        transactions.put(xid, tr);
        return tr;
    }

    public void start(Xid xid, int flags) throws XAException {
        if (isAssociated()) {
            log.error("Resource already associated with a transaction.");
            throw new XAException(XAException.XAER_PROTO);
        }
        TransactionContext tx = (TransactionContext) transactions.get(xid);
        if (flags == TMNOFLAGS) {
            if (tx != null) {
                throw new XAException(XAException.XAER_DUPID);
            }
            tx = createTransaction(xid);
        } else if (flags == TMJOIN) {
            if (tx == null) {
                throw new XAException(XAException.XAER_NOTA);
            }
        } else if (flags == TMRESUME) {
            if (tx == null) {
                throw new XAException(XAException.XAER_NOTA);
            }
            if (!tx.isSuspended()) {
                log.error("Unable to resume: transaction not suspended.");
                throw new XAException(XAException.XAER_PROTO);
            }
            tx.setSuspended(false);
        } else {
            throw new XAException(XAException.XAER_INVAL);
        }

        associate(tx);
    }

    public void end(Xid xid, int flags) throws XAException {
        TransactionContext tx = (TransactionContext) transactions.get(xid);
        if (tx == null) {
            throw new XAException(XAException.XAER_NOTA);
        }
        if (flags == TMSUSPEND) {
            if (!isAssociated()) {
                log.error("Resource not associated with a transaction.");
                throw new XAException(XAException.XAER_PROTO);
            }
            associate(null);
            tx.setSuspended(true);
        } else if (flags == TMFAIL || flags == TMSUCCESS) {
            if (!tx.isSuspended()) {
                if (!isAssociated()) {
                    log.error("Resource not associated with a transaction.");
                    throw new XAException(XAException.XAER_PROTO);
                }
                associate(null);
            } else {
                tx.setSuspended(false);
            }
        } else {
            throw new XAException(XAException.XAER_INVAL);
        }
    }

    public void commit(Xid xid, boolean onePhase) throws XAException {
        TransactionContext tx = (TransactionContext) transactions.get(xid);
        if (tx == null) {
            throw new XAException(XAException.XAER_NOTA);
        }
        try {
            if (onePhase) {
                tx.prepare();
            }
            tx.commit();
        } catch (ResourceException e) {
            XAException ee = new XAException(XAException.XAER_RMERR);
            ee.initCause(e);
            throw ee;
        }

        transactions.remove(xid);
    }

    public int prepare(Xid xid) throws XAException {
        TransactionContext tx = (TransactionContext) transactions.get(xid);
        if (tx == null) {
            throw new XAException(XAException.XAER_NOTA);
        }
        try {
            tx.prepare();
        } catch (ResourceException e) {
            XAException ee = new XAException(XAException.XAER_RMERR);
            ee.initCause(e);
            throw ee;
        }
        return XA_OK;
    }

    public void rollback(Xid xid) throws XAException {
        TransactionContext tx = (TransactionContext) transactions.get(xid);
        if (tx == null) {
            throw new XAException(XAException.XAER_NOTA);
        }
        try {
            tx.rollback();
        } catch (ResourceException e) {
            XAException ee = new XAException(XAException.XAER_RMERR);
            ee.initCause(e);
            throw ee;
        }
        transactions.remove(xid);
    }

    public void forget(Xid xid) throws XAException {
        // no recovery support
    }

    public Xid[] recover(int flag) throws XAException {
        // no recovery support
        return new Xid[0];
    }

}