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);
Related
I am trying to setup a durable subscription with JMS 1.1 but I get in a Catch 22:
if I don't set the clientID, I get a "clientID cannot be null" error...
if I try to set it, I get:
com.ibm.msg.client.jms.DetailedIllegalStateException: JMSCC3031: A client ID cannot be set after connection has been used.
The client ID of a connection can be set only once, and only before the connection is used.
Set the client ID before using the connection.
How do I solve this? How do I make the connection 'unused'?
Or - as the exception message suggests - how do I set the ID before I use the connection?
My code snippet:
public class BbsListener implements MessageListener {
...
public BbsListener(BbsListenerConfig config) {
try {
Context context = new InitialContext();
TopicConnectionFactory topicConnectionFactory = (TopicConnectionFactory) context.lookup(config.getConnectionFactoryName());
TopicConnection topicConnection = topicConnectionFactory.createTopicConnection();
topicConnection.setClientID("ID");
TopicSession topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = (Topic) context.lookup(config.getTopicName());
topicSubscriber = topicSession.createDurableSubscriber(topic, "EAMPtestSubscriber");
topicSubscriber.setMessageListener(this);
topicConnection.start();
}
...
Thank you
It looks like your app is running on a Java EE application server. If that's the case you'll need to be careful about what kind of connection factory you use and where you invoke setMessageListener(). First, an "outbound" connection factory is meant to be used for sending messages (hence the name "outbound"). This is part of JCA. Second, you can't call setMessageListener() in an EJB as that's not allowed by spec. I recommend you just use a normal JMS connection factory rather than a pooled one from the application server.
I am using RMQ and it's JMS client to publish messages to RMQ (this is a requirement i have, I can't use their java client instead of JMS client).
So, basically I do this:
RMQConnectionFactory factory = new RMQConnectionFactory() ;
factory.setUsername(props.getProperty("rmq.username"));
factory.setPassword(props.getProperty("rmq.password"));
factory.setHost(props.getProperty("rmq.host"));
factory.setVirtualHost(props.getProperty("rmq.virtualHost"));
factory.setPort(Integer.parseInt(props.getProperty("rmq.port")));
Connection connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
String queueName = managerProps.getProperty("rmq.queue.name");
Queue queue = session.createQueue(queueName);
producer = session.createProducer(queue);
TextMessage msg = session.createTextMessage(text);
msg.setText(text);
producer.send(msg);
I have a policy set up on RMQ overflow: reject-publish, so if it's over the limit RMQ is supposed to send a nack when the queue is full, but I don't seem to get it.
The question is - how do I determine if the message was rejected? I assume the producer.send(msg) to be synchronous and throw exception if the message is not published, but I don't get any exceptions, it just looks like everything got published.
JMS spec has a send(msg, CompletionListener) with a listener with two methods onCompletion and onException, but it doesn't look like RMQ JMS client implemented this method.
Is there another way to make sure that message made it through?
RabbitMQ use Publisher Confirms to guarantee that a message isn't lost, so if your Queue overflow behavior is reject-publish, the confirm channel will got a nack. It is also contains in many AMQP client.
But in JMS client, I have check the code in rabbitmq-jms-client, and no send implementaion contains CompletionListener. So if you want to enjoy reliable publish, please use AMQP client.
I did some digging, the CompletionListener is part of JMS 2.0 and RMQ only implements JMS 1.1, that's the reason it's not there.
But it looks like I can do something with transactions. I would need to change the code like this:
RMQConnectionFactory factory = new RMQConnectionFactory() ;
// ... skipping the code here
connection.start();
// set session to be transacted
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
String queueName = managerProps.getProperty("rmq.queue.name");
Queue queue = session.createQueue(queueName);
producer = session.createProducer(queue);
TextMessage msg = session.createTextMessage(text);
msg.setText(text);
producer.send(msg);
// commit transaction
session.commit();
This will work if the queue is not full, but will throw an exception after a rejected message with this:
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method(reply-code=406, reply-text=PRECONDITION_FAILED - partial tx completion, class-id=90, method-id=20)
I can then catch the exception and do what I need to do to resend/save the message.
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.
I am using IBM MQ to produce messages while receiving it through a consumer on my client. To create the connection I'm using JmsConnectionFactory, along with provided properties to set up the connection with the server. So from what I understand is, as the consumer the only way to recognize the messages produced by the server is through the onMessage call. I'm currently testing this by creating a local producer and local consumer and assuring that every message sent by the producer is received by the consumer.
I'm running into the following problems:
I'm not receiving all messages produced.
Depending on the size of the message, more of them are received if they are smaller.
Here is code for the creation of the producer:
JmsConnectionFactory cf = ff.createConnectionFactory();
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, qm.getHost());
int port = ###;
cf.setIntProperty(WMQConstants.WMQ_PORT, port);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, qm.getChannel());
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, qm.getQueueManagerName());
Connection connection = cf.createConnection(qm.getUser().getUsername(), qm.getUser().getPassword());
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(qm.getDestinationName());
LOG.debug("Destination Created at " +qm.getDestinationName());
msgSender = session.createProducer(destination);
msgSender.setDeliveryMode(DeliveryMode.PERSISTENT);
And this is how the producer is sending messages:
/**
* msgSender is the MessageProducer object
**/
private void produceMessages(int numOfMessages) throws JMSException, InterruptedException {
for (int i = 0; i < numOfMessages; i++) {
String text = "Message #" +i;
TextMessage message = session.createTextMessage(text);
msgSender.send(message);
}
}
On the consumer side, I am simply printing received messages and verifying visually:
#Override
public void onMessage(Message m) {
System.out.println(((TextMessage)m).getText());
}
I am not fully familiar with how IBM MQ works. Could the reason for the missing messages reside on the MQ simply ignoring messages that are produced before a message is fully sent?
I would say the issue is residing on your consumer side, rather than your simulated producer. Your message producer should be sending messages to MQ just fine, but multiple consumers are probably competing to retrieve these messages from the connection you have set up (given the same queue manager properties). So unless no one else is trying to consume from your IBM MQ, you're going to be expected to miss some messages.
You should use other method of send(Message m, CompletionListener l) to send new messages only after completion.
And if you use "Best Effort", it still will lose messages. You can try "Express" instead.
I am using Glassfish JMS.
I am able to add messages to a queue.
I can see the messages using the QueueBrowser object.
However the MessageConsumer (nor the QueueReceiver) cannot receice any message and return null.
Message expiration is set to 0 and I remember to open the connection.
Any ideas?
Here is the code:
Session session = null;
Connection conn = null;
try
{
InitialContext jndi = new InitialContext();
ConnectionFactory qFactory = (ConnectionFactory)jndi.
lookup("myConnectionFactory");
conn = qFactory.createConnection();
conn.start();
Queue queue = (Queue)jndi.lookup("myQueueName");
session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
QueueReceiver mc = ((QueueSession)session).createReceiver(queue);
Object m = mc.receive(2000);
//m is NULL!
QueueBrowser browser = session.createBrowser(queue);
for(Enumeration e = browser.getEnumeration(); e.hasMoreElements(); )
{
//there are many messages here...
}
That would be good to have the client code.
Similar thing happened to me when not properly committing/closing the connection on the sender side. The message would be visible when using the admin console, however, not available yet to the MDB.
Hope it helps.
Does this code run in the appserver? If it does, I'd obtain the required objects via annotations, and for a message receiver I'd use a MDB.
If this is a piece of standalone code, I had a hell of a time getting a JNDI based client working, I reverted to using the "raw" Java API.
I witnessed the same behavior happening after the first session commit, meaning that before the messages where received correctly. In my case the issue was that I was re-creating the receiver while keeping the same session.
As pointed out in this article:
Creating temporary destinations, consumers, producers and connections
are all synchronous request-response operations with the broker and so
should be avoided for processing each request as it results in lots of
chat with the JMS broker.
The solution was as simple as reusing the same receiver.