/*
* 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.jms.ra;
import javax.jms.JMSException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.HashMap;
import com.sun.messaging.jmq.util.XidImpl;
import com.sun.messaging.jmq.jmsclient.Debug;
import com.sun.messaging.jmq.jmsservice.JMSService;
import com.sun.messaging.jmq.jmsservice.JMSServiceReply;
import com.sun.messaging.jmq.jmsservice.JMSServiceException;
import com.sun.messaging.jms.ra.util.DirectXAResourceMap;
/**
* DirectXAResource encapsulates XAResource functionality for DIRECT mode.<p>
*
* The XAResource interface is a Java mapping of the industry standard
* XA interface based on the X/Open CAE Specification (Distributed
* Transaction Processing: The XA Specification).
*
* <p>The XA interface defines the contract between a Resource Manager
* and a Transaction Manager in a distributed transaction processing
* (DTP) environment. A JDBC driver or a JMS provider implements
* this interface to support the association between a global transaction
* and a database or message service connection.
*
* <p>The XAResource interface can be supported by any transactional
* resource that is intended to be used by application programs in an
* environment where transactions are controlled by an external
* transaction manager. An example of such a resource is a database
* management system. An application may access data through multiple
* database connections. Each database connection is enlisted with
* the transaction manager as a transactional resource. The transaction
* manager obtains an XAResource for each connection participating
* in a global transaction. The transaction manager uses the
* <code>start</code> method
* to associate the global transaction with the resource, and it uses the
* <code>end</code> method to disassociate the transaction from
* the resource. The resource
* manager is responsible for associating the global transaction to all
* work performed on its data between the start and end method invocations.
*
* <p>At transaction commit time, the resource managers are informed by
* the transaction manager to prepare, commit, or rollback a transaction
* according to the two-phase commit protocol.</p>
*/
public class DirectXAResource
implements XAResource {
/** The jmsservice that is assciated with this DirectXAResource */
private JMSService jmsservice;
/** The DirectConnection that is associated with this DirectXAResource */
private DirectConnection dc;
/** The connectionId (from DirectConnection) for this DirectXAResource */
private long connectionId;
/** The default transactionId and xid for this XAResource */
private long mTransactionId = 0L;
private XidImpl mXid = null;
private int id = 0;
/** Indicates whether this DirectXAResource has been enlisted or not */
private boolean isEnlisted = false;
/** Indicates whether this DirectXAResource is used by an MDB */
private boolean usedByMDB = false;
/** The xid / transactionId mapping table */
private HashMap<String, Long> transactionIds = null;
/**
* Logging
*/
private static transient final String _className = "com.sun.messaging.jms.ra.DirectXAResource";
private static transient final String _lgrNameOutboundConnection = "javax.resourceadapter.mqjmsra.outbound.connection";
private static transient final String _lgrNameJMSXAResource = "javax.resourceadapter.mqjmsra.xa";
private static transient final Logger _loggerOC = Logger.getLogger(_lgrNameOutboundConnection);
private static transient final Logger _loggerJX = Logger.getLogger(_lgrNameJMSXAResource);
private static transient final String _lgrName = "com.sun.messaging.jms.ra.DirectXAResource";
private static transient final Logger _logger = Logger.getLogger(_lgrName);
private static transient final String _lgrMIDPrefix = "MQJMSRA_DXA";
private static transient final String _lgrMID_EET = _lgrMIDPrefix+"1001: ";
private static transient final String _lgrMID_INF = _lgrMIDPrefix+"1101: ";
private static transient final String _lgrMID_WRN = _lgrMIDPrefix+"2001: ";
private static transient final String _lgrMID_ERR = _lgrMIDPrefix+"3001: ";
private static transient final String _lgrMID_EXC = _lgrMIDPrefix+"4001: ";
private static int idCounter = 0;
/** For optimized logging */
protected static int _logLevel;
protected static boolean _logFINE = false;
/**
* Possible states of this XAResource
*/
public static final int CREATED = 0; // after first creation, or after commit() or rollback()
public static final int STARTED = 1; // after start() called
public static final int FAILED = 2; // after end(fail) called
public static final int INCOMPLETE = 3; // after end(suspend) called
public static final int COMPLETE = 4; // after end (success) called
public static final int PREPARED = 5; // after prepare() called
/**
* State of this XAresource
*/
private int resourceState = CREATED;
static {
// _loggerOC = Logger.getLogger(_lgrNameOutboundConnection);
// _loggerJX = Logger.getLogger(_lgrNameJMSXAResource);
java.util.logging.Level _level = _loggerJX.getLevel();
if (_level != null) {
_logLevel = _level.intValue();
if (_logLevel <= java.util.logging.Level.FINE.intValue()){
_logFINE = true;
}
}
}
/**
* Creates a new instance of DirectXAResource
*/
public DirectXAResource(DirectConnection dc,
JMSService jmsservice, long connectionId) {
Object params[] = new Object[3];
params[0] = dc;
params[1] = jmsservice;
params[2] = connectionId;
_loggerOC.entering(_className, "constructor()", params);
this.dc = dc;
this.jmsservice = jmsservice;
this.connectionId = dc.getConnectionId();
this.isEnlisted = false;
this.id = ++idCounter;
}
/**
* Commit the global transaction specified by xid.
*
* @param foreignXid A global transaction identifier
*
* @param onePhase If true, the resource manager should use a one-phase
* commit protocol to commit the work done on behalf of xid.
*
* @throws XAException An error has occurred. Possible XAExceptions
* are XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR,
* XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
*
* <P>If the resource manager did not commit the transaction and the
* parameter onePhase is set to true, the resource manager may throw
* one of the XA_RB* exceptions. Upon return, the resource manager has
* rolled back the branch's work and has released all held resources.
*/
public synchronized void commit(Xid foreignXid, boolean onePhase)
throws XAException {
if (_logger.isLoggable(Level.FINE)){
_logger.fine(_lgrMID_INF+"DirectXAResource ("+this.hashCode()+") Commit "+printXid(foreignXid)+" (onePhase="+onePhase+")");
}
//convert to XidImpl
//XidImpl mqxid = new XidImpl(foreignXid);
String methodName = "commit()";
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName + ":onePhase=" + onePhase +
":transactionId=" + this.mTransactionId //+
//+"Xid="+mqxid.toString()
);
}
JMSServiceReply reply = null;
JMSServiceReply.Status status;
try {
reply = jmsservice.commitTransaction(this.connectionId,
this.mTransactionId, foreignXid,
(onePhase ? XAResource.TMONEPHASE : XAResource.TMNOFLAGS)
);
this.setEnlisted(false);
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName +
":connectionId=" + this.connectionId +
":committed transactionId=" + this.mTransactionId
//+"Xid="+mqxid.toString()
);
}
} catch (JMSServiceException jse) {
status = jse.getJMSServiceReply().getStatus();
String failure_cause;
switch (status) {
case CONFLICT:
failure_cause = "CONFLICT: "+jse.getCause().toString();
break;
default:
failure_cause = "Unknown JMSService server error "+status+": "+jse.getCause().toString();
}
//XXX:tharakan:This message should be in the JMSServiceException
String exerrmsg =
"commitTransaction (XA) on JMSService:" +
jmsservice.getJMSServiceID() +
" failed for connectionId:"+ connectionId +
" and onePhase:" + onePhase +
//"and Xid:" + mqxid.toString() +
" due to " + failure_cause;
_loggerOC.severe(exerrmsg);
XAException xae = new XAException(XAException.XAER_RMERR);
xae.initCause(jse);
throw xae;
} finally {
// finish up this resource and any others joined to it in this transaction
boolean throwExceptionIfNotFound = false;
XidImpl savedXid = this.mXid;
DirectXAResource[] resources = DirectXAResourceMap.getXAResources(this.mXid,throwExceptionIfNotFound);
for (int i = 0; i < resources.length; i++) {
DirectXAResource xari = resources[i];
try {
xari.clearTransactionInfo();
} catch (JMSException jmse) {
System.err.println("MQRA:DXAR:commit:XAException-Exception=" + jmse.getMessage());
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
}
DirectXAResourceMap.unregister(savedXid);
}
}
public void clearTransactionInfo() throws JMSException{
dc.deleteTemporaryDestinationsIfClosed();
this.resourceState=CREATED;
this.mTransactionId = 0L;
this.mXid = null;
}
/**
* End the work performed on behalf of a transaction branch.
* The resource manager disassociates the XA resource from the
* transaction branch specified and lets the transaction
* complete.
*
* <p>If TMSUSPEND is specified in the flags, the transaction branch
* is temporarily suspended in an incomplete state. The transaction
* context is in a suspended state and must be resumed via the
* <code>start</code> method with TMRESUME specified.</p>
*
* <p>If TMFAIL is specified, the portion of work has failed.
* The resource manager may mark the transaction as rollback-only</p>
*
* <p>If TMSUCCESS is specified, the portion of work has completed
* successfully.</p>
*
* @param foreignXid A global transaction identifier that is the same as
* the identifier used previously in the <code>start</code> method.
*
* @param flags One of TMSUCCESS, TMFAIL, or TMSUSPEND.
*
* @throws XAException If an error has occurred. Possible XAException
* values are XAER_RMERR, XAER_RMFAILED, XAER_NOTA, XAER_INVAL,
* XAER_PROTO, or XA_RB*.
*/
public synchronized void end(Xid foreignXid, int flags)
throws XAException {
if (_logger.isLoggable(Level.FINE)){
_logger.fine(_lgrMID_INF+"DirectXAResource ("+this.hashCode()+") End "+printXid(foreignXid)+printFlags(flags));
}
String methodName = "end()";
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName + ":flags=" + flags +
":transactionId=" + this.mTransactionId //+
//":Xid="+mqxid.toString()
);
}
// update the resource state
if (isFail(flags)){
resourceState=FAILED;
} else if (isSuspend(flags)){
resourceState=INCOMPLETE;
} else {
resourceState=COMPLETE;
}
if (ResourceAdapter.isRevert6882044()) {
// revert to pre-6882044 behaviour and forward all events to broker
sendEndToBroker(foreignXid, flags);
} else {
// now decide whether to send an END packet to the broker on the basis of the resource state
if (resourceState==COMPLETE ){
// only send an END packet to the broker when all joined resources are complete
// this works around Glassfish issue 7118
boolean allComplete = true;
DirectXAResource[] resources = DirectXAResourceMap.getXAResources(this.mXid,true);
for (int i = 0; i < resources.length; i++) {
DirectXAResource xari = resources[i];
if (xari.getResourceState()!=COMPLETE){
allComplete = false;
}
}
if (allComplete){
sendEndToBroker(foreignXid, flags);
}
} else if (resourceState==FAILED){
sendEndToBroker(foreignXid, flags);
} else if (resourceState==INCOMPLETE){
// don't send the END to the broker
}
}
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName +
":connectionId=" + this.connectionId +
":ended transactionId=" + this.mTransactionId
//+"Xid="+mqxid.toString()
);
}
}
/**
* Notify the broker than end() has been called
*
* @param foreignXid
* @param flags
* @throws XAException
*/
public synchronized void sendEndToBroker(Xid foreignXid, int flags)
throws XAException {
//convert to XidImpl
//XidImpl mqxid = new XidImpl(foreignXid);
String methodName = "endToBroker()";
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName + ":flags=" + flags +
":transactionId=" + this.mTransactionId //+
//":Xid="+mqxid.toString()
);
}
JMSServiceReply reply = null;
JMSServiceReply.Status status;
try {
reply = jmsservice.endTransaction(this.connectionId,
this.mTransactionId, foreignXid, flags);
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName +
":connectionId=" + this.connectionId +
":ended transactionId=" + this.mTransactionId
//+"Xid="+mqxid.toString()
);
}
} catch (JMSServiceException jse) {
status = jse.getJMSServiceReply().getStatus();
String failure_cause;
switch (status) {
case CONFLICT:
failure_cause = "CONFLICT: "+jse.getCause().toString();
break;
default:
failure_cause = "Unknown JMSService server error "+status+": "+jse.getCause().toString();
}
//XXX:tharakan:This message should be in the JMSServiceException
String exerrmsg =
"endTransaction (XA) on JMSService:" +
jmsservice.getJMSServiceID() +
" failed for connectionId:"+ connectionId +
//"and Xid:" + mqxid.toString() +
" and flags=" + flags +
" due to " + failure_cause;
_loggerOC.severe(exerrmsg);
XAException xae = new XAException(XAException.XAER_RMERR);
xae.initCause(jse);
throw xae;
}
}
/**
* Tell the resource manager to forget about a heuristically
* completed transaction branch.
*
* @param foreignXid A global transaction identifier.
*
* @throws XAException If an error has occurred. Possible exception
* values are XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
* XAER_PROTO.
*/
public void forget(Xid foreignXid)
throws XAException {
//convert to XidImpl
//XidImpl mqxid = new XidImpl(foreignXid);
if (_logFINE){
_loggerJX.warning(_lgrMID_INF+"forget()UNSUPPORTED"+
":Xid="+foreignXid.toString());
}
XidImpl xidToForget = new XidImpl(foreignXid);
DirectXAResourceMap.unregister(xidToForget);
if (mXid!=null){
if (mXid.equals(xidToForget)){
try {
clearTransactionInfo();
} catch (JMSException jmse) {
System.err.println("MQRA:DXAR:forget:XAException-Exception=" + jmse.getMessage());
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
}
}
}
/**
* Obtains the current transaction timeout value set for this
* XAResource instance. If <CODE>XAResource.setTransactionTimeout</CODE>
* was not used prior to invoking this method, the return value
* is the default timeout set for the resource manager; otherwise,
* the value used in the previous <CODE>setTransactionTimeout</CODE>
* call is returned.
*
* @return The transaction timeout value in seconds.
*
* @throws XAException If an error has occurred. Possible exception
* values are XAER_RMERR and XAER_RMFAIL.
*/
public int getTransactionTimeout()
throws XAException {
if (_logFINE){
_loggerJX.fine(_lgrMID_INF+"getTransactionTimeout() = 0");
}
return 0;
}
public int getResourceState() {
return resourceState;
}
/**
* This method is called to determine if the resource manager
* instance represented by the target object is the same as the
* resouce manager instance represented by the parameter <i>xares</i>.
*
* @param foreignXaRes An XAResource object whose resource manager
* instance is to be compared with the resource manager instance
* of the target object.
*
* @return <i>true</i> if it's the same RM instance; otherwise
* <i>false</i>.
*
* @throws XAException If an error has occurred. Possible exception
* values are XAER_RMERR and XAER_RMFAIL.
*
*/
public boolean isSameRM(XAResource foreignXaRes)
throws XAException {
if (_logFINE){
_loggerJX.fine(_lgrMID_INF+"isSameRM()");
}
if (ResourceAdapter.isSameRMAllowed() && (foreignXaRes instanceof DirectXAResource)){
return true;
} else {
return false;
}
}
/**
* Ask the resource manager to prepare for a transaction commit
* of the transaction specified in xid.
*
* @param foreignXid A global transaction identifier.
*
* @return A value indicating the resource manager's vote on the
* outcome of the transaction. The possible values are: XA_RDONLY
* or XA_OK. If the resource manager wants to roll back the
* transaction, it should do so by raising an appropriate
* XAException in the prepare method.
*
* @throws XAException If an error has occurred. Possible exception
* values are: XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_NOTA,
* XAER_INVAL, or XAER_PROTO.
*/
public synchronized int prepare(Xid foreignXid)
throws XAException {
if (_logger.isLoggable(Level.FINE)){
_logger.fine(_lgrMID_INF+"DirectXAResource ("+this.hashCode()+") Prepare "+printXid(foreignXid));
}
//convert to XidImpl
//XidImpl mqxid = new XidImpl(foreignXid);
String methodName = "prepare()";
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName +
":transactionId=" + this.mTransactionId //+
//:Xid="+mqxid.toString()
);
}
//JMS does not do RDONLY transactions
int result = XA_OK;
JMSServiceReply reply = null;
JMSServiceReply.Status status;
try {
reply = jmsservice.prepareTransaction(this.connectionId,
this.mTransactionId, foreignXid);
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName +
":connectionId=" + this.connectionId +
":prepared transactionId=" + this.mTransactionId
//+"Xid="+mqxid.toString()
);
}
} catch (JMSServiceException jse) {
status = jse.getJMSServiceReply().getStatus();
String failure_cause;
switch (status) {
case CONFLICT:
failure_cause = "CONFLICT: "+jse.getCause().toString();
break;
default:
failure_cause = "Unknown JMSService server error "+status+": "+jse.getCause().toString();
}
//XXX:tharakan:This message should be in the JMSServiceException
String exerrmsg =
"prepareTransaction (XA) on JMSService:" +
jmsservice.getJMSServiceID() +
" failed for connectionId:"+ connectionId +
//"and Xid:" + mqxid.toString() +
" due to " + failure_cause;
_loggerOC.severe(exerrmsg);
XAException xae = new XAException(XAException.XAER_RMERR);
xae.initCause(jse);
throw xae;
}
// update the resource state
resourceState=PREPARED;
return result;
}
/**
* Obtain a list of prepared transaction branches from a resource
* manager. The transaction manager calls this method during recovery
* to obtain the list of transaction branches that are currently in
* prepared or heuristically completed states.
*
* @param flags One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS
* must be used when no other flags are set in the parameter.
*
* @return The resource manager returns zero or more XIDs of the
* transaction branches that are currently in a prepared or
* heuristically completed state. If an error occurs during the
* operation, the resource manager should throw the appropriate
* XAException.
*
* @throws XAException If an error has occurred. Possible values are
* XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
*/
public Xid[] recover(int flags)
throws XAException {
if (_logFINE){
//String methodName = "recover()";
_loggerJX.fine(_lgrMID_INF+"recover():flags="+flags);
}
javax.transaction.xa.Xid[] result = null;
JMSServiceReply.Status status;
try {
result = jmsservice.recoverXATransactions(this.connectionId, flags);
} catch (JMSServiceException jse) {
status = jse.getJMSServiceReply().getStatus();
String failure_cause;
switch (status) {
case CONFLICT:
failure_cause = "CONFLICT: "+jse.getCause().toString();
break;
default:
failure_cause = "Unknown JMSService server error "+status+": "+jse.getCause().toString();
}
//XXX:tharakan:This message should be in the JMSServiceException
String exerrmsg =
"recoverXATransactions (XA) on JMSService:" +
jmsservice.getJMSServiceID() +
" failed for connectionId:"+ connectionId +
" due to " + failure_cause;
_loggerOC.severe(exerrmsg);
XAException xae = new XAException(XAException.XAER_RMERR);
xae.initCause(jse);
throw xae;
}
return result;
}
/**
* Inform the resource manager to roll back work done on behalf
* of a transaction branch.
*
* @param foreignXid A global transaction identifier.
*
* @throws XAException If an error has occurred.
*/
public synchronized void rollback(Xid foreignXid)
throws XAException {
if (_logger.isLoggable(Level.FINE)){
_logger.fine(_lgrMID_INF+"DirectXAResource ("+this.hashCode()+") Rollback "+printXid(foreignXid)+")");
}
//convert to XidImpl
//XidImpl mqxid = new XidImpl(foreignXid);
String methodName = "rollback()";
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName +
":transactionId="+ this.mTransactionId //+
//":Xid="+mqxid.toString());
);
}
JMSServiceReply reply = null;
JMSServiceReply.Status status;
try {
reply = jmsservice.rollbackTransaction(this.connectionId,
this.mTransactionId, foreignXid,
true, true
);
this.setEnlisted(false);
if (_logFINE){
_loggerJX.fine(_lgrMID_INF + methodName +
":connectionId=" + this.connectionId +
":rolled back transactionId=" + this.mTransactionId
//+"Xid="+mqxid.toString()
);
}
} catch (JMSServiceException jse) {
status = jse.getJMSServiceReply().getStatus();
String failure_cause;
switch (status) {
case CONFLICT:
failure_cause = "CONFLICT: "+jse.getCause().toString();
break;
default:
failure_cause = "Unknown JMSService server error "+status+": "+jse.getCause();
}
//XXX:tharakan:This message should be in the JMSServiceException
String exerrmsg =
"rollbackTransaction (XA) on JMSService:" +
jmsservice.getJMSServiceID() +
" failed for connectionId:"+ connectionId +
":transactionId=" + this.mTransactionId +
//"and Xid:" + mqxid.toString() +
" due to " + failure_cause;
_loggerOC.severe(exerrmsg);
XAException xae = new XAException(XAException.XAER_RMERR);
xae.initCause(jse);
throw xae;
} finally {
// finish up this resource and any others joined to it in this transaction
boolean throwExceptionIfNotFound = false;
DirectXAResource[] resources = DirectXAResourceMap.getXAResources(this.mXid,throwExceptionIfNotFound);
XidImpl savedXid = this.mXid;
for (int i = 0; i < resources.length; i++) {
DirectXAResource xari = resources[i];
try {
xari.clearTransactionInfo();
} catch (JMSException jmse) {
System.err.println("MQRA:DXAR:rollback:XAException-Exception=" + jmse.getMessage());
Debug.printStackTrace(jmse);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jmse);
throw xae;
}
}
DirectXAResourceMap.unregister(savedXid);
}
}
/**
* Set the current transaction timeout value for this <CODE>XAResource</CODE>
* instance. Once set, this timeout value is effective until
* <code>setTransactionTimeout</code> is invoked again with a different
* value. To reset the timeout value to the default value used by the resource
* manager, set the value to zero.
*
* If the timeout operation is performed successfully, the method returns
* <i>true</i>; otherwise <i>false</i>. If a resource manager does not
* support explicitly setting the transaction timeout value, this method
* returns <i>false</i>.
*
* @param transactionTimeout The transaction timeout value in seconds.
*
* @return <i>true</i> if the transaction timeout value is set successfully;
* otherwise <i>false</i>.
*
* @throws XAException If an error has occurred. Possible exception values
* are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
*/
public boolean setTransactionTimeout(int transactionTimeout)
throws XAException {
if (_logFINE){
//String methodName = "setTransactionTimeout()";
_loggerJX.fine(_lgrMID_INF+"setTransactionTimeout()="+
transactionTimeout+
":returning false.");
}
return false;
}
/**
* Start work on behalf of a transaction branch specified in
* <code>foreignXid</code>.
*
* If TMJOIN is specified, the start applies to joining a transaction
* previously seen by the resource manager. If TMRESUME is specified,
* the start applies to resuming a suspended transaction specified in the
* parameter <code>foreignXid</code>.
*
* If neither TMJOIN nor TMRESUME is specified and the transaction
* specified by <code>foreignXid</code> has previously been seen by the resource
* manager, the resource manager throws the XAException exception with
* XAER_DUPID error code.
*
* @param foreignXid A global transaction identifier to be associated
* with the resource.
*
* @param flags One of TMNOFLAGS, TMJOIN, or TMRESUME.
*
* @throws XAException If an error has occurred. Possible exceptions
* are XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE,
* XAER_NOTA, XAER_INVAL, or XAER_PROTO.
*
*/
public synchronized void start(Xid foreignXid, int flags) throws XAException {
if (_logger.isLoggable(Level.FINE)){
_logger.fine(_lgrMID_INF+"DirectXAResource ("+this.hashCode()+") Start "+printXid(foreignXid)+printFlags(flags));
}
// convert to XidImpl
// XidImpl mqxid = new XidImpl(foreignXid);
JMSServiceReply.Status status;
JMSServiceReply reply = null;
long transactionId = 0L;
String methodName = "start()";
if (_logFINE) {
_loggerJX.fine(_lgrMID_INF + methodName + ":flags=" + flags + ":connectioId=" + this.connectionId
// +"Xid="+mqxid.toString()
);
}
// if we're reverting to the pre-6882044 behaviour always send the START to the broker
// otherwise send the START to the broker only if this is not a TMRESUME
if (!isResume(flags) || ResourceAdapter.isRevert6882044()){
transactionId = sendStartToBroker(foreignXid, flags, reply, transactionId);
validateAndSaveXidTransactionID(foreignXid, transactionId);
DirectXAResourceMap.register(mXid, this,isJoin(flags));
}
this.setEnlisted(true);
if (_logFINE) {
_loggerJX.fine(_lgrMID_INF + methodName + ":connectionId=" + this.connectionId + ":started transactionId="
+ this.mTransactionId
// +"Xid="+mqxid.toString()
);
}
}
private long sendStartToBroker(Xid foreignXid, int flags, JMSServiceReply reply, long transactionId)
throws XAException {
JMSServiceReply.Status status;
try {
// We must be sessionId agnostic
reply = jmsservice.startTransaction(connectionId, 0L, foreignXid, flags,
JMSService.TransactionAutoRollback.UNSPECIFIED, 0L);
try {
transactionId = reply.getJMQTransactionID();
} catch (NoSuchFieldException nsfe) {
String exerrmsg = _lgrMID_EXC + "JMSServiceException:Missing JMQTransactionID";
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(nsfe);
_loggerOC.severe(exerrmsg);
throw xae;
}
} catch (JMSServiceException jse) {
status = jse.getJMSServiceReply().getStatus();
String failure_cause;
switch (status) {
case NOT_IMPLEMENTED:
failure_cause = "TransactionAutoRollback not implemented.";
break;
case CONFLICT:
failure_cause = "CONFLICT: " + jse.getCause().toString();
break;
default:
failure_cause = "Unknown JMSService server error " + status + ": " + jse.getCause().toString();
}
String exerrmsg = "startTransaction (XA) on JMSService:" + jmsservice.getJMSServiceID()
+ " failed for connectionId:" + connectionId +
// "and Xid:" + mqxid.toString() +
" due to " + failure_cause;
_loggerOC.severe(exerrmsg);
XAException xae = new XAException(XAException.XAER_RMFAIL);
xae.initCause(jse);
throw xae;
}
return transactionId;
}
/**
* Validate the specified foreignXid and transactionID
* Convert the specified foreignXid to our own implementation
* and save in the fields mTransactionId and mXid
*
* @param foreignXid
* @param transactionId
*/
private void validateAndSaveXidTransactionID(Xid foreignXid, long transactionId) {
assert transactionId != 0L;
if ((this.mTransactionId == 0L) && (this.mXid == null)) {
this.mTransactionId = transactionId;
this.mXid = new XidImpl(foreignXid); // mqxid;
} else {
if (this.mXid.equals(foreignXid)) {
if (this.mTransactionId != transactionId) {
System.out.println("DXAR:start():Warning:" + "Received diff txId for same Xid:"
+ "switching transactionId:" + "\nDXAR TXid=" + this.mTransactionId + "\ngot TXid="
+ transactionId + "\nFor Xid=" + printXid(mXid));
this.mTransactionId = transactionId;
}
} else {
System.out
.println("DXAR:start():Warning:" + "Received diff Xid for open txnId:"
+ "switching transactionId:" + "\nDXAR Xid=" + printXid(mXid) + "\nDXAR TXid="
+ this.mTransactionId + "\ngot Xid=" + printXid(foreignXid) + "\ngot TXid="
+ transactionId);
// remove transactionId and xid as it will be wrong if used
this.mTransactionId = transactionId;
this.mXid = new XidImpl(foreignXid); // mqxid;
}
}
}
/**
* Set the enlisted flag on this DirectXAResource
*
* @param value The boolean indicating whether it is enlisted or not
*/
public synchronized void setEnlisted(boolean value) {
this.isEnlisted = value;
//Only change the enlisted state on the connection if it is not an MDB
if (!this.usedByMDB && (this.dc != null)) {
this.dc.setEnlisted(value);
}
}
protected void _setUsedByMDB(boolean value){
this.usedByMDB = value;
}
/**
* Get the value of the enlisted flag for this DirectXAResource
*
* @return The enlisted state of this DirectXAResource.
* {@code true} If it has been enlisted in an XA transaction.<b>
* {@code false} If it currently not enlistedin an XA transaction.
*/
public synchronized boolean isEnlisted() {
return this.isEnlisted;
}
public synchronized void setRollback(boolean state, Throwable cause){
}
public int _getId(){
return this.id;
}
public long _getTransactionId(){
return this.mTransactionId;
}
// Used for debugging only
private String printXid(Xid foreignXid){
return ("(GlobalTransactionID="+foreignXid.getGlobalTransactionId()) +
", BranchQualifier="+foreignXid.getBranchQualifier()+") ";
}
private boolean isJoin(int flags){
return((flags & XAResource.TMJOIN) == XAResource.TMJOIN);
}
private boolean isResume(int flags){
return((flags & XAResource.TMRESUME) == XAResource.TMRESUME);
}
// Used for debugging only
private boolean isNoFlags(int flags){
return((flags & XAResource.TMNOFLAGS) == XAResource.TMNOFLAGS);
}
// Used for debugging only
private boolean isFail(int flags){
return((flags & XAResource.TMFAIL) == XAResource.TMFAIL);
}
// Used for debugging only
private boolean isOnePhase(int flags){
return((flags & XAResource.TMONEPHASE) == XAResource.TMONEPHASE);
}
// Used for debugging only
private boolean isSuccess(int flags){
return((flags & XAResource.TMSUCCESS) == XAResource.TMSUCCESS);
}
// Used for debugging only
private boolean isSuspend(int flags){
return((flags & XAResource.TMSUSPEND) == XAResource.TMSUSPEND);
}
// Used for debugging only
private boolean isTMENDRSCAN(int flags){
return((flags & XAResource.TMENDRSCAN) == XAResource.TMENDRSCAN);
}
// Used for debugging only
private boolean TMSTARTRSCAN(int flags){
return((flags & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN);
}
// Used for debugging only
private String printFlags(int flags){
String result = ("(Flags: ");
if (isJoin(flags)){
result=result+("JOIN ");
}
if (isNoFlags(flags)){
result=result+("TMNOFLAGS ");
}
if (isFail(flags)){
result=result+("TMFAIL ");
}
if (isOnePhase(flags)){
result=result+("TMONEPHASE ");
}
if (isResume(flags)){
result=result+("TMRESUME ");
}
if (isSuccess(flags)){
result=result+("TMSUCCESS ");
}
if (isSuspend(flags)){
result=result+("TMSUSPEND ");
}
if (isTMENDRSCAN(flags)){
result=result+("TMENDRSCAN ");
}
if (TMSTARTRSCAN(flags)){
result=result+("TMSTARTRSCAN ");
}
result=result+(")");
return result;
}
}