|
1 | 1 | /******************************************************************************* |
2 | | - * Copyright (c) 2009, 2015 IBM Corp. |
| 2 | + * Copyright (c) 2009, 2016 IBM Corp. |
3 | 3 | * |
4 | 4 | * All rights reserved. This program and the accompanying materials |
5 | 5 | * are made available under the terms of the Eclipse Public License v1.0 |
|
15 | 15 | * Ian Craggs - per subscription message handlers (bug 466579) |
16 | 16 | * Ian Craggs - ack control (bug 472172) |
17 | 17 | * James Sutton - checkForActivity Token (bug 473928) |
| 18 | + * James Sutton - Automatic Reconnect & Offline Buffering. |
18 | 19 | */ |
19 | 20 | package org.eclipse.paho.client.mqttv3.internal; |
20 | 21 |
|
21 | 22 | import java.util.Enumeration; |
22 | 23 | import java.util.Properties; |
23 | 24 | import java.util.Vector; |
24 | 25 |
|
| 26 | +import org.eclipse.paho.client.mqttv3.BufferedMessage; |
25 | 27 | import org.eclipse.paho.client.mqttv3.IMqttActionListener; |
26 | 28 | import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; |
27 | 29 | import org.eclipse.paho.client.mqttv3.IMqttMessageListener; |
28 | 30 | import org.eclipse.paho.client.mqttv3.MqttCallback; |
| 31 | +import org.eclipse.paho.client.mqttv3.MqttCallbackExtended; |
29 | 32 | import org.eclipse.paho.client.mqttv3.MqttClientPersistence; |
30 | 33 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
31 | 34 | import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; |
32 | 35 | import org.eclipse.paho.client.mqttv3.MqttException; |
| 36 | +import org.eclipse.paho.client.mqttv3.MqttMessage; |
33 | 37 | import org.eclipse.paho.client.mqttv3.MqttPersistenceException; |
34 | 38 | import org.eclipse.paho.client.mqttv3.MqttPingSender; |
35 | 39 | import org.eclipse.paho.client.mqttv3.MqttToken; |
@@ -74,7 +78,9 @@ public class ClientComms { |
74 | 78 | private byte conState = DISCONNECTED; |
75 | 79 | private Object conLock = new Object(); // Used to synchronize connection state |
76 | 80 | private boolean closePending = false; |
77 | | - |
| 81 | + private boolean resting = false; |
| 82 | + private DisconnectedMessageBuffer disconnectedMessageBuffer; |
| 83 | + |
78 | 84 | /** |
79 | 85 | * Creates a new ClientComms object, using the specified module to handle |
80 | 86 | * the network calls. |
@@ -141,7 +147,19 @@ public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttExce |
141 | 147 | if (isConnected() || |
142 | 148 | (!isConnected() && message instanceof MqttConnect) || |
143 | 149 | (isDisconnecting() && message instanceof MqttDisconnect)) { |
144 | | - this.internalSend(message, token); |
| 150 | + if(disconnectedMessageBuffer != null && disconnectedMessageBuffer.getMessageCount() != 0){ |
| 151 | + //@TRACE 507=Client Connected, Offline Buffer available, but not empty. Adding message to buffer. message={0} |
| 152 | + log.fine(CLASS_NAME, methodName, "507", new Object[] {message.getKey()}); |
| 153 | + this.clientState.persistBufferedMessage(message); |
| 154 | + disconnectedMessageBuffer.putMessage(message, token); |
| 155 | + } else { |
| 156 | + this.internalSend(message, token); |
| 157 | + } |
| 158 | + } else if(disconnectedMessageBuffer != null && isResting()){ |
| 159 | + //@TRACE 508=Client Resting, Offline Buffer available. Adding message to buffer. message={0} |
| 160 | + log.fine(CLASS_NAME, methodName, "508", new Object[] {message.getKey()}); |
| 161 | + this.clientState.persistBufferedMessage(message); |
| 162 | + disconnectedMessageBuffer.putMessage(message, token); |
145 | 163 | } else { |
146 | 164 | //@TRACE 208=failed: not connected |
147 | 165 | log.fine(CLASS_NAME, methodName, "208"); |
@@ -336,7 +354,10 @@ public void shutdownConnection(MqttToken token, MqttException reason) { |
336 | 354 | } |
337 | 355 |
|
338 | 356 | try { |
339 | | - if (persistence != null) {persistence.close();} |
| 357 | + if(disconnectedMessageBuffer == null && persistence != null){ |
| 358 | + persistence.close(); |
| 359 | + } |
| 360 | + |
340 | 361 | }catch(Exception ex) { |
341 | 362 | // Ignore as we are shutting down |
342 | 363 | } |
@@ -499,12 +520,22 @@ public boolean isClosed() { |
499 | 520 | return conState == CLOSED; |
500 | 521 | } |
501 | 522 | } |
| 523 | + |
| 524 | + public boolean isResting() { |
| 525 | + synchronized (conLock) { |
| 526 | + return resting; |
| 527 | + } |
| 528 | + } |
502 | 529 |
|
503 | 530 |
|
504 | 531 | public void setCallback(MqttCallback mqttCallback) { |
505 | 532 | this.callback.setCallback(mqttCallback); |
506 | 533 | } |
507 | 534 |
|
| 535 | + public void setReconnectCallback(MqttCallbackExtended callback){ |
| 536 | + this.callback.setReconnectCallback(callback); |
| 537 | + } |
| 538 | + |
508 | 539 | public void setManualAcks(boolean manualAcks) { |
509 | 540 | this.callback.setManualAcks(manualAcks); |
510 | 541 | } |
@@ -720,4 +751,62 @@ private void handleRunException(Exception ex) { |
720 | 751 |
|
721 | 752 | shutdownConnection(null, mex); |
722 | 753 | } |
| 754 | + |
| 755 | + /** |
| 756 | + * When Automatic reconnect is enabled, we want ClientComs to enter the |
| 757 | + * 'resting' state if disconnected. This will allow us to publish messages |
| 758 | + * @param resting |
| 759 | + */ |
| 760 | + public void setRestingState(boolean resting) { |
| 761 | + this.resting = resting; |
| 762 | + } |
| 763 | + |
| 764 | + public void setDisconnectedMessageBuffer(DisconnectedMessageBuffer disconnectedMessageBuffer) { |
| 765 | + this.disconnectedMessageBuffer = disconnectedMessageBuffer; |
| 766 | + } |
| 767 | + |
| 768 | + public int getBufferedMessageCount(){ |
| 769 | + return this.disconnectedMessageBuffer.getMessageCount(); |
| 770 | + } |
| 771 | + |
| 772 | + public MqttMessage getBufferedMessage(int bufferIndex){ |
| 773 | + MqttPublish send = (MqttPublish) this.disconnectedMessageBuffer.getMessage(bufferIndex).getMessage(); |
| 774 | + return send.getMessage(); |
| 775 | + } |
| 776 | + |
| 777 | + public void deleteBufferedMessage(int bufferIndex){ |
| 778 | + this.disconnectedMessageBuffer.deleteMessage(bufferIndex); |
| 779 | + } |
| 780 | + |
| 781 | + |
| 782 | + /** |
| 783 | + * When the client automatically reconnects, we want to send all messages from the |
| 784 | + * buffer first before allowing the user to send any messages |
| 785 | + * @throws MqttException |
| 786 | + */ |
| 787 | + public void notifyReconnect() { |
| 788 | + final String methodName = "notifyReconnect"; |
| 789 | + if(disconnectedMessageBuffer != null){ |
| 790 | + //@TRACE 509=Client Reconnected, Offline Buffer Available. Sending Buffered Messages. |
| 791 | + log.fine(CLASS_NAME, methodName, "509"); |
| 792 | + disconnectedMessageBuffer.setPublishCallback(new IDisconnectedBufferCallback() { |
| 793 | + |
| 794 | + public void publishBufferedMessage(BufferedMessage bufferedMessage) throws MqttException { |
| 795 | + if (isConnected()) { |
| 796 | + //@TRACE 510=Publising Buffered message message={0} |
| 797 | + log.fine(CLASS_NAME, methodName, "510", new Object[] {bufferedMessage.getMessage().getKey()}); |
| 798 | + internalSend(bufferedMessage.getMessage(), bufferedMessage.getToken()); |
| 799 | + // Delete from persistence if in there |
| 800 | + clientState.unPersistBufferedMessage(bufferedMessage.getMessage()); |
| 801 | + } else { |
| 802 | + //@TRACE 208=failed: not connected |
| 803 | + log.fine(CLASS_NAME, methodName, "208"); |
| 804 | + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED); |
| 805 | + } |
| 806 | + } |
| 807 | + }); |
| 808 | + new Thread(disconnectedMessageBuffer).start(); |
| 809 | + } |
| 810 | + } |
| 811 | + |
723 | 812 | } |
0 commit comments