MQTT subscribe client doesn't receive messages after connection - java

I am trying to learn MQTT and have been playing around with it. I've written a client for publishing and a client for subscribing (see below).
If I run the subscribe client and then run the publish client (while subscribe is running), then everything works fine. My subscribe client receives the messages published to the topic correctly.
However, if I run the publish client first (ie. I publish a message to a topic) and then I run the subscribe client, I receive no messages.
In other words, if I connect with the sub client first and then publish messages with the pub client while sub client is connected, everything works fine. However, if I publish a message first, and then connect with my sub client, I receive no messages. My understanding is that I should receive the messages that are present on the topic once I connect with a client and subscribe to the topic.
I found what seems a similar issue: Cannot receive already published messages to subscribed topic on mqtt paho, although that case seems a little different. I've tried changing different QoS setting or cleanSession flag, but that didn't resolve the issue.
Any help would be appreciated!
Publish Client:
public class MQTT_Client_Pub implements MqttCallback{
MqttClient client;
public static void main(String[] args) {
new MQTT_Client_Pub().mqttPub();
}
public void mqttPub(){
try {
this.setConnection();
// Connect
client.connect();
// Create new message
MqttMessage message = new MqttMessage();
message.setPayload("A single test message from b112358".getBytes());
message.setQos(0);
// Publish message to a topic
System.out.println("Publishing a message.");
client.publish("pahodemo/test/b112358", message);
// Disconnect
client.disconnect();
} catch (MqttException e) {
e.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
}
public void setConnection(){
// Client
try{
client = new MqttClient("tcp://iot.eclipse.org:1883", "mqtt_test_b112358_pub");
} catch (MqttException e) {
e.printStackTrace();
}
// Connection Options
MqttConnectOptions options = new MqttConnectOptions();
// Set the will
options.setWill("pahodemo/clienterrors", "CRASHED - CONNECTION NOT CLOSED CLEANLY".getBytes(),2,true);
// Set Callback
client.setCallback(this);
}
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("Message delivered to the broker.");
}
public void messageArrived(String topic, MqttMessage message) throws Exception {}
public void connectionLost(Throwable cause) {}
}
Subscribe Client:
public class MQTT_Client_Sub implements MqttCallback{
MqttClient client;
public static void main(String[] args) {
new MQTT_Client_Sub().mqttSub();
}
public void mqttSub(){
try {
// Set connection
this.setConnection();
// Connect
client.connect();
// Subscribe
client.subscribe("pahodemo/test/b112358", 0);
// Disconnect
// client.disconnect();
} catch (MqttException e) {
e.printStackTrace();
}
}
public void setConnection(){
try {
// Client
client = new MqttClient("tcp://iot.eclipse.org:1883", "mqtt_test_b112358_sub");
} catch (MqttException e) {
e.printStackTrace();
}
// Connection Options
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
// Set the will
options.setWill("pahodemo/clienterrors", "CRASHED - CONNECTION NOT CLOSED CLEANLY".getBytes(),2,true);
client.setCallback(this);
}
public void deliveryComplete(IMqttDeliveryToken token) {}
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("Message Arrived: " + message.getPayload() + " on tipic: " + topic.getBytes());
}
public void connectionLost(Throwable cause) {}
}

Messages published before the subscriber connects and subscribes will only be delivered under the following 2 situations
When the messages was published as retained. This means the last message on that topic will be delivered to a new subscriber at the point of subscription. This will only deliver the last message.
If the client had been previously connected and subscribed, then been disconnected. A message is then published and the client connects again with cleansession = false. (and when the subscription is at QOS1/2)
This may help: http://www.thingsprime.com/?p=2897

Related

MQTT5 message is lost on HiveMQ Cloud when correlationData is added

In a Java server application, we want to use the correlationData, which is part of MQTT5, so we can use this in the reply message to link and validate it when the reply is received.
I'm using the hivemq-mqtt-client library 1.2.2 and connecting to HiveMQ Cloud.
The connection is made like this:
private Mqtt5AsyncClient client;
public MqttConfig(Environment environment) {
client = MqttClient.builder()
.useMqttVersion5()
.identifier("TestServer")
.serverHost(MQTT_SERVER)
.serverPort(MQTT_PORT)
.sslWithDefaultConfig()
.automaticReconnectWithDefaultConfig()
.buildAsync();
client.connectWith()
.simpleAuth()
.username(MQTT_USER)
.password(MQTT_PASSWORD.getBytes())
.applySimpleAuth()
.send()
.whenComplete((connAck, throwable) -> {
if (throwable != null) {
logger.error("Could not connect to MQTT: {}", throwable.getMessage());
} else {
logger.info("Connected to MQTT: {}", connAck.getReasonCode());
}
});
}
public Mqtt5AsyncClient getClient() {
return client;
}
Sending a message is done with this method:
mqttConfig.getClient()
.publishWith()
.topic(destinationTopic)
//.correlationData(correlationData.getBytes())
.responseTopic(responseTopic)
.payload(message.getBytes())
.qos(MqttQos.AT_LEAST_ONCE)
.send()
.whenComplete((result, throwable) -> {
if (throwable != null) {
logger.info("Error sending to '{}': {} - {}", destinationTopic, message, throwable.getMessage());
} else {
logger.info("Message sent to '{}': {} - {}", destinationTopic, message, result);
}
});
When monitoring the messages on http://www.hivemq.com/demos/websocket-client/ and on a subscriber, messages are only received when the line with `correlationData()' is commented, as you can see above.
Both with or without that line, the application logs a successful sending, e.g. with correlation data enabled:
Message sent to 'server/test': testMessage - MqttQos2Result{publish=MqttPublish{topic=server/test, payload=11byte, qos=EXACTLY_ONCE, retain=false, responseTopic=server/test/reply, correlationData=6byte}, pubRec=MqttPubRec{packetIdentifier=1}}
Any idea why the additional correlationData seems to cause that they are not shown on the websocket test page, and are not received on any of the subscribers?
As an experiment, I used the paho 5 library instead of the HiveMQ library with the following code, but had exactly the same behavior and needed to disable the line to see messages passing:
MqttProperties properties = new MqttProperties();
//properties.setCorrelationData(correlationData.getBytes());
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setQos(1);
mqttMessage.setPayload(message.getBytes());
mqttMessage.setProperties(properties);
try {
mqttConfig.getClient().publish(destinationTopic, mqttMessage);
logger.info("Message was sent to '{}': {}", destinationTopic, message);
} catch (MqttException ex) {
logger.error("Error sending to '{}': {} - {}", destinationTopic, message, ex.getMessage());
}
This behaviour has now been fixed by HiveMQ Cloud.

How to subscribe mqttClient for particular duration while reading mqtt messages

I am new to MQTT and I have some questions that I hope you guys could help me with. I'm working on a project that will require me to utilize the MQTT protocol and the program needs to be written in java(Just some background info)
Can a MQTT client subscribe for particular time interval? I need to read mqtt messages using eclipse paho client mqttv3 and subscribe to a particular topic for certain duration (e.g. 15 minutes)and read those mqtt messages.
Please find below the code which I have tried .
private void initializeConnectionOptions() {
try {
mqttConnectOptions.setCleanSession(false);
mqttConnectOptions.setAutomaticReconnect(false);
mqttConnectOptions.setSocketFactory(SslUtil.getSocketFactory(this.caCrt, this.clientCrt, this.clientKey));
mqttConnectOptions.setKeepAliveInterval(300);
mqttConnectOptions.setConnectionTimeout(300);
mqttClient = new MqttClient("ssl://IP:port", "clientID", memoryPersistence);
mqttClient.setCallback(new MqttCallback() {
#Override
public void connectionLost(Throwable cause) {
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
String attribute = "Attribute";
JSONObject json = new JSONObject(message.toString());
LOGGER.info("json value is "+ json.toString());
if (json.toString().contains(attribute)) {
int value = json.getInt(attribute);
Long sourceTimestamp = json.getLong("sourceTimestamp");
String deviceName = json.getString("deviceName");
String deviceType = json.getString("deviceType");
if (!nodeValueWithDevice.containsKey(deviceName)) {
List<Integer> attributeValue = new ArrayList<Integer>();
if (!attributeValue.contains(value)) {
attributeValue.add(value);
}
nodeValueWithDevice.put(deviceName, attributeValue);
} else {
List<Integer> temList = nodeValueWithDevice.get(deviceName);
if (!temList.contains(value)) {
temList.add(value);
}
nodeValueWithDevice.put(deviceName, temList);
}
if (!sourceTimestampWithDevice.containsKey(deviceName)) {
List<Long> Time = new ArrayList<Long>();
if (!Time.contains(sourceTimestamp)) {
Time.add(sourceTimestamp);
}
sourceTimestampWithDevice.put(deviceName, Time);
} else {
List<Long> tempList2 = sourceTimestampWithDevice.get(deviceName);
if (!tempList2.contains(sourceTimestamp)) {
tempList2.add(sourceTimestamp);
}
sourceTimestampWithDevice.put(deviceName, tempList2);
}
LOGGER.info(" map of source time stamp is :::" + sourceTimestampWithDevice);
LOGGER.info(" map of value is :::" + nodeValueWithDevice);
}
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
}
});
} catch (MqttException | NoSuchAlgorithmException me) {
LOGGER.error("Error while connecting to Mqtt broker. Error message {} Error code {}", me.getMessage());
}
}
public void subscription(String inputTopic) {
try {
connectToBroker();
mqttClient.subscribe(getOutputTopic(inputTopic), 1);
LOGGER.info("subscription is done::::");
} catch (Exception e) {
LOGGER.error("Error while subscribing message to broker", e.getMessage());
e.printStackTrace();
}
}
No, the clients all designed to receive all messages for the lifetime of the client connection.
If you only want to be subscribed for a given duration it's up to you to find a way to be be notified when that time has passed and explicitly disconnect the client.
According to the MQTT specification for both v5.0 and v3.1.1, there is no specified way to only subscribe to a topic for a fixed interval. However, this could be done through your application logic.
In your case, assuming you have full control of the client, you can subscribe to some topic, keep track of the time connected, then after 15 minutes (or whatever interval you specify) send an UNSUBSCRIBE packet for that topic.

How to identify the MQTT topic that received the message?

The client is subscribed to a x / # topic. There is the possibility of receiving message in the topics x / start and x / stop, and depending on the topic, it performs an action. I wonder how I can identify if it's coming up in the start or stop topic.
In the current code, I send an "action" key in the JSON: "start" or "stop". I want to delete this key and use the format that said above, identifying the topic.
Any further information they deem necessary, please request that I edit the post!
JDK 8
The code:
private MqttCallback callback = new MqttCallback() {
public void connectionLost(Throwable throwable) {
try {
connect();
} catch (MqttException e) {
e.printStackTrace();
}
}
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
String messageReceived = new String(mqttMessage.getPayload());
actionPerformed(messageReceived);
}
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
};
private void actionPerformed(String message) throws IOException {
ClientDTO clientDTO = new ObjectMapper().readValue(message, ClientDTO.class);
if (clientDTO.getAction().equalsIgnoreCase("start")) {
startView(clientDTO);
} else if (clientDTO.getAction().equalsIgnoreCase("stop")) {
stopView();
}
}
public void connect() throws MqttException {
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName("a_nice_username");
options.setPassword("a_cool_password".toCharArray());
options.setAutomaticReconnect(true);
MqttClient client = new MqttClient("someaddress", MqttClient.generateClientId());
client.setCallback(callback);
try {
client.connect(options);
client.subscribe(topic);
TaskbarIcon.alteraIconeOnline();
} catch (Exception e) {
TaskbarIcon.alteraIconeOffline();
}
}
public void tipoConexao(int tipoConex) throws IOException {
switch (tipoConex) {
case 0:
topic += "/operador/" + getIdReceived() + "/#";
System.out.println(topic);
break;
//etc
}
The s in this method is the topic: public void messageArrived(String s, MqttMessage mqttMessage)
As is very well documented here:
messageArrived
void messageArrived(java.lang.String topic, MqttMessage message) throws java.lang.Exception
This method is called when a message arrives from the server.
This method is invoked synchronously by the MQTT client. An acknowledgment is not sent back to the server until this method
returns cleanly.
If an implementation of this method throws an Exception, then the client will be shut down. When the client is next re-connected, any
QoS 1 or 2 messages will be redelivered by the server.
Any additional messages which arrive while an implementation of this method is running, will build up in memory, and will then back up
on the network.
If an application needs to persist data, then it should ensure the data is persisted prior to returning from this method, as after
returning from this method, the message is considered to have been
delivered, and will not be reproducible.
It is possible to send a new message within an implementation of this callback (for example, a response to this message), but the
implementation must not disconnect the client, as it will be
impossible to send an acknowledgment for the message being processed,
and a deadlock will occur.
Parameters:
topic - name of the topic on the message was published to
message - the actual message.
Throws:
java.lang.Exception - if a terminal error has occurred, and the client should be shut down.

Phoenix channel.push not working on Android

I am trying to have chat in my application using Phoenix channels. I have a web client and and an Android client. Right now it is working correctly on the web. I am having an issue with the Android side.
It is able to receive messages pushed to the channel, but it wont send any out. When I try to push a message I get the following exceptions thrown:
java.net.SocketTimeoutException: timeout exception
java.lang.IllegalStateException: Another message writer is active. Did you call close()?
My chat channel
defmodule GoodApi2.ChatChannel do
use Phoenix.Channel
intercept(["chat_send"])
def join("chat:"<> _room_code, _message, socket) do
{:ok, socket}
end
def handle_in("chat_send", message, socket) do
broadcast! socket, "chat_send", message
{:noreply, socket}
end
def handle_out("chat_send", payload, socket) do
push socket, "new_message", payload
{:noreply, socket}
end
end
On the Android app creating the channel
try{
socket = new Socket("ws:"+ApiUtils.BASE.toString()+"socket/websocket");
socket.connect();
channel = socket.chan("chat:"+chatName, null);
channel.join()
.receive("ok", new IMessageCallback() {
#Override
public void onMessage(Envelope envelope) {
System.out.println("IGNORE");
}
});
}
catch (IOException e) {
System.out.println("error connecting to chat");
e.printStackTrace();
}
On the Android app pushing to the channel
public void sendMessage(final String message){
ObjectNode node = new ObjectNode(JsonNodeFactory.instance)
.put("sender", email)
.put("sender_name", userName)
.put("content", message);
try{
channel.push("chat_send", node);
}
catch (Exception e){
Log.e("message failed to send", message);
}
}

How do I detect a mqtt message delivery failure sent with QoS 2?

I'm trying to implement a JAVA application, with the aim to publish into a specific MQTT topic. The message should be delivered with QoS 2 (delivered exactly once).
But I seem to forget anything in my implementation (code of a JUnit implementation below), so the messages always seem to be delivered though there's no client subscribed to my topic. Does anyone have an idea what's my fault here?
I'm using a mosquitto MQTT broker on Ubuntu 12.04 and Eclipse Paho on JAVA side.
MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", MqttClient.generateClientId(), new MemoryPersistence());
try {
client.connect().waitForCompletion();
}
catch (MqttException e) {
throw new RuntimeException("Failed to connect message-broker. Maybe it has to be started via typing \"sudo mosquitto\" in a new terminal window.");
}
client.setCallback(new MqttCallback() {
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// No part of that test
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
throw new RuntimeException("Message with QoS 2 marked as delivered, but no client subscribed to topic.");
}
#Override
public void connectionLost(Throwable cause) {
// Not part of that test
}
});
IMqttDeliveryToken token = client.publish("just/another/topic/where/nobody/is/listening", "Important message with QoS 2".getBytes(), 2, false, null, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
throw new RuntimeException("Message with QoS 2 marked as delivered, but no client subscribed to topic.");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
// Expected behaviour
}
});
token.waitForCompletion();
assertEquals(true, token.isComplete());
assertNotNull(token.getException()); // Should be not null due to unsuccessful delivery with QoS 2

Categories