MQTT - subscribe method is not working - java

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

Related

Wait and notify for sending bunch of messages

Would be super grateful if someone can explain me how wait/notify/notifyAll works and if is there better solution for the problem I am facing. Basically, we have to send a bunch of SMS messages. For sending messages an object called SMPPSession is used but in this example I'll just use superficial code. SMPPSession is supposed to send messages to SMSC server and to reestablish session in situations when connection breaks. I would like to use multiple threads for sending multiple messages, and to have a separate single thread, some sort of "guardian"/ "watcher"/"notifier". The role of that separate thread is to stop all other threads from executing their code, while it works on reestablishing session. Naturally, SMPPSession is shared among all these threads. Once that guardian finishes reconnecting, all other thread needs to continue with using the session and proceed with sending.
Now, I have some code and getting exception. Any help?
In reality we do send real SMS messages using jsmpp library and inside it there is SMPPSession object.
public class SMPPSession {
private boolean bind;
private static final Random idGenerator = new Random();
public int sendMessage(String msg){
try{
Thread.sleep(1000L);
System.out.println("Sending message: " + msg);
return Math.abs(idGenerator.nextInt());
} catch (InterruptedException e){
e.printStackTrace();
}
return -1;
}
public void reBind(){
try{
System.out.println("Rebinding...");
Thread.sleep(1000L);
this.bind = true;
System.out.println("Session established!");
} catch (InterruptedException e){
e.printStackTrace();
}
}
public boolean isBind(){
return this.bind;
}
}
public class Sender extends Thread{
private SMPPSession smppSession;
public Sender(String name, SMPPSession smppSession){
this.setName(name);
this.smppSession = smppSession;
}
#Override
public void run(){
while (!Client.messages.isEmpty()){
synchronized (Client.messages){
if (smppSession.isBind()){
final String msg = Client.messages.remove(0);
final int msgId = smppSession.sendMessage(msg);
System.out.println(Thread.currentThread().getName() + " sent msg and received msgId: " + msgId);
Client.messages.notifyAll();
} else {
try {
Client.messages.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
public class SessionProducer extends Thread{
private SMPPSession smppSession;
public SessionProducer(String name, SMPPSession smppSession){
this.setName(name);
this.smppSession = smppSession;
}
#Override
public void run(){
while (!Client.messages.isEmpty()){
synchronized (Client.messages){
if (!smppSession.isBind()){
smppSession.reBind();
System.out.println(Thread.currentThread().getName() + " managed to reestablish SMPP session.");
Client.messages.notifyAll();
} else{
try {
Client.messages.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
public class Client {
public static final List<String> messages = new CopyOnWriteArrayList<>();
public static void main(String[] args) {
//populate messages from db
messages.add("msg1"); messages.add("msg2"); messages.add("msg3"); messages.add("msg4"); messages.add("msg5"); messages.add("msg6");
SMPPSession smppSession = new SMPPSession();
SessionProducer sessionProducer = new SessionProducer("SessionProducer1", smppSession);
Sender sender1 = new Sender("Sender1", smppSession);
Sender sender2 = new Sender("Sender2", smppSession);
Sender sender3 = new Sender("Sender3", smppSession);
Sender sender4 = new Sender("Sender4", smppSession);
sessionProducer.start();
sender1.start();
sender2.start();
sender3.start();
sender4.start();
}
}
Naturally, I get exception and have no idea why. Somehow threads are not in sync.
Rebinding...
Session established!
SessionProducer1 managed to reestablish SMPP session.
Sending message: msg1
Sender4 sent msg and received msgId: 432995458
Sending message: msg2
Sender4 sent msg and received msgId: 113629699
Sending message: msg3
Sender4 sent msg and received msgId: 611735717
Sending message: msg4
Sender4 sent msg and received msgId: 1234995659
Sending message: msg5
Sender4 sent msg and received msgId: 922228968
Sending message: msg6
Sender4 sent msg and received msgId: 2097204472
Exception in thread "Sender2" Exception in thread "Sender1" Exception in thread "Sender3" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at java.base/java.util.concurrent.CopyOnWriteArrayList.elementAt(CopyOnWriteArrayList.java:385)
at java.base/java.util.concurrent.CopyOnWriteArrayList.remove(CopyOnWriteArrayList.java:478)
at demo.Sender.run(Sender.java:20)
java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at java.base/java.util.concurrent.CopyOnWriteArrayList.elementAt(CopyOnWriteArrayList.java:385)
at java.base/java.util.concurrent.CopyOnWriteArrayList.remove(CopyOnWriteArrayList.java:478)
at demo.Sender.run(Sender.java:20)
java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at java.base/java.util.concurrent.CopyOnWriteArrayList.elementAt(CopyOnWriteArrayList.java:385)
at java.base/java.util.concurrent.CopyOnWriteArrayList.remove(CopyOnWriteArrayList.java:478)
at demo.Sender.run(Sender.java:20)
Your loops call Client.messages.isEmpty() with no synchronization. I haven't spent the time to really understand what your code does—can't see all of it anyway—but I can guess what's happening.
Maybe the list contains one message.
Four threads all see it as not empty.
Four threads all try enter the synchronized(Client.messages) block.
One-by-one, they get in to the block, see that smppSession.isBind() is true, and try to remove a message from the list.
The first thread to remove a message succeeds, and then each of the other four throws an exception because it tried to remove from an empty list.
Recommend a SMS development library sms-client from China to support Smpp.
<dependency>
<groupId>com.chinamobile.cmos</groupId>
<artifactId>sms-client</artifactId>
<version>0.0.7</version>
</dependency>
public void testsmpp() throws Exception {
SmsClientBuilder builder = new SmsClientBuilder();
SmsClient smsClient = builder.uri("smpp://127.0.0.1:18890?username=test01&password=1qaz2wsx&version=52&window=32&maxchannel=1")
.receiver(new MessageReceiver() {
public void receive(BaseMessage message) {
logger.info("receive : {}",message.toString());
}
}).build();
for (int i = 0; i < 5; i++) {
SubmitSm pdu = new SubmitSm();
pdu.setRegisteredDelivery((byte)1);
pdu.setSourceAddress(new Address((byte)0,(byte)0,"10086"));
pdu.setDestAddress(new Address((byte)0,(byte)0,"13800138000"));
pdu.setSmsMsg(new SmsTextMessage("SmsTextMessage " + i,SmsDcs.getGeneralDataCodingDcs(SmsAlphabet.GSM,SmsMsgClass.CLASS_UNKNOWN)));
try {
smsClient.send(pdu, 1000);
} catch (Exception e) {
logger.info("send ", e);
}
}
Thread.sleep(5000000);
}

Subscribe to Multiple MQTT topics

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

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.

Proper MQTT subscription code that maintains persistence

I am looking for a java code for MQTT client that subscribes to a given topic, every message published on that topic should reach the client only once.I have written many codes and in all the cases messages are delivered properly to the client when it is connected to the broker but if the subscribed client disconnects from the broker for some time and then again connects back, it does not receive the messages that were sent during the time that it was not connected and I have set the clean session flag also as false but still its not working, the code that I used is given below
import org.fusesource.hawtbuf.*;
import org.fusesource.mqtt.client.*;
/**
* Uses an callback based interface to MQTT. Callback based interfaces
* are harder to use but are slightly more efficient.
*/
class Listener {
public static void main(String []args) throws Exception {
String user = env("APOLLO_USER", "admin");
String password = env("APOLLO_PASSWORD", "password");
String host = env("APOLLO_HOST", "localhost");
int port = Integer.parseInt(env("APOLLO_PORT", "61613"));
final String destination = arg(args, 1, "subject");
MQTT mqtt = new MQTT();
mqtt.setHost(host, port);
mqtt.setUserName(user);
mqtt.setPassword(password);
mqtt.setCleanSession(false);
mqtt.setClientId("newclient");
final CallbackConnection connection = mqtt.callbackConnection();
connection.listener(new org.fusesource.mqtt.client.Listener() {
long count = 0;
long start = System.currentTimeMillis();
public void onConnected() {
}
public void onDisconnected() {
}
public void onFailure(Throwable value) {
value.printStackTrace();
System.exit(-2);
}
public void onPublish(UTF8Buffer topic, Buffer msg, Runnable ack) {
System.out.println("Nisha Messages : " + msg);
System.out.println("Nisha topic" + topic);
System.out.println("Nisha Receive acknowledgement : " + ack);
String body = msg.utf8().toString();
if("SHUTDOWN".equals(body)) {
long diff = System.currentTimeMillis() - start;
System.out.println(String.format("Received %d in %.2f seconds", count, (1.0*diff/1000.0)));
connection.disconnect(new Callback<Void>() {
#Override
public void onSuccess(Void value) {
System.exit(0);
}
#Override
public void onFailure(Throwable value) {
value.printStackTrace();
System.exit(-2);
}
});
} else {
if( count == 0 ) {
start = System.currentTimeMillis();
}
if( count % 1000 == 0 ) {
System.out.println(String.format("Received %d messages.", count));
}
count ++;
}
}
});
connection.connect(new Callback<Void>() {
#Override
public void onSuccess(Void value) {
System.out.println("connected in :::: ");
Topic[] topics = {new Topic(destination, QoS.AT_MOST_ONCE)};
connection.subscribe(topics, new Callback<byte[]>() {
public void onSuccess(byte[] qoses) {
}
public void onFailure(Throwable value) {
value.printStackTrace();
System.exit(-2);
}
});
}
#Override
public void onFailure(Throwable value) {
value.printStackTrace();
System.exit(-2);
}
});
// Wait forever..
synchronized (Listener.class) {
while(true)
Listener.class.wait();
}
}
private static String env(String key, String defaultValue) {
String rc = System.getenv(key);
if( rc== null )
return defaultValue;
return rc;
}
private static String arg(String []args, int index, String defaultValue) {
if( index < args.length )
return args[index];
else
return defaultValue;
}
}
Am I doing something wrong here?
it does not receive the messages that were sent during the time that it was not connected
MQTT does not retain all messages. If the client goes offline, undelivered messages are lost. The retain mechanism retains only the last message published to a topic.
You can read more in the specs point 3.3.1.3 RETAIN

I'm getting a deadlock when calling consumer.setMessageListener(MessageListener)

In activemq I'm doing something that might be wrong. I'm creating a consumer and setting a messageListener (consumer.setMessageListener(MessageListener)) while consuming a message. This causes activeMQ to deadlock.
Here is a code sample of what I am doing:
class DriverClass extends MessageListener
{
Destination destination = session.createTopic("Some.Topic");
MessageListener msgSub = new MyClass(destination);
Session session = passedInSession;
MessageConsumer consumer;
try
{
consumer = session.createConsumer(destination);
msgSub.setConsumer( consumer );
if(msgSub.getMessageListener() != null)
{
consumer.setMessageListener( msgSub );
}
}
catch (Exception ex)
{
LOGGER.error( "Error subscribing to "+msgSub.getTopic()
+" Error message: ", ex );
}
}
class MyClass extends MessageListener
{
Destination dest;
public MyClass(Destination destToUse)
{
dest = destToUse;
}
#Override
public void onMessage( final Message message )
{
//note here we are using the same topic
MessageListener msgSub = new MyMessageListener("Some.Topic");
MessageConsumer consumer;
try
{
//note here we are using the same dest as above
consumer = session.createConsumer(dest);
msgSub.setConsumer( consumer );
if(msgSub.getMessageListener() != null)
{
//this is where it deadlocks subscribing while consuming a message
//on the same topic/destination
consumer.setMessageListener( msgSub );
}
}
catch (Exception ex)
{
LOGGER.error( "Error subscribing to "+msgSub.getTopic()
+" Error message: ", ex );
}
}
}
Basically, I'm subscribing within a consumer within the ActiveMQ thread. The only reference to this problem can be found here: https://issues.apache.org/jira/browse/AMQ-336 but I can't tell what the workaround is. For now I'm just going to put the subscription call on a worker thread and return control back to ActiveMQ but it seems like there is a better solution. Is the issue that I'm subscribing to a topic/destination while handling a message of the same topic/destination?

Categories