public boolean handle(IMQConnection con, Packet msg)
throws BrokerException
{
long messagetid = 0;
TransactionUID id = null;
TransactionState ts = null;
JMQXid xid = null;
Integer xaFlags = null;
boolean redeliverMsgs = false;
boolean startNextTransaction = false;
boolean setRedeliverFlag = true;
boolean isIndemp = msg.getIndempotent();
boolean replay = false;
boolean jmqonephase = false;
Boolean jmqonephaseFlag = null;
Hashtable props = null;
String reason = null;
try {
props = msg.getProperties();
if (props == null)
props = new Hashtable();
} catch (Exception ex) {
logger.log(Logger.INFO,"Internal Error: unable to retrieve "+
" properties from transaction message " + msg, ex);
props = new Hashtable();
}
// performance optimisation:
// start a new transaction immediately after commit or rollback and return
// transactionID in message ack.
// The client then does not need to make a separate call to startTransaction.
Boolean startNextTransactionBool = (Boolean)props.get("JMQStartNextTransaction");
startNextTransaction = (startNextTransactionBool == null ? false :
startNextTransactionBool.booleanValue());
Boolean redeliverMsgBool = (Boolean)props.get("JMQRedeliver");
redeliverMsgs = (redeliverMsgBool == null ? false :
redeliverMsgBool.booleanValue());
Boolean redeliverFlag = (Boolean)props.get("JMQSetRedelivered");
setRedeliverFlag = (redeliverFlag == null ? true :
redeliverFlag.booleanValue());
jmqonephaseFlag = (Boolean)props.get("JMQXAOnePhase");
jmqonephase = (jmqonephaseFlag == null ? false:jmqonephaseFlag.booleanValue());
if (DEBUG) {
logger.log(Logger.DEBUG, PacketType.getString(msg.getPacketType())+": "+
"TUID=" + id + ", JMQRedeliver=" + redeliverMsgBool+
(jmqonephaseFlag == null ? "":", JMQXAOnePhase="+jmqonephase));
}
List conlist = (List)con.getClientData(IMQConnection.TRANSACTION_LIST);
if (conlist == null) {
conlist = new ArrayList();
con.addClientData(IMQConnection.TRANSACTION_LIST, conlist);
}
// If there is a message body, then it should contain an Xid.
ByteBuffer body = msg.getMessageBodyByteBuffer();
if (body != null) {
JMQByteBufferInputStream bbis = new JMQByteBufferInputStream(body);
try {
xid = JMQXid.read(new DataInputStream(bbis));
startNextTransaction=false;
} catch (IOException e) {
logger.log(Logger.ERROR,
BrokerResources.E_INTERNAL_BROKER_ERROR,
"Could not decode xid from packet: " + e +
" Ignoring " +
PacketType.getString(msg.getPacketType()));
reason = e.getMessage();
sendReply(con, msg, msg.getPacketType() + 1,
Status.BAD_REQUEST, 0, reason);
return true;
}
}
// Get XAFlags. Note, not all packets will have this -- that's OK.
if (props != null) {
xaFlags = (Integer)props.get("JMQXAFlags");
}
// tidMap maps an old style transaction identifier to a TransactionUID.
// In iMQ2.0 the transaction identifier in the packet was an int
// generated by the client and only unique on the connection.
// In Falcon it is a long that is unique accross the cluster.
// So for 2.0 clients we allocate a TransactionUID and map the old
// style identifier to it.
//
// Get tidMap
HashMap tidMap = null;
synchronized(con) {
tidMap = (HashMap)con.getClientData(IMQConnection.TRANSACTION_IDMAP);
if (tidMap == null) {
tidMap = new HashMap();
con.addClientData(IMQConnection.TRANSACTION_IDMAP, tidMap);
}
}
// Go ahead and get the value of "JMQTransactionID" from the packet.
// may not be used in all cases.
messagetid = getJMQTransactionID(props);
// handle initial fault injections
if (fi.FAULT_INJECTION)
checkFIBeforeProcess(msg.getPacketType());
// If it is a new transaction then create a new transaction id.
// else wrap the one specified in the packet
if (msg.getPacketType() == PacketType.START_TRANSACTION &&
(xaFlags == null ||
TransactionState.isFlagSet(XAResource.TMNOFLAGS, xaFlags) )) {
// It's a START_TRANSACTION that is not a Join, or Resume.
// Allocate a new TransactionUID
if (isIndemp) { // deal with indemp flag
id = translist.getTransaction(msg.getSysMessageID().toString());
if (id != null) {
replay = true;
} else {
id = new TransactionUID();
}
} else {
id = new TransactionUID();
}
} else if (msg.getPacketType() == PacketType.RECOVER_TRANSACTION) {
if (messagetid != 0) {
// Recovering a specific transaction.
id = new TransactionUID(messagetid);
}
xid = null;
} else {
// Operation on an existing transaction
// Get TransactionUID out of packet
// If only Xid was specified need to lookup TransactionUID
if (messagetid == 0 && xid != null) {
id = translist.xidToUID(xid);
if (id != null) {
messagetid = id.longValue();
} else {
// Hmmm...haven't seen this Xid before.
// XXX I18N
logger.log(Logger.WARNING,
PacketType.getString(msg.getPacketType()) +
": Ignoring unknown XID=" + xid +
" broker will "
+ (msg.getSendAcknowledge() ?
"notify the client" :
" not notify the client" ));
if (msg.getSendAcknowledge()) {
reason = "Uknown XID " + xid;
sendReply(con, msg, msg.getPacketType() + 1,
Status.NOT_FOUND, 0, reason);
}
return true;
}
} else if (messagetid != 0) {
if (con.getClientProtocolVersion() == PacketType.VERSION1) {
// Map old style to new
synchronized(tidMap) {
id = (TransactionUID)tidMap.get(new Long(messagetid));
}
} else {
// Wrap new style
id = new TransactionUID(messagetid);
}
}
// Get the state of the transaction
if (id == null) {
logger.log(Logger.INFO,"InternalError: "
+ "Transaction ID was not passed by "
+"the jms api on a method that reqires an "
+ "existing transaction ");
sendReply(con, msg, msg.getPacketType() + 1,
Status.ERROR, 0, "Internal Error: bad MQ protocol,"
+ " missing TransactionID");
return true;
} else {
ts = translist.retrieveState(id);
}
if (ts == null) {
// Check if we've recently operated on this transaction
if (isIndemp && ( msg.getPacketType() ==
PacketType.ROLLBACK_TRANSACTION ||
msg.getPacketType() ==
PacketType.COMMIT_TRANSACTION )) {
if (msg.getSendAcknowledge()) {
sendReply(con, msg, msg.getPacketType() + 1,
Status.OK, id.longValue(), reason);
return true;
} else {
if (fi.FAULT_INJECTION) {
checkFIAfterProcess(msg.getPacketType());
checkFIAfterReply(msg.getPacketType());
}
}
} else {
ts = cacheGetState(id, con);
if (ts != null) {
// XXX l10n
logger.log(Logger.ERROR,
"Transaction ID " + id +
" has already been resolved. Ignoring request: " +
PacketType.getString(msg.getPacketType()) +
". Last state of this transaction: " +
ts.toString() +
" broker will "
+ (msg.getSendAcknowledge() ?
"notify the client" :
" not notify the client" ));
} else {
logger.log((BrokerStateHandler.shuttingDown? Logger.DEBUG : Logger.WARNING),
Globals.getBrokerResources().getKString(
(msg.getSendAcknowledge() ? BrokerResources.W_UNKNOWN_TRANSACTIONID_NOTIFY_CLIENT :
BrokerResources.W_UNKNOWN_TRANSACTIONID_NONOTIFY_CLIENT),
""+id+ "(" + messagetid + ")"+ (xid == null ? "":"XID="+xid),
PacketType.getString(msg.getPacketType())) + "\n" +
com.sun.messaging.jmq.jmsserver.util.PacketUtil.dumpPacket(msg));
}
// Only send reply if A bit is set
if (msg.getSendAcknowledge()) {
reason = "Unknown transaction " + id;
sendReply(con, msg, msg.getPacketType() + 1,
Status.NOT_FOUND, id.longValue(), reason);
}
return true;
}
}
}
if (DEBUG) {
logger.log(Logger.INFO, this.getClass().getName() + ": " +
PacketType.getString(msg.getPacketType()) + ": " +
"TUID=" + id +
" XAFLAGS=" + TransactionState.xaFlagToString(xaFlags) +
(jmqonephaseFlag == null ? "":" JMQXAOnePhase="+jmqonephase)+
" State=" + ts + " Xid=" + xid);
}
// If a packet came with an Xid, make sure it matches what
// we have in the transaction table.
if (xid != null && ts != null) {
if (ts.getXid() == null || !xid.equals(ts.getXid())) {
// This should never happen
logger.log(Logger.ERROR,
BrokerResources.E_INTERNAL_BROKER_ERROR,
"Transaction Xid mismatch. " +
PacketType.getString(msg.getPacketType()) +
" Packet has tuid=" +
id + " xid=" + xid + ", transaction table has tuid=" +
id + " xid=" + ts.getXid() +
". Using values from table.");
xid = ts.getXid();
}
}
if (xid == null && ts != null && ts.getXid() != null &&
msg.getPacketType() != PacketType.RECOVER_TRANSACTION) {
// Client forgot to put Xid in packet.
xid = ts.getXid();
logger.log(Logger.WARNING,
BrokerResources.E_INTERNAL_BROKER_ERROR,
"Transaction Xid "+xid+" not found in " +
PacketType.getString(msg.getPacketType()) +
" packet for tuid " +
id + ". Will use " + xid);
}
int status = Status.OK;
// retrieve new 4.0 properties
AutoRollbackType type = null;
long lifetime = 0;
boolean sessionLess = false;
Integer typeValue = (Integer)props.get("JMQAutoRollback");
Long lifetimeValue = (Long)props.get("JMQLifetime");
Boolean sessionLessValue = (Boolean)props.get("JMQSessionLess");
if (typeValue != null) {
type = AutoRollbackType.getType(typeValue.intValue());
}
if (lifetimeValue != null) {
lifetime = lifetimeValue.longValue();
}
if (sessionLessValue != null) {
sessionLess = sessionLessValue.booleanValue();
} else {
sessionLess = xid != null;
}
switch (msg.getPacketType()) {
case PacketType.START_TRANSACTION:
{
try {
SessionUID suid = null;
Long sessionID = (Long)props.get("JMQSessionID");
if (sessionID != null) {
suid = new SessionUID(sessionID.longValue());
}
doStart(id, conlist, con,
type, xid, sessionLess, lifetime, messagetid,
xaFlags, msg.getPacketType(), replay, msg.getSysMessageID().toString());
} catch (Exception ex) {
status = Status.ERROR;
logger.logStack(Logger.ERROR,
BrokerResources.E_INTERNAL_BROKER_ERROR,
ex.toString() + ": TUID=" + id + " Xid=" + xid, ex);
reason = ex.getMessage();
if (ex instanceof BrokerException) {
status = ((BrokerException)ex).getStatusCode();
}
}
sendReply(con, msg, PacketType.START_TRANSACTION_REPLY,
status, id.longValue(), reason);
break;
}
case PacketType.END_TRANSACTION:
try {
doEnd(msg.getPacketType(), xid, xaFlags, ts, id);
} catch (Exception ex) {
status = Status.ERROR;
reason = ex.getMessage();
if (ex instanceof BrokerException) status = ((BrokerException)ex).getStatusCode();
}
sendReply(con, msg, msg.getPacketType() + 1, status, id.longValue(), reason);
break;
case PacketType.PREPARE_TRANSACTION:
BrokerException bex = null;
try {
doPrepare(id, xaFlags, ts, msg.getPacketType(), jmqonephase, null);
} catch (Exception ex) {
status = Status.ERROR;
if ((!(ex instanceof BrokerDownException) &&
!(ex instanceof AckEntryNotFoundException)) || DEBUG_CLUSTER_TXN) {
logger.logStack(Logger.ERROR,
ex.toString() + ": TUID=" + id + " Xid=" + xid, ex);
} else {
logger.log(((ex instanceof AckEntryNotFoundException) ? Logger.WARNING:Logger.ERROR),
ex.toString() + ": TUID=" + id + " Xid=" + xid);
}
reason = ex.getMessage();
if (ex instanceof BrokerException) {
status = ((BrokerException)ex).getStatusCode();
bex = (BrokerException)ex;
}
}
sendReply(con, msg, msg.getPacketType() + 1, status,
id.longValue(), reason, bex);
break;
case PacketType.RECOVER_TRANSACTION:
Vector v = null;
if (id != null) {
// Check if specified transaction is in PREPARED state
v = new Vector();
ts = translist.retrieveState(id);
if (ts.getState() == TransactionState.PREPARED) {
v.add(id);
}
} else {
// XA Transaction only. We return all Xids on a STARTRSCAN
// and nothing on ENDRSCAN or NOFLAGS
if (xaFlags == null ||
!TransactionState.isFlagSet(XAResource.TMSTARTRSCAN,
xaFlags)) {
Hashtable hash = new Hashtable();
hash.put("JMQQuantity", new Integer(0));
sendReplyBody(con, msg,
PacketType.RECOVER_TRANSACTION_REPLY,
Status.OK, hash, null);
break;
}
// Get list of transactions in PENDING state and marshal
// the Xid's to a byte array.
v = translist.getTransactions(TransactionState.PREPARED);
}
int nIDs = v.size();
int nWritten = 0;
ByteArrayOutputStream bos =
new ByteArrayOutputStream(nIDs * JMQXid.size());
DataOutputStream dos = new DataOutputStream(bos);
for (int n = 0; n < nIDs; n++) {
TransactionUID tuid = (TransactionUID)v.get(n);
TransactionState _ts = translist.retrieveState(tuid);
if (_ts == null) {
// Should never happen
logger.log(Logger.ERROR,
BrokerResources.E_INTERNAL_BROKER_ERROR,
"Could not find state for TUID " + tuid);
continue;
}
JMQXid _xid = _ts.getXid();
if (_xid != null) {
try {
_xid.write(dos);
nWritten++;
} catch (Exception e) {
logger.log(Logger.ERROR,
BrokerResources.E_INTERNAL_BROKER_ERROR,
"Could not write Xid " +
_xid + " to message body: " +
e.toString());
}
}
}
Hashtable hash = new Hashtable();
hash.put("JMQQuantity", new Integer(nWritten));
if (id != null) {
hash.put("JMQTransactionID", new Long(id.longValue()));
}
// Send reply with serialized Xids as the body
sendReplyBody(con, msg, PacketType.RECOVER_TRANSACTION_REPLY,
Status.OK, hash, bos.toByteArray());
break;
case PacketType.COMMIT_TRANSACTION:
//XXX-LKS clean up any destination locks ???
try {
// doCommit will send reply if successful
if (xaFlags != null && jmqonephase) {
Integer newxaFlags = new Integer(xaFlags.intValue() & ~XAResource.TMONEPHASE);
doCommit(id, xid, newxaFlags, ts, conlist, true, con, msg);
} else {
doCommit(id, xid, xaFlags, ts, conlist, true, con, msg, startNextTransaction);
}
} catch (BrokerException ex) {
// doCommit has already logged error
status = ex.getStatusCode();
reason = ex.getMessage();
if (msg.getSendAcknowledge()) {
sendReply(con, msg, msg.getPacketType() + 1, status,
id.longValue(), reason, ex);
} else {
if (fi.FAULT_INJECTION) {
checkFIAfterProcess(msg.getPacketType());
checkFIAfterReply(msg.getPacketType());
}
}
}
break;
case PacketType.ROLLBACK_TRANSACTION:
{
try {
preRollback(id, xid, xaFlags, ts);
try {
// if redeliverMsgs is true, we want to redeliver
// to both active and inactive consumers
boolean processActiveConsumers = redeliverMsgs;
redeliverUnacked(id, processActiveConsumers, setRedeliverFlag);
} catch (BrokerException ex) {
logger.logStack(Logger.ERROR,
BrokerResources.E_INTERNAL_BROKER_ERROR,
"REDELIVER: " +
ex.toString() + ": TUID=" + id + " Xid=" + xid, ex);
reason = ex.getMessage();
status = ex.getStatusCode();
}
try {
doRollback(id, xid, xaFlags, ts, conlist,
con, RollbackReason.APPLICATION);
} catch (BrokerException ex) {
// doRollback has already logged error
reason = ex.getMessage();
status = ex.getStatusCode();
}
} catch (BrokerException ex) {
// preRollback has already logged error
reason = ex.getMessage();
status = ex.getStatusCode();
}
// performance optimisation
// start next transaction and return transaction id
long nextTxnID = 0;
if(startNextTransaction)
{
try {
TransactionUID nextid = new TransactionUID();
doStart(nextid, conlist, con,
type, xid, sessionLess, lifetime, 0,
xaFlags, PacketType.START_TRANSACTION, replay, msg.getSysMessageID().toString());
nextTxnID = nextid.longValue();
} catch (Exception ex) {
status = Status.ERROR;
logger.logStack(Logger.ERROR,
BrokerResources.E_INTERNAL_BROKER_ERROR,
ex.toString() + ": TUID=" + id + " Xid=" + xid, ex);