9
9
import com .facebook .react .bridge .ReadableMap ;
10
10
import com .facebook .react .bridge .WritableMap ;
11
11
import com .facebook .react .bridge .WritableNativeMap ;
12
- import com .facebook .react .modules .core .DeviceEventManagerModule ;
12
+ import com .facebook .react .modules .core .RCTNativeAppEventEmitter ;
13
13
14
14
import org .eclipse .paho .client .mqttv3 .IMqttActionListener ;
15
15
import org .eclipse .paho .client .mqttv3 .IMqttDeliveryToken ;
25
25
import java .security .SecureRandom ;
26
26
import java .security .cert .CertificateException ;
27
27
import java .security .cert .X509Certificate ;
28
+ import java .util .HashMap ;
29
+ import java .util .Iterator ;
30
+ import java .util .Map ;
31
+ import java .util .Timer ;
32
+ import java .util .TimerTask ;
33
+ import java .util .UUID ;
34
+ import java .util .concurrent .TimeUnit ;
35
+ import java .util .concurrent .atomic .AtomicBoolean ;
28
36
29
37
import javax .annotation .Nullable ;
30
38
import javax .net .ssl .SSLContext ;
@@ -35,20 +43,20 @@ public class RCTMqtt
35
43
implements MqttCallback
36
44
{
37
45
private static final String TAG = "RCTMqttModule" ;
38
- private final ReactApplicationContext _reactContext ;
46
+ private final ReactApplicationContext reactContext ;
39
47
private final WritableMap defaultOptions ;
40
- private final int clientRef ;
41
- MqttAsyncClient client ;
42
- MemoryPersistence memPer ;
43
- MqttConnectOptions mqttoptions ;
48
+ private final String clientRef ;
49
+ private MqttAsyncClient client ;
50
+ private MemoryPersistence memPer ;
51
+ private MqttConnectOptions mqttOptions ;
52
+ private Map <String , Integer > topics = new HashMap <>();
44
53
45
-
46
- public RCTMqtt (final int ref ,
54
+ public RCTMqtt (@ NonNull final String ref ,
47
55
final ReactApplicationContext reactContext ,
48
56
final ReadableMap options )
49
57
{
50
58
clientRef = ref ;
51
- _reactContext = reactContext ;
59
+ this . reactContext = reactContext ;
52
60
defaultOptions = new WritableNativeMap ();
53
61
defaultOptions .putString ("host" , "localhost" );
54
62
defaultOptions .putInt ("port" , 1883 );
@@ -63,7 +71,6 @@ public RCTMqtt(final int ref,
63
71
defaultOptions .putString ("pass" , "" );
64
72
defaultOptions .putBoolean ("will" , false );
65
73
defaultOptions .putInt ("protocolLevel" , 4 );
66
- defaultOptions .putBoolean ("will" , false );
67
74
defaultOptions .putString ("willMsg" , "" );
68
75
defaultOptions .putString ("willtopic" , "" );
69
76
defaultOptions .putInt ("willQos" , 0 );
@@ -153,14 +160,14 @@ private void createClient(@NonNull final ReadableMap params)
153
160
// Set this wrapper as the callback handler
154
161
155
162
156
- mqttoptions = new MqttConnectOptions ();
163
+ mqttOptions = new MqttConnectOptions ();
157
164
158
165
if (options .getInt ("protocolLevel" ) == 3 )
159
166
{
160
- mqttoptions .setMqttVersion (MqttConnectOptions .MQTT_VERSION_3_1 );
167
+ mqttOptions .setMqttVersion (MqttConnectOptions .MQTT_VERSION_3_1 );
161
168
}
162
169
163
- mqttoptions .setKeepAliveInterval (options .getInt ("keepalive" ));
170
+ mqttOptions .setKeepAliveInterval (options .getInt ("keepalive" ));
164
171
165
172
StringBuilder uri = new StringBuilder ("tcp://" );
166
173
if (options .getBoolean ("tls" ))
@@ -197,7 +204,7 @@ public X509Certificate[] getAcceptedIssuers()
197
204
}
198
205
}}, new SecureRandom ());
199
206
200
- mqttoptions .setSocketFactory (sslContext .getSocketFactory ());
207
+ mqttOptions .setSocketFactory (sslContext .getSocketFactory ());
201
208
}
202
209
catch (Exception e )
203
210
{
@@ -213,11 +220,11 @@ public X509Certificate[] getAcceptedIssuers()
213
220
String pass = options .getString ("pass" );
214
221
if (user .length () > 0 )
215
222
{
216
- mqttoptions .setUserName (user );
223
+ mqttOptions .setUserName (user );
217
224
}
218
225
if (pass .length () > 0 )
219
226
{
220
- mqttoptions .setPassword (pass .toCharArray ());
227
+ mqttOptions .setPassword (pass .toCharArray ());
221
228
}
222
229
}
223
230
@@ -244,37 +251,44 @@ public void setCallback()
244
251
}
245
252
246
253
247
- private void sendEvent (final ReactContext reactContext ,
248
- final String eventName ,
249
- @ Nullable WritableMap params )
250
- {
251
- params .putInt ("clientRef" , this .clientRef );
252
- reactContext .getJSModule (DeviceEventManagerModule .RCTDeviceEventEmitter .class ).emit (eventName , params );
253
- }
254
-
255
254
public void connect ()
256
255
{
257
256
try
258
257
{
258
+ WritableMap params = Arguments .createMap ();
259
+ params .putString ("event" , "connecting" );
260
+ params .putString ("message" , "try to connect" );
261
+ sendEvent (reactContext , "mqtt_events" , params );
262
+
259
263
// Connect using a non-blocking connect
260
- client .connect (mqttoptions , _reactContext , new IMqttActionListener ()
264
+ client .connect (mqttOptions , reactContext , new IMqttActionListener ()
261
265
{
262
266
public void onSuccess (IMqttToken asyncActionToken )
263
267
{
264
268
WritableMap params = Arguments .createMap ();
265
269
params .putString ("event" , "connect" );
266
270
params .putString ("message" , "connected" );
267
- sendEvent (_reactContext , "mqtt_events" , params );
271
+ sendEvent (reactContext , "mqtt_events" , params );
268
272
log ("Connected" );
273
+
274
+ Iterator <String > iterator = topics .keySet ().iterator ();
275
+ while (iterator .hasNext ())
276
+ {
277
+ final String topic = iterator .next ();
278
+ subscribe (topic , topics .get (topic ));
279
+ }
269
280
}
270
281
271
282
public void onFailure (IMqttToken asyncActionToken ,
272
283
Throwable exception )
273
284
{
274
285
WritableMap params = Arguments .createMap ();
275
286
params .putString ("event" , "error" );
276
- params .putString ("message" , "connection failure" );
277
- sendEvent (_reactContext , "mqtt_events" , params );
287
+ final String errorDescription = new StringBuilder ("connection failure " )
288
+ .append (exception ).toString ();
289
+ params .putString ("message" , errorDescription );
290
+ sendEvent (reactContext , "mqtt_events" , params );
291
+ reconnectIfNeeded (exception );
278
292
}
279
293
});
280
294
}
@@ -283,7 +297,7 @@ public void onFailure(IMqttToken asyncActionToken,
283
297
WritableMap params = Arguments .createMap ();
284
298
params .putString ("event" , "error" );
285
299
params .putString ("message" , "Can't create connection" );
286
- sendEvent (_reactContext , "mqtt_events" , params );
300
+ sendEvent (reactContext , "mqtt_events" , params );
287
301
}
288
302
}
289
303
@@ -297,7 +311,7 @@ public void onSuccess(IMqttToken asyncActionToken)
297
311
WritableMap params = Arguments .createMap ();
298
312
params .putString ("event" , "closed" );
299
313
params .putString ("message" , "Disconnect" );
300
- sendEvent (_reactContext , "mqtt_events" , params );
314
+ sendEvent (reactContext , "mqtt_events" , params );
301
315
}
302
316
303
317
public void onFailure (IMqttToken asyncActionToken ,
@@ -309,7 +323,7 @@ public void onFailure(IMqttToken asyncActionToken,
309
323
310
324
try
311
325
{
312
- client .disconnect (_reactContext , discListener );
326
+ client .disconnect (reactContext , discListener );
313
327
}
314
328
catch (MqttException e )
315
329
{
@@ -322,6 +336,7 @@ public void subscribe(@NonNull final String topic,
322
336
{
323
337
try
324
338
{
339
+ topics .put (topic , qos );
325
340
IMqttToken subToken = client .subscribe (topic , qos );
326
341
subToken .setActionCallback (new IMqttActionListener ()
327
342
{
@@ -353,6 +368,9 @@ public void unsubscribe(@NonNull final String topic)
353
368
{
354
369
try
355
370
{
371
+ if (topics .containsKey (topic )) {
372
+ topics .remove (topic );
373
+ }
356
374
client .unsubscribe (topic ).setActionCallback (new IMqttActionListener ()
357
375
{
358
376
@ Override
@@ -415,9 +433,12 @@ public void connectionLost(Throwable cause)
415
433
// logic at this point. This sample simply exits.
416
434
log (new StringBuilder ("Connection to lost! " ).append (cause ).toString ());
417
435
WritableMap params = Arguments .createMap ();
418
- params .putString ("event" , "closed" );
419
- params .putString ("message" , "Connection to lost!" );
420
- sendEvent (_reactContext , "mqtt_events" , params );
436
+ params .putString ("event" , "error" );
437
+ final String errorDescription = new StringBuilder ("Connection to lost! " )
438
+ .append (cause ).toString ();
439
+ params .putString ("message" , errorDescription );
440
+ sendEvent (reactContext , "mqtt_events" , params );
441
+ reconnectIfNeeded (cause );
421
442
}
422
443
423
444
/**
@@ -463,15 +484,69 @@ public void messageArrived(@NonNull final String topic,
463
484
WritableMap params = Arguments .createMap ();
464
485
params .putString ("event" , "message" );
465
486
params .putMap ("message" , data );
466
- sendEvent (_reactContext , "mqtt_events" , params );
487
+ sendEvent (reactContext , "mqtt_events" , params );
488
+ }
489
+
490
+ private void sendEvent (final ReactContext reactContext ,
491
+ final String eventName ,
492
+ @ Nullable WritableMap params )
493
+ {
494
+ params .putString ("clientRef" , this .clientRef );
495
+ reactContext .getJSModule (RCTNativeAppEventEmitter .class ).emit (eventName , params );
496
+ }
497
+
498
+ private boolean needToReconnect (@ NonNull final MqttException exception )
499
+ {
500
+ int reasonCode = exception .getReasonCode ();
501
+ return reasonCode == MqttException .REASON_CODE_SERVER_CONNECT_ERROR ||
502
+ reasonCode == MqttException .REASON_CODE_CLIENT_EXCEPTION ||
503
+ reasonCode == MqttException .REASON_CODE_CONNECTION_LOST ;
504
+ }
505
+
506
+ private void reconnectIfNeeded (@ NonNull final Throwable cause )
507
+ {
508
+ if (!(cause instanceof MqttException ))
509
+ {
510
+ final String notMqttExceptionError = new StringBuilder ("Not MqttException " )
511
+ .append (cause ).toString ();
512
+ log (notMqttExceptionError );
513
+ return ;
514
+ }
515
+
516
+ final MqttException mqttError = (MqttException ) cause ;
517
+
518
+ if (!needToReconnect (mqttError ))
519
+ {
520
+ final String noNeedToReconnect = new StringBuilder ("No need to reconnect " )
521
+ .append (mqttError .getReasonCode ()).toString ();
522
+ log (noNeedToReconnect );
523
+ return ;
524
+ }
525
+
526
+ final String timerName = new StringBuilder ("reconnect-" )
527
+ .append (UUID .randomUUID ().toString ())
528
+ .toString ();
529
+
530
+ final Timer timer = new Timer (timerName );
531
+ timer .schedule (new TimerTask ()
532
+ {
533
+ @ Override
534
+ public void run ()
535
+ {
536
+ log ("[ MQTT ] reconnect" );
537
+ connect ();
538
+ timer .cancel ();
539
+ timer .purge ();
540
+ }
541
+ }, TimeUnit .SECONDS .toMillis (5 ));
467
542
}
468
543
469
544
/**
470
545
* Utility method to handle logging. If 'quietMode' is set, this method does nothing
471
546
*
472
547
* @param message the message to log
473
548
*/
474
- void log (@ NonNull final String message )
549
+ private void log (@ NonNull final String message )
475
550
{
476
551
if (!BuildConfig .DEBUG )
477
552
{
0 commit comments