001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.ra;
018
019import javax.jms.JMSException;
020import javax.resource.ResourceException;
021import javax.resource.spi.LocalTransaction;
022import javax.transaction.xa.XAException;
023import javax.transaction.xa.XAResource;
024import javax.transaction.xa.Xid;
025
026import org.apache.activemq.TransactionContext;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029
030/**
031 * Used to provide a LocalTransaction and XAResource to a JMS session.
032 */
033public class LocalAndXATransaction implements XAResource, LocalTransaction {
034    private static final Logger LOG = LoggerFactory.getLogger(LocalAndXATransaction.class);
035
036    private TransactionContext transactionContext;
037    private boolean inManagedTx;
038
039    public LocalAndXATransaction(TransactionContext transactionContext) {
040        this.transactionContext = transactionContext;
041    }
042
043    public void setTransactionContext(TransactionContext transactionContext) {
044        this.transactionContext = transactionContext;
045    }
046
047    public void setInManagedTx(boolean inManagedTx) throws JMSException {
048        this.inManagedTx = inManagedTx;
049        if (!inManagedTx) {
050            transactionContext.cleanup();
051        }
052    }
053
054    public void begin() throws ResourceException {
055        try {
056            transactionContext.begin();
057            setInManagedTx(true);
058        } catch (JMSException e) {
059            throw new ResourceException("begin failed.", e);
060        }
061    }
062
063    public void commit() throws ResourceException {
064        try {
065            transactionContext.commit();
066        } catch (JMSException e) {
067            throw new ResourceException("commit failed.", e);
068        } finally {
069            try {
070                setInManagedTx(false);
071            } catch (JMSException e) {
072                throw new ResourceException("commit failed.", e);
073            }
074        }
075    }
076
077    public void rollback() throws ResourceException {
078        try {
079            transactionContext.rollback();
080        } catch (JMSException e) {
081            throw new ResourceException("rollback failed.", e);
082        } finally {
083            try {
084                setInManagedTx(false);
085            } catch (JMSException e) {
086                throw new ResourceException("rollback failed.", e);
087            }
088        }
089    }
090
091    public void commit(Xid arg0, boolean arg1) throws XAException {
092        transactionContext.commit(arg0, arg1);
093    }
094
095    public void end(Xid arg0, int arg1) throws XAException {
096        LOG.debug("{} end {} with {}", new Object[]{this, arg0, arg1});
097        try {
098            transactionContext.end(arg0, arg1);
099        } finally {
100            try {
101                setInManagedTx(false);
102            } catch (JMSException e) {
103                throw (XAException)new XAException(XAException.XAER_PROTO).initCause(e);
104            }
105            if ((arg1 & TMFAIL) != 0) {
106                // do no further work in this context
107                LOG.debug("Marking transaction: {} rollbackOnly", this);
108                transactionContext.setRollbackOnly(true);
109            }
110        }
111    }
112
113    public void forget(Xid arg0) throws XAException {
114        transactionContext.forget(arg0);
115    }
116
117    public int getTransactionTimeout() throws XAException {
118        return transactionContext.getTransactionTimeout();
119    }
120
121    public boolean isSameRM(XAResource xaresource) throws XAException {
122        boolean isSame = false;
123        if (xaresource != null) {
124            // Do we have to unwrap?
125            if (xaresource instanceof LocalAndXATransaction) {
126                xaresource = ((LocalAndXATransaction)xaresource).transactionContext;
127            }
128            isSame = transactionContext.isSameRM(xaresource);
129        }
130        LOG.trace("{} isSameRM({}) = {}", new Object[]{this, xaresource, isSame});
131        return isSame;
132    }
133
134    public int prepare(Xid arg0) throws XAException {
135        return transactionContext.prepare(arg0);
136    }
137
138    public Xid[] recover(int arg0) throws XAException {
139        Xid[] answer = null;
140        LOG.trace("{} recover({})", new Object[]{this, arg0});
141        answer = transactionContext.recover(arg0);
142        LOG.trace("{} recover({}) = {}", new Object[]{this, arg0, answer});
143        return answer;
144    }
145
146    public void rollback(Xid arg0) throws XAException {
147        transactionContext.rollback(arg0);
148    }
149
150    public boolean setTransactionTimeout(int arg0) throws XAException {
151        return transactionContext.setTransactionTimeout(arg0);
152    }
153
154    public void start(Xid arg0, int arg1) throws XAException {
155        LOG.trace("{} start {} with {}", new Object[]{this, arg0, arg1});
156        transactionContext.start(arg0, arg1);
157        try {
158            setInManagedTx(true);
159        } catch (JMSException e) {
160            throw (XAException)new XAException(XAException.XAER_PROTO).initCause(e);
161        }
162    }
163
164    public boolean isInManagedTx() {
165        return inManagedTx;
166    }
167
168    public void cleanup() {
169        transactionContext.cleanup();
170        inManagedTx = false;
171    }
172
173    @Override
174    public String toString() {
175        return "[" + super.toString() + "," + transactionContext + "]";
176    }
177}