{"action":"created","issue":{"url":"https://api.github.com/repos/eclipse/paho.mqtt.java/issues/9","labels_url":"https://api.github.com/repos/eclipse/paho.mqtt.java/issues/9/labels{/name}","comments_url":"https://api.github.com/repos/eclipse/paho.mqtt.java/issues/9/comments","events_url":"https://api.github.com/repos/eclipse/paho.mqtt.java/issues/9/events","html_url":"https://github.com/eclipse/paho.mqtt.java/issues/9","id":129780713,"number":9,"title":"New Feature Plan: Automatic Reconnect and Disconnected Buffering","user":{"login":"jpwsutton","id":1068763,"avatar_url":"https://avatars.githubusercontent.com/u/1068763?v=3","gravatar_id":"","url":"https://api.github.com/users/jpwsutton","html_url":"https://github.com/jpwsutton","followers_url":"https://api.github.com/users/jpwsutton/followers","following_url":"https://api.github.com/users/jpwsutton/following{/other_user}","gists_url":"https://api.github.com/users/jpwsutton/gists{/gist_id}","starred_url":"https://api.github.com/users/jpwsutton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jpwsutton/subscriptions","organizations_url":"https://api.github.com/users/jpwsutton/orgs","repos_url":"https://api.github.com/users/jpwsutton/repos","events_url":"https://api.github.com/users/jpwsutton/events{/privacy}","received_events_url":"https://api.github.com/users/jpwsutton/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2016-01-29T14:29:09Z","updated_at":"2016-02-01T09:55:03Z","closed_at":null,"body":"# Automatic Reconnect and Disconnected Publishing Plan\r\n\r\nCurrently, the Paho Java client is lacking two major areas of functionality: Automatic Reconnect and Disconnected (or Offline) Publishing.\r\nThe goal is to implement these features in time for the next release [Neon](https://projects.eclipse.org/projects/technology.paho/releases/1.2.0).\r\n\r\nThis issue aims to outline the plan for adding this new functionality into the client and has been modeled off Mike Tran's plan and work for the same functionality in the Javascript Client.\r\n\r\n## Recap: Possible Client States\r\nThere are 5 main potential states that the client can be in. The User will usually desire the client to either be in the ```connected``` or ```disconnected``` states.\r\n * ```never connected```: This is the initial state of the client where:\r\n   * The client has not yet sent a connect request.\r\n   * The client has never received a ```CONNACK``` after it's initial connect request.\r\n * ```connecting```: A connect request is in progress.\r\n * ```connected```: The client is connected and ready to send and receive messages.\r\n * ```disconnecting```: A disconnect request is in progress.\r\n * ```disconnected```: The client goes from ```connected``` to ```disconnected``` state when:\r\n   * A disconnect request has been completed\r\n   * As a result of a networking error, or the server is no longer reachable.\r\n\r\n\r\n## What does it do?\r\n\r\n### Automatic Reconnect\r\nWill automatically attempt to reconnect to the broker (or one of the servers in the host list) while the client is in ```disconnected``` state.\r\n* The client will not perform automatic reconnection if it is not in ```disconnected``` state.\r\n* When the connection is list, the ```connectionLost()``` callback is called before the client starts the reconnect process. Since the state of the client is ```disconnected```, the application is allowed to call the ```connect``` function with new connection options if they wish.\r\n* When ```disconnect``` is called while connected, the client goes to the ```disconnected``` state and automatic reconnect is disabled.\r\n* If the client application calls ```connect``` after it had reconnected, an invalid state error will be thrown.\r\n* The client library does not subscribe for the application after it successfully reconnects. A callback will be provided to notify the application when it has been reconnected allowing it to make any subscriptions to topics itself.\r\n\r\n\r\n### Disconnected Publishing\r\nWill allow the client to save messages in a buffer whilst the client is in the ```disconnected``` state.\r\n* Once the client is reconnected (To the same or different broker), the buffered messages are scheduled to be sent.\r\n* To maintain order of messages, the client library must send buffered messages before sending new messages.\r\n* The client library does not save any messages while the client is in the ```never connected``` state. So it cannot send any messages before it connects for the first time.\r\n* When disconnect is called while connected, the client goes to ```disconnected``` state and Disconnected Publishing remains active if enabled.\r\n\r\n## API Changes\r\n\r\n### Automatic Reconnect\r\nThe following **optional** attributes will be added to the the ```MqttClient``` and ```MqttAsyncClient``` classes:\r\n* ```setReconnect(boolean reconnect)``` : If true, the client will attempt to reconnect. _Default: False_\r\n* ```setReconnectTimeInterval(int reconnectTimeInterval)``` : Time interval in seconds between reconnect attempts. _Default: 10_\r\n\r\n_Note: In the Javascript client, these attributes are in the Connection Options object, however in the Java client, they need to be in the Client classes to allow the developer to change them after ```conect()``` has been called._\r\n\r\n\r\nThe following change will be made to the ```MqttCallback``` Interface:\r\n* ```connectionLost(Throwable cause, boolean reconnect)``` : If the ```reconnect``` boolean is true, the client will perform automatic reconnection whilst it is disconnected.\r\n* Addition of a new callback ```connectionComplete(boolean reconnect)```. This would not replace the existing ```IMqttToken``` that you get if you call ```connectWithResult``` (So that we don't break any functionality). However it would serve the same purpose, it would be called every time that the client connects or reconnects to the broker. This will allow the application to re-make any subscriptions that were lost or to send any held messages if it is not using Disconnected Publishing.\r\nThe ```boolean reconnect``` attribute is set to true if the connection was the result of an automatic reconnection, else it is false.\r\n\r\n### Disconnected Publising\r\n\r\n* To maintain the order of the messages, the client must ensure that buffered messages are scheduled to be sent before new messages.\r\n\r\nThe following **optional** attributes will be added to the ```MqttClient``` and ```MqttAsyncClient``` classes:\r\n* ```boolean setDisconnectedPublishing``` : If present and true, the client will store messages whilst disconnected. _Default: False_\r\n* ```int setDisconnectedBufferSize``` : The maximum number of messages that will be stored in memory while the client is disconnected. _Default: 5000_\r\n* ```boolean setPersistDisconnectedBuffer``` : If true, the client will persist the messages to disk, if false or not present, the messages will only be saved in memory. _Default: False_\r\n* ```getBufferedMessagesCount``` : Returns the number of messages in the buffer.\r\n* ```deleteBufferedMessage(int index)``` : Deletes the buffered message at the index location.\r\n\r\nThe following change will be made to the ```MqttClient``` and ```MqttAsyncClient``` classes:\r\n* ```publish``` : Currently throws an MqttException. If the buffer is full, this will be thrown containing a message explaining that the Message buffer is full.\r\n\r\n## Sample application\r\n\r\n## Example 1\r\nThis application wants to use both Automatic Reconnect and Disconnected Publising. The Application does not want to persist buffered messages.\r\n\r\n```\r\npublic static void main( String[] args )\r\n    {\r\n\r\n        String topic        = \"/greenhouse/temperature\";\r\n        String broker       = \"tcp://iot.eclipse.org:1883\";\r\n        String clientId     = \"TemperatureMonitor_42\";\r\n        MemoryPersistence persistence = new MemoryPersistence();\r\n\r\n        try {\r\n            MqttClient sampleClient = new MqttClient(broker, clientId, persistence);\r\n            MqttConnectOptions connOpts = new MqttConnectOptions();\r\n            connOpts.setCleanSession(true);\r\n\r\n            sampleClient.setCallback(new MqttCallback() {\r\n\r\n\t\t\t\tpublic void messageArrived(String topic, MqttMessage message) throws Exception {\r\n\t\t\t\t\t// Not used\r\n\t\t\t\t}\r\n\r\n\t\t\t\tpublic void deliveryComplete(IMqttDeliveryToken token) {\r\n\t\t\t\t\t// Not used\r\n\t\t\t\t}\r\n\r\n\t\t\t\tpublic void connectionLost(Throwable cause, boolean reconnect) {\r\n\t\t\t\t\tSystem.out.println(\"Connection Lost: \" + cause.getMessage());\r\n                    if(reconnect){\r\n                        System.out.println(\"Client will attempt to automatically reconnect\");\r\n                    }\r\n\t\t\t\t}\r\n\r\n                public void connectionComplete(boolean reconnect) {\r\n                    // Make or re-make subscriptions here\r\n                    if(reconnect){\r\n                        System.out.println(\"Automatically Reconnected to Broker!\");\r\n                    } else {\r\n                        System.out.println(\"Connected To Broker for the first time!\");\r\n                    }\r\n                }\r\n\t\t\t});\r\n\r\n\r\n            sampleClient.setReconnect(true);  // Enable Automatic Reconnect\r\n            sampleClient.setReconnectTimeInterval(30);  // Attempt to reconnect every 30 seconds\r\n            sampleClient.setDisconnectedPublishing(true);  // Enable Disconnected Publishing\r\n            sampleClient.setDisconnectedBufferSize(1000);  // Only Store 1000 messages in the buffer\r\n            sampleClient.setPersistDisconnectedBuffer(false);  // Do not persist the buffer\r\n\r\n\r\n            System.out.println(\"Connecting to broker: \"+broker);\r\n            sampleClient.connect(connOpts);\r\n            System.out.println(\"Connected\");\r\n\r\n\r\n            /*\r\n             * Sample code to continually publish messages\r\n             */\r\n            Timer timer = new Timer();\r\n            timer.scheduleAtFixedRate(new TimerTask() {\r\n\r\n\t\t\t\t@Override\r\n\t\t\t\tpublic void run() {\r\n\t\t\t\t\t// Publish the current Temperature\r\n\t\t\t\t\tString temp = String.format(\"%.2f\", getTemperature());\r\n\t\t\t\t\tSystem.out.format(\"Publising temperature %s to topic %s. \", temp, topic);\r\n\t\t\t\t\tMqttMessage message = new MqttMessage(temp.getBytes());\r\n\t\t\t\t\tmessage.setQos(0);\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tsampleClient.publish(topic, message);\r\n\t\t\t\t\t} catch (MqttException e) {\r\n\t\t\t\t\t\te.printStackTrace();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}, 5000, 5000);  // Every 5 seconds\r\n\r\n\r\n\r\n        } catch(MqttException me) {\r\n            System.out.println(\"reason \"+me.getReasonCode());\r\n            System.out.println(\"msg \"+me.getMessage());\r\n            System.out.println(\"loc \"+me.getLocalizedMessage());\r\n            System.out.println(\"cause \"+me.getCause());\r\n            System.out.println(\"excep \"+me);\r\n            me.printStackTrace();\r\n        }\r\n    }\r\n\r\n```\r\n\r\nBefore I start work on this, I'd be very interested in hearing back from the community. Because of the very nature of the features that need to be implemented, it means adding a lot to the API which would mean a small amount of work for developers upgrading their application to use Neon (For example the addition of a callback to ```MqttCallback```). If anyone can spot anything that might cause issues down the line, or thinks that there might be a better way of accomplishing this functionality please do comment!\r\n"},"comment":{"url":"https://api.github.com/repos/eclipse/paho.mqtt.java/issues/comments/177883521","html_url":"https://github.com/eclipse/paho.mqtt.java/issues/9#issuecomment-177883521","issue_url":"https://api.github.com/repos/eclipse/paho.mqtt.java/issues/9","id":177883521,"user":{"login":"kamilfb","id":8521600,"avatar_url":"https://avatars.githubusercontent.com/u/8521600?v=3","gravatar_id":"","url":"https://api.github.com/users/kamilfb","html_url":"https://github.com/kamilfb","followers_url":"https://api.github.com/users/kamilfb/followers","following_url":"https://api.github.com/users/kamilfb/following{/other_user}","gists_url":"https://api.github.com/users/kamilfb/gists{/gist_id}","starred_url":"https://api.github.com/users/kamilfb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kamilfb/subscriptions","organizations_url":"https://api.github.com/users/kamilfb/orgs","repos_url":"https://api.github.com/users/kamilfb/repos","events_url":"https://api.github.com/users/kamilfb/events{/privacy}","received_events_url":"https://api.github.com/users/kamilfb/received_events","type":"User","site_admin":false},"created_at":"2016-02-01T09:55:03Z","updated_at":"2016-02-01T09:55:03Z","body":"James, great summary of the proposed changes.\r\n\r\nI have a concern about the MqttCallback interface through - is there any chance we could make it backwards compatible? I believe if we make those changes, existing applications that implement the callback won't compile."}}