Behaviour setTimeToWait() method in java paho library - java

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.

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

unable to subscribe paho mqtt java

I'm new to mqtt. Getting started I tried publishing and subscribing topics to mosquitto broker. I was able to publish messages. But my subscriber is not listening to the topic, it will start and stop without waiting/polling for messages.
Here is the subscriber code,
public class MqttSubscriber implements MqttCallback {
private static final String TOPIC = "iot/endpoint";
public static void main(String[] args) {
new MqttSubscriber().listen();
}
public void listen() {
MqttClient client = null;
try {
client = MqttClientGenerator.generateSubscriberClient();
client.connect();
System.out.println("Fetching messages...");
client.subscribe(TOPIC);
client.setCallback(this);
client.disconnect();
} catch (MqttException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public void connectionLost(Throwable t) {
t.printStackTrace();
}
public void deliveryComplete(IMqttDeliveryToken arg0) {
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("Message received from broker...");
System.out.println("Received Message: -- ");
System.out.println(message.getPayload().toString());
}
}
MqttClientGenerator :
public class MqttClientGenerator {
private static final String BROKER_URI = "tcp://localhost:1883";
private static final String CLIENT_ID = "pub";
private static final String SUBSCRIBER_ID = "sub";
private MqttClientGenerator () {}
public static MqttClient generatePublisherClient() throws MqttException{
//adding timestamp to make client name unique every time
return new MqttClient(BROKER_URI, CLIENT_ID+new Date().getTime());
}
public static MqttClient generateSubscriberClient() throws MqttException{
//adding timestamp to make client name unique every time
return new MqttClient(BROKER_URI, SUBSCRIBER_ID+new Date().getTime());
}
}
what am i missing here?
Try deleting the line where you disconnect the client.

Java Eclipse Paho Implementation - Auto reconnect

I'm trying to implement eclipse.paho in my project to connect Mqtt Broker (Both subscribing and publishing purpose). The problem is, when I using the subscribing feature (Implementing MqttCallback interface), I couldn't figure our how can I reconnect if the connection lost. MqttCallback interface has a connectionLost method, but it is useful for the debug what causes the connection lost. I searched but couldn't find a way to establish auto reconnect. Can you suggest a way or document about this problem?
I'm using the paho client 1.2.0.
With the MqttClient.setAutomaticReconnect(true) and interface MqttCallbackExtended API, and thanks to https://github.com/eclipse/paho.mqtt.java/issues/493, I could manage to reconnect automatically when the connection to broker is down.
See below the code.
//Use the MqttCallbackExtended to (re-)subscribe when method connectComplete is invoked
public class MyMqttClient implements MqttCallbackExtended {
private static final Logger logger = LoggerFactory.getLogger(MqttClientTerni.class);
private final int qos = 0;
private String topic = "mytopic";
private MqttClient client;
public MyMqttClient() throws MqttException {
String host = "tcp://localhost:1883";
String clientId = "MQTT-Client";
MqttConnectOptions conOpt = new MqttConnectOptions();
conOpt.setCleanSession(true);
//Pay attention here to automatic reconnect
conOpt.setAutomaticReconnect(true);
this.client = new org.eclipse.paho.client.mqttv3.MqttClient(host, clientId);
this.client.setCallback(this);
this.client.connect(conOpt);
}
/**
* #see MqttCallback#connectionLost(Throwable)
*/
public void connectionLost(Throwable cause) {
logger.error("Connection lost because: " + cause);
/**
* #see MqttCallback#deliveryComplete(IMqttDeliveryToken)
*/
public void deliveryComplete(IMqttDeliveryToken token) {
}
/**
* #see MqttCallback#messageArrived(String, MqttMessage)
*/
public void messageArrived(String topic, MqttMessage message) throws MqttException {
logger.info(String.format("[%s] %s", topic, new String(message.getPayload())));
}
public static void main(String[] args) throws MqttException, URISyntaxException {
MyMqttClient s = new MyMqttClient();
}
#Override
public void connectComplete(boolean arg0, String arg1) {
try {
//Very important to resubcribe to the topic after the connection was (re-)estabslished.
//Otherwise you are reconnected but you don't get any message
this.client.subscribe(this.topic, qos);
} catch (MqttException e) {
e.printStackTrace();
}
}
}
The best way to do this is to structure your connection logic so it lives in a method on it's own so it can be called again from the connectionLost callback in the MqttCallback instance.
The connectionLost method is passed a Throwable that will be the exception that triggered the disconnect so you can make decisions about the root cause and how this may effect when/how you reconnect.
The connection method should connect and subscribe to the topics you require.
Something like this:
public class PubSub {
MqttClient client;
String topics[] = ["foo/#", "bar"];
MqttCallback callback = new MqttCallback() {
public void connectionLost(Throwable t) {
this.connect();
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("topic - " + topic + ": " + new String(message.getPayload()));
}
public void deliveryComplete(IMqttDeliveryToken token) {
}
};
public static void main(String args[]) {
PubSub foo = new PubSub();
}
public PubSub(){
this.connect();
}
public void connect(){
client = new MqttClient("mqtt://localhost", "pubsub-1");
client.setCallback(callback);
client.connect();
client.subscribe(topics);
}
}
To use auto reconnect, just set setAutomaticReconnect(true) on the MqttConnectOptions object.
MqttAndroidClient mqttClient = new MqttAndroidClient(context, mqttUrl, clientId);
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(true);
mqttClient.connect(mqttConnectOptions, null, mqttActionListener());

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

Categories