Not seeing message properties that I set explicitly (Spring/JMS/MQ) - jms

Two separate processes are communicating via JMS over MQ.
The writer sets a string property:
new MessageAction() {
public void actOn(Message message) throws JMSException {
logger.debug("Setting message property (GmmThreadId, CASHFLOW-1234)");
message.setStringProperty("GmmThreadId", "CASHFLOW-1234");
}
});
This MessageAction is executed, as shown in the logs:
Setting message property (GmmThreadId, CASHFLOW-1234)
The reader uses a message selector to obtain this message. (using Spring's JmsTemplate v1 - an old version I know, but it's baked into our vendor's framework and we're compelled to use it.)
template.receiveSelected(queue, "GmmThreadId = 'CASHFLOW-1234' or GmmThreadId IS NULL");
The only messages on the queue are ones for which the logs show that the writer set the message property. In the logs for the reader, I enumerate the properties and also try to log the value of GmmThreadId. It also shows that the property GmmThreadId does not exist.
message property JMS_IBM_PutDate = 20111003
message property JMSXAppID = WebSphere MQ Client for Java
message property JMS_IBM_Format = MQSTR
message property JMS_IBM_PutApplType = 28
message property JMS_IBM_MsgType = 8
message property JMSXUserID = MUSR_MQADMIN
message property JMS_IBM_PutTime = 23551798
message property JMSXDeliveryCount = 2
message had GmmThreadId property of null
Note, if my message selector is only "GmmThreadId = 'CASHFLOW-1234'" then nothing is dequeued.
Using WebSphere MQ Explorer to view the message, I cannot find anywhere to view the application set message properties. The message type is MQSTR, if that makes any difference.
Both reader and writer applications are running in WebSphere v6.
Where am I going wrong?

As indicated by this thread the message properties will be suppressed if the destination type is 'MQ'. For me, this is configured in the WebSphere App Server console. I changed the queue type to 'JMS' and the message properties were persisted.

Related

Redeliver message to MQ when using #JmsListener

I'm using #EnableJms and #JmsListener annotation to register a queue listener in my application, basing on this tutorial. I'm connecting to IBM MQ, getting connection factory using jndi. I have read about acknowledge modes etc. but still it's a new thing to me. And my problem is that the message is not being returned to a queue (the listener is never called again).
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory
= new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setSessionTransacted(true);
factory.setSessionAcknowledgeMode(Session.AUTO_ACKNOWLEDGE); //I have tried also CLIENT_ACKNOWLEDGE
return factory;
}
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "myQueue")
#SendTo("secondQueue")
public String testListener(String message){
if(true) throw new NullPointerException();
else return message;
}
Any help would be much appreciated.
I would have also a second question. From what I understand if I would like to implement any operation on a database, only way to rollback a commit (if something went wrong after this) would be to create a transaction manager? If not, I would need to detect a duplicated message.
First set the acknowledgement mode to Session.CLIENT_ACKNOWLEDGE
and when receiving the messages, if it's processed correctly then just call message.acknowledge() method or else don't call.
It will automatically stay in the queue and you don't need to resend it.
You need to use
import javax.jms.Message
I created simple Spring Boot app and Docker container of IBM MQ to test your case.
I found good instructions in this tutorial: https://developer.ibm.com/tutorials/mq-jms-application-development-with-spring-boot/
And in your case this environment behaves as expected: endless cycle of receive message -> NullPointerException -> return message -> ...
Than I found feature of IBM MQ called "Backout Queues & Thresholds", you'll found the explanation in this blog post: https://community.ibm.com/community/user/imwuc/browse/blogs/blogviewer?BlogKey=28814801-083d-4c80-be5f-90aaaf81cdfb
Briefly, it is possible to restrict number of times message returned to queue after exception, and after this limit send message to another queue.
May be in your case this feature used on your destination queue.

JMSTemplate with multiple brokers. Destination resolving exception

I have problem which I am trying solve all day, without success...
I have an application which trying to send/receive messages to/from external system A and external system B. A and B it is WLS based external systems.
While my application is coming up - i am reading all configurations and building my applicational JMSProducer and injecting JMSTemlate with predefined destination name in it.
Here is my code:
private JMSProducer initProducer(Conf conf) {
DestinationResolver destinationResolver = getDestinationResolver(conf);
ConnectionFactory connectionFactory = getConnectionFactory();
String destinationName = conf.getDestinationName();
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(connectionFactory);
jmsTemplate.setDestinationResolver(destinationResolver);
jmsTemplate.setDefaultDestinationName(destinationName);
return new JMSProducer(jmsTemplate);
}
public DestinationResolver getDestinationResolver(Conf conf) {
JndiDestinationResolver destinationResolver = new JndiDestinationResolver();
destinationResolver.setCache(false);
destinationResolver.setJndiTemplate(getJNDITemplate(conf));
return destinationResolver;
}
private JndiTemplate getJNDITemplate(Conf conf) {
JndiTemplate jndiTemplate = new JndiTemplate();
Properties properties = new Properties();
String connectionFactoryClassName = externalSystemConf.getConnectionParam().getConnectionFactory();
properties.setProperty("java.naming.factory.initial", connectionFactoryClassName);
properties.setProperty("java.naming.provider.url", getProviderURL(conf.getConnectionParam()));
jndiTemplate.setEnvironment(properties);
return jndiTemplate;
}
Now scenario what happens.
My app is up and running, external system A with 2 queues and external system B with 1 queue also is up and running.
I am retrieving relevant, already initialized JMSProducer in which I have already injected JMSTemplate with destinationName.
Sending messages to queues of external system A
Again retrieving next instance of JMSProducer relevant for system B
Sending messages to queue of external system B
At this stage everything is good, all messages delivered to relevant queues in external systems.
Now I am getting again JMSProducer which relevant for external system A, and trying to send messages to one of the queues. And in this stage I have a problem, DestinationResolutionException is thrown:
Destination [topic2.queueName] not found in JNDI
javax.naming.NameNotFoundException: While trying to lookup 'topic2.queueName' didn't find subcontext 'topic2'. Resolved ""
How it is possible, I have just sent messages to external system A with the same destination and it worked fine. Why it throwing exception when I am sending message to A after I tried to sent it to B?
By the way, If I will try to change cache flag to true when defining destination resolver, it is solving this problem. However in this case I starting to have problem when my external system is going to be restarted. After restart it also have some exception related to destination resolving.
Solved.
Problem was that in both external systems, in WLS - domain name, jms server name, and jms module name were the same. And weblogic client stores state, map with this names.
This is from WLS documentation
Interoperating WebLogic Server domains have the following
restrictions:
Domain names must be unique.
WebLogic server names must be unique, even if they are in two
different domains.
JMS server names must be unique, even if they are in two different
domains.
Interoperating domains may have special Security considerations.
https://docs.oracle.com/cd/E28280_01/web.1111/e13738/best_practice.htm#JMSAD635
After changing all above mentioned names - problem was solved.

How can I get messageId after sending to RabbitMq?

I want some messageId kind of thing when I am sending to Rabbit Mq Queue as I will get when sending to IBM MQ using jms. I am using spring MQ amqp starter dependency with Spring Boot. Configuration is done only in application.yml (property file). I am using Rabbit template for sending.
rabbitMqTemplate.convertAndSend(EMPTY_STRING,queueName, message, messagePostProcessor);
I have tried messagePostProcessor. Any help is appreciated. I had a look into below content. But didnt understand how to implement. Does it require special configuration (connectionfactory/ container)?
https://www.rabbitmq.com/confirms.html
Unlike JMS, the rabbit client doesn't assign message ids.
However, you can configure the RabbitTemplate's MessageConverter to create an id, which you can then retrieve with a post processor.
See AbstractMessageConverter...
/**
* Flag to indicate that new messages should have unique identifiers added to their properties before sending.
* Default false.
* #param createMessageIds the flag value to set
*/
public void setCreateMessageIds(boolean createMessageIds) {
this.createMessageIds = createMessageIds;
}
For message confirmations, see the reference manual. But that is unrelated to the message id property.

Spring JMS + IBM MQ: How to set message buffer size or wait timeout?

I'm unable to process large messages from IBM MQ and get the below error:
JMSCMQ0001: WebSphere MQ call failed with compcode '1' ('MQCC_WARNING') reason '2080' ('MQRC_TRUNCATED_MSG_FAILED')
I'm using the DefaultListenerContainer and not consuming via a MessageConsumer using IBM MQ Java API classes directly. I believe by using IBM MQ JMS API you can specific options before retrieving the message from the queue. But how do I do that with DefaultListenerContainer, is there a system property I can set for these?
If using IBM MQ JMS API(I'm not consuming message like this, pasted just for reference):
MQGetMessageOptions mqGetMessageOptions = new MQGetMessageOptions();
mqGetMessageOptions.waitInterval = ipreoProperties.getMqReceiveWaitTime();
mqGetMessageOptions.options = MQC.MQGMO_WAIT | MQC.MQPMO_SYNCPOINT | MQC.MQGMO_ACCEPT_TRUNCATED_MSG;
Below is my Java Config for the IBM MQ Connection:
#Bean
public CachingConnectionFactory ipreoMQCachingConnectionFactory() {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
//Not defining MQQueueConnectionFactory as separate bean as Spring boot's auto-configuration finds two instances
//of ConnectionFactory and throws ambiguous implementation exception
//One implementation is CachingConnectionFactory and other one would be MQQueueConnectionFactory if defined separately
MQQueueConnectionFactory mqConnectionFactory = new MQQueueConnectionFactory();
try {
mqConnectionFactory.setHostName(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_MQ_HOSTNAME));
mqConnectionFactory.setQueueManager(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_MQ_QUEUE_MGR));
mqConnectionFactory.setPort(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_MQ_PORT, Integer.class));
mqConnectionFactory.setChannel(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_MQ_CHANNEL));
//mqConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
//Setting connection mode as Client so it doesn't complain for native IBM MQ libraries
mqConnectionFactory.setIntProperty(CommonConstants.WMQ_CONNECTION_MODE, CommonConstants.WMQ_CM_CLIENT);
} catch (JMSException exception) {
exception.printStackTrace();
}
cachingConnectionFactory.setTargetConnectionFactory(mqConnectionFactory);
//Setting session caching size as 10, don't think we need more
cachingConnectionFactory.setSessionCacheSize(10);
cachingConnectionFactory.setReconnectOnException(true);
return cachingConnectionFactory;
}
public DefaultMessageListenerContainer ipreoDealActivityListenerContainer() {
DefaultMessageListenerContainer factory = new DefaultMessageListenerContainer();
factory.setConnectionFactory(ipreoMQCachingConnectionFactory());
factory.setDestinationName(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_DEAL_QUEUE_NAME));
factory.setMessageListener(ipreoDealActivityListener());
factory.setSessionAcknowledgeMode(Session.AUTO_ACKNOWLEDGE);
return factory;
}
#Bean
public MessageListener ipreoDealActivityListener() {
return new IpreoDealActivityListener();
}
Appreciate your help, thanks.
Adding a late response as it might be useful to someone.
In my case, when the java client had this exception, we noticed the actual message size was larger than the default 4 MB buffer size.
The Java API does not provide a hook to change buffer size. Hence, the buffer size has to be updated at the MQ server level.
First, we increased the message size in queue properties - It did not work.
Then, we increased the message size property at the MQ channel level as well, which finally resolved the issue.
To summarise, increase the buffer size at the MQ server for queue & the channel both.
On a client connection to a queue manager you can limit the size of messages on both the server and client side. I've seen this error before when the client side limit was smaller then the size of the message.
I don't know how you can set the message size limit directly in the JMS client, but you could use a Client Channel Definition Table. It's a file containing the details for connecting to queue managers, created on a queue manager and then copied to the client host. You need to reference the file by issuing setCCDTURL on the connection factory (setting the host, port and channel is not required when using a CCDT, the CCDT will specify those).
When the CCDT is created on the queue manager the appropriate message size limit needs to be set on the client channel.
The server side limit is set on the server connection channel.
Within the JMS client code handling of the receive buffer us handled automatically; the theory is that specific error should never be received by a JMS Application.
The first snippet of code is the Java Classes API and this could get that error.
How big actually are these messages? What level of the JMS client code are you using - make sure that it is the latest version. And certainly one of the 7.5 or 8 releases.
This answer also has some more information on this.

why message getting re-delivered to a MDB

We have a MDB running on weblogic 12c with the following definition
#TransactionManagement(TransactionManagementType.BEAN)
#MessageDriven(activationConfig = { #ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") })
public class NonPersistentSalesMessageBean implements MessageListener
{
}
The functionality of this bean is
Receives the message
uses session beans for all business logic (session bean in turn use entity beans(JPA2.0))
reply with the response message back to the destination queue
The bean is receiving duplicate messages frequently. As stated earlier there are some exceptions at the entity bean level while inserting into the database but that's not related to the reply message. The MDB is successfully posting the response back to the destination queue but after 6 or 7 seconds am seeing the same message being sent again.
Why is the message getting redelivered when the app is successfully sending the response back to the destination queue?
Can someone please tell me what are the scenarios for a message to be redelivered ?
I can check using message.getJMSRedelivered() but would like to understand why is this getting redelivered? any help or answer s will be highly appreciated. please
Please note this is a MDB which was running fine on weblogic 10 and we upgraded to 12c when we started to have this issue. So we reverted to weblogic 10 and now its back to working fine.
Thanks...Venkat

Resources