/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.narayana.rest.bridge.inbound;
import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.SubordinationManager;
import org.jboss.logging.Logger;
import org.jboss.narayana.rest.integration.api.Aborted;
import org.jboss.narayana.rest.integration.api.HeuristicException;
import org.jboss.narayana.rest.integration.api.HeuristicType;
import org.jboss.narayana.rest.integration.api.Participant;
import org.jboss.narayana.rest.integration.api.ParticipantException;
import org.jboss.narayana.rest.integration.api.Prepared;
import org.jboss.narayana.rest.integration.api.ReadOnly;
import org.jboss.narayana.rest.integration.api.Vote;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.io.Serializable;
/**
* @author <a href="mailto:gytis@redhat.com">Gytis Trikleris</a>
*/
public class InboundBridgeParticipant implements Participant, Serializable {
private static final Logger LOG = Logger.getLogger(InboundBridgeParticipant.class);
private final Xid xid;
public InboundBridgeParticipant(final Xid xid) {
this.xid = xid;
}
@Override
public Vote prepare() {
if (LOG.isTraceEnabled()) {
LOG.trace("InboundBridgeParticipant.prepare: xid=" + xid);
}
startBridge();
final Vote outcome = prepareSubordinate();
if (!(outcome instanceof Prepared)) {
cleanup();
} else {
stopBridge();
}
if (LOG.isTraceEnabled()) {
LOG.trace("InboundBridgeParticipant.prepare: xid=" + xid + ", outcome=" + outcome.getClass().getName());
}
return outcome;
}
@Override
public void commit() throws HeuristicException {
if (LOG.isTraceEnabled()) {
LOG.trace("InboundBridgeParticipant.commit: xid=" + xid);
}
startBridge();
try {
commitSubordinate();
} finally {
cleanup();
}
}
@Override
public void commitOnePhase() {
if (LOG.isTraceEnabled()) {
LOG.trace("InboundBridgeParticipant.commitOnePhase: xid=" + xid);
}
startBridge();
final Vote outcome = prepareSubordinate();
if (LOG.isTraceEnabled()) {
LOG.trace("InboundBridgeParticipant.commitOnePhase: xid=" + xid + ", outcome=" + outcome);
}
if (outcome instanceof Prepared) {
try {
commitSubordinate();
} catch (HeuristicException e) {
}
}
cleanup();
}
@Override
public void rollback() throws HeuristicException {
if (LOG.isTraceEnabled()) {
LOG.trace("InboundBridgeParticipant.rollback: xid=" + xid);
}
startBridge();
try {
rollbackSubordinate();
} finally {
cleanup();
}
}
private void startBridge() {
final InboundBridge inboundBridge = getInboundBridge();
if (inboundBridge != null) {
inboundBridge.start();
} else {
throw new ParticipantException("Inbound bridge is not available.");
}
}
private void stopBridge() {
final InboundBridge inboundBridge = getInboundBridge();
if (inboundBridge != null) {
try {
inboundBridge.stop();
} catch (Exception e) {
LOG.warn(e.getMessage(), e);
}
}
}
private Vote prepareSubordinate() {
final InboundBridge inboundBridge = getInboundBridge();
int prepareResult = -1;
try {
if (inboundBridge != null) {
prepareResult = SubordinationManager.getXATerminator().prepare(inboundBridge.getXid());
}
} catch (XAException e) {
}
return prepareResultToVote(prepareResult);
}
private void commitSubordinate() throws HeuristicException {
final InboundBridge inboundBridge = getInboundBridge();
try {
if (inboundBridge != null) {
SubordinationManager.getXATerminator().commit(inboundBridge.getXid(), false);
}
} catch (XAException e) {
LOG.warn(e.getMessage(), e);
switch (e.errorCode) {
case XAException.XA_HEURCOM:
throw new HeuristicException(HeuristicType.HEURISTIC_COMMIT);
case XAException.XA_HEURRB:
throw new HeuristicException(HeuristicType.HEURISTIC_ROLLBACK);
case XAException.XA_HEURMIX:
throw new HeuristicException(HeuristicType.HEURISTIC_MIXED);
case XAException.XA_HEURHAZ:
throw new HeuristicException(HeuristicType.HEURISTIC_MIXED);
}
}
}
private void rollbackSubordinate() throws HeuristicException {
final InboundBridge inboundBridge = getInboundBridge();
try {
if (inboundBridge != null) {
SubordinationManager.getXATerminator().rollback(inboundBridge.getXid());
}
} catch (XAException e) {
LOG.warn(e.getMessage(), e);
switch (e.errorCode) {
case XAException.XA_HEURCOM:
throw new HeuristicException(HeuristicType.HEURISTIC_COMMIT);
case XAException.XA_HEURRB:
throw new HeuristicException(HeuristicType.HEURISTIC_ROLLBACK);
case XAException.XA_HEURMIX:
throw new HeuristicException(HeuristicType.HEURISTIC_MIXED);
case XAException.XA_HEURHAZ:
throw new HeuristicException(HeuristicType.HEURISTIC_MIXED);
}
}
}
private void cleanup() {
stopBridge();
InboundBridgeManager.getInstance().removeInboundBridge(xid);
}
private InboundBridge getInboundBridge() {
return InboundBridgeManager.getInstance().getInboundBridge(xid);
}
private Vote prepareResultToVote(final int prepareResult) {
if (prepareResult == XAResource.XA_OK) {
return new Prepared();
} else if (prepareResult == XAResource.XA_RDONLY) {
return new ReadOnly();
} else {
return new Aborted();
}
}
}