Subscribe to Multiple MQTT topics - java

I was struggling an issue or maybe it's because of my small background in programming, the issue was about subscribing to multiple topics and showing the subscribed topics in multiple textviews in android
I used to subscribe it like that :
private void setSub()
{
try{
client.subscribe(topic,0);
}
catch (MqttException e){
e.printStackTrace();
}
}
then I've called the setsub() function after the success of the connection to the MQTT client
then I've implemented the setCallBack method and under the messageArrived I've added the line to change the textview value with the message payload I've received from the subscription
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
temp.setText(new String(message.getPayload()));
}
and ofcourse when I define another textview it just took the same result as the first one
so my question is how to configure the MessageArrived function to show each single topic in a single textview?
Thank you in advance.

You can call client.subscribe() as many times as needed with different topics.
private void setSub()
{
try{
client.subscribe(topic1,0);
client.subscribe(topic2,0);
client.subscribe(topic3,0);
}
catch (MqttException e){
e.printStackTrace();
}
}
The messageArrived() callback is passed the topic for each message so you just set up an if statement to decide which textView to update depending on the topic.
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
if (topic.equals(topic1) {
temp.setText(new String(message.getPayload()));
} else if (topic.equals(topic2) {
foo.setText(new String(message.getPayload()));
}
}
But you should not be calling the setText() method in the callback as it happens on the client thread. You need to look at using runOnUiThread() to do updates.

I've just figured it out,
if (topic.contains("button") ) {
temp.setText(new String(message.getPayload()));
}
else if (topic.contains("test"))
{
volt.setText(new String(message.getPayload()));
}
I've just put in the name of each topic i wanted to display.

Try runOnUiThread method because onMessageArived() is a client thread.
public void messageArrived(String topic, MqttMessage message)
throws Exception {
String msg = new String(message.getPayload());
Log.i(TAG, "Message Arrived: " + msg);
runOnUiThread(new Runnable(){
public void run() {
textView.setText(msg);
}
});
}

Related

Unit Testing a Kafka SpringBoot producer

I am trying to create a unit test for my Kafka Producer which is integrated into a file. Here's my Kafka Producer:
FileName: MessageProducer.java
public boolean sendMessage(ReceivedMessage message) {
private String topicName = "output-flow";
try{
logger.info("Sending message: {} to topic: {}", message, topicName);
kafkaProducer.send(topicName, message).get();
return true;
} catch (Exception e){
logger.error("Error sending message: {} to topic: {}", message, topicName, e);
return false;
}
}
And here is what I have done so far for my unit test, obviously, with not success at all:
#Mock
private KafkaTemplate<String, ReceivedMessage > kafkaProducer;
private static final String TRANSACTION_TOPIC = "test";
// Function for parameterized values
#ParameterizedTest
#MethodSource("getTransactionProvider")
public void sendMessageTest(ReceivedMessage message) {
MessageProducer mockProducer = new MessageProducer(kafkaProducer);
when(kafkaProducer.send(TRANSACTION_TOPIC, message)).thenReturn({no idea what to put here});
when(mockProducer.sendMessage(message)).thenReturn(true);
assertTrue(mockProducer.sendMessage(message));
}
// Test for exception
// Fails too
#ParameterizedTest
#MethodSource("getTransactionProvider")
public void sendMessageTest_ThrowsException(ReceivedMessage message) {
MessageProducer mockProducer = new MessageProducer(kafkaProducer);
when(kafkaProducer.send(TRANSACTION_TOPIC, message)).thenThrow(new RuntimeException());
assertThrows(RuntimeException.class, () -> mockProducer.sendMessage(null));
}
I get Exception: org.opentest4j.AssertionFailedError: Expected java.lang.RuntimeException to be thrown, but nothing was thrown. for the latter unit test.
If I understood your question, you should return a new SendResult that would have the methods implemented with the data you expect
https://docs.spring.io/spring-kafka/api/org/springframework/kafka/support/SendResult.html
And wrap it in a Future
ListenableFuture<SendResult<K,​V>>
Alternatively, make sendMessage void method (or return a Future itself), and pass in a producer callback parameter that's carried through to send, rather than making it block. Then you can assert the response of the callback
Welcome to SO...
Why does your test case fail?
Because your logic will not throw an error.
Your function will not throw an exception since you catch the exception inside the function as follow and you return boolean value.
catch (Exception e){
logger.error("Error sending message: {} to topic: {}", message, topicName, e);
return false;
}
In that case, you need to test whether the function returns false or not.
As I commented earlier, don't block the main thread by calling the get method in the future object. You can simply implement the future callbacks which can be invoked once you get the result as following
public void sendMessage(ReceivedMessage message) {
private String topicName = "output-flow";
try{
logger.info("Sending message: {} to topic: {}", message, topicName);
ListenableFuture<SendResult<String, String>> future = kafkaProducer.send(topicName, message);
future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
#Override
public void onSuccess(SendResult<String, String> result) {
System.out.println("Message Sent " + result.getRecordMetadata().timestamp());
//your logic for the success scenario
}
#Override
public void onFailure(Throwable ex) {
System.out.println(" sending failed ");
// your logic if failed
throw new RuntimeException("Kafka Failed");
}
});
} catch (Exception e){
logger.error("Error sending message: {} to topic: {}", message, topicName, e);
throw new RuntimeException("Exception occurred");
}
}

Is there any way to close MqttClient Threads spawned in the backend?

We have a streaming application taking data from MQTT and load into other resource. And this application have multiple threads to handle some tasks.
Here we have two tasks(threads):
First one is a READER
Second one is a WRITER
So READER will read data from MQTT broker and write on a java queue and WRITER will take this data from that queue and write it over one database. This application itself monitoring these threads for finding any failure. If any one of the threads failed then we will stop remaining threads gracefully. In case of paho MqttClient class (READER Class) wont create a thread even its a threaded class. But it will creating multiple threads in the background.
Because of this we could not check whether these threads is failed or running by java isAlive() function. So we just checking this class have connection by MqttClient isConnected() method. Once isConnected method return false (5 times) , then we will stop Writer thread gracefully. But Reader class threads which spawned in the background are not able to stop. I have tried disconnect() and close()
methods. But its not stopping any of the background threads. Its throws error disconnected threads could not stop.
So please anybody help.
What you suggest sounds like an awkward design.
Why not just use the Paho callbacks, in particular the connectionLost as below?
private final MqttCallbackExtended mCallback = new MqttCallbackExtended() {
#Override
public void connectComplete(boolean reconnect, String brokerAddress) {
mqttClient.subscribe("topic", 1, null, mSubscribeCallback);
}
#Override
public void connectionLost(Throwable ex) {
}
#Override
public void deliveryComplete(IMqttDeliveryToken deliveryToken) {
}
#Override
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
}
};
private final IMqttActionListener mConnectionCallback = new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
// do nothing, this case is handled in mCallback.connectComplete()
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
}
};
private final IMqttActionListener mSubscribeCallback = new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken subscribeToken) {
}
#Override
public void onFailure(IMqttToken subscribeToken, Throwable ex) {
}
};
MqttConnectOptions connectOptions = new MqttConnectOptions();
connectOptions.setCleanSession(true);
connectOptions.setAutomaticReconnect(true);
connectOptions.setUserName("username");
connectOptions.setPassword("password".toCharArray());
MqttAsyncClient mqttClient = new MqttAsyncClient("tcp:// test.mosquitto.org");
mqttClient.setCallback(mCallback);
try {
mqttClient.connect(connectOptions, null, mConnectionCallback);
} catch (Exception ex) {
System.err.println(ex.toString());
}

Behaviour setTimeToWait() method in java paho library

I use library paho for connectivity with mqtt broker, sending messages and almost everything work fine, but i have problem with setTimeToWait() method. it does not metter how many milliseconds i put into method setTimeToWait(2000) or setTimeToWait(10). I always get messages from publiusher.
Why can i get messages within the hours? If i set waiting time 2000 milliseconds. I thought after 2 secs absence of messages from publisher my subscriber cannot get messages from publiser and control will be returned.
What Am i doing wrong?
Publisher code:
public class MqttPublishSample {
public static void main(String[] args) throws MqttException {
String messageString = "{\"device_status\": \"ready\"}";
if (
args.length == 2 ) {
messageString = args[1];
}
System.out.println("== START PUBLISHER ==");
MqttClient client = new MqttClient("tcp://localhost:1883" , MqttClient.generateClientId());
client.connect();
MqttMessage message = new MqttMessage();
message.setPayload(messageString.getBytes());
message.setQos(1);
client.publish("/catalog", message);
System.out.println("\tMessage '"+ messageString +"' to 'iot_data'");
client.disconnect();
System.out.println("== END PUBLISHER ==");
}
}
Subscriber code:
public class MqttSuscribeSample {
public static void main(String[] args) {
System.out.println("== START SUBSCRIBER ==");
try{
MqttClient client=new MqttClient("tcp://localhost:1883", MqttClient.generateClientId());
client.setTimeToWait(2000);
client.setCallback( new SimpleMqttCallback() );
client.connect();
client.subscribe( "/catalog");
}catch (Exception ex){
ex.printStackTrace();
}
}
}
SimpleMqttCallback code
public class SimpleMqttCallback implements MqttCallback {
public void connectionLost(Throwable throwable) {
System.out.println("Connection to MQTT broker lost!");
}
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
System.out.println("Message received:\t"+ new String(mqttMessage.getPayload()) );
}
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
}
The setTimeToWait() method is how long the the client should wait for a response from the broker when carrying out specific actions e.g.
publishing a message
making a subscription
connecting or disconnecting from the broker
It is not how long the client should wait or a message to be delivered for an existing subscription.
If you want this sort of behaviour you need to run your own timer and either unsubscribe from the topic or disconnect from the broker when it times out.

MQTT - subscribe method is not working

i have this Mqtt ckient app that am working on, the publish method is working fine but am having a hard time with the subscribe method.
this is the subscribe method, am suppose to click a button to subcribe to a topic:
public void subscribe( MqttClient MC) {
String topic = jTextField3.getText();
int qos = jComboBox1.getSelectedIndex() ;
String[] topics = {topic};
int[] QoS = {qos};
if ( jLabel3.getText().equals("Connected") ) {
try {
MC.subscribe( topics, QoS );
System.out.println(topics +" "+QoS);
System.out.println(topic +" "+qos);
jButton2.setText("Subscribed");
jTextField4.setText(topics.toString());
} catch ( Exception ex ) {
JOptionPane.showMessageDialog( this, ex.getMessage(),
"MQTT Subscription Exception", JOptionPane.ERROR_MESSAGE );
}
} else {
jTextArea1.setText("Not connected");
}
}
this is the actionPerformed method for the button
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
subscribe(MC);
}
this is my publishedArrived method:
public void publishArrived( String topic, byte[] data, int QoS, boolean retained ) {
jTextField4.setText(topic);
String msgData = new String(data);
jTextArea1.setText(new String(data));
}
someone should please help me out here.
You need to implement a callback and a message arrived function. This function will handle the message. See paho mqtt message client as example in here Subscribe and Read MQTT Message Using PAHO

Turning a listener into a future in java

I'm trying to turn a listener into a Future, for asynchronous connection. I'm not used to using java futures yet, I've some experience with javascript promises but I fail to see how to write it in java (I've seen "CompletableFuture" in Java 8 may solve my problem, unfortunately I'm stuck with java 7). Here's what I've done so far:
public Future<Boolean> checkEmailClientConfiguration(final EmailClientConfiguration config) {
final Future<Boolean> future = ???;
// In some other languages I would create a deferred
Transport transport = null;
try {
transport = session.getTransport("smtp");
transport.addConnectionListener(new ConnectionListener() {
#Override
public void opened(ConnectionEvent connectionEvent) {
System.out.println("!!!opened!!! ; connected=" + ((SMTPTransport) connectionEvent.getSource()).isConnected());
// HERE I would like to make my future "resolved"
}
#Override
public void disconnected(ConnectionEvent connectionEvent) {
}
#Override
public void closed(ConnectionEvent connectionEvent) {
}
});
transport.connect(config.getMailSMTPHost(),
config.getMailSMTPPort(),
config.getMailUsername(),
config.getMailPassword());
return future;
} catch (final MessagingException e) {
throw e;
} finally{
if(transport != null){
transport.close();
}
}
}
I can't find any easy way to do it. The only solution I've found so far is to extend FutureTask and at the end of the Callable run, wait/sleep until some state variable is set as resolved. I don't really like the idea of waiting/sleeping in my business code, there is probably something that already exist to make it deferred? (in java, or popular libraries such as Apache commons or guava?)
I finally got my answer from a colleague. What I'm looking for exists in Guava: SettableFuture. Here's how the code looks like:
final SettableFuture<Boolean> future = SettableFuture.create();
Transport transport = null;
try {
transport = session.getTransport("smtp");
transport.addConnectionListener(new ConnectionListener() {
#Override
public void opened(ConnectionEvent connectionEvent) {
future.set(((SMTPTransport) connectionEvent.getSource()).isConnected());
}
#Override
public void disconnected(ConnectionEvent connectionEvent) {
}
#Override
public void closed(ConnectionEvent connectionEvent) {
}
});
transport.connect(config.getMailSMTPHost(),
config.getMailSMTPPort(),
config.getMailUsername(),
config.getMailPassword());
} catch (final MessagingException e) {
future.setException(e);
} finally{
if(transport != null){
transport.close();
}
}
return future;

Categories