...
|
...
|
@@ -40,23 +40,26 @@ import java.util.concurrent.atomic.AtomicInteger; |
40
|
40
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
41
|
41
|
final class MqttClientImpl implements MqttClient {
|
42
|
42
|
|
43
|
|
- private final Set<String> serverSubscribtions = new HashSet<>();
|
|
43
|
+ private final Set<String> serverSubscriptions = new HashSet<>();
|
44
|
44
|
private final IntObjectHashMap<MqttPendingUnsubscribtion> pendingServerUnsubscribes = new IntObjectHashMap<>();
|
45
|
45
|
private final IntObjectHashMap<MqttIncomingQos2Publish> qos2PendingIncomingPublishes = new IntObjectHashMap<>();
|
46
|
46
|
private final IntObjectHashMap<MqttPendingPublish> pendingPublishes = new IntObjectHashMap<>();
|
47
|
|
- private final HashMultimap<String, MqttSubscribtion> subscriptions = HashMultimap.create();
|
48
|
|
- private final IntObjectHashMap<MqttPendingSubscribtion> pendingSubscribtions = new IntObjectHashMap<>();
|
|
47
|
+ private final HashMultimap<String, MqttSubscription> subscriptions = HashMultimap.create();
|
|
48
|
+ private final IntObjectHashMap<MqttPendingSubscribtion> pendingSubscriptions = new IntObjectHashMap<>();
|
49
|
49
|
private final Set<String> pendingSubscribeTopics = new HashSet<>();
|
50
|
|
- private final HashMultimap<MqttHandler, MqttSubscribtion> handlerToSubscribtion = HashMultimap.create();
|
|
50
|
+ private final HashMultimap<MqttHandler, MqttSubscription> handlerToSubscribtion = HashMultimap.create();
|
51
|
51
|
private final AtomicInteger nextMessageId = new AtomicInteger(1);
|
52
|
52
|
|
53
|
53
|
private final MqttClientConfig clientConfig;
|
54
|
54
|
|
|
55
|
+ private final MqttHandler defaultHandler;
|
|
56
|
+
|
55
|
57
|
private EventLoopGroup eventLoop;
|
56
|
58
|
|
57
|
|
- private Channel channel;
|
|
59
|
+ private volatile Channel channel;
|
58
|
60
|
|
59
|
|
- private boolean disconnected = false;
|
|
61
|
+ private volatile boolean disconnected = false;
|
|
62
|
+ private volatile boolean reconnect = false;
|
60
|
63
|
private String host;
|
61
|
64
|
private int port;
|
62
|
65
|
private MqttClientCallback callback;
|
...
|
...
|
@@ -65,8 +68,9 @@ final class MqttClientImpl implements MqttClient { |
65
|
68
|
/**
|
66
|
69
|
* Construct the MqttClientImpl with default config
|
67
|
70
|
*/
|
68
|
|
- public MqttClientImpl() {
|
|
71
|
+ public MqttClientImpl(MqttHandler defaultHandler) {
|
69
|
72
|
this.clientConfig = new MqttClientConfig();
|
|
73
|
+ this.defaultHandler = defaultHandler;
|
70
|
74
|
}
|
71
|
75
|
|
72
|
76
|
/**
|
...
|
...
|
@@ -75,8 +79,9 @@ final class MqttClientImpl implements MqttClient { |
75
|
79
|
*
|
76
|
80
|
* @param clientConfig The config object to use while looking for settings
|
77
|
81
|
*/
|
78
|
|
- public MqttClientImpl(MqttClientConfig clientConfig) {
|
|
82
|
+ public MqttClientImpl(MqttClientConfig clientConfig, MqttHandler defaultHandler) {
|
79
|
83
|
this.clientConfig = clientConfig;
|
|
84
|
+ this.defaultHandler = defaultHandler;
|
80
|
85
|
}
|
81
|
86
|
|
82
|
87
|
/**
|
...
|
...
|
@@ -100,12 +105,15 @@ final class MqttClientImpl implements MqttClient { |
100
|
105
|
*/
|
101
|
106
|
@Override
|
102
|
107
|
public Future<MqttConnectResult> connect(String host, int port) {
|
|
108
|
+ return connect(host, port, false);
|
|
109
|
+ }
|
|
110
|
+
|
|
111
|
+ private Future<MqttConnectResult> connect(String host, int port, boolean reconnect) {
|
103
|
112
|
if (this.eventLoop == null) {
|
104
|
113
|
this.eventLoop = new NioEventLoopGroup();
|
105
|
114
|
}
|
106
|
115
|
this.host = host;
|
107
|
116
|
this.port = port;
|
108
|
|
-
|
109
|
117
|
Promise<MqttConnectResult> connectFuture = new DefaultPromise<>(this.eventLoop.next());
|
110
|
118
|
Bootstrap bootstrap = new Bootstrap();
|
111
|
119
|
bootstrap.group(this.eventLoop);
|
...
|
...
|
@@ -113,22 +121,47 @@ final class MqttClientImpl implements MqttClient { |
113
|
121
|
bootstrap.remoteAddress(host, port);
|
114
|
122
|
bootstrap.handler(new MqttChannelInitializer(connectFuture, host, port, clientConfig.getSslContext()));
|
115
|
123
|
ChannelFuture future = bootstrap.connect();
|
|
124
|
+
|
116
|
125
|
future.addListener((ChannelFutureListener) f -> {
|
117
|
126
|
if (f.isSuccess()) {
|
118
|
127
|
MqttClientImpl.this.channel = f.channel();
|
119
|
|
- } else if (clientConfig.isReconnect() && !disconnected) {
|
120
|
|
- eventLoop.schedule((Runnable) () -> connect(host, port), 1L, TimeUnit.SECONDS);
|
|
128
|
+ MqttClientImpl.this.channel.closeFuture().addListener((ChannelFutureListener) channelFuture -> {
|
|
129
|
+ if (isConnected()) {
|
|
130
|
+ return;
|
|
131
|
+ }
|
|
132
|
+ ChannelClosedException e = new ChannelClosedException("Channel is closed!");
|
|
133
|
+ if (callback != null) {
|
|
134
|
+ callback.connectionLost(e);
|
|
135
|
+ }
|
|
136
|
+ pendingSubscriptions.clear();
|
|
137
|
+ serverSubscriptions.clear();
|
|
138
|
+ subscriptions.clear();
|
|
139
|
+ pendingServerUnsubscribes.clear();
|
|
140
|
+ qos2PendingIncomingPublishes.clear();
|
|
141
|
+ pendingPublishes.clear();
|
|
142
|
+ pendingSubscribeTopics.clear();
|
|
143
|
+ handlerToSubscribtion.clear();
|
|
144
|
+ scheduleConnectIfRequired(host, port, true);
|
|
145
|
+ });
|
|
146
|
+ } else {
|
|
147
|
+ scheduleConnectIfRequired(host, port, reconnect);
|
121
|
148
|
}
|
122
|
149
|
});
|
123
|
150
|
return connectFuture;
|
124
|
151
|
}
|
125
|
152
|
|
|
153
|
+ private void scheduleConnectIfRequired(String host, int port, boolean reconnect) {
|
|
154
|
+ if (clientConfig.isReconnect() && !disconnected) {
|
|
155
|
+ if (reconnect) {
|
|
156
|
+ this.reconnect = true;
|
|
157
|
+ }
|
|
158
|
+ eventLoop.schedule((Runnable) () -> connect(host, port, reconnect), 1L, TimeUnit.SECONDS);
|
|
159
|
+ }
|
|
160
|
+ }
|
|
161
|
+
|
126
|
162
|
@Override
|
127
|
163
|
public boolean isConnected() {
|
128
|
|
- if (!disconnected) {
|
129
|
|
- return channel == null ? false : channel.isActive();
|
130
|
|
- };
|
131
|
|
- return false;
|
|
164
|
+ return !disconnected && channel != null && channel.isActive();
|
132
|
165
|
}
|
133
|
166
|
|
134
|
167
|
@Override
|
...
|
...
|
@@ -183,7 +216,7 @@ final class MqttClientImpl implements MqttClient { |
183
|
216
|
*/
|
184
|
217
|
@Override
|
185
|
218
|
public Future<Void> on(String topic, MqttHandler handler, MqttQoS qos) {
|
186
|
|
- return createSubscribtion(topic, handler, false, qos);
|
|
219
|
+ return createSubscription(topic, handler, false, qos);
|
187
|
220
|
}
|
188
|
221
|
|
189
|
222
|
/**
|
...
|
...
|
@@ -210,7 +243,7 @@ final class MqttClientImpl implements MqttClient { |
210
|
243
|
*/
|
211
|
244
|
@Override
|
212
|
245
|
public Future<Void> once(String topic, MqttHandler handler, MqttQoS qos) {
|
213
|
|
- return createSubscribtion(topic, handler, true, qos);
|
|
246
|
+ return createSubscription(topic, handler, true, qos);
|
214
|
247
|
}
|
215
|
248
|
|
216
|
249
|
/**
|
...
|
...
|
@@ -224,7 +257,7 @@ final class MqttClientImpl implements MqttClient { |
224
|
257
|
@Override
|
225
|
258
|
public Future<Void> off(String topic, MqttHandler handler) {
|
226
|
259
|
Promise<Void> future = new DefaultPromise<>(this.eventLoop.next());
|
227
|
|
- for (MqttSubscribtion subscribtion : this.handlerToSubscribtion.get(handler)) {
|
|
260
|
+ for (MqttSubscription subscribtion : this.handlerToSubscribtion.get(handler)) {
|
228
|
261
|
this.subscriptions.remove(topic, subscribtion);
|
229
|
262
|
}
|
230
|
263
|
this.handlerToSubscribtion.removeAll(handler);
|
...
|
...
|
@@ -242,9 +275,9 @@ final class MqttClientImpl implements MqttClient { |
242
|
275
|
@Override
|
243
|
276
|
public Future<Void> off(String topic) {
|
244
|
277
|
Promise<Void> future = new DefaultPromise<>(this.eventLoop.next());
|
245
|
|
- ImmutableSet<MqttSubscribtion> subscribtions = ImmutableSet.copyOf(this.subscriptions.get(topic));
|
246
|
|
- for (MqttSubscribtion subscribtion : subscribtions) {
|
247
|
|
- for (MqttSubscribtion handSub : this.handlerToSubscribtion.get(subscribtion.getHandler())) {
|
|
278
|
+ ImmutableSet<MqttSubscription> subscribtions = ImmutableSet.copyOf(this.subscriptions.get(topic));
|
|
279
|
+ for (MqttSubscription subscribtion : subscribtions) {
|
|
280
|
+ for (MqttSubscription handSub : this.handlerToSubscribtion.get(subscribtion.getHandler())) {
|
248
|
281
|
this.subscriptions.remove(topic, handSub);
|
249
|
282
|
}
|
250
|
283
|
this.handlerToSubscribtion.remove(subscribtion.getHandler(), subscribtion);
|
...
|
...
|
@@ -310,7 +343,7 @@ final class MqttClientImpl implements MqttClient { |
310
|
343
|
ChannelFuture channelFuture = this.sendAndFlushPacket(message);
|
311
|
344
|
|
312
|
345
|
if (channelFuture != null) {
|
313
|
|
- pendingPublish.setSent(channelFuture != null);
|
|
346
|
+ pendingPublish.setSent(true);
|
314
|
347
|
if (channelFuture.cause() != null) {
|
315
|
348
|
future.setFailure(channelFuture.cause());
|
316
|
349
|
return future;
|
...
|
...
|
@@ -352,6 +385,15 @@ final class MqttClientImpl implements MqttClient { |
352
|
385
|
|
353
|
386
|
///////////////////////////////////////////// PRIVATE API /////////////////////////////////////////////
|
354
|
387
|
|
|
388
|
+ public boolean isReconnect() {
|
|
389
|
+ return reconnect;
|
|
390
|
+ }
|
|
391
|
+
|
|
392
|
+ public void onSuccessfulReconnect() {
|
|
393
|
+ callback.onSuccessfulReconnect();
|
|
394
|
+ }
|
|
395
|
+
|
|
396
|
+
|
355
|
397
|
ChannelFuture sendAndFlushPacket(Object message) {
|
356
|
398
|
if (this.channel == null) {
|
357
|
399
|
return null;
|
...
|
...
|
@@ -359,11 +401,7 @@ final class MqttClientImpl implements MqttClient { |
359
|
401
|
if (this.channel.isActive()) {
|
360
|
402
|
return this.channel.writeAndFlush(message);
|
361
|
403
|
}
|
362
|
|
- ChannelClosedException e = new ChannelClosedException("Channel is closed");
|
363
|
|
- if (callback != null) {
|
364
|
|
- callback.connectionLost(e);
|
365
|
|
- }
|
366
|
|
- return this.channel.newFailedFuture(e);
|
|
404
|
+ return this.channel.newFailedFuture(new ChannelClosedException("Channel is closed!"));
|
367
|
405
|
}
|
368
|
406
|
|
369
|
407
|
private MqttMessageIdVariableHeader getNewMessageId() {
|
...
|
...
|
@@ -371,16 +409,16 @@ final class MqttClientImpl implements MqttClient { |
371
|
409
|
return MqttMessageIdVariableHeader.from(this.nextMessageId.getAndIncrement());
|
372
|
410
|
}
|
373
|
411
|
|
374
|
|
- private Future<Void> createSubscribtion(String topic, MqttHandler handler, boolean once, MqttQoS qos) {
|
|
412
|
+ private Future<Void> createSubscription(String topic, MqttHandler handler, boolean once, MqttQoS qos) {
|
375
|
413
|
if (this.pendingSubscribeTopics.contains(topic)) {
|
376
|
|
- Optional<Map.Entry<Integer, MqttPendingSubscribtion>> subscribtionEntry = this.pendingSubscribtions.entrySet().stream().filter((e) -> e.getValue().getTopic().equals(topic)).findAny();
|
|
414
|
+ Optional<Map.Entry<Integer, MqttPendingSubscribtion>> subscribtionEntry = this.pendingSubscriptions.entrySet().stream().filter((e) -> e.getValue().getTopic().equals(topic)).findAny();
|
377
|
415
|
if (subscribtionEntry.isPresent()) {
|
378
|
416
|
subscribtionEntry.get().getValue().addHandler(handler, once);
|
379
|
417
|
return subscribtionEntry.get().getValue().getFuture();
|
380
|
418
|
}
|
381
|
419
|
}
|
382
|
|
- if (this.serverSubscribtions.contains(topic)) {
|
383
|
|
- MqttSubscribtion subscribtion = new MqttSubscribtion(topic, handler, once);
|
|
420
|
+ if (this.serverSubscriptions.contains(topic)) {
|
|
421
|
+ MqttSubscription subscribtion = new MqttSubscription(topic, handler, once);
|
384
|
422
|
this.subscriptions.put(topic, subscribtion);
|
385
|
423
|
this.handlerToSubscribtion.put(handler, subscribtion);
|
386
|
424
|
return this.channel.newSucceededFuture();
|
...
|
...
|
@@ -395,7 +433,7 @@ final class MqttClientImpl implements MqttClient { |
395
|
433
|
|
396
|
434
|
final MqttPendingSubscribtion pendingSubscribtion = new MqttPendingSubscribtion(future, topic, message);
|
397
|
435
|
pendingSubscribtion.addHandler(handler, once);
|
398
|
|
- this.pendingSubscribtions.put(variableHeader.messageId(), pendingSubscribtion);
|
|
436
|
+ this.pendingSubscriptions.put(variableHeader.messageId(), pendingSubscribtion);
|
399
|
437
|
this.pendingSubscribeTopics.add(topic);
|
400
|
438
|
pendingSubscribtion.setSent(this.sendAndFlushPacket(message) != null); //If not sent, we will send it when the connection is opened
|
401
|
439
|
|
...
|
...
|
@@ -405,7 +443,7 @@ final class MqttClientImpl implements MqttClient { |
405
|
443
|
}
|
406
|
444
|
|
407
|
445
|
private void checkSubscribtions(String topic, Promise<Void> promise) {
|
408
|
|
- if (!(this.subscriptions.containsKey(topic) && this.subscriptions.get(topic).size() != 0) && this.serverSubscribtions.contains(topic)) {
|
|
446
|
+ if (!(this.subscriptions.containsKey(topic) && this.subscriptions.get(topic).size() != 0) && this.serverSubscriptions.contains(topic)) {
|
409
|
447
|
MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
|
410
|
448
|
MqttMessageIdVariableHeader variableHeader = getNewMessageId();
|
411
|
449
|
MqttUnsubscribePayload payload = new MqttUnsubscribePayload(Collections.singletonList(topic));
|
...
|
...
|
@@ -421,11 +459,11 @@ final class MqttClientImpl implements MqttClient { |
421
|
459
|
}
|
422
|
460
|
}
|
423
|
461
|
|
424
|
|
- IntObjectHashMap<MqttPendingSubscribtion> getPendingSubscribtions() {
|
425
|
|
- return pendingSubscribtions;
|
|
462
|
+ IntObjectHashMap<MqttPendingSubscribtion> getPendingSubscriptions() {
|
|
463
|
+ return pendingSubscriptions;
|
426
|
464
|
}
|
427
|
465
|
|
428
|
|
- HashMultimap<String, MqttSubscribtion> getSubscriptions() {
|
|
466
|
+ HashMultimap<String, MqttSubscription> getSubscriptions() {
|
429
|
467
|
return subscriptions;
|
430
|
468
|
}
|
431
|
469
|
|
...
|
...
|
@@ -433,12 +471,12 @@ final class MqttClientImpl implements MqttClient { |
433
|
471
|
return pendingSubscribeTopics;
|
434
|
472
|
}
|
435
|
473
|
|
436
|
|
- HashMultimap<MqttHandler, MqttSubscribtion> getHandlerToSubscribtion() {
|
|
474
|
+ HashMultimap<MqttHandler, MqttSubscription> getHandlerToSubscribtion() {
|
437
|
475
|
return handlerToSubscribtion;
|
438
|
476
|
}
|
439
|
477
|
|
440
|
|
- Set<String> getServerSubscribtions() {
|
441
|
|
- return serverSubscribtions;
|
|
478
|
+ Set<String> getServerSubscriptions() {
|
|
479
|
+ return serverSubscriptions;
|
442
|
480
|
}
|
443
|
481
|
|
444
|
482
|
IntObjectHashMap<MqttPendingUnsubscribtion> getPendingServerUnsubscribes() {
|
...
|
...
|
@@ -481,4 +519,9 @@ final class MqttClientImpl implements MqttClient { |
481
|
519
|
ch.pipeline().addLast("mqttHandler", new MqttChannelHandler(MqttClientImpl.this, connectFuture));
|
482
|
520
|
}
|
483
|
521
|
}
|
|
522
|
+
|
|
523
|
+ MqttHandler getDefaultHandler() {
|
|
524
|
+ return defaultHandler;
|
|
525
|
+ }
|
|
526
|
+
|
484
|
527
|
} |
...
|
...
|
|