|
18 | 18 |
|
19 | 19 | import java.time.Instant;
|
20 | 20 | import java.util.concurrent.ScheduledFuture;
|
21 |
| -import java.util.concurrent.atomic.AtomicReference; |
22 | 21 |
|
| 22 | +import org.apache.commons.logging.Log; |
| 23 | +import org.apache.commons.logging.LogFactory; |
23 | 24 | import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
|
24 | 25 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
|
25 |
| -import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
26 | 26 | import org.eclipse.paho.client.mqttv3.MqttCallback;
|
27 | 27 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
28 | 28 | import org.eclipse.paho.client.mqttv3.MqttException;
|
29 | 29 | import org.eclipse.paho.client.mqttv3.MqttMessage;
|
30 | 30 |
|
31 |
| -import org.springframework.integration.context.IntegrationObjectSupport; |
32 |
| -import org.springframework.integration.support.management.ManageableLifecycle; |
| 31 | +import org.springframework.scheduling.TaskScheduler; |
| 32 | +import org.springframework.util.Assert; |
33 | 33 |
|
34 |
| -public class Mqttv3ClientManager extends IntegrationObjectSupport implements ClientManager<IMqttAsyncClient>, |
35 |
| - ManageableLifecycle, MqttCallback { |
| 34 | +public class Mqttv3ClientManager extends AbstractMqttClientManager<IMqttAsyncClient> implements MqttCallback { |
36 | 35 |
|
37 |
| - private AtomicReference<ScheduledFuture<?>> scheduledReconnect; |
| 36 | + /** |
| 37 | + * The default reconnect timeout in millis. |
| 38 | + */ |
| 39 | + private static final long DEFAULT_RECOVERY_INTERVAL = 10_000; |
38 | 40 |
|
39 |
| - private final MqttConnectOptions connectOptions; |
| 41 | + private final Log logger = LogFactory.getLog(this.getClass()); |
40 | 42 |
|
41 |
| - private final String clientId; |
| 43 | + private final MqttPahoClientFactory clientFactory; |
42 | 44 |
|
43 |
| - private IMqttAsyncClient client; |
| 45 | + private final TaskScheduler taskScheduler; |
44 | 46 |
|
45 |
| - public Mqttv3ClientManager(MqttConnectOptions connectOptions, String clientId) throws MqttException { |
46 |
| - this.connectOptions = connectOptions; |
47 |
| - this.client = new MqttAsyncClient(connectOptions.getServerURIs()[0], clientId); |
48 |
| - this.client.setCallback(this); |
49 |
| - this.clientId = clientId; |
| 47 | + private volatile ScheduledFuture<?> scheduledReconnect; |
| 48 | + |
| 49 | + private volatile IMqttAsyncClient client; |
| 50 | + |
| 51 | + private long recoveryInterval = DEFAULT_RECOVERY_INTERVAL; |
| 52 | + |
| 53 | + public Mqttv3ClientManager(MqttPahoClientFactory clientFactory, TaskScheduler taskScheduler, String url, |
| 54 | + String clientId) { |
| 55 | + |
| 56 | + super(url, clientId); |
| 57 | + Assert.notNull(clientId, "'clientFactory' is required"); |
| 58 | + Assert.notNull(clientId, "'taskScheduler' is required"); |
| 59 | + if (url == null) { |
| 60 | + Assert.notEmpty(clientFactory.getConnectionOptions().getServerURIs(), "'serverURIs' must be provided in the 'MqttConnectionOptions'"); |
| 61 | + } |
| 62 | + this.clientFactory = clientFactory; |
| 63 | + this.taskScheduler = taskScheduler; |
50 | 64 | }
|
51 | 65 |
|
52 | 66 | @Override
|
53 | 67 | public IMqttAsyncClient getClient() {
|
54 |
| - return client; |
| 68 | + return this.client; |
55 | 69 | }
|
56 | 70 |
|
57 | 71 | @Override
|
58 |
| - public void start() { |
| 72 | + public synchronized void start() { |
59 | 73 | if (this.client == null) {
|
60 | 74 | try {
|
61 |
| - this.client = new MqttAsyncClient(this.connectOptions.getServerURIs()[0], this.clientId); |
| 75 | + this.client = this.clientFactory.getAsyncClientInstance(getUrl(), getClientId()); |
| 76 | + this.client.setManualAcks(isManualAcks()); |
| 77 | + this.client.setCallback(this); |
62 | 78 | }
|
63 | 79 | catch (MqttException e) {
|
64 | 80 | throw new IllegalStateException("could not start client manager", e);
|
65 | 81 | }
|
66 |
| - this.client.setCallback(this); |
67 | 82 | }
|
68 | 83 | try {
|
69 | 84 | connect();
|
70 | 85 | }
|
71 | 86 | catch (MqttException e) {
|
72 |
| - logger.error(e, "could not start client manager, scheduling reconnect, client_id=" + |
73 |
| - this.client.getClientId()); |
| 87 | + this.logger.error("could not start client manager, scheduling reconnect, client_id=" + |
| 88 | + this.client.getClientId(), e); |
74 | 89 | scheduleReconnect();
|
75 | 90 | }
|
76 | 91 | }
|
77 | 92 |
|
78 | 93 | @Override
|
79 |
| - public void stop() { |
| 94 | + public synchronized void stop() { |
80 | 95 | if (this.client == null) {
|
81 | 96 | return;
|
82 | 97 | }
|
83 | 98 | try {
|
84 |
| - this.client.disconnectForcibly(this.connectOptions.getConnectionTimeout()); |
| 99 | + this.client.disconnectForcibly(this.clientFactory.getConnectionOptions().getConnectionTimeout()); |
85 | 100 | }
|
86 | 101 | catch (MqttException e) {
|
87 |
| - logger.error(e, "could not disconnect from the client"); |
| 102 | + this.logger.error("could not disconnect from the client", e); |
88 | 103 | }
|
89 | 104 | finally {
|
90 | 105 | try {
|
91 | 106 | this.client.close();
|
92 | 107 | }
|
93 | 108 | catch (MqttException e) {
|
94 |
| - logger.error(e, "could not close the client"); |
| 109 | + this.logger.error("could not close the client", e); |
95 | 110 | }
|
96 | 111 | this.client = null;
|
97 | 112 | }
|
98 | 113 | }
|
99 | 114 |
|
100 | 115 | @Override
|
101 |
| - public boolean isRunning() { |
| 116 | + public synchronized boolean isRunning() { |
102 | 117 | return this.client != null;
|
103 | 118 | }
|
104 | 119 |
|
105 |
| - private synchronized void connect() throws MqttException { |
106 |
| - if (this.client == null) { |
107 |
| - logger.error("could not connect on a null client reference"); |
108 |
| - return; |
109 |
| - } |
110 |
| - MqttConnectOptions options = Mqttv3ClientManager.this.connectOptions; |
111 |
| - this.client.connect(options).waitForCompletion(options.getConnectionTimeout()); |
| 120 | + @Override |
| 121 | + public synchronized void connectionLost(Throwable cause) { |
| 122 | + this.logger.error("connection lost, scheduling reconnect, client_id=" + this.client.getClientId(), |
| 123 | + cause); |
| 124 | + scheduleReconnect(); // todo: do we need to resubscribe if con lost? |
112 | 125 | }
|
113 | 126 |
|
114 | 127 | @Override
|
115 |
| - public synchronized void connectionLost(Throwable cause) { |
116 |
| - logger.error(cause, "connection lost, scheduling reconnect, client_id=" + this.client.getClientId()); |
117 |
| - scheduleReconnect(); |
| 128 | + public void messageArrived(String topic, MqttMessage message) { |
| 129 | + // not this manager concern |
| 130 | + } |
| 131 | + |
| 132 | + @Override |
| 133 | + public void deliveryComplete(IMqttDeliveryToken token) { |
| 134 | + // nor this manager concern |
| 135 | + } |
| 136 | + |
| 137 | + public long getRecoveryInterval() { |
| 138 | + return this.recoveryInterval; |
| 139 | + } |
| 140 | + |
| 141 | + public void setRecoveryInterval(long recoveryInterval) { |
| 142 | + this.recoveryInterval = recoveryInterval; |
118 | 143 | }
|
119 | 144 |
|
120 |
| - private void scheduleReconnect() { |
121 |
| - if (this.scheduledReconnect.get() != null) { |
122 |
| - this.scheduledReconnect.get().cancel(false); |
| 145 | + private synchronized void connect() throws MqttException { |
| 146 | + MqttConnectOptions options = this.clientFactory.getConnectionOptions(); |
| 147 | + this.client.connect(options).waitForCompletion(options.getConnectionTimeout()); |
| 148 | + } |
| 149 | + |
| 150 | + private synchronized void scheduleReconnect() { |
| 151 | + if (this.scheduledReconnect != null) { |
| 152 | + this.scheduledReconnect.cancel(false); |
123 | 153 | }
|
124 |
| - this.scheduledReconnect.set(getTaskScheduler().schedule(() -> { |
| 154 | + this.scheduledReconnect = this.taskScheduler.schedule(() -> { |
125 | 155 | try {
|
| 156 | + if (this.client.isConnected()) { |
| 157 | + return; |
| 158 | + } |
| 159 | + |
126 | 160 | connect();
|
127 |
| - this.scheduledReconnect.set(null); |
| 161 | + this.scheduledReconnect = null; |
128 | 162 | }
|
129 | 163 | catch (MqttException e) {
|
130 |
| - logger.error(e, "could not reconnect"); |
| 164 | + this.logger.error("could not reconnect", e); |
131 | 165 | scheduleReconnect();
|
132 | 166 | }
|
133 |
| - }, Instant.now().plusSeconds(10))); |
| 167 | + }, Instant.now().plusMillis(getRecoveryInterval())); |
134 | 168 | }
|
135 | 169 |
|
136 |
| - @Override |
137 |
| - public void messageArrived(String topic, MqttMessage message) { |
138 |
| - // not this manager concern |
139 |
| - } |
140 |
| - |
141 |
| - @Override |
142 |
| - public void deliveryComplete(IMqttDeliveryToken token) { |
143 |
| - // nor this manager concern |
144 |
| - } |
145 | 170 | }
|
0 commit comments