JMS on Websphere MQ and backout queue - jms

I have MQ as an external JMS server on my Weblogic server. The thing is that I need to rollback the message and retry until backout threshold is reached. Then I need to move the message to backout queue.
The MessageDrivenContext.setRollbackOnly() method takes care of that just fine. The problem, however, is with the message on the backout queue - it is uncommited.
Moreover, the messages are being taken from backout queue and processed again as soon as new message appears on the main queue.
This suggest me that there is something teribly wrong with my approach. I cannot, however, change the fact, that I have to retry the onMessage() with the same message a couple of times and send it to backout queue is backout threshold was reached.
#MessageDriven( name="MQListener", mappedName = "jms.mq.SOME.QUEUE.NAME",
activationConfig =
{
#ActivationConfigProperty(propertyName = "destinationType",propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "jms.mq.SOME.QUEUE.NAME"),
#ActivationConfigProperty(propertyName = "connectionFactoryJndiName", propertyValue = "jms.mq.MQ"),
#ActivationConfigProperty(propertyName = "useJNDI", propertyValue = "true")
})
public class MQListener implements MessageListener {
#Resource
private MessageDrivenContext context;
#Override
public void onMessage(Message message) {
String messageContent="";
try {
messageId = message.getJMSMessageID();
if (message != null) {
messageContent = ((TextMessage)message).getText();
if(!doSomething(messageContent)){
// doSomething fails, I need to rollback the message and try again:
context.setRollbackOnly();
}
}
} catch (Exception e) {
throw new RuntimeException();
}
}
private boolean doSomething(String messageContent){
// ...
}
}

I am a tyro in EJB. But from what I can see in your code snippet, I think you are missing initialization of MessageDrivenContext. I think you will have to do either
context = getMessageDrivenContext();
context.setRollbackOnly();
or
getMessageDrivenContext().setRollbackOnly();

Related

listening to IBM mq using message driven beans in Websphere application server

I have an application in springboot which is using jms to receive messages from ibm mq synchronously i.e., using .receive() method which is running fine, Now, I am implementing another process to run in background to receive async messages which is older than 2 minutes, from same queue which uses #Async and Message driven beans(onMessage()) .
My method implementation for mdb is as below:
#Service
#Slf4j
#MessageDriven(mappedName = "REPL.AI", activationConfig = {
#ActivationConfigProperty(propertyName = "connectionFactoryLookup", propertyValue = "jms/queueConnectionFactory"),
#ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "port", propertyValue = "1414")})
public class MessageBean implements MessageListener {
#Autowired
private AsyncMessageReceiver asyncMessageReceiver;
#Override
#Async("AsyncExecutor")
public void onMessage(Message message) {
log.info("ONMESSAGE-START");
TextMessage msg = null;
try {
if (message instanceof TextMessage) {
msg = (TextMessage) message;
log.info("received an async message");
asyncMessageReceiver.processIntoText(msg); //calling other method for further processing
}
} catch (Exception e) {
log.error("Exception occurs in onMessage(): " + e);
}
log.info("ONMESSAGE-END");
}
}
Also I have created a listener port '1414' in WAS server console to bind the mdb to port.
All configuration is already provided.
problem is, mdb is not receiving any messages from the queue , nor it is throwing any error.
I can see in logs
MDB Listener 1414 started successfully for JMSDestination jms/ReplQueue.
after this I dont see any exception and not any incoming messages too,messages have been sent through sender.
any pointers for this?

JMS Temporary Queue - Replies not returning back to client

I'm trying to move away from Weblogic to JBoss, and as such I'm trying to implement the things I was able to implement on Weblogic on JBoss.
One of those things is our notification system where the client sends a request to an MDB and the MDB sends a reply back to the client.
This was a breeze in Weblogic, but on Jboss, nothing seems to work. I keep getting this error:
javax.jms.InvalidDestinationException: Not an ActiveMQ Artemis Destination:ActiveMQTemporaryQueue[da00b1a2-114d-4be9-930d-926fc20c2fce]
Is there something I need to configure on my Jboss?
EDIT
I realise that I probably didn't phrase the question very well.
What happens is this: I have a client and a server MDB (message driven bean). The client sends a message to a queue and waits for a response from the server. The server picks the message from the queue and sends a response to the client, which the client picks up and displays.
On Jboss, messages from the client go smoothly, and the server picks it up, but as soon as the server MDB tries to send a response to the client, that error is thrown.
My client code (excerpt):
int TIME_OUT = 60000;
//prepare factory
Properties prop = new Properties();
prop.setProperty("java.naming.factory.initial", "org.jboss.naming.remote.client.InitialContextFactory");
prop.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
prop.setProperty("java.naming.provider.url", "http-remoting://remotehost:8080");
prop.setProperty("java.naming.security.principal", "guest-user")
prop.setProperty("java.naming.security.credentials", "Password#1")
String queueConnectionFactory = "jms/RemoteConnectionFactory";
Context context = new InitialContext(prop);
QueueConnectionFactory qconFactory = (QueueConnectionFactory) context.lookup(queueConnectionFactory);
//prepare queue and sessions
QueueConnection qcon = qconFactory.createQueueConnection(prop.getProperty("java.naming.security.principal"), prop.getProperty("java.naming.security.credentials"));
QueueSession qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) context.lookup("jms/TestQueue2");
//create message
NotificationWrapper wrapper = //object initialised with something
ObjectMessage om = qsession.createObjectMessage(wrapper);//NotificationWrapper wrapper
//create producer
MessageProducer producer = qsession.createProducer(queue);
//create temporary queue
TemporaryQueue tempqueue = qsession.createTemporaryQueue();
om.setJMSReplyTo(tempqueue);
//start connection
qcon.start();
//send message and wait for response
producer.send(om);
MessageConsumer consumer = qsession.createConsumer(tempqueue);
Message callback = consumer.receive(TIME_OUT);
//print message from server
if (callback != null) {
System.out.println("Response received from server. Print here...");
//message from server
} else {
System.out.println("No Response received from server. Problems!!!");
}
//close all connections
if (consumer != null) {
consumer.close();
}
if (producer != null) {
producer.close();
}
if (qsession != null) {
qsession.close();
}
if (qcon != null) {
qcon.close();
}
My Server code (excerpt):
#MessageDriven(mappedName = "TestQueue2", activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/TestQueue2"),
#ActivationConfigProperty(propertyName = "maxSession", propertyValue = "10")
})
public class ServerSide implements MessageListener {
private static final QueueConfigProperties queueConfigProp = QueueConfigProperties.getInstance();
private Context context;
private QueueConnectionFactory qconFactory;
private QueueConnection qcon;
private QueueSession qsession;
private MessageProducer producer;
public ServerSide() {
try {
initialiseQueueFactory("jms/RemoteConnectionFactory");
//initialiseQueueFactory("jms/GreenpoleConnectionFactory");
prepareResponseQueue();
Properties prop = new Properties();
prop.setProperty("java.naming.factory.initial", "org.jboss.naming.remote.client.InitialContextFactory");
prop.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
prop.setProperty("java.naming.provider.url", "http-remoting://remotehost:8080");
prop.setProperty("java.naming.security.principal", "guest-user")
prop.setProperty("java.naming.security.credentials", "Password#1")
String queueConnectionFactory = "jms/RemoteConnectionFactory";
Context context = new InitialContext(prop);
QueueConnectionFactory qconFactory = (QueueConnectionFactory) context.lookup(queueConnectionFactory);
qcon = qconFactory.createQueueConnection(queueConfigProp.getProperty("java.naming.security.principal"), queueConfigProp.getProperty("java.naming.security.credentials"));
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
} catch (NamingException | ConfigNotFoundException | IOException | JMSException ex) {
//error log
}
}
#Override
public void onMessage(Message message) {
try {
if (((ObjectMessage) message).getObject() instanceof NotificationWrapper) {
//send response
if (message.getJMSReplyTo() != null) {
logger.info("sending response");
respondToSenderPositive(message);
Response resp = new Response();
resp.setRetn(0);
resp.setDesc("Notification submitted to queue.");
producer = qsession.createProducer(message.getJMSReplyTo());
producer.send(qsession.createObjectMessage(resp));
producer.send(msg_to_send);
}
} else {
//some message printed here
}
} catch (JMSException ex) {
//error logs
} catch (Exception ex) {
//error logs
}
}
}
The issue was to do with the configuration of the destination queue which is a remote queue: the client and server are both running on different JVMs. Remote Queues on Jboss are named differently from those on Weblogic.
The name of a remote queue should be something like this: java:jboss/exported/jms/TestQueue2
Refer to the JBoss documentation for a more detailed explanation on queues: https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.0/html-single/configuring_messaging/index

JMS Connection delivering messages sent to the queue while the connection was stopped

I am facing an issue with JMS Connection stop() and start(). A simple java program illustrating the same is:
public class Test {
static Connection producerConn = null;
static BufferedWriter consumerLog = null;
static BufferedWriter producerLog = null;
public static final void main(String[] args) throws Exception {
ConnectionFactory cf = new ActiveMQConnectionFactory("failover:(tcp://localhost:61616)");
producerConn = cf.createConnection();
producerLog = new BufferedWriter(new FileWriter("produced.log"));
consumerLog = new BufferedWriter(new FileWriter("consumed.log"));
new Thread(new Runnable() {
public void run() {
try {
producerConn.start();
Session session = producerConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("SampleQ1");
MessageProducer producer = session.createProducer(queue);
Random random = new Random();
byte[] messageBytes = new byte[1024];
for (int i = 0; i < 100; i++) {
random.nextBytes(messageBytes);
Message message = session.createObjectMessage(messageBytes);
producer.send(message);
Thread.currentThread().sleep(10);
producerLog.write(message.getJMSMessageID());
producerLog.newLine();
producerLog.flush();
}
System.out.println("Produced 100000 messages...");
producerLog.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
}).start();
System.out.println("Started producer...");
new Thread(new Runnable() {
public void run() {
int count = 0;
try {
producerConn.start();
Session session = producerConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("SampleQ1");
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new Test().new MyListener());
}
catch (Exception e) {
e.printStackTrace();
}
}
}).start();
System.out.println("Started consumer...");
}
private class MyListener implements MessageListener{
private int count = 0;
public void onMessage(Message message) {
try {
message.acknowledge();
System.out.println("count is " +count++);
if(count == 5){
producerConn.stop();
System.out.println("Sleeping Now for 5 seconds. . ." +System.currentTimeMillis());
Thread.currentThread().sleep(5000);
producerConn.start();
}
System.out.println("Waking up . . ." +System.currentTimeMillis());
consumerLog.write(message.getJMSMessageID());
consumerLog.newLine();
consumerLog.flush();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
My idea is to simulate the connection stop() and start(). Therefore, in the consumer thread after calling stop(), I have placed a sleep of 5 seconds. However, in the mean time the producer thread continues its job of sending message to the queue.
I expected the test to just consume only the message delivered before the consumer calls stop() and after it calls start() again after waking up from the sleep. But what's happening here is, when consumer wakes up it reads all the messages from the server even those that were sent to the queue when consumer's message reception was stopped.
Am I doing anything wrong here ?
There is nothing wrong there, it's the correct behavior. In asynchronous messaging producer and consumer are loosely decoupled. A producer does not care whether a consumer is consuming messages or not. It keeps putting messages to a queue while the consumer may be down, or stopped consuming messages or actively consuming messages.
The connection.stop() method has no effect on producer. It affects only consumer, stop() method pauses the delivery of messages from JMS provider to a consumer while start() method starts/resumes message delivery.
Hope this helped.

Error on JMS Queue because of destination in websphere used by Message Driven Beans

I am getting the following error:
Application EBS_Calc#EBS_Calc_EJB.jar#MBIntegrations has an , useJNDI, for which there is no corresponding property on the ActivationSpec class jms/ASQueue(com.ibm.ws.sib.api.jmsra.impl.JmsJcaActivationSpecImpl) of ResourceAdapter cells/USWSA0102235Node01Cell/nodes/USWSA0102235Node01/servers/server1/resources.xml#J2CResourceAdapter_1364909976437. This property will be ignored. This may have undesirable effects.
ActivationSpe W J2CA0161W: The type of the object referred to by the supplied destination JNDI name is wrong. The object must implement javax.jms.destination. The destination JNDI name was: jms/ASQueue. The supplied objects class was: {1}
ActivationSpe E J2CA0137E: The ActivationSpec validate() method failed with an InvalidPropertyException. The ActivationSpec is jms/ASQueue (com.ibm.ws.sib.api.jmsra.impl.JmsJcaActivationSpecImpl), which belongs to the installed ResourceAdapter cells/USWSA0102235Node01Cell/nodes/USWSA0102235Node01/servers/server1/resources.xml#J2CResourceAdapter_1364909976437 and is associated with the MDB application EBS_Calc#EBS_Calc_EJB.jar#MBIntegrations. See the following list of failed properties along with their values:
destination null
destination null
...
CWSJR1181E: The JMS activation specification has invalid values - the reason(s) for failing to validate the JMS activation specification are: [CWSJR1188E: The destination on a JMS activation specification must be given a value, CWSJR1192E: JMS activation specs using a destination type of queue must have a destination of type [com.ibm.websphere.sib.api.jms.JmsQueue] but the destination passed was of type [null]]
I have configured accordingly the queue and bus and destination in websphere
My java code looks as follows:
#MessageDriven(mappedName = "jms/ASQueue", activationConfig = {
#ActivationConfigProperty(propertyName = "connectionFactoryJndiName", propertyValue = "jms/CalcConnectionFactory"),
#ActivationConfigProperty(propertyName = "destinationName", propertyValue = "jms/calcInQueue"),
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") })
#Resources ({
#Resource(name="jms/CalcConnectionFactory",
mappedName="jms/CalcConnectionFactory", // External JNDI name
type=javax.jms.ConnectionFactory.class)
})
public class MBIntegrations implements MessageListener {
private static final String CONNECTION_FACTORY_NAME = "jms/CalcConnectionFactory";
private static final String DESTINATION_NAME = "jms/calcInQueue";
#Resource
private MessageDrivenContext mdc;
private static final Logger logger = Logger.getLogger(MBIntegrations.class);
public void onMessage(Message inMessage) {
TextMessage msg = null;
try {
if (inMessage instanceof TextMessage) {
msg = (TextMessage) inMessage;
logger.info("MESSAGE BEAN: Message received: " + msg.getText());
} else {
logger.warn("Message of wrong type: "
+ inMessage.getClass().getName());
}
} catch (JMSException e) {
e.printStackTrace();
mdc.setRollbackOnly();
} catch (Throwable te) {
te.printStackTrace();
}
}
}
and IBM binder
<message-driven name="MBIntegrations">
<jca-adapter activation-spec-binding-name="jms/ASQueue"
destination-binding-name="jms/ASQueue" />
<resource-ref name="jms/CalcConnectionFactory"
binding-name="jms/CalcConnectionFactory" />
<resource-ref name="jdbc/OracleDS" binding-name="jdbc/ORACLE" />
</message-driven>
I am not sure what I am doing wrong, could someone help?
Found the problem in the binder after reviewing:
http://pic.dhe.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=%2Fcom.ibm.websphere.ejbfep.multiplatform.doc%2Finfo%2Fae%2Fae%2Fcejb_bindingsejbfp.html
there it said:
<jca-adapter activation-spec-binding-name=
"jms/InternalProviderSpec"
destination-binding-name="jms/ServiceQueue"/>
so in my case would have been:
... destination-binding-name="jms/calcInQueue" />
Go to WebSphere Admin console
Activation specifications - Open your Activation Specification
Destination JNDI name - may be wrong. If it is topic make sure you gave topic jndi name.

Correct usage of JMS-Topic communication

I want to use JMS (Topic) in my JavaEE 6 project. I have one class which acts as a publisher and subscriber of a topic at once. The following code shows the most important parts of the class.
public class MessageHandler implements MessageListener {
private static TopicConnectionFactory factory;
private static Topic topic;
private TopicSubscriber subscriber;
private TopicPublisher publisher;
public MessageHandler() throws NamingException, JMSException {
if (factory == null) {
Context context = new InitialContext();
factory = (TopicConnectionFactory) new InitialContext()
.lookup("jms/myfactory");
topic = (Topic) context.lookup("jms/mytopic");
}
TopicConnection connection = factory.createTopicConnection();
connection.start();
TopicSession session = connection
.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
subscriber = session.createSubscriber(topic);
}
#Override
public void onMessage(Message message) {
try {
ObjectMessage msg = (ObjectMessage) message;
Object someO= msg.getObject();
System.out.println(this + " receives "+someO);
} catch (JMSException e) {
e.printStackTrace();
}
}
public void sendMessage(Object someO) {
try {
ObjectMessage msg = session.createObjectMessage();
msg.setObject(someO);
publisher = session.createPublisher(topic);
publisher.publish(msg);
publisher.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
My question is, if this is a good way to design such a class. My idea was to share one connection and session for both subscribing and publishing. But I'm scared that this could lead to some overhead or blocking because I'm not closing the connection, session, subscriber and publisher until the object is not needed anymore. All examples I found online directly close everything after a message was sent or received...
Thanks in advance!
Why do you want the class to be subscriber and publisher at once?
Whenever using a messaging system, you may well act as both, but why would you do it for the same topic, you surely don't want to receive your own messages?
So, the purpose of a topic, is to be used among several parts within an application or among several applications - one is placing a message into the topic and others receive the message they subscribed for.
And that also explains what you saw in the examples - the message processing is a one time thing, thus the connection can be closed afterwards.
By the way, since you ask this question within the "java-ee 6" area - can't you use a message driven bean, annotate your topic configuration and let the application server do the infrastructure part for you?

Resources