/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2000-2010 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.
*/
package com.sun.messaging.bridge.service.jms.tx;
import java.util.Map;
import java.util.HashMap;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.transaction.*;
import javax.transaction.xa.*;
import java.lang.IllegalStateException;
import java.lang.SecurityException;
import com.sun.messaging.bridge.service.FaultInjection;
import com.sun.messaging.bridge.service.jms.JMSBridge;
import com.sun.messaging.bridge.service.jms.resources.JMSBridgeResources;
/**
* @author amyk
*/
public class XAParticipant {
private Logger _logger = null;
private FaultInjection _fi = FaultInjection.getInjection();
private enum XAState {
NOT_STARTED, START_FAILED, STARTED,
END_FAILED, ENDED,
PREPARE_FAILED, PREPARED,
COMMIT_FAILED, COMMITTED,
ROLLBACK_FAILED, ROLLEDBACK,
ROLLEDBACK_ONCOMMIT, ROLLEDBACK_ONCOMMIT_1PHASE,
COMMITTED_ONROLLBACK };
private XAState _state = XAState.NOT_STARTED;
private String _rm = null;
private XAResource _xar = null;
private BranchXid _bxid = null;
private boolean _recovery = false;
private static JMSBridgeResources _jbr = JMSBridge.getJMSBridgeResources();
public XAParticipant(String rm, XAResource xar, BranchXid bxid)
throws SystemException {
this(rm, xar, bxid, false);
}
public XAParticipant(String rm, XAResource xar,
BranchXid bxid, boolean recover)
throws SystemException {
if (rm == null) {
throw new SystemException("null RM name");
}
if (xar == null) {
throw new SystemException("null XAResource object");
}
if (bxid == null) {
throw new SystemException("null branch xid");
}
_bxid = bxid;
_xar = xar;
_rm = rm;
_recovery = recover;
_state = XAState.NOT_STARTED;
}
protected void setLogger(Logger logger) {
_logger = logger;
}
public BranchXid getBranchXid() {
return _bxid;
}
public void start(int flags) throws RollbackException, SystemException {
if (_state != XAState.NOT_STARTED) {
_logger.log(Level.SEVERE, "start called at an illegal state "+this);
throw new IllegalStateException(toString());
}
Exception ex = null;
try {
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_START_1, p, "javax.transaction.xa.XAException", true);
}
_xar.start(_bxid, flags);
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_START_2, p, "javax.transaction.xa.XAException", true);
}
_state = XAState.STARTED;
} catch(XAException e) {
switch (e.errorCode) {
case XAException.XA_RBROLLBACK:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
_state = XAState.NOT_STARTED;
_logger.log(Level.SEVERE, "start returns XA_RB exception from "+this, e);
ex = new RollbackException(xaEString(e.errorCode)+": "+e.getMessage()+" on start tranaction from "+this);
ex.initCause(e);
throw (RollbackException)ex;
default:
_state = XAState.START_FAILED;
_logger.log(Level.SEVERE, "start failed from "+this, e);
ex = new RollbackException(xaEString(e.errorCode)+": "+e.getMessage()+" on start transaction from "+this);
ex.initCause(e);
throw (RollbackException)ex;
}
} catch (Throwable t) {
_state = XAState.START_FAILED;
_logger.log(Level.SEVERE, "start failed from "+this, t);
String emsg = t.getMessage();
if (t instanceof XAException) {
emsg = xaEString(((XAException)t).errorCode)+": "+t.getMessage();
}
ex = new SystemException(emsg+" on start transaction from "+this);
ex.initCause(t);
throw (SystemException)ex;
}
}
public void end(int flags) throws IllegalStateException,
RollbackException,
SystemException {
if (_state != XAState.STARTED &&
_state != XAState.START_FAILED &&
_state != XAState.END_FAILED) {
_logger.log(Level.SEVERE, "end called at an illegal state "+this);
throw new IllegalStateException(toString());
}
Exception ex = null;
try {
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_END_1, p, "javax.transaction.xa.XAException", true);
}
_xar.end(_bxid, flags);
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_END_2, p, "javax.transaction.xa.XAException", true);
}
_state = XAState.ENDED;
} catch(XAException e) {
switch (e.errorCode) {
case XAException.XA_RBROLLBACK:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
_state = XAState.ENDED;
if (_logger.isLoggable(Level.FINEST)) {
_logger.log(Level.INFO, "XAResource.end() returned XA_RB exception from "+this, e);
} else {
_logger.log(Level.INFO, "XAResource.end() returned XA_RB exception from "+this);
}
ex = new RollbackException(xaEString(e.errorCode)+": "+e.getMessage()+" on end transaction from "+this);
ex.initCause(e);
throw (RollbackException)ex;
default:
_state = XAState.END_FAILED;
_logger.log(Level.SEVERE, "end failed from "+this, e);
ex = new SystemException(xaEString(e.errorCode)+": "+e.getMessage()+ " on end transaction from "+this);
ex.initCause(e);
throw (SystemException)ex;
}
} catch (Throwable t) {
_logger.log(Level.SEVERE, "end failed from "+this, t);
_state = XAState.END_FAILED;
String emsg = t.getMessage();
if (t instanceof XAException) {
emsg = xaEString(((XAException)t).errorCode)+": "+t.getMessage();
}
ex = new SystemException(emsg+ " on end transaction from "+this);
ex.initCause(t);
throw (SystemException)ex;
}
}
public void rollback() throws IllegalStateException,
SystemException,
HeuristicCommitException,
HeuristicRollbackException,
HeuristicMixedException {
if (!_recovery &&
_state != XAState.ENDED &&
_state != XAState.END_FAILED &&
_state != XAState.PREPARED &&
_state != XAState.PREPARE_FAILED &&
_state != XAState.ROLLEDBACK &&
_state != XAState.ROLLBACK_FAILED) {
_logger.log(Level.SEVERE, "rollback called at an illegal state "+this);
throw new IllegalStateException(toString());
}
if (_state == XAState.ROLLEDBACK) {
_logger.log(Level.INFO, _jbr.getString(_jbr.I_ALREADY_ROLLEDBACK, this.toString()));
return;
}
Exception ex = null;
try {
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_ROLLBACK_1, p, "javax.transaction.xa.XAException", true);
}
_xar.rollback(_bxid);
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_ROLLBACK_2, p, "javax.transaction.xa.XAException", true);
}
_state = XAState.ROLLEDBACK;
} catch(XAException e) {
switch (e.errorCode) {
case XAException.XA_HEURCOM:
_logger.log(Level.SEVERE, _bxid+" has been heuristically committed by "+this);
_state = XAState.COMMITTED_ONROLLBACK;
ex = new HeuristicCommitException(xaEString(e.errorCode)+": "+e.getMessage()+" on rollback transaction from "+this);
ex.initCause(e);
throw (HeuristicCommitException)ex;
case XAException.XA_HEURHAZ:
_logger.log(Level.SEVERE,
_bxid+" may have been heuristically completed by "+this);
case XAException.XA_HEURMIX:
_logger.log(Level.SEVERE,
_bxid+" has been heuristically partialy committed or partially rolledback by "+this);
_state = XAState.ROLLBACK_FAILED;
ex = new HeuristicMixedException(xaEString(e.errorCode)+": "+e.getMessage()+" on rollback transaction from "+this);
ex.initCause(e);
throw (HeuristicMixedException)ex;
case XAException.XA_HEURRB:
_state = XAState.ROLLEDBACK;
try {
_xar.forget(_bxid);
return;
} catch (Throwable t) {
String emsg = "Failed to forget heuristically rolledback transaction from "+this;
_logger.log(Level.WARNING, emsg, t);
ex = new HeuristicRollbackException(emsg);
ex.initCause(e);
throw (HeuristicRollbackException)ex;
}
case XAException.XA_RBROLLBACK:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
_state = XAState.ROLLEDBACK;
_logger.log(Level.INFO, _bxid+" reported already rolled back by "+this);
return;
default:
_state = XAState.ROLLBACK_FAILED;
ex = new SystemException(xaEString(e.errorCode)+": "+e.getMessage()+" on rollback transaction from "+this);
ex.initCause(e);
throw (SystemException)ex;
}
} catch (Throwable t) {
_state = XAState.ROLLBACK_FAILED;
String emsg = t.getMessage();
if (t instanceof XAException) {
emsg = xaEString(((XAException)t).errorCode)+": "+t.getMessage();
}
ex = new SystemException(emsg+" on rollback transaction from "+this);
ex.initCause(t);
throw (SystemException)ex;
}
}
public void prepare() throws IllegalStateException,
RollbackException,
SystemException {
if (_state != XAState.ENDED) {
throw new IllegalStateException(toString());
}
Exception ex = null;
int vote;
try {
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_PREPARE_1, p, "javax.transaction.xa.XAException", true);
}
vote = _xar.prepare(_bxid);
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_PREPARE_2, p, "javax.transaction.xa.XAException", true);
}
if (vote == XAResource.XA_OK) {
_state = XAState.PREPARED;
} else if (vote == XAResource.XA_RDONLY) {
_state = XAState.COMMITTED;
} else {
throw new SystemException(
"Unexpected prepare return :"+vote+" from "+this);
}
} catch(XAException e) {
switch (e.errorCode) {
case XAException.XA_RBROLLBACK:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
_state = XAState.ROLLEDBACK;
ex = new RollbackException(xaEString(e.errorCode)+": "+e.getMessage()+" on prepare transaction from "+this);
ex.initCause(e);
throw (RollbackException)ex;
default:
_state = XAState.PREPARE_FAILED;
ex = new SystemException(xaEString(e.errorCode)+": "+e.getMessage()+"on prepare transaction from "+this);
ex.initCause(e);
throw (SystemException)ex;
}
} catch (Throwable t) {
_state = XAState.PREPARE_FAILED;
if (t instanceof SystemException) {
throw (SystemException)t;
}
String emsg = t.getMessage();
if (t instanceof XAException) {
emsg = xaEString(((XAException)t).errorCode)+": "+t.getMessage();
}
ex = new SystemException(emsg+"on prepare transaction from "+this);
ex.initCause(t);
throw (SystemException)ex;
}
}
public void commit(boolean onePhase) throws IllegalStateException,
RollbackException,
HeuristicCommitException,
HeuristicMixedException,
HeuristicRollbackException,
SystemException {
if (onePhase) {
if (_state != XAState.ENDED) {
throw new IllegalStateException(toString());
}
} else {
if (!_recovery && _state != XAState.PREPARED && _state != XAState.COMMITTED) {
throw new IllegalStateException(toString());
}
}
if (_state == XAState.COMMITTED) {
_logger.log(Level.INFO, _jbr.getString(_jbr.I_ALREADY_COMMITTED, this.toString()));
}
Exception ex = null;
try {
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_COMMIT_1, p, "javax.transaction.xa.XAException", true);
}
_xar.commit(_bxid, onePhase);
if (_fi.FAULT_INJECTION) {
Map p = new HashMap();
p.put(FaultInjection.CFREF_PROP, _rm);
_fi.setLogger(_logger);
_fi.checkFaultAndThrowException(FaultInjection.FAULT_XA_COMMIT_2, p, "javax.transaction.xa.XAException", true);
}
_state = XAState.COMMITTED;
} catch(XAException e) {
switch (e.errorCode) {
case XAException.XA_HEURCOM:
_state = XAState.COMMITTED;
_logger.log(Level.INFO, _bxid+" has been heuristically committed by "+this);
try {
_xar.forget(_bxid);
return;
} catch (Throwable t) {
String emsg = "Failed to forget heuristically committed transaction from "+this;
_logger.log(Level.WARNING, emsg, t);
ex = new HeuristicCommitException(emsg);
ex.initCause(e);
throw (HeuristicCommitException)ex;
}
case XAException.XA_HEURHAZ:
_logger.log(Level.SEVERE,
_bxid+" may have been heuristically completed by "+this);
case XAException.XA_HEURMIX:
_logger.log(Level.SEVERE,
_bxid+" has been heuristically partialy committed or partially rolledback by "+this);
_state = XAState.COMMIT_FAILED;
ex = new HeuristicMixedException(xaEString(e.errorCode)+": "+e.getMessage()+" on commit transaction from "+this);
ex.initCause(e);
throw (HeuristicMixedException)ex;
case XAException.XA_HEURRB:
_state = XAState.ROLLEDBACK_ONCOMMIT;
_logger.log(Level.SEVERE, _bxid+" has been heuristically rolledback by "+this);
ex = new HeuristicRollbackException(xaEString(e.errorCode)+": "+e.getMessage()+" on commit transaction from "+this);
ex.initCause(e);
throw (HeuristicRollbackException)ex;
case XAException.XA_RBROLLBACK:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
_state = XAState.ROLLEDBACK_ONCOMMIT;
if (!onePhase) {
_logger.log(Level.SEVERE, "Unexpected rollback exception on commit from "+this);
ex = new SystemException("Unexpected "+xaEString(e.errorCode)+": "+e.getMessage()+" on commit transaction from "+this);
ex.initCause(e);
throw (SystemException)ex;
}
ex = new RollbackException(xaEString(e.errorCode)+": "+e.getMessage()+" on commit transation from "+this);
ex.initCause(e);
throw (RollbackException)ex;
default:
_state = XAState.COMMIT_FAILED;
ex = new SystemException(xaEString(e.errorCode)+": "+e.getMessage()+" on commit transation from "+this);
ex.initCause(e);
throw (SystemException)ex;
}
} catch (Throwable t) {
_state = XAState.START_FAILED;
String emsg = t.getMessage();
if (t instanceof XAException) {
emsg = xaEString(((XAException)t).errorCode)+": "+t.getMessage();
}
ex = new SystemException(emsg+" on commit transaction from "+this);
ex.initCause(t);
throw (SystemException)ex;
}
}
public String getRM() {
return _rm;
}
public boolean equals(Object o) {
if (!(o instanceof XAParticipant)) return false;
XAParticipant that = (XAParticipant)o;
if (that == this) return true;
return (_rm.equals(that._rm) &&
_bxid.equals(that._bxid) &&
_xar.equals(that._xar));
}
public int hashCode() {
return _xar.hashCode();
}
public String toString() {
return _bxid+"["+_rm+":"+_xar+"]"+toString(_state);
}
public static String toString(XAState state) {
switch(state) {
case NOT_STARTED: return "NOT_STARTED";
case START_FAILED: return "STARTED_FAILED";
case STARTED: return "STARTED";
case END_FAILED: return "END_FAILED";
case ENDED: return "ENDED";
case PREPARE_FAILED: return "PREPARE_FAILED";
case PREPARED: return "PREPARED";
case COMMIT_FAILED: return "COMMIT_FAILED";
case COMMITTED: return "COMMITTED";
case ROLLBACK_FAILED: return "ROLLBACK_FAILED";
case ROLLEDBACK: return "ROLLEDBACK";
default: return "UNKNOWN";
}
}
private String xaEString(int errorCode) {
switch (errorCode) {
case XAException.XA_RBROLLBACK: return "XA_RBROLLBACK";
case XAException.XA_RBCOMMFAIL: return "XA_RBCOMMFAIL";
case XAException.XA_RBDEADLOCK: return "XA_RBDEADLOCK";
case XAException.XA_RBINTEGRITY: return "XA_RBINTEGRITY";
case XAException.XA_RBOTHER: return "XA_RBOTHER";
case XAException.XA_RBPROTO: return "XA_RBPROTO";
case XAException.XA_RBTIMEOUT: return "XA_RBTIMEOUT";
case XAException.XA_RBTRANSIENT: return "XA_RBTRANSIENT";
case XAException.XA_HEURCOM: return "XA_HEURCOM";
case XAException.XA_HEURHAZ: return "XA_HEURHAZ";
case XAException.XA_HEURMIX: return "XA_HEURMIX";
case XAException.XA_HEURRB: return "XA_HEURRB";
case XAException.XA_NOMIGRATE: return "XA_NOMIGRATE";
case XAException.XA_RETRY: return "XA_RETRY";
case XAException.XA_RDONLY: return "XA_RDONLY";
case XAException.XAER_ASYNC: return "XAER_ASYNC";
case XAException.XAER_RMERR: return "XAER_RMERR";
case XAException.XAER_NOTA: return "XAER_NOTA";
case XAException.XAER_INVAL: return "XAER_INVAL";
case XAException.XAER_PROTO: return "XAER_PROTO";
case XAException.XAER_RMFAIL: return "XAER_RMFAIL";
case XAException.XAER_DUPID: return "XAER_DUPID";
case XAException.XAER_OUTSIDE: return "XAER_OUTSIDE";
default: return "UNKNOWN";
}
}
}