synchronous messaging with STOMP over HornetQ - java

I am trying to figure out how to do syncronous messaging using stomp with hornetq, or if its even possible. I have an async stomp client working, but I can't see how I would implement a sync version.
On the server side, my acceptor looks like this:
<acceptor name="stomp-acceptor">
<factory-class>org.hornetq.core.remoting.impl.netty.NettyAcceptorFactory</factory-class>
<param key="protocol" value="stomp" />
<param key="port" value="61613" />
</acceptor>
and my listener looks like this:
public class SimpleSyncListener extends BaseListener implements SessionAwareMessageListener<Message> {
#Override
public void onMessage(Message message, Session session) throws JMSException {
String lastMessage = "";
try {
lastMessage = ((TextMessage) message).getText();
//System.out.println("server recieved: " + lastMessage);
Destination replyDestination = message.getJMSReplyTo();
StringBuffer sb = new StringBuffer();
sb.append("reply ");
sb.append(Calendar.getInstance().getTimeInMillis());
sb.append(" ");
sb.append(lastMessage);
TextMessage replyMessage = session.createTextMessage(sb.toString());
replyMessage.setJMSCorrelationID(message.getJMSMessageID());
MessageProducer replyProducer = session.createProducer(replyDestination);
replyProducer.send(replyMessage);
} catch (JMSException e) {
throw new RuntimeException(e);
}
incrementCount();
}
I assume I need to put something in the temp queue and send it back like you do with JMS. Its just not clear to me how that works with STOMP. Do i need to open another tcp connection back on the client side that correspond to the "temp queue" on the server side?

Stomp is a simple protocol, and on this case I don't think you can have a multiplexed channel. So you will probably need a Stream to send, and a Stream to receive.

The common strategy to implement synchronous (request / response) communication with JMS - using temporary destinations - is also available with the STOMP implementations of many message brokers (ActiveMQ, Apollo, OpenMQ and RabbitMQ are examples).
However, HornetQ does not support temporary destinations in the current 2.4.0.Final version.

Related

How to check if subscriber is valid to accept the message received for a published topic on MQTT

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":""
}

How do I create WebSockets with Play framework 2.6?

I am trying to do the following with Play Framework 2.6:
The browser targets the server and a WebSocket is created
Later on (after some other request is performed), the servers sends a message to the browser via the WebSocket previously created
Point 1 can be easily done with a route:
public WebSocket socket() {
return WebSocket.Text.accept(request -> {
// Log events to the console
Sink<String, ?> in = Sink.foreach(System.out::println);
// Send a single 'Hello!' message and then leave the socket open
Source<String, ?> out = Source.single("Hello!").concat(Source.maybe());
return Flow.fromSinkAndSource(in, out);
});
}
and the WebSocket can be saved server side.
But then how can I send data via the WebSocket? (triggered server side)
This was easy to do with 2.5 but the documentation is not very helpful for Play 2.6.
I've managed to implement websocket with help of Akka actors. At first step define actor that will handle messages
public class WebSocketActor extends AbstractActor {
private final ActorRef out;
#Inject
public WebSocketActor(ActorRef out) {
this.out = out;
}
#Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, message ->
out.tell("Sending message at " + LocalDateTime.now().toString(), self())
)
.build();
}
public static Props props(final ActorRef out) {
return Props.create(WebSocketActor.class, out);
}
}
This actor will be created per client. ActorRef out will send message to connected client. In this example response is send to client on each string message passed to WebSocketActor.
Now define API endpoint to open access to websocket for clients. Define ActorFlow that will create new instance of actor on new connection
public WebSocket ws() {
return WebSocket.Text.accept(request -> ActorFlow.actorRef((out) -> WebSocketActor.props(out), actorSystem, materializer));
}
According to source code ActorFlow creates actors with flowActor name. So to send message to websockets somewhere in the code we can find actors by their path. This will broadcast message to all connected clients
actorSystem.actorSelection("/user/*/flowActor").tell("Hello", ActorRef.noSender());
Unfortunately I didn't find easy way to change ActorFlow default name but maybe this answer may help you play-scala-akka-websockets-change-actor-path.
Also you can check play-java-websocket-example project from playframework examples.

JMS Get message/response from EJB

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.

Implementing ActiveMQ Message Listener in/using JSP

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.

Not receiving reply on Websphere MQ temporary queue

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.

Categories

Resources