I am working with a send/receive mechanism to a Websphere MQ system.
The xml that I send in text format should receive a reply, however I receive no reply.
I know that the xml is being "sent" ok, since "things are happening" in the target system - it is just that I am not receiving a reply. The reply is important to me, since it could include an error message if something should fail.
So, the reason I am not receiving a reply - I am not sure if there is a problem with my code or with the Websphere MQ configuration.
Any pointers on my code or what I should ask the Websphere MQ administrators to look at are greatly appreciated!!
A small self contained example to demonstrate the receive is not happening looks like this:
public class CustomQueueConnection {
private MQQueueConnectionFactory connectionFactory;
private MQQueueConnection connection;
private void runTest() throws JMSException {
connect();
MQQueueSession session = (MQQueueSession) connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
MQQueue queue = (MQQueue) session.createQueue("queue:///REQ_SNAPSHOT.HT");
MQQueueSender sender = (MQQueueSender) session.createSender(queue);
TemporaryQueue temporaryQueue = session.createTemporaryQueue();
MQQueueReceiver receiver = (MQQueueReceiver) session.createReceiver(temporaryQueue);
TextMessage message = session.createTextMessage(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
// my well constructed xml goes here...
);
message.setJMSReplyTo(temporaryQueue);
sender.send(message);
System.out.println("Sent: " + message);
JMSMessage receivedMessage = (JMSMessage) receiver.receive(10000);
System.out.println("Received: " + receivedMessage);
}
public boolean connect() {
boolean connected = false;
try {
connectionFactory = new MQQueueConnectionFactory();
connectionFactory.setCCSID(819);
connectionFactory.setPort(1417);
connectionFactory.setHostName("1.2.3.4");
connectionFactory.setQueueManager("GATE1");
connectionFactory.setChannel("CLIENTS.CHANNEL");
connectionFactory.setTemporaryModel("GATEWAY_MODEL_QUEUE");
connectionFactory.setTempQPrefix("MACHINE.USER_NAME.*");
connectionFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
connection = (MQQueueConnection) connectionFactory.createQueueConnection();
connected = true;
} catch (JMSException e) {
connected = false;
}
return connected;
}
public static void main(String[] args) throws JMSException {
new CustomQueueConnection().runTest();
}
}
And here is the output:
Sent:
JMS Message class: jms_text
JMSType: null
JMSDeliveryMode: 2
JMSExpiration: 0
JMSPriority: 4
JMSMessageID: ID:414d512050314f47415445312020202053599032201b4d05
JMSTimestamp: 1398680728618
JMSCorrelationID:null
JMSDestination: queue:///REQ_SNAPSHOT.HT
JMSReplyTo: queue://GATE1/MACHINE.USER_NAME.53599032201B4E04?persistence=1
JMSRedelivered: false
JMS_IBM_PutDate:20140428
JMSXAppID:WebSphere MQ Client for Java
JMS_IBM_PutApplType:28
JMSXUserID:aomis
JMS_IBM_PutTime:10252859
JMSXDeliveryCount:0
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<esb:esbMessage xmlns:esb="http://ESBServices
Another 557 character(s) omitted
Received: null
(NB: Received: null)
edit: Websphere MQ version is 6.0.25
Your code looks OK to me, message send is successful. I would like you to check:
1) Is there an application running to receive message from REQ_SNAPSHOT.HT queue?
2) Assuming there is an application running and receiving messages, has that application processed the incoming XML message successfully? is it throwing any exceptions?
3) If the incoming message is processed successfully, has it put a reply to the correct reply queue "MACHINE.USER_NAME.53599032201B4E04"? check if it faced any problems while putting the reply message.
The solution was twofold.
First, the connection needed to be started right after it was created.
connect();
connection.start();
Secondly, the message needed to be sent with DeliveryMode.NON_PERSISTENT.
Related
I'm newbie in MQTT.
I am implementing MQTT in java and I am using below code for publisher to publish to a particualr topic,
public void publish()
{
MqttClient myClient = null;
MqttConnectOptions connOpt;
try {
// Subscription with Brokers
connOpt = new MqttConnectOptions();
connOpt.setAutomaticReconnect(true);
connOpt.setCleanSession(true);//if your not setting cleanSession to false then subscriptions shouldn't be persisted.
String clientID = UUID.randomUUID().toString().replace("-", "");
System.out.println("clientID " + clientID);
myClient = new MqttClient("tcp://192.168.10.500:1883", clientID);
myClient.connect(connOpt);
String myTopic = "Device1";
MqttTopic topic = myClient.getTopic(myTopic);
int pubQoS = 0;
MqttMessage message = new MqttMessage("mqttMessage".getBytes());
message.setQos(pubQoS);
message.setRetained(false);
MqttDeliveryToken token = null;
token = topic.publish(message);
System.out.println("publish successful with the message :: " + message);
// Wait until the message has been delivered to the broker
token.waitForCompletion();
} catch (MqttException me) {
} catch (Exception e) {
}
}
And then I am using below code to read the published message for a particular topic as a subscriber,
public void subscribe()
{
try {
MqttConnectOptions connOpt;
// Subscription with mqttBrokerEndPoint
connOpt = new MqttConnectOptions();
connOpt.setAutomaticReconnect(true);
connOpt.setCleanSession(true);//if your not setting cleanSession to false then subscriptions shouldn't be persisted.
String clientID = UUID.randomUUID().toString().replace("-", "");
System.out.println("clientID " + clientID);
MqttSubscriber mqttConnection = new MqttSubscriber();
myClient = new MqttClient("tcp://192.168.10.500:1883" clientID);
myClient.setCallback(mqttConnection);
myClient.connect(connOpt);
myClient.subscribe("Device1");
} catch (MqttException e) {
}
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
try {
System.out.println(message);
boolean isValidClient = true;// Here i need to check if this is the valid subscriber for the message publised on topic "Device1"
//if(isValidClient) {
if(message != null) {
System.out.println("message" + message.toString());
}
myClient.unsubscribe("Device1");
myClient.disconnect();
//}
}
catch(Exception e){}
}
The above implementation is working fine as it is.
Since I am very new to mqtt I have some doubts with the above implementation.
1) Is Client id in both publisher and subscriber should be SAME for one particular flow ?
or should different in both publisher and subscriber as above : which can be randomly generated?
String clientID = UUID.randomUUID().toString().replace("-", "");
This randomly generated clientID is working fine with both subscribing and publishing.
But, If I use same client for both publisher and subscriber and validate subscriber?
I mean use "clientID" myClient = new MqttClient(mqttBrokerEndPoint, "clientID"); in subsriber and then same "clientID" myClient = new MqttClient(mqttBrokerEndPoint, "clientID"); in publisher
I am getting the below socket error in mqtt broker console(used windows version) ,
1549414715: Socket error on client 82, disconnecting.
1549414715: New client connected from 192.168.10.500 as clientID (c1, k60).
1549414715: No will message specified.
1549414715: Sending CONNACK to 82 (0, 0)
1549414716: New connection from 192.168.10.500 on port 1883.
1549414716: Client 82 already connected, closing old connection.
1549414716: Socket error on client 82, disconnecting.
1549414716: New client connected from 192.168.10.500 as clientID (c1, k60).
1549414716: No will message specified.
1549414716: Sending CONNACK to 82 (0, 0)
1549414716: New connection from 192.168.10.500 on port 1883.
1549414716: Client 82 already connected, closing old connection.
1549414716: Socket error on client 82, disconnecting.
1549414716: New client connected from 192.168.10.500 as clientID (c1, k60).
1549414716: No will message specified.
1549414716: Sending CONNACK to 82 (0, 0)
and the above program is not working.
Can't we use same clientID for for both subscriber and publisher? Why is it resulting in socket error and programs not working?
2) Is username and password mandatory while implementation? Or can we establish a connection without those 2 below properties?
connOpt.setUserName(USERNAME);
connOpt.setPassword(PASSWORD.toCharArray());
3) Is pubQoS is mandatory to be used for publisher? I am currently using it as zero '0' ?
MqttMessage message = new MqttMessage(mqttMessage.getBytes());
message.setQos(0);
message.setRetained(false);
Also is retained attribute is mandatory for publisher?
4) Is these 2 below attributes mandatory while subscribing? I am using as below in code.
connOpt.setAutomaticReconnect(true);
connOpt.setCleanSession(true);//if your not setting cleanSession to
false then subscriptions shouldn't be persisted.
5) Also, Once Message is received from MQTT publisher in to MessageArrived callback as below, how to validate if this is valid subscriber and proceed with further logic?
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
try {
System.out.println(message);
boolean isValidClient = true;// Here i need to check if this is the valid subscriber for the message publised on topic "Device1"
//if valid subscriber only i need to read message publised on the topic "Device1"
**//if(isValidClient) {**
if(message != null) {
System.out.println("message" + message.toString());
}
myClient.unsubscribe("Device1");
myClient.disconnect();
//}
}
catch(Exception e){}
I mean which attribute MQTT API to check that this message is only sent for this subscriber and he can proceed further to use the message received in message arrived callback?
I.e, which attribute of MQTT api can be used to check if the received message is related to current PROCESS/STEP.(In SUbscriber Program as below)
IS the topic is the only common attributes between subscriber and publisher to validate subscriber in messageArrived callback ? or do we have any other common attribute to check a valid contract between subscriber and publisher?
Or should we used clientID to validate subscriber? But if i use the same clientID for subscriber and publisher I am getting the socket error which i have mentioned in point number 1.
How to proceed further on this?
1) You cannot use same client id for two mqtt client. Client ids for mqtt must be unique. You can create different topic for handling different operations. You can refer to the below answer
Two paho.mqtt clients subscribing to the same client localy
2) Username and password is optional. If your mqtt server is configured to use username and password then your client also need to pass username and password to mqtt server. Refer to the below link for understanding the code usage
How can i connect a Java mqtt client with username and password to an emqttd(EMQ) broker?
If you want advanced level go for tls/ssl security
https://dzone.com/articles/secure-communication-with-tls-and-the-mosquitto-broker
3) QOS - The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. Refer to the below link for more information on qos
https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels/
4) Auto Reconnect (True) - If mqtt server is disconnected during run time then client tries to reconnect to the server.
Clean Session (True) - On reconnecting to the mqtt server all older sessions associated with same client id will be deleted.
Clean Session (False) - On reconnecting to the mqtt server all older sessions associated with same client id will be retained. Mqtt server in some cases retains messages based on QOS level.
5) Try to create multiple topics for multiple operations. In message arrived method you can check on which topic message has arrived. Then you can call different methods based on that. Below codes are one of the way. You can find out best way that suits your needs. Cheers !!!!!
#Override
public void deliveryComplete(IMqttDeliveryToken token)
{
try
{
if(token.getTopics()[0].equals("TOPIC_A"))
{
//Do something
}
else if(token.getTopics()[0].equals("TOPIC_B"))
{
//Do something
}
}
catch (Exception e)
{
MQTTLogger.logSevere(e);
}
}
Json Format for data
First message
{
"client-name":"client1"
"data":""
}
Second message
{
"client-name":"client2"
"data":""
}
This is my sender class:
private void sendJMSMessage(Object data) throws JMSException {
Connection con = null;
Session s = null;
try {
con = context.createConnection();
s = con.createSession(false, Session.CLIENT_ACKNOWLEDGE);
MessageProducer producer = s.createProducer(glassFishQueue);
ObjectMessage msg = s.createObjectMessage();
ArrayList list = new ArrayList();
list.add("name");
msg.setObject(p);
producer.send(msg);
}
And my Message-driven Bean:
public void onMessage(Message message) {
try {
ObjectMessage om = (ObjectMessage) message;
ArrayList al = (ArrayList) om.getObject();
System.out.println("Msg: " + al.get(0));
} catch (JMSException jex) {
System.out.println("Exception: " + jex);
}
I got the message sent from sender class but I need a message back from EJB to the sender.
Im doing a web client with a table but I need to fill it getting the info from a database remotely, I really doesnt know what i should to use to do this, so if im doing right let me know or tell me any suggestion
Thank u
JMS is asynchronous, so it won't work in request-response style out of the box.
If you want to send a reply, one way is to use a separate queue. Your MDB can write the response to this second queue and your client can listen to this queue by creating a QueueReceiver.
Another way is to use QueueRequestor. From javadocs:
It creates a TemporaryQueue for the responses and provides a request
method that sends the request message and waits for its reply.
Look here and here for QueueRequestor examples.
I wanted to create implement a JMS sender app for messaging and created the same with JAVA. This is my sample code snippet in Java.
try {
factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("SAMPLEQUEUE");
producer = session.createProducer(destination);
try {
TextMessage message = session.createTextMessage();
message.setText("hello");
producer.send(message);
System.out.println("Sent: " + message.getText());
} catch (IOException e) {
e.printStackTrace();
}
} catch (JMSException e) {
e.printStackTrace();
}
This works fine and I am able to receive the messages with my receiver also. I want to change the sender implementation in Node JS and make it a Node JS application. I am new to Node JS didn't understand much after searching on ActiveMQ in Node JS. Any pointer to it would be really helpful.
Regards,
Subhankar
EDIT
I used stomp for node JS. The sample code is the following :
var Stomp = require('stomp-client');
var destination = '/queue/sensorstreamqueue';
var client = new Stomp('10.53.219.153', 61613, 'user', 'pass');
var lazy = require("lazy"),
fs = require("fs");
client.connect(function(sessionId) {
new lazy(fs.createReadStream('input.csv'))
.lines
.forEach(function(line){
client.publish(destination, line.toString());
}
);
console.log("published");
});
The code works and my receiver also gets the message but then my receiver expects it to be a textMesssage format and gives the following error:
02-19-2015 08:42:31.288 ERROR [Thread-25] (JmsInputTransporter.handleTextMessage) Error code:401306, Severity : 3 (Error)
Error message:JMS Transporter is expected a TextMessage, received class org.apache.activemq.command.ActiveMQBytesMessage.
Error description:JMS Transporter is expected a TextMessage, received class org.apache.activemq.command.ActiveMQBytesMessage.
Can someone help me how can I achieve that?
You can try the activemq-node module, or you can enable the STOMP protocol on ActiveMQ and use this node.js library.
You need to add a header:
client.publish(destination, 'your content', {
"amq-msg-type": "text"
});
https://issues.apache.org/jira/browse/AMQ-2833
I have implemented Message Listener in core java using Active MQ/JMS. The purpose of this listener is to subscribe a topic on ActiveMQ and then listen to the messages received from the topic. My code is working fine as a console application. Now I need to extend my application into a web application so that the messages received could be used in the web page i.e JSP. I am confused about how the message listener will work in JSP, how I will receive and process messages from active MQ topic. So far I have following code but doesn't seem to help in current scenario:
<%!
public void handleReceivedMessages() {
String url = ActiveMQConnection.DEFAULT_BROKER_URL;
String subject = "XXXXX";
try {
ConnectionFactory connectionFactory
= new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(subject);
MessageConsumer consumer = session.createConsumer(topic);
MessageListener listner = new MessageListener() {
#Override
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message : "
+ textMessage.getText() + "'");
}
} catch (JMSException e) {
System.out.println("Caught:" + e);
}
}
};
consumer.setMessageListener(listner);
try {
System.in.read();
} catch (IOException e) {
}
connection.close();
} catch (JMSException ex) {
// Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
}
}//end method
%>
How I am supposed to use this code so that i can e.g print every message on my web page which is received by the topic?
JSP pages are used the moment the web page is loaded to render the HTML. After that, they have no function.
Typically, you need to create a Message Driven Bean(MDB) or something similar using Spring. The MDB will receive messages, process the data and store it somewhere (typically a database, but could be also be a global cache, local files or similar). Your JSP then simply uses the data stored by JMS messages.
If you really want the messages to interact with the user more dynamically - you can connect to ActiveMQ using JavaScript from the client browser. The ActiveMQ distribution have some examples regarding this. Look into examples/mqtt/websocket or examples/stomp/websocket to see some working code.
EDIT: Rephrased the question:
I want to use ActiveMQ as a messenger service between my server and client applications.
I am trying to set up an embedded broker (i.e. not a separate process) within the server to handle the produced messages for my clients to consume. This queue is persisted.
The broker initialisation as follows:
BrokerService broker = new BrokerService();
KahaPersistenceAdapter adaptor = new KahaPersistenceAdapter();
adaptor.setDirectory(new File("activemq"));
broker.setPersistenceAdapter(adaptor);
broker.setUseJmx(true);
broker.addConnector("tcp://localhost:61616");
broker.start();
After tinkering, I ended up with the server part being:
public static class HelloWorldProducer implements Runnable {
public void run() {
try {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost"); // apparently the vm part is all i need
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("TEST.FOO");
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
String text = "Hello world! From: " + Thread.currentThread().getName() + " : " + this.hashCode();
TextMessage message = session.createTextMessage(text);
System.out.println("Sent message: "+ message.hashCode() + " : " + Thread.currentThread().getName());
producer.send(message);
session.close();
connection.close();
}
catch (Exception e) {
System.out.println("Caught: " + e);
e.printStackTrace();
}
}
}
The client is very similar and looks like this:
public static class HelloWorldConsumer implements Runnable, ExceptionListener {
public void run() {
try {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost");
Connection connection = connectionFactory.createConnection(); // exception happens here...
connection.start();
connection.setExceptionListener(this);
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("TEST.FOO");
MessageConsumer consumer = session.createConsumer(destination);
Message message = consumer.receive(1000);
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
String text = textMessage.getText();
System.out.println("*****Received: " + text);
} else {
System.out.println("*****Received obj: " + message);
}
consumer.close();
session.close();
connection.close();
} catch (Exception e) {
System.out.println("Caught: " + e);
e.printStackTrace();
}
}
The main method simply starts each of these in a thread to start producing/receiving messages.
...but I am running into the following with the start of each thread:
2013-01-24 07:54:31,271 INFO [org.apache.activemq.broker.BrokerService] Using Persistence Adapter: AMQPersistenceAdapter(activemq-data/localhost)
2013-01-24 07:54:31,281 INFO [org.apache.activemq.store.amq.AMQPersistenceAdapter] AMQStore starting using directory: activemq-data/localhost
2013-01-24 07:54:31,302 INFO [org.apache.activemq.kaha.impl.KahaStore] Kaha Store using data directory activemq-data/localhost/kr-store/state
2013-01-24 07:54:31,339 INFO [org.apache.activemq.store.amq.AMQPersistenceAdapter] Active data files: []
2013-01-24 07:54:31,445 DEBUG [org.apache.activemq.broker.jmx.ManagementContext] Probably not using JRE 1.4: mx4j.tools.naming.NamingService
2013-01-24 07:54:31,450 DEBUG [org.apache.activemq.broker.jmx.ManagementContext] Failed to create local registry
java.rmi.server.ExportException: internal error: ObjID already in use
at sun.rmi.transport.ObjectTable.putTarget(ObjectTable.java:186)
at sun.rmi.transport.Transport.exportObject(Transport.java:92)
at sun.rmi.transport.tcp.TCPTransport.exportObject(TCPTransport.java:247)
at sun.rmi.transport.tcp.TCPEndpoint.exportObject(TCPEndpoint.java:411)
at sun.rmi.transport.LiveRef.exportObject(LiveRef.java:147)
<snip....>
It seems like the messages are produced and consumed successfully (the other issues I previously posted about was resolved), but the above exception is worrying me.
EDIT: During broker shutdown, I am now also greeted by the following:
2013-01-25 08:40:17,486 DEBUG [org.apache.activemq.transport.failover.FailoverTransport] Transport failed with the following exception:
java.io.EOFException
at java.io.DataInputStream.readInt(DataInputStream.java:392)
at org.apache.activemq.openwire.OpenWireFormat.unmarshal(OpenWireFormat.java:269)
at org.apache.activemq.transport.tcp.TcpTransport.readCommand(TcpTransport.java:210)
at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:202)
at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:185)
at java.lang.Thread.run(Thread.java:722)
You can embed a broker into your code in a number of ways, much of which is documented here. You may want to try upgrading you version since what you are using appears to be quite old as it defaulting to the now deprecated AMQ Store instead of the newer KahaDB store. You might be having issues because of a race between the client threads as they use the different connection factories which could race to create in VM brokers. If you set the create=false option on the producer and ensure the consumer thread starts after that could address the issue, or you could create the VM broker ahead of time and the add create=false to both thread's and that might do the trick.
BrokerService broker = new BrokerService();
// configure the broker
broker.setBrokerName("localhost");
broker.setUseJmx(false);
broker.start();
And then in the client code just attach via this connection factory configuration.
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("vm://localhost?create=false");
When I run your code, I got the below exception:
javax.jms.JMSException: Could not connect to broker URL: tcp://localhost.
Reason java.lang.IllegalArgumentException: port out of range:-1
Your broker is running and listening to port 61616, so any client which tries to connect to broker need to have the port in its URL.
The client code tries to connect to localhost but doesn't mention the port to which it has to connect.
Both the producer and consumer code needs to be fixed.
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost");
To
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
After fixing the port, I was able to run your code.