I'm trying to write a simple test case to pull messages from a queue based on a message property, hitting a 7.5.0.3 QMgr and using the 7.5.0.3 client jars.
Everything I have seen online says that I need to specify the message selector when I open the queue. I'm fine with that, but I only see two ways to open it:
MQQueueManager.accessQueue(
String queueName,
int openOptions);
MQQueueManager.accessQueue(
String queueName,
int openOptions,
String queueMgr,
String dynamicQueueName,
String altUserId);
Neither of these allow me to specify a message selector. I'm running this from a command line batch application, not in an app server, so using JMS selectors is not possible.
Here is the IBM documentation on selectors: WebSphere MQ Message Selectors which shows that selection must happen as part of the MQOPEN call.
MQ JMS API provides the type of message selection syntax you are looking for. The base MQ Java API provides message selection based on MessageId and CorrelationId and it does not yet provide type selection syntax you are looking for. The documentation link you provided is for MQ C API.
Using MQ JMS API, message selection can be done as shown here:
// Create JMS objects
connection = cf.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create queue destination
Destination queDest= session.createQueue(que);
// Create consumer with selector
String selector = "category='bucket1'";
MessageConsumer cons= session.createConsumer(queDest, selector);
connection.start();
// receive messages
Message inMessage = cons.receive(5000);
You should specify selector when trying to read message from queue, like below:
MQMessage ResponseMsg = new MQMessage();
ResponseMsg.correlationId = CorrelationId;
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options = MQConstants.MQGMO_WAIT;
gmo.waitInterval = WaitTime * 1000;
gmo.matchOptions = MQConstants.MQMO_MATCH_CORREL_ID;
ResponseQueue.get(ResponseMsg, gmo);
Related
After I went to multiple sites and learned JMS I wrote a JMS standalone client to read messages from a database and send them one by one. I also want to receive message one by one message and then update the database.
I need to send a message to a queue and the other application using standard JMS which will consume a TextMessage and whose body will be read as an ISO-8859-1 string. Also they will similarly send reply as a TextMessage.
I wrote a for loop to read the message one by one from the DB.
I am new to JMS so could you please correct me whether my below code works properly to read and send messages to a queue and receive and update the DB. Is there any thing I need to change in the JMS Type or any thing I need to correct. Does the for loop work fine?
/*MQ Configuration*/
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName(url);
mqQueueConnectionFactory.setChannel(channel);//communications link
mqQueueConnectionFactory.setPort(port);
mqQueueConnectionFactory.setQueueManager(qmgr);//service provider
mqQueueConnectionFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
/*Create Connection */
QueueConnection queueConnection = mqQueueConnectionFactory.createQueueConnection();
queueConnection.start();
/*Create session */
QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
/*Create response queue */
// Queue queue = queueSession.createQueue("QUEUE.RESPONSE");
int messageCount = 0;
Queue queue = queueSession.createQueue(replytoQueueName);
QueueSender queueSender = null;
QueueReceiver queueReceiver=null;
for (Testbean testBean : testbeanList) {
String testMessage = testBean.getMessage();
/*Create text message */
textMessage = queueSession.createTextMessage(testMessage);
logger.info("Text messages sent: " + messageCount);
textMessage.setJMSReplyTo(queue);
textMessage.setJMSType("mcd://xmlns");//message type
textMessage.setJMSExpiration(2*1000);//message expiration
textMessage.setJMSDeliveryMode(DeliveryMode.PERSISTENT); //message delivery mode either persistent or non-persistemnt
/*Create sender queue */
// QueueSender queueSender = queueSession.createSender(queueSession.createQueue("QUEUE.REQEST"));
queueSender = queueSession.createSender(queueSession.createQueue(outputQName));
queueSender.setTimeToLive(2*1000);
queueSender.send(textMessage);
/*After sending a message we get message id */
System.out.println("after sending a message we get message id "+ textMessage.getJMSMessageID());
String jmsCorrelationID = " JMSCorrelationID = '" + textMessage.getJMSMessageID() + "'";
/*Within the session we have to create queue reciver */
queueReceiver = queueSession.createReceiver(queue,jmsCorrelationID);
/*Receive the message from*/
Message message = queueReceiver.receive(60*1000);
// String responseMsg = ((TextMessage) message).getText();
byte[] by = ((TextMessage) message).getText().getBytes("ISO-8859-1");
logger.info(new String(by));
String responseMsg = new String(by,"UTF-8");
testDAO rmdao = new testDAO();
rmdao.updateTest(responseMsg, jmsCorrelationID);
messageCount += 1;
}
queueSender.close();
queueReceiver.close();
queueSession.close();
queueConnection.close();
Couple of things:
I would create your QueueSender and the Queue object its sending messages to outside the for loop since they don't appear to be changing.
Without the corresponding consumer code it's ultimately impossible to tell if the selector will work or not, but not invoking setCorrelationID() on the message you send looks a bit strange to me. Using the provider-assigned message ID may be a common pattern with IBM MQ request/reply applications, but the general pattern for using a correlation ID is to invoke setJMSCorrelationID() on the sent message. This makes the code more clear and also allows the application to directly control the uniqueness of the correlation ID. This is potentially important for application portability (e.g. if you migrated from IBM MQ to a different JMS provider) since different JMS providers use styles/formats of message ID specific to their particular implementation. Also, regarding the message ID the JMS spec states, "The exact scope of uniqueness is provider defined," which in my opinion is not a strong enough guarantee of uniqueness especially when using something like java.util.UUID.randomUUID().toString() is so simple.
You should ensure that you're using an XA transaction for both the JMS & database work so that they are atomic.
Close your JMS resources in a finally block.
We have a use case of putting a group of messages with a same groupId but differing by MessageSequenceNumber. This is used to group the messages for a logical ordering so that on the receiver side, receiver can group all of the messages based on group order. I was following the IBM MQ v7.5 Knowledge Center page "Message groups".
I have a code written to put the messages : -
public boolean writeMessage(String[] messages, String queueName) {
Session session = getQueueConnection().createSession(true,
Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(queueName);
messageProducer = session.createProducer(destination);
for (int i = 0; i < messages.length; i++) {
TextMessage message = session.createTextMessage(messages[i]);
messageProducer.send(message);
}
// Commit the send (Actually put messages to Queue)
session.commit();
return true;
}
Now , I want to add a 1 unique groupID to all the messages which are inside an array and add a sequence number(msgSeqNum) (1,2,3..) . How can i do it through the JMS API? I am looking for JMS version of the code on the IBM IIB v8 Knowledge center page "Sending messages in a WebSphere MQ message group.
There was a good IBM developerWorks blog written by David Currie in 2006 titled "Grouping messages using the WebSphere MQ Java and JMS APIs" that described how to do what you are asking, however it appears this was recently removed by IBM.
Wayback Machine link to "Grouping messages using the WebSphere MQ Java and JMS APIs"
Below is the information that was provided by David in the post, it appears the putting logic is much simpler to implement compared to the getting logic. I am only including the putting logic code here since this is what you inquired about. I reached out to David via email to ask if this blog will be republished.
Sending a message group
Let's start by looking at the sending application. As mentioned above,
the put message option MQPMO_LOGICAL_ORDER was simply an instruction
to the queue manager to automatically allocate message group
identifiers and sequence numbers. The example in Listing 3 below
demonstrates how, in the absence of this option in the JMS API, we can
set these properties explicitly.
Listing 3. Sending a message group using the WebSphere MQ JMS API
MQConnectionFactory factory = new MQConnectionFactory();
factory.setQueueManager("QM_host")
MQQueue destination = new MQQueue("default");
destination.setTargetClient(JMSC.MQJMS_CLIENT_NONJMS_MQ);
Connection connection = factory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(destination);
String groupId = "ID:" + new BigInteger(24 * 8, new Random()).toString(16);
for (int i = 1; i <= 5; i++) {
TextMessage message = session.createTextMessage();
message.setStringProperty("JMSXGroupID", groupId);
message.setIntProperty("JMSXGroupSeq", i);
if (i == 5) {
message.setBooleanProperty("JMS_IBM_Last_Msg_In_Group", true);
}
message.setText("Message " + i);
producer.send(message);
}
connection.close();
I set up a connection with Weblogic IBM Webpsphere MQ through JMS with using a secure channel using SSL.
My application on Weblogic received message from MQ.
Sending answer to reply queue.
The response header is present MQMD, it fills java. In parameter Persistence JMS send value "1". Other system need to received value "0" at Persistence. How to set this parameter to java?
I guess that parameter is javax.jms.deliverymode. But how to set it i don't know.
Anyway thank you for help.
The corresponding property on JMS is the delivery mode (Int parameter to be set) to set Persistent and non persistent messages.
You can refer this URL from IBM for details
You should try like this:
public String sendMessage(ConnectionFactory connectionFactory,
Destination destination,
Destination jmsReplyTo,
CorrelationType correlationType,
CallOptions<String> callOptions,
String rqUid,
JMSAbstract transport) throws JMSException {
Connection connection = null;
Session session = null;
MessageProducer producer = null;
try {
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(destination);
// Set JMS DeliverMode (1/2)
producer.setDeliveryMode(1);
// create message
Message message = createTextMessage(session, jmsReplyTo, correlationType, callOptions, rqUid, transport);
// send message
producer.send(message);
return correlationType.getCorrelationId(message);
} finally {
closeResource(connection, session, null, producer, rqUid);
}
}
It`s just a java example. Also you can set persistence flag in Queue configuration in IBM WebSphere. I mean MQQueue have method setPersistence. If you using IBM java objects in your project, you can set persistence by calling that method:
MQQueue mqQueue = new MQQueue("QueueName");
mqQueue.setPersistence(1);
I The answer of 0x5a4d is ok but better to use this like IBM best practices
//Persistentmode = 1
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//Persistentmode = 2
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
Can sombody share WebSphere Code for setting up expiration of message in Queue.
I know that ther is a JMSAPI to do set the expiration of message.\
But this JMS-API is not applicable to WebSphere MQ API of Java Code.
I am using the following Code to produce the messages.
MQMessage.write & MQMessage.writeUTF
regards
karthik
Use the link for more information. Here is the basic of an MQ/Java application:
MQPutMessageOptions pmo = new MQPutMessageOptions();
pmo.options = CMQC.MQPMO_FAIL_IF_QUIESCING;
MQMessage sendmsg = new MQMessage();
sendmsg.messageId = CMQC.MQMI_NONE;
sendmsg.correlationId= CMQC.MQCI_NONE;
sendmsg.format = CMQC.MQFMT_STRING;
sendmsg.messageType = CMQC.MQMT_DATAGRAM;
sendmsg.expiry = timeToLive; // expiry is in tenths of a second
sendmsg.write(msg.getBytes());
outQ(sendmsg, pmo);
The base class to MQMessage has an attribute MQMD.expiry, an int measured in tenth-of-seconds.
Source: https://www.ibm.com/support/knowledgecenter/SSFKSJ_7.5.0/com.ibm.mq.javadoc.doc/WMQJavaClasses/com/ibm/mq/MQMD.html%23expiry
I am not sure how to add message header to the message which we send to IBM MQ websphere server. Below is code I tried to use but I get exception :
Creating Connection to the server
qMgr = new MQQueueManager(queueMgr, props);
int openOptions = MQC.MQOO_INPUT_AS_Q_DEF
| MQC.MQOO_OUTPUT | MQC.MQOO_INQUIRE;
queue = qMgr.accessQueue(queueName, openOptions);
message = new MQMessage();
Sending the Message
public String sendMessage(){
MQPutMessageOptions pmo = new MQPutMessageOptions();
message.format = MQC.MQFMT_STRING;
message.feedback = MQC.MQFB_NONE;
message.messageType = MQC.MQMT_DATAGRAM;
message.messageId = MQC.MQMI_NONE;
message.correlationId = MQC.MQCI_NONE;
message.writeString(sMsg);
queue.put(message,pmo);
}
I tried with the below code to add Header
((TextMessage)message).setStringProperty(header_name,header_value);
But i get the exception java.lang.ClassCastException: com.ibm.mq.MQMessage cannot be cast to javax.jms.TextMessage.
And I am Stuck here. If this is solved then the riddle is completed.
Why are you mixing Java SE MQ API calls with JMS calls?
From the manual, in the section 'Handling message properties' for WebSphere MQ classes for Java:
Function calls to process message handles have no equivalent in
WebSphere MQ classes for Java. To set, return, or delete message
handle properties, use methods of the MQMessage class.
Therefore, why aren't you simply doing:
MQPutMessageOptions pmo = new MQPutMessageOptions();
pmo.options = MQC.MQPMO_FAIL_IF_QUIESCING | MQC.MQPMO_NO_SYNCPOINT;
message.format = MQC.MQFMT_STRING;
message.feedback = MQC.MQFB_NONE;
message.messageType = MQC.MQMT_DATAGRAM;
message.messageId = MQC.MQMI_NONE;
message.correlationId = MQC.MQCI_NONE;
message.setStringProperty(header_name,header_value)
message.writeString(sMsg);
queue.put(message,pmo);
One final note, please do not reply saying you can't do that, as you are using WMQ v6. WMQ v6 went out of support almost 2 years ago, so you must upgrade to WMQ v7.* (preferably to WMQ v7.5).