HOWTO: Using MSMQ within an MTS Component in Java

ID: Q176816

The information in this article applies to:

SUMMARY

This article describes how to use Microsoft Message Queue Server (MSMQ) from within Microsoft Transaction Server (MTS) components written in Java using Visual J++. The two MSMQ transactional operations available are the message Send and Receive operations.

MSMQ Send operations can be included in MTS transactions. The Send operation is either committed or rolled back with the MTS transaction. As a result, the message is not sent until the transaction commits. To include the Send operation in a transaction, use the MQ_MTS_TRANSACTION constant for the Transaction parameter of the MSMQMessage.Send method from within a transactional MTS component. MSMQ enlists the Send operation in the MTS transaction. The destination queue must be a transactional queue.

Transactional messages can only be sent to a transactional destination queue. If the MTS component is not participating in a transaction, the Send operation described above fails. This happens because the message would be non-transactional, and you cannot send a non-transactional message to a transactional queue. You cannot always predict whether a component will participate in a transaction. Therefore, it is important to verify that the component is participating in the transaction and use MQ_MTS_TRANSACTION in the MSMQMessage.Send to a transactional queue only if the component is participating. If it is not participating, use the MQ_NO_TRANSACTION to send to a non-transactional queue or MQ_SINGLE_MESSAGE constant to send to a transactional queue.

The component is participating in the transaction if it is marked as "Requires [new] Transactions" or "Supports Transactions" in MTS Explorer. The component is not participating in the transaction if it is marked as "Does Not Support Transactions."

When the MTS component is marked as "Supports Transaction," it participates in a transaction if its caller participates. For sending from such a component, you must dynamically check for the current transaction existence (use the ObjectContext.IsInTransaction method) and use an appropriate Transaction flag and destination queue.

For a local MSMQ transactional queue, the receive operation specifying MQ_MTS_TRANSACTION works regardless of whether or not the component is transactional. The receive operation is only included in a transaction if the component is transactional.

MORE INFORMATION

The following Java class used in an MTS component contains two methods, MQSend and MQReceive.

MQSend demonstrates sending an MSMQ message containing a string from within an MTS component. The MTS context object is used to commit or abort the transaction. The context objects IsInTransaction() method is used to determine whether or not the component is transactional.

MQReceive demonstrates receiving the message. This method also uses MTS context object to commit or abort the transaction.

Here is the sample code:

   import mqoa.* ;
   import com.ms.com.*;
   import com.ms.mtx.*;
   import javasample1.*;

   class MQSendRcv implements IMQJavaSpl
   {

   public String MQSend (String strSend)
   {
   try
   {
   IMSMQQueueInfo qinfoSend = (IMSMQQueueInfo) new
   MSMQQueueInfo();
   IMSMQQueue qSend;
   IMSMQMessage qmsgMessage = (IMSMQMessage) new MSMQMessage();
   // Add code for database updates if needed.
   // Open destination queue with MQ_SEND_ACCESS
   // and MQ_DENY_NONE options.
   qinfoSend.putPathName ("myMachine\\Localx") ;
   qSend = qinfoSend.Open(MQACCESS.MQ_SEND_ACCESS,
   MQSHARE.MQ_DENY_NONE) ;
   if (qSend != null)
   {
   // Set up variant for the Body property.
   Variant vBody = new Variant();
   vBody.putString (strSend) ;
   // Assemble message.
   qmsgMessage.putBody (vBody) ;
   qmsgMessage.putLabel ("Java Message") ;
   // Select the transaction option. If you are in a
   // transactional component, you need to use
   // MQ_MTS_TRANSACTION,
   // otherwise, since you are sending to a transactional
   // queue,
   // you need to use MQ_SINGLE_MESSAGE.
   Variant vTransaction = new Variant();
   if (MTx.GetObjectContext().IsInTransaction())
   vTransaction.putInt(MQTRANSACTION.MQ_MTS_TRANSACTION) ;
   else
   vTransaction.putInt(MQTRANSACTION.MQ_SINGLE_MESSAGE) ;
   // Send the message.
   qmsgMessage.Send (qSend, vTransaction) ;
   qSend.Close() ;
   // Commit the transaction.
   MTx.GetObjectContext().SetComplete();
   return "Message Sent" ;
   }
   else
   {
   // Abort the transaction.
   MTx.GetObjectContext().SetAbort();
   return "Couldn't open queue" ;
   }
   }
   catch (ComFailException e)
   {
   // Abort the transaction.
   MTx.GetObjectContext().SetAbort();
   return "An Error Occurred" ;
   }
   }
   public String MQReceive (String[] strRcv)
   {
   try
   {
   IMSMQQueueInfo qinfoRcv = (IMSMQQueueInfo) new
   MSMQQueueInfo();
   IMSMQQueue qRcv;
   IMSMQMessage qmsgMessage  ;
   // Add code for database updates if needed.
   // Open destination queue with MQ_RECEIVE_ACCESS
   // and MQ_DENY_NONE options.
   qinfoRcv.putPathName ("myMachine\\Localx") ;
   qRcv = qinfoRcv.Open(MQACCESS.MQ_RECEIVE_ACCESS,
   MQSHARE.MQ_DENY_NONE) ;
   if (qRcv != null)
   {
   // Create and set up the Variant parameters
   // required by the Receive method.
   Variant vTransaction = new Variant();
   Variant vWantDestQueue = new Variant();
   Variant vWantBody = new Variant();
   Variant vReceiveTimeout = new Variant();
   vTransaction.putInt (MQTRANSACTION.MQ_MTS_TRANSACTION) ;
   vWantDestQueue.putBoolean(false) ; // You do not want the
                                     // destination queue.
   vWantBody.putBoolean(true) ;  // You want the message body.
   vReceiveTimeout.putInt(0) ;  // You do not want to wait for
                               // a message.
   // Receive the message & close queue.
   qmsgMessage = qRcv.Receive(vTransaction, vWantDestQueue,
   vWantBody, vReceiveTimeout) ;
   qRcv.Close() ;

   // Process message if you received one.
   if (qmsgMessage != null)
   {
   // Get the message body (it's a Variant).
   Variant vBody;
   vBody = qmsgMessage.getBody () ;
   // Get the string (since you know that's what you sent),
   // and return it.
   strRcv[0] = vBody.getString ();

   MTx.GetObjectContext().SetComplete();
   return "Message Received" ;
   }
   else
   {
   MTx.GetObjectContext().SetComplete();
   return "No Message Received" ;
   }
   }
   else
   {
   MTx.GetObjectContext().SetAbort();
   return "Couldn't open queue" ;
   }

   }
   catch (ComFailException e)
   {
   MTx.GetObjectContext().SetAbort();
   return "An Error Occurred" ;
   }
   }
   }

REFERENCES

For more information on creating an MTS COM component from your Java class, please see the documentation that comes with Microsoft Visual J++ and Microsoft Transaction Server.

For more details on using a single queue for Send/Receive and to avoid queue and component type mismatch, see the following Knowledge Base article:

   ARTICLE-ID: Q174387
   TITLE     : INFO: Using a Single MSMQ Queue for an MTS Component

Additional query words: viper Falcon
Keywords          : kbcode MQJava MQProg 
Version           : WINDOWS:1.0; WINNT:1.0
Platform          : WINDOWS winnt
Issue type        : kbhowto

Last Reviewed: November 27, 1997