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

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?

Related

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

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.

JMS on Websphere MQ and backout queue

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();

How to filter by message type before sending a JMS message or send to a particular server?

Assume Active MQ is the broker and there are 6 servers where JMS listeners are attached.
When a Topic is send then all the six servers will consume the messages.
Now i want a particular server only consume the message instead of all six using selector and it should be filtered before sending the JMS.
How to define a selector in JMS so that the topic will be consumed by a particular server instead of all.
You need JMS message selectors. There is no need to filter before sending, but upon receiving. An example where only server with id "serv_5" receives the message:
Producer:
...
Message message = session.createMessage();
message.setObjectProperty("server_id", "serv_5");
producer.send(message);
Consumer (MDB):
#MessageDriven(mappedName="jms/YourQueue", activationConfig = {
#ActivationConfigProperty(propertyName = "acknowledgeMode",
propertyValue = "Auto-acknowledge"),
#ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(
propertyName="messageSelector",
propertyValue="server_id = 'serv_5'")
})
public class YourMessageBean implements MessageListener { ..

jms sending message on any server

I want to write generic code for sending message on any jms server. so for that i thought if i have jndi.properties file then we can place server configuration in this file and we can access this file through the code but i am able to do this only for 'ActiveMQ Server'. Now i am facing problems to send the message on any other server like glassfish server or jboss server. can somebody help me to do this task.
Here is my code :
public class Producer
{
public Producer() throws JMSException, NamingException,IOException
{
InputStream is = getClass().getResourceAsStream("my.jndi.properties");
Properties jndiParamaters = new Properties();
jndiParamaters.load(is);
Context jndi = new InitialContext(jndiParamaters);
ConnectionFactory conFactory = (ConnectionFactory) jndi.lookup("connectionFactory");
Connection connection;
connection = conFactory.createConnection();
try
{
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Destination destination = (Destination) jndi.lookup("Myqueue");
MessageProducer producer = session.createProducer(destination);
TextMessage message = session.createTextMessage("Hello World!");
producer.send(message);
System.out.println("Sent message '" + message.getText() + "'");
}
finally
{
connection.close();
}
}
public static void main(String[] args) throws JMSException
{
try
{
BasicConfigurator.configure();
new Producer();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
thanks
Have you tried using the Spring JMS Template? http://static.springsource.org/spring/docs/2.0.x/reference/jms.html
It provides an abstraction layer to JMS and could probably help you when your implementation changes.

Resources