From e2979b415a3476d5fd9aac5005191960507801e0 Mon Sep 17 00:00:00 2001 From: CommentSectionScientist Date: Sat, 21 Apr 2018 19:05:27 +0200 Subject: [PATCH] Converted Project to Maven Project. Added Dependency for jssc and mqttv3. Deleted jssc.jar and the copied mqttv3 source files. --- EasyModbus/.classpath | 18 +- EasyModbus/.gitignore | 9 +- EasyModbus/.project | 6 + .../.settings/org.eclipse.jdt.core.prefs | 3 + .../.settings/org.eclipse.m2e.core.prefs | 4 + EasyModbus/lib/jssc.jar | Bin 152500 -> 0 bytes EasyModbus/pom.xml | 42 + .../paho/client/mqttv3/BufferedMessage.java | 42 - .../mqttv3/DisconnectedBufferOptions.java | 91 - .../client/mqttv3/IMqttActionListener.java | 30 - .../paho/client/mqttv3/IMqttAsyncClient.java | 875 --------- .../paho/client/mqttv3/IMqttClient.java | 966 ---------- .../client/mqttv3/IMqttDeliveryToken.java | 40 - .../client/mqttv3/IMqttMessageListener.java | 56 - .../paho/client/mqttv3/IMqttToken.java | 163 -- .../paho/client/mqttv3/MqttAsyncClient.java | 1610 ----------------- .../paho/client/mqttv3/MqttCallback.java | 79 - .../client/mqttv3/MqttCallbackExtended.java | 34 - .../paho/client/mqttv3/MqttClient.java | 735 -------- .../client/mqttv3/MqttClientPersistence.java | 102 -- .../client/mqttv3/MqttConnectOptions.java | 633 ------- .../paho/client/mqttv3/MqttDeliveryToken.java | 53 - .../paho/client/mqttv3/MqttException.java | 239 --- .../paho/client/mqttv3/MqttMessage.java | 246 --- .../paho/client/mqttv3/MqttPersistable.java | 102 -- .../mqttv3/MqttPersistenceException.java | 60 - .../paho/client/mqttv3/MqttPingSender.java | 46 - .../client/mqttv3/MqttSecurityException.java | 51 - .../eclipse/paho/client/mqttv3/MqttToken.java | 101 -- .../eclipse/paho/client/mqttv3/MqttTopic.java | 284 --- .../mqttv3/ScheduledExecutorPingSender.java | 92 - .../paho/client/mqttv3/TimerPingSender.java | 80 - .../client/mqttv3/internal/ClientComms.java | 881 --------- .../mqttv3/internal/ClientDefaults.java | 20 - .../client/mqttv3/internal/ClientState.java | 1401 -------------- .../client/mqttv3/internal/CommsCallback.java | 506 ------ .../client/mqttv3/internal/CommsReceiver.java | 206 --- .../client/mqttv3/internal/CommsSender.java | 190 -- .../mqttv3/internal/CommsTokenStore.java | 254 --- .../internal/ConnectActionListener.java | 200 -- .../mqttv3/internal/DestinationProvider.java | 31 - .../internal/DisconnectedMessageBuffer.java | 128 -- .../mqttv3/internal/ExceptionHelper.java | 62 - .../paho/client/mqttv3/internal/FileLock.java | 96 - .../internal/IDisconnectedBufferCallback.java | 25 - .../mqttv3/internal/MessageCatalog.java | 46 - .../mqttv3/internal/MqttPersistentData.java | 98 - .../client/mqttv3/internal/NetworkModule.java | 35 - .../internal/ResourceBundleCatalog.java | 37 - .../mqttv3/internal/SSLNetworkModule.java | 120 -- .../mqttv3/internal/TCPNetworkModule.java | 146 -- .../paho/client/mqttv3/internal/Token.java | 394 ---- .../mqttv3/internal/nls/logcat.properties | 169 -- .../mqttv3/internal/nls/messages.properties | 37 - .../internal/nls/messages_cs.properties | 35 - .../internal/nls/messages_de.properties | 35 - .../internal/nls/messages_es.properties | 35 - .../internal/nls/messages_fr.properties | 35 - .../internal/nls/messages_hu.properties | 35 - .../internal/nls/messages_it.properties | 35 - .../internal/nls/messages_ja.properties | 35 - .../internal/nls/messages_ko.properties | 35 - .../internal/nls/messages_pl.properties | 35 - .../internal/nls/messages_pt_BR.properties | 35 - .../internal/nls/messages_ru.properties | 35 - .../internal/nls/messages_zh_CN.properties | 35 - .../internal/nls/messages_zh_TW.properties | 35 - .../security/SSLSocketFactoryFactory.java | 1358 -------------- .../security/SimpleBase64Encoder.java | 127 -- .../mqttv3/internal/websocket/Base64.java | 97 - .../ExtendedByteArrayOutputStream.java | 49 - .../websocket/HandshakeFailedException.java | 22 - .../internal/websocket/WebSocketFrame.java | 306 ---- .../websocket/WebSocketHandshake.java | 215 --- .../websocket/WebSocketNetworkModule.java | 105 -- .../internal/websocket/WebSocketReceiver.java | 146 -- .../WebSocketSecureNetworkModule.java | 105 -- .../internal/wire/CountingInputStream.java | 59 - .../client/mqttv3/internal/wire/MqttAck.java | 37 - .../mqttv3/internal/wire/MqttConnack.java | 70 - .../mqttv3/internal/wire/MqttConnect.java | 168 -- .../mqttv3/internal/wire/MqttDisconnect.java | 54 - .../mqttv3/internal/wire/MqttInputStream.java | 152 -- .../internal/wire/MqttOutputStream.java | 94 - .../wire/MqttPersistableWireMessage.java | 67 - .../mqttv3/internal/wire/MqttPingReq.java | 56 - .../mqttv3/internal/wire/MqttPingResp.java | 46 - .../mqttv3/internal/wire/MqttPubAck.java | 52 - .../mqttv3/internal/wire/MqttPubComp.java | 51 - .../mqttv3/internal/wire/MqttPubRec.java | 46 - .../mqttv3/internal/wire/MqttPubRel.java | 64 - .../mqttv3/internal/wire/MqttPublish.java | 181 -- .../internal/wire/MqttReceivedMessage.java | 35 - .../mqttv3/internal/wire/MqttSuback.java | 66 - .../mqttv3/internal/wire/MqttSubscribe.java | 143 -- .../mqttv3/internal/wire/MqttUnsubAck.java | 42 - .../mqttv3/internal/wire/MqttUnsubscribe.java | 119 -- .../mqttv3/internal/wire/MqttWireMessage.java | 359 ---- .../wire/MultiByteArrayInputStream.java | 56 - .../internal/wire/MultiByteInteger.java | 48 - .../client/mqttv3/logging/JSR47Logger.java | 278 --- .../paho/client/mqttv3/logging/Logger.java | 579 ------ .../client/mqttv3/logging/LoggerFactory.java | 155 -- .../mqttv3/logging/SimpleLogFormatter.java | 104 -- .../client/mqttv3/logging/jsr47min.properties | 83 - .../paho/client/mqttv3/logging/package.html | 18 - .../eclipse/paho/client/mqttv3/package.html | 136 -- .../mqttv3/persist/MemoryPersistence.java | 93 - .../persist/MqttDefaultFilePersistence.java | 306 ---- .../mqttv3/persist/PersistanceFileFilter.java | 18 - .../persist/PersistanceFileNameFilter.java | 18 - .../paho/client/mqttv3/persist/package.html | 12 - .../paho/client/mqttv3/util/Debug.java | 183 -- .../paho/client/mqttv3/util/Strings.java | 173 -- .../paho/client/mqttv3/util/package.html | 5 - 115 files changed, 74 insertions(+), 19111 deletions(-) create mode 100644 EasyModbus/.settings/org.eclipse.m2e.core.prefs delete mode 100644 EasyModbus/lib/jssc.jar create mode 100644 EasyModbus/pom.xml delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/BufferedMessage.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/DisconnectedBufferOptions.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttActionListener.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttClient.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttMessageListener.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttToken.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttCallback.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttCallbackExtended.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttClient.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttException.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttMessage.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPersistable.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPingSender.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttSecurityException.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttToken.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttTopic.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/ScheduledExecutorPingSender.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/TimerPingSender.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientComms.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientState.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsSender.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ConnectActionListener.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/DisconnectedMessageBuffer.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/FileLock.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/IDisconnectedBufferCallback.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/Token.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_cs.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_de.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_es.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_fr.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_hu.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_it.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ja.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ko.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pl.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pt_BR.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ru.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_CN.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_TW.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/security/SSLSocketFactoryFactory.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/security/SimpleBase64Encoder.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/Base64.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/HandshakeFailedException.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketFrame.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketHandshake.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketNetworkModule.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketReceiver.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModule.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubAck.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubscribe.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttWireMessage.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteInteger.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/JSR47Logger.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/Logger.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/package.html delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/package.html delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/PersistanceFileFilter.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/PersistanceFileNameFilter.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/package.html delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/util/Debug.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/util/Strings.java delete mode 100644 EasyModbus/src/org/eclipse/paho/client/mqttv3/util/package.html diff --git a/EasyModbus/.classpath b/EasyModbus/.classpath index d95cd7e..f56f9df 100644 --- a/EasyModbus/.classpath +++ b/EasyModbus/.classpath @@ -1,11 +1,21 @@ - + + + - - - + + + + + + + + + + + diff --git a/EasyModbus/.gitignore b/EasyModbus/.gitignore index 852a6f7..92532f8 100644 --- a/EasyModbus/.gitignore +++ b/EasyModbus/.gitignore @@ -1,4 +1,5 @@ -/doc/ -/EasyModbus/ -/nbproject/ -/manifest.mf +/doc/ +/EasyModbus/ +/nbproject/ +/manifest.mf +/target/ diff --git a/EasyModbus/.project b/EasyModbus/.project index 7d794ab..63d02de 100644 --- a/EasyModbus/.project +++ b/EasyModbus/.project @@ -10,8 +10,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature diff --git a/EasyModbus/.settings/org.eclipse.jdt.core.prefs b/EasyModbus/.settings/org.eclipse.jdt.core.prefs index 6aa22b2..8c7b5f8 100644 --- a/EasyModbus/.settings/org.eclipse.jdt.core.prefs +++ b/EasyModbus/.settings/org.eclipse.jdt.core.prefs @@ -8,6 +8,8 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable org.eclipse.jdt.core.compiler.annotation.nullable.secondary= org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.autoboxing=ignore org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning @@ -93,3 +95,4 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/EasyModbus/.settings/org.eclipse.m2e.core.prefs b/EasyModbus/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..14b697b --- /dev/null +++ b/EasyModbus/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/EasyModbus/lib/jssc.jar b/EasyModbus/lib/jssc.jar deleted file mode 100644 index c0ca2979bc1f0ead1d7570d8bafa7c9ebadc9cb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152500 zcmaI81CS`uk}cY{ZQHhO+qP}nwr$&|ZFirxZR<4NxpUvle=~Rft*DHOs92G;Gb%DG zS7z=~kOl^U0sw#j01!`L5drvr4oCnH09jELL0U;UG5YUG004#m3xxm>`wKNCC-9E{ z3orXCD1YU@pt6E;l47FDDs-}9vQdptgA51)uL9ruVz@|J^F-TLjt&uUQ5_Skoc9E> zrRhXp-j`%O=e?&4_sJyL*s+7cRfR2~YO){9GuU6z29DBO>q^ybB-1F6C%S{BTUtWqNxVJ-XJe?APm^L+4t-!g6J?6hQcKh+IH0fk1mH;dsp6F^*w& zh6fleix<%UX~4hQ3i{UoD`#h8`hO3xu{3i2HxTH*TK)rKV`=B=@o#`|{|U4;G}gCw z*8g`{*#E>j+uImAS^m4{{}ej@5wlz+OSjD|aTPj%$`Xpg<=>Fe2we0wPnS z^Fbm_JCce+Cbu2Y(%Z})Pg+Yg%1(1?O=%7hf@V{PCuk{2DW;$U^a7t$+)&zZ+C8S4 zSCXO>U;OD!wIOY!+&`Sj_Ixxundy882EXZxk4S4Q$lf^jBj?>CgZ@gRhkrOG?BlsA z5zl$zW|ws77m0Uz^N#!;*vH>~!uXnyjPJIj>VGl+fZqK|rI&Yjf+XjiFy%QWBIl)D zU`FDlUT{X@y)8pO6OGhMIx~&bOFXmBxmJzTOFnaD?-jI{dUB84IoNC`^%5`gmw1wo z+(|vtN9rYC=qL5kFZz*q3KaQCJrj=POFA>2{f-m?=is4WOu}$xhHjRc9xdFSz>zy~ zVUFzFYKiOIZb?IxoF3Xo(J>l9%7zNbOVEfMF8d`#CS8+)FPpvw183efF){hZ6iu&( zF>B^L$|=W|p2%J2>RQoNNQxYDw0b=$SISmL;$=;2sVn_dXT(yIJVHCqqC70%M$L-rFLi!&(fHI6?8a3NiTC3 zA(CZCkwrKE@>)-6qpHy(hAjWnn!i*-EgwzeyLe6j4upNmnxe;+6>VYZZ>A(Vsa-zvpt8W z2xF10lmb#~?G_GoWt%F%BxNNOc17hBZCsg4DH>QjF>3PpA5Drav2ZR7S+h4{sM&0A zV=JmypHMILA(}6&H&1c8J>i1r~)n58Z|EQ7daN(PNPnR^yyjLB?gq z7od;Aku;gg$hH!=Vl%nD_1aWygM`AeO%&a8@j(<-o4do^)mpzB1$C<8*Qyg&ZaENI zG2Y^m+7uwo5<#yIP?xn&;!Qll2@z@EO!IsXX%LKwE(mWgiYpKf# z4*`{=EpEt#8nkbrJOHrz$Vppd&|(b^-a@RzmZVvX!Q{vru#8x7>1yM99YFNQ@QV99 zHeO6+*t=9Ti77;WJ{hs5!FH`8Kq|-Pj-soHxf+6V=Bz|=JIO- zR&T2C*Cl`$${BYBjZW~~S+sWz#&1qE+5m*f?L=ax;oa?o#hGASR)&p=mxj>mjutFy zG)0Ew)pre2Z5Vx7HH$emk>PY^s(5NMRy;Ln+L~N~+IJ1542fM3mL~jtCnZ^>6w(4` zgR_||R!HMOh+iiu16faorq| zPDQEb3@!+*3hkA5LnW`2`p9A-ddm@7h>%nP!dF9$RhE^F$I@r?>j#r+KTPaIi zY5{B1a8-D1aJezJ%@0yWXCS>QJx@%Y@m2@0Jh@o!OEVEYHK1X=*C@BLNw1~sTl#+C zUt3s4Q+d(9%EQhvY4qYQwO|>>c&&Niv5CvRB7EK$`t8qWT|>$iV6Tb6=L+iI&#S*X z;rVV4X?f0qX>hW{kjqq9b4$UN41x1lRTxy9GCC*7Gs$xg*9=my3_}QcZBK3lkBa_GXD#a)RCMv!6 zk~6p&>l=j^sKXi-Xlb6EZ%7-Oqo<~)n}M6BWqzR|DcfaMqR1X9`+GhQYh+#=WKaJ zMaSi-Oj4JvlA0WnW!ePie^1*0@whbtlRcEhu2uhTnJ);q9I=iaT|8(WdWy4or01bbRn}L z^>MDS`OKz*k|=Yw&{@#&5$q!qg_}(66X3@YE)ZVmURdF7oG_~Yg)(ZPMRw%MtZ6^w z4C`{E$0$Gc{c8G@g7lOxiR(2<+RePu0`fbZSHnO-F|Ev`a?Sj8<5l^03sRT(gXA9z zs($?gXOC+=O6eu8%bc{!%Hhj9sa4Ebnu%`sz7toI%4O@hMVHNu?UwOVCP=n>HS}<1 z_FJ;bnZNnbzykr&bEZo1(Jq)v!lUwb*YllZ`};d zfB6#=38@kOVGr8i)=@D}Qg_oFX> z{pb$+oD9%?X$I&!z>i{&ma`+=k7Td*$r#YScoVdT*!^dA>nnd=7s$-{)cc(?hcD|E zVwHVwl(Hg(Z7NrVY!0)u6VMZHwSWh|dX5Z#Bv6;`bl_L@fVD8D$@!UKQV-iv!S8+N zN$9sm6;k}n-t1u3n0hNy-t!>lt-WUF|bxF0TgSA%X3eXzX})gmBGiXra%3(^b@ z(hA2NK576L)y02iGr>8H! zJb)+?DJeoz64buYU~aLdLOe$b7haBp+wGopr_3*7o*NyX1G}sHDW)N!*~FATd#aNF zgxcKWhDI+iIFTOu3L7?7M(($o&P%T8Gw4a#2HeA%5K;|DatvrVQl&?Pdnd{Nhbh7E zq&<&7007Wn004yliz%XRrgknW7EY#yCjXb8sc6e1DUc8O8wP{$-ys!%;g~ zuQYU=!G%pwL9te2*_Jy?64`Db`GtOiNFSE}o!=uXI*A@%L)3R2fgZgEIN5unu(N<2fEF@B7tEsV+z5fxek<%|?_ib}0va^4*hR|{8>S#@zb zS7ip}r3bRnreP%;4_VfoeVx!i(=b~aQqCK^Oo7CDDK!JTGk ztFRz1<9F6fl%gmdwfGIFWTkbF~>#)62N{DuR(Q16*c3ssd5)My$T*GRwgi5l{h)g~u(D(EP z_V+O;KT!U&?8uTiePb>}OefNP!c7Nwl^w_U5H>1twR8a>@_7R9VQi50ByKJ*k7rlP zxpM?xodNVhC(;lPrcZaMFpsJ{g^yVD1atY_Jm3-18LN%>>=aBA+@8rcLEeRvx=7qQ z$9#h9`;TLu$v651q4qjwJNgp{K)fa`S%!%TeC)ypAbnn7{SgJP)s9^8Zt}vzjY*Ce z#0f%x!wgz88AP-`v@EEVo`X=iXi~&yAPC}v$TIa``@6r6`mZOs&qB|niGDch67fi6 zCw~5)64XDcRcge^(zM_J03d&h8f5>A$H@Gn4D&B;6RPH`prelZv;8`IcufcWC#|V4 zr8G1RMFR;!vWt@8k-xrm8&hz*mg8?+1~tfD6=Hb+Z~yL;lpg_HphFm^K{4E?{sH7U)T4Z zCqRtnLIks|V^6$&xlQUU(IG!X57L+J$vAr%jKoJ}&<&ECdT@+ni+&$4^Hz}a z`VeW5%uSl}EC^osy`ga|v=@U7k+E@Zm>h&hcl!&sw|CR)1K;NI+M-uozy%VeC4LlX z!hwbpAueu{t3Yso7k#5Y*ER%R_V&tld-0bUcy_PzNiDF_xmoxKob~L&(#GaWfABS` z4SmdTk{b^nUz$zyrs0nIx2y2a%lpl&hsW><&u>`mS82?{S{?T1ki*2I5`i6!ExX&> zDXQ!bFHjjZFJa$oCoT2Y?JF5LaU|7hkEKH2$YBHFeBAhuV&39=6J&075u-$tW&y_; zBKzwAfYx}pdpDj!iRl|W{B_&0KSSNcHk44YaAC#3J;0+-X<&IL6zhE~@%< zo*~K;zIW-4Rrir_s2tO+7)hb=BUc{YEIATuIF6Ttxhk|di!QXj);xG8LTBA>jFnwf z$$G@E*D`C3gsj2nyOag%$UuGbeRtmu!YnwOoAZo}7V)IS#f-C}#QPx1N-W|NB7Ydb z*M%RF5AGM<&`|d-uyNzzH*yi*Yx6q4R;F6)YDTfxSB;iqowlyTN<%hDXwns8vt?sC z?CpgvJYpsR}e7GouG5~}f89LHnB z6PWHBu*2^o24JOqS_MXasG9`V2NU2uA5UXta1i&{>h-onqsMJjVvjWqPAyirqX2`a)bIzc#t-ylBKNBw7a`ZY(IajIq7_aJe{_6yDT`56-3)k&^ku55+gQV zFs8D+m$i08pYD8BN84%k1QV}J%AV22v73U%QXfa#AKuzL52^6z^V$1y$5tLkSwEc) zsd*EUT{%06$kk6weC61Vj;eBOQWl$zG=*PQ^`f~kg13Cl3y9-ue;}4~!yuL$1))7y zbHecMkf5PFaA{%!ej6pC->Ee)T+R+QirUlZJNki{tEbejUIq^bT?B{4%Q^P3^vjJ$ z<4XwcJPw!>+`1Y_x$*Mrr)2M+^1V0znwz!Z0BhclFdJgTyTdHspEhV#(!ty+P5Fcg zl5#X}P@)9IRG}*>8=8Pg| z3nSU|Cj;OcqfW5Xg&;LiBkYZ4k_%-SLUe6v_#Dv%$LrC7M+6xK3Tpec3ieCE#Z0W? zo6i^zefX1=cbJJxO48($yG-(7P*&}p1LjNt#~}5oa3d)hAx4t8V~8&T=?aQDBq}#k zNK{!)p&?Eo{3%2)9`UnRr;xrI=eyM$%SO6AXNG3KYEsiynP{wPQi}^gaL`AlA{o%m z#UcsN&I(Rq8Ph5G!>Xp^KjTg~a7tkQs&MXD=_y0k#KP;lN-seyJd=8R#L3rw^l;^G zxuej0S%Wmyf=lA-p^U{RC}oVLCnz7Z;*(A?hmOa*RXuqua8&hU_L`JaSz;=luN3vE zqhFKO@4Y~je&vWt-*U&VWsQpOy?)h1rm8Z|`I7dr`ARIYQu&7Z1j z*`J)BA$`H>=iMCw<@ad40q$oo>~EZQH~0?D{UJZ|@S$@2NOB*I6h-Qym|rR?i`GS1 zdgzyDu#JIq5ojNsXC=`_)L%k2#rjd;XX-`~e$--TwuuGq!-OBX974BI#!uq+B)#N$ zMbbuLbkWLBeHo?v(dg&&2c{q89>L$Fbc?9>bh@bNFtR#qE;E927NKFv)ND{KNW}Wo ztWd1|`-=MhY64^rWCdv3G3Cn^~ z$*i;zPOBt5%fKetwh5swYDRu)uU+rmKRe#LHu%1eeTO2;6*TuttVZ&5c;4@g@e^NuU1~bx zN1~q|@ceWqW8S_8Nc``yjncjcOtLz4k+1KR`Z)8$sDAf;_kjGZ5!9ZG;GT=&Ecb@D z+9&yBbyBUn%fsSI*FTmA#g#AqtoMv)`&P0#L$}tf_l~kUW1oD5k@63Jykvc%cEK}$ zV7Iyyk=ZQ|%5INI!GP`069+Ee`1LdbN@D zpCak+L+BsRc(Xdxk@XEfsz2Yd#D2dI8}hqGK)*9i#1m(6Oa|+}M*kUR)N|wK$D1E^ zl;x?23}3_9-NW2{infom|1&(qcOQ`tHsap#*!}s2$B(x%Y)muq%6i{H<~J~dZ*?I4 z`Q1q7H+Y->a0n^OQyH1>_;{M-A%)yOKTOYh|2H9TAWr@$%m?qDrJ>EFbA1Bv5v}nlKfQ}2)a%%y31D0vt)z3oSVnKs~ ztEqw?GhReo3EFF{5$7S&cHYvR!ckC~2oW<;7cd-zc4%lzvxz$gClV{>d(dH+GBoidVWrJkWN@8HNQu$IVz;e{5hc zE^jB#?Zzl^x^Ab{dg<^wd((SS#a((A?2fteJC@egsWA$7t)tT3_fK43Ep2VDFECqI zZFO(yToJg=A#mk=&rRAlEYBAWcWYg>DeoN#YeQ?V_u&A+mTrq4@wmK77-7kn&;tWQhO{3Tx6|h4f&9|ryQ|>AgDH3v_zign zHzM4<@Ur5zCttUXnT2r!O$t~J8=h3SDuHyUMTCjXp2c~Xz}Ja?d7Ndt>7fee@!YuS zXnYMIbFl(p7rEQb@mry-U5LOMsy-e06B5H|`U*KC znfw$3EYB#tbnyg4R!;oHs8~v^DL4PZE^`%=dCgtla?g7!uBOOb+hX?^LpN5`*eSt??HrpVF0?pfg~*A- zZ6I+-QX@xu^1a&9Xjfx2B_tHFqRE$uzn9YhVR7Xb%PI%9i5*E+OCucY=1e4OgVFhp z0tqE;J82RQZp0Pb7@0xC15Gy=`q;V3;2)5cH8Il}#C#0-u<%;o!I8kk)7a)y=`xH{ zr)uH_>do=JdlKV8TnD_PHrYclHdCu+Tk)65<0Q>CMBuXET=k-vsgvAww6qm7^KqX6 zJFYl2aTlLKmUfnkb8l3Ppf1fZAdczFQ6q1~8Ir@sMmx+mAYx6fea3}-wX1zl94)dv zI0j~XO`CkjMNj$5wq}M<;o|*5Xts^0W}w5PaOAHE1*f2LF(jsi{a70CZV{xMsu-|E zoJCa&kB)6k!Pz?BKS)X2!r0Aa`A$LSi@9>V3njKZ)ME~0nL3N!2x89>23D_K>uvXW z8b8DH6^f!8r$=KiA%{#|7>*4Xqvw#ft}2_&9GX40Sk#f>?!k#uBAvpB^dT<7zQ#G^S-)zh3+)9y~DLNCLry@X%O#*G6gLF9RFlhEiksZ6k zMXW>n79r>tLH}4y>WDu`c-XA6vY*TSfI~d zd8v<{vVlp&w|;FdadsnPZvLTg=S30ZC@rBykD37{v5IggaUE(ANTP>QB5GJNr<9#w zu|dI>xt6@TfDXGZBl%LNTjbp$qEzhIapQ|ek##E10l3GoR4yj=HxtcDEX|NqU71Nr zIt|Wt_+u=JbDLT4VR6G_OQNxKXptjb7HBvR+tJ$OM8-q=9d#%apmSlGt6Ng0_oQ8^P~ZTP> z22JdfRXvJ#_{z^FdG6B+s3>J*sQ|V1DNTIdDjZLdU(xY*Zr^F9mOaDEmx)eHCU|WI zQN=JA#Hq2!or0*=1V}2WRnNarE-y$Q@u(B)zJnLQQtD_Xp|g)%(_#gu-f8E zc(LjXNqKjOmQTAl^es(_xmg`??ssxeiOq=0x>jscexs6nrD|F=C!g=PzGyVz7u~Yi z2Rm|D=G^LgPH3px*mLZlNrl8-U70o4)*&xhamRXR^>}*--gBqRYZw{)bo79_I^_)~ zIHp%$v)ssHon){l;pmO7l=Ub(aw21mX$Bsn<*2+{hB5WkkQrBY3pO(M#5g4S7iiWj!8J7Kp$sDOgb##Vqza|GQ;egGiX)8J&=tK?mH?!&gg{XPs5E@;>g4R zq%@@W?2%BP;P^QYg$I~yA+4D_xDfr~rn_uT-h zLes<*jj4xeZGwBA#Flk#`)d$h4|p_z}RoXGyL^4{NV3M>o0lT%Y27g|3$GMn5Q}2$9$)WS7`Jf{jSxY zN!IU{=w0Tz_kOo~|5Y)5@DESyFYNjL)`foHFK_HG@Ol511%IF~^K{?ugQVMfy7%vs zq}$uSm;ECBzbY3R{lI#U2C@a|xlgEdf~VLf5j~4Qdd2Dw-@z)rj($NX>fS+k2Imc> z48h~`w-j*5bpHAnI)cO_{@FvEbxo3U zwV!pIaL;6%b3)KPl7K%+gQpd3V`<@ON{vpt5PHt8_#y0fnJP3em_^LeuH6yKGJhs~ zP(R%g({pVtpFqzbf2}8Am|L3^r)Zxp*NX~#KN;}Z3_u4yK}(kuo$N8?H0_hDlBI4= zc)0t7O!=VHl2<^CGsPU#;S^ej38@E#(3D0@TO~wsB|K_a^bN~MTNM7UGiWkpOk@e@5fxwDlB_>?M$R)(IE*!xYp{_PcoWh*= z{vif0wg^+Xh-145Jb|Y?mSGpgM92k`;EMbbRJ#b{tL*5;peg+H+GY|=XFSHl$ft@@N zXxf+10~EO;xR>&cxE^-XV&+9Con+Zy;D8PzDD0UDjtA!v9^?b3Cr6$vbeI|-k+1td zt#)%47axc23W~z6zR!c!U~nG8h3$GUY#a*a1DcXL(97}4c^z;R`2&<4aaP06N>KzI zC&li4nC~Z=57R?=D?)KRRVc1&yr=sxQ6Dnkn%7Uim~R~^e=s8)Pf;e>O(^;Sz*&Kf zUmJ^+NB+o6Cq*|KxaaYt5mCI-^=4hYrN-V`I zf>ZsPg(`W3U0a7uUS6r$;HGv`?zKvO4FE&$=ogZmqIE9y0CM7wRQYC3LFNx9%_{=C zhqglLFL6DvKsin66g1W4#d5@AnYKGGz^^D(zgP&`ZIt zK2F>x?wnv6AX@_9k_usV@IV2nU>gE}GgXgVDQnUPrKh9Q3JUa7URR^=?&q()^f~K# zB7+i$HxiV|`RyI?AmG%kPW6%^+vvH*tmKnYY*9kpsZ#S+sWefnjFO9$Dx?MZAwz1A z8dNH=OR1_w^N%xaYOS`QL6;`=fy3!I(tX4m@a{PSELII8Vc}3h}Gx!y#7!7Yr zLi-vtf(f=ORk@BS7b*oRt8hBNE|o>SvPjDmjaEf2G_To`az}Pjhf6n899OD#`IYAF zMyW59;PAubMDlzy^3%KeXc3li`*O;kSkFOm)9$4XY$IStvJW71xp_8YG zwl_`3V@}9?@PusALFMesguhLM@~*>ppvLQgANU0DfRBL5VLK_Xk2Yk+owpL=fgTtI z{O}Xvfgi%j;VHXh$930`@G|qlbW}|R)K<84)&_jAGUfmte-HSeWz21|xplQRE|mg1 z=cd_<(5dTPPpEWoEAKzsIhcqYg9h|N+@%A2PfAkvK)uGmjDO3FUVb%i~jV`CKk7k};w1?~;MZr_AAq>A8vI|ia(6B}3O%Qzwv?&VD zExL7Lz5zSTqZHXaLeky;VKdtKl`XUI+%ivGC!dzbmpxDkuMPEc3Z*>l_ZXx$tB-46 zAaGGE?rV_J?^(j%G%0bx8SazkY?*c1LjxC)rbxv>!({Q^GqZ>oUOMNlOU9#!LE;7^ zZ7=Q1QR<%QLmeClo#Vp`t@^#{);{(;GvrJNKv(26=Up*q{8OQw32NcZR=`M}z=b*= z4(Ng!zz0-NEf3)0##XQ`M{uhmzz%q32CxlOu=)3<99uyJGKD!+0X@)}G{6T_K%Wy( zf%D(q;ce_v0qCXb9AZ@I*W}tg_`W*)x{p+|j}-FhLM&ZAmwE`rbKr0V&_KJ?%;2lz z-B%zjr^#Jvt&zHu!W+M82Kq8BhlCG&Cnp95yG+TpK>Fb^w?y9s_W0ZkHU+x|tK{v@ zAiomDuRX*l{4x+Rq{?h{q;xty_0etXBr*YtDy&SfY)SSNX+nv(}(~d3}r; z1$gBf!Uy(4zCByD%oP&d>3n?FKjB~Z1KzR5RSNW^7Vx?xyXXtvGR!VUo8yl(Y}cHV zuRQY2JpF~8dAo}9kKS4b@4(Mq-!jB`{quHn`jq>gnJ*sSa)0jn#vh4vuRK%XJiaB> zJ$DMSd4Ae%Ac@CWgk8vETIF{z!WOzT+6J34de)g9X3`Sv*tcpcT@4I7AmbsuT;&QWIoo?Cm>GU=d%v!(O`Jw zw^my=_t85He0mZ)rqn@rd059U{vkgH9u?E^+!m~2&%)=CXzo;%!p|zHhEudmkDU@H zL4UtQ{fR5xfeqYf6!x9<&ejs*w4yw&Hbv;oIjLBptWcXjzksn)udYz9E#z}0d#-M_ zkl;-fTj6Zc&b7u(_oGUBWm98IJI2Vful&+`)SewQ*efpE0C#jFJ2%!|H2=9^rh-@M zd%o0`Gvxvq*WXwkNoYe{q$>>QiZQF2I!9@ZJgKIx5Y)Bg9)^}3nhWs(JPltalGPy? zW$GivCtUf;&hFK?i`t$>h%P2_W8z;k)#stx$u(_sa*94AN))x({}!a2GJ5k=;v+vp z9^S?0eRAvy%UmS&@JG6&ANu2^^bP;tn-i)fDTS|p=Vw6vP6S~6Yjfg1<^cZLp>R#! zlt&gs8J%m{gf`WNQbPa*t+!R29KjCKpb^EUPHM9Z7!EJ3>nLei+fI5b_$4muvpmes zdJh{*fnCyst7CPjyh zf9f}I3{SiBd8W4xNn-MBWDad!a^|VRR(OxH*hoVe=CjC8zYiHQH4(3>Z%DP9No*9K zzP|o~HK=VV%|>lni<4qCrp~6|h?h}DOjomYnWof{t4$jEoxOw_|E*o`64q5?*U_iBT^8R8N?6xBSymACQxrTN!@$=(^GI29kGnryGW!6oI$o@ zi=r8~oN{Ifu;7Pq*QIw*Sos1_mY!Cg%p`<^~2Q26l6>Tr@q5^Sf!e2T2S}5;ni);K1a|pus@E zPZ3gMP>VSxF_4<0zlDNxg_fB-HBk^uHdNlsQPN7!Dp60(NRvnbE1QymH3kNVK=p$& z()0Y|TT-)fg48AkCI&_ZhG1ZPaC~5T0LX~30Z5VzjO~9604;33IE(`W0C@g&dA$F{ z1`lIX2Nz3wyML}fR`ptGQw)W7s+=JVK?vfg81)MZBW9D#HYsTyOB;l>?MiBpmY)mH z&|a{T*0Y3FS+8VnVK&92EcYDvsgSF)+s#IBEAxAPCC__~_jPBx^>eP@{~LCX`I8}* zoRYz()KI)H-^!!RP<`k(l5g@L&jWMDP#z1YWz-TwS>s&80iJ5CSF2I?v(E(E!$`4L zsg;IUqG99HBKM;cEj`BsC(%Q2GHzI>5XSHzw8)ILYiHOgjI=Oh^})*$D^5aC6v{)I zwmVF_-YhpgO9qU4O1}vgo#M6NzE2|sa=+<3J@OW?WJPwG_1cT!N@@){OtrA*CWX4y z5DYA7n0IgAgIk}Cg3-2%ihpR7EjFmSaOQqmkw$oF5|k=zw@sJSj!_2wY^(bjj%5YI zRtL|e-|;+=Q>tMndIC_Kv09DSLW2zD6Yh{LDs!!$=?3)Xbb3k@ShnD0NMb_|o4G`5 zCkJdcFTKuP5a~;7;#=6n$;fpy z2<-GRX--3St2o;}%( z553?*4386NJqP_WMs%;uXn(T~>zsRmGg^MM;RrA!y=KaHYxThcl`vO+1fHlaTtd!x zuPj9SaC4p0bfmT?CyjXs4` zvt^#K?YA5<%;^{9HCPIdUaCk(8u!i#V12SJmf$|3SQ>Wabbf%adsk1v@*X%_=!GU? zyZ3JQYX3Kv5f;Q39Th_rD_HBNP{_+)qDA^dWC@({c7d#+w*c2%xrKRYV7kOww| z(r(%F^%&&v4cYI|S7)rFXk+?@M#*_2l1$o8jG|=J=u0Mgg5I&2(v=*7t9^knN|WxkzEoIb1`;)#>-DOV5K|9cz*^~#;0 z_`83T0}=oL`(IZaX8ZqWsu4Wy`i{Q_Q!DX^RS4X8YZlF1H1dqQHif zDNv-;H_4{CNOx(wu`A>q3o2$fij7qs!Z7zNhx6@mAY+&=JL2GbJWP;(qdmK`TUuH& zFniy2p8olsOT|4KXOml3^S#-ccHsUNksELv^^pG3zL`7H8NkPDF!sX^5Alz7IB@~?P5S1Q0 z$+^}jPVKau;x?5_pIFawhBosYe6n$@V?H(*eIXSH%5%k;no5z~Z6CLsn)&c6^xrD- z@|=DS%DD#Teua@zfWF!4IbZNlH@92uv3p6EEnnux^Jr7X+8RHfl#1|9)29UgZU`BF zXFU~0N*RnfPoy`!VDU_viMMLJH0v~ijoxDK&`nh0Wi;pJoT3C}Hu+$FNj2~YEm)?| z6oid1EYD#6BDh4U1>I>7d|9VU%vCYS5*pDcW^7VuI04>t#o)XE&jXo(m$4twPDT#P zK%hjFnS-LrL(cm=4VaXl(+V|kAQlHe)m-cSZgb5{xZ0N;yG}A*WHpBA9XBU!nEJ|W zkTBcx62`9g4ESzwY(GG)A#Sq>w!OUEBBxmsCYCF_`e@;>17)^T93jP(>?tyaN_Wl3 zSXBgAPm80bIL`>IHBuW>O>w;%;lp)yaOub(+nO_XtT0NCPAmUg6(1Iw6fY<$I^?oD z(-26EWTglQNjvpD_dw>>s1M`qmg^5o30W0Wi1fhbo!{wFW1RA9JL!#K zbFNYF<&x{9pGUOStz)MqPaBh+ONq-S%>J4-JexKXzIG7E2;IuOo~#Kb5suV^pdqW< z_%`e|3Ae&blu+I~*}~2_W6=GD(pJ4*+bcbtli!Y0w7p5O4_o84zb1VB^ud4NvA3!b z((PPK#M;JlXqr1f70M1d#^qmn|EFnHc3VWGDVG3;?ds%mZS?fYly8`czl~bHKI!6g zHXTmtRak;QA)+AHn#KfA6kNTxEa3MY*l_lESrw+V|Dqu;Es6W4uM5j?vgX9zkbTmFAbct!CmA)LQ*OXUbBA^Mwem1?%! zcuxAAqEcfc%q#VlC`hSj%iGvusp4BFEJ*k%+fTudURsPODiQX5B{kVnd?pn{-ZeHF z^l!gT7nr=H36tcLoLXKH+f*7^`$0?vBq!l9=)##pY_xHAdTm(t zl*`^QwH&g0=GLwB@4Vb8x}`Pjmkz8qXcjN9gFbB`eoGF{%U*whkcipeh6hlj0N)bG zMx|?pnnB&J7G@55FGn-%fNnfF*&8~7L$q!&O^*jZLvo5qrg%HMU$D2l+*J>1*l|#9 zT1Qp?%3=(G7xH9=ONc_)3@$tl$AEhk9uFb*<}|e-E(#%&$t-trf6;qkh*RXi3tH8i>%0MIK^o3h|f_jIv?i4D6u;#W1WP{MMAV z)}SIWCB@lvyeetUCX-!C|1%)FH0+yDMvq+(14lKElr|I_@nv@1Ec zuA2}*N~vr!+xvRex$C*@d~+$>S$F2(4!$5;dWo*+a2U zO3!v^Eu&}l;7V4{@D7%2-&ALh>Sy(Jcd?1oH)K2+i(T{CZRax=d)s{v%w9&i^+Xhs z5`r(~p$Y)>kSHRw!hI;kRa*uswOCb0c~2Y}Q_RS$%rMgmsRmNGGa zQ<(I90z8vH%fzS6BDC?8nR%>N%Q%_%239F@>$`)gIbD zmQK$BtpNVvs4HUaqWFcWffOEH8LENPFxCRw-J3UNd(a?;uFa2q;H=C0-G8GWtPQR{ zx1=S*-6@vAs=?8c_J0Vwr{GHdsBiR{iETR*Ol)i7iEZ09_r$hsYcj!(cWm3XZM^x{ zb90{e)TwjT-PP4qUDbWjzt8%v)m^py2jQRi9*BB2c~O^O1#?o9 zkm7;{>>-FWYU+%oA_rA;2kafS2MEWMM#quiXj^D^ZN)zb6X`C7n`1R`L5KUiX^Gbi z8|X_7FNjnM#6u)QJs9 z-Xu&`KP-C-Il(l``&u;1!Zo}!c30=k-Ki6aQWK=gZB(kWyWmn>MJ2;|6WKKHnqjLj zY&6C8Q8L~AZ${>;ijl2&mb2!Dh2*oE*UhrOY>?jR{Z|1(CVyXX@i>}0)v((H=lU+k zh$_8f4713zgtHQyw|(A4ahks?*|hr_3yTs~z!)8&Ey4@nquq9BmxrJ#^bN}GQ3 zp$H5~Ku<|H4H^nH^}hOnp$dQ~KZtd}Ziikxe~rtk?N!!wCcan|BI4r)r#V^^;)Bz) z5h-7ai8g{J!ur;mCn3`%RtwbrwuhT}FvM!xW|+~-VjC7=>d_6@CL`dymN45MFZkJR zu}rY1JpZV$ragOh$9tFw2cMxMkXn0%)%2V#pe|C6aVM==HCkYTx1p6&OWb*Y#+v(4 z*u)X3)H76Y=4&(bTpt+lT@hK5E$=s*QO6P@$U-=9DUhQMTbE6cQVZb|YN!Irrc3fh zzCch^gew@8=B^sX2t!`Mgyz_OYL?Bp?mjXcw+`(`e5ih2GPvK(EkX`Cz85w+w-LqY zTgenKxWV(;)^K1zQSL>mIVaF`U-PMi6w>@Jy@pzRKlnj`5fZ-qdZ1uhKc$)d$WFI0 zLFWdyjewZ$J}HtO7P9=VE)h^>*OrLblHm?}0u+K9lh~FwynFk! z2E5ABzwyM&=voo_WGsjb0i$)lk45LS&hHlFC4IN{c(zrfIbnW!s{1m zh+qg3Yq1jf?M)eBN1yzWm^n(m zFD(9C{cjABymz$C=o46D7+HuB4FMv}b%X;XbyhFVzBvKotR_w=g8UBTE?x8SD*G;- zhsC1o$}M?zgpk*+Y#hNN3f4)GnOgP~pk5B|+T15z4kFhd8I_7~qCe57fZAsO;rWfm zDe)sXa_@4W{CmxWAr%Fg1GZkp84zIuH%(BdRy!)D7aQ3juj)h~z+F*6%@;ccw;^d> z<5bI7S62;xvzj*xYNs z)t8MiV%A-V&x%RT3k^7wkFx-etQJ`UkLWX zO{~NHWyVGjU|hB-wfiTf%k8$u6~?$t>j4&Cg!bD9j5s*0QG`0&A9`{u=5Q>FwQUpN z^9*G`94fgc?Z4d_1l@D_dnq|DLLo5^USEnd@&>LZpG-zIO$*Ii@{INOkHPzxXj1c` z#))X4_&Cd%Q1y9B34a-DQOFvv^NUdMS-QxDOnZ%??9osLLb!Q}trsGcs%{9Qd4^Uh z>BG!1I^Qabtsft5juX&0OG^N})LcWim%ZOJC(6_OAT581hd9=|k)t$s0XF^uLMLY9 z{%MGn@N0zty~f)zS6^dg7{Q`)_YzS?m(2?}im1S&mTt@rU)`;h5i)wcbgndl>OI&g z!$zMyaaI>6Lbi1MUa`ccSpJiRXq&m$N*6ZlZ6tA|6-`E|ujA)LSX&5A!5C+4+4t8V z(i((8E!6>KjJ_an+{$K#A5>#_3*ldJpZ>oOnb*m5;ia@vIEARdSA^s^YhArl9a>v= zY1@xe;2yV71S#=COC(vx1RekRPcmUNG1UB#NAf72s0|k)d=t*&yBr$hJ^UB-zns`% zdj+h0Qg{11CL?6O1B{rrV?f2g(B{%qB?m;|HMa)IPAMe*rDn0TS&(-^R1{)dM zaz8cnAc%tkfcZgxSc$Q^q7@vbYg=E@|2JG@pS(XGqJx2D{saRf{(ATRACsfLcCD0^ zMd(==IQ0#kY%RU4a7twqD7q|Oo3QN751-OsQ%{^tZ9WLy>o(Un@1yNI-(v;WTUyU*y6Pc0 zLjWBIv*i|*kTT#&;0G}H4w1><2EJ9`=FVzNb6{U_2GoG*d~YUTB4mPR5{t@2{>k#J z3fn9^wfn6f4DVCw4?-H^5cn9Fj%!VL_O3bmc)DLItPg$O7N2j;H-pt>nLoGIHu~dF zcquS`WLru?ykHDM%5PJh7#S~eL;p;nWkwRFy=Gp9zcZXBpX%n+y*J|9P@EYe*ONop z{!6@X24ZQ@ubSw(2V4bywNNajlO70zto&=yKE_dRo7IMjQ>|lsS^eIgt{*>{4z>Zd zAFm&Hqx_>8r!TsX0`8{siM61|+Z_1;pP$4}d%YlS9naow$CDjFAHZ|pfGUvxcC3SQ z`Gb7@7T&ct0-aw%aAT9xPU)*}$?-=2qJ41nZd#WLKUOE#dvdV)ltbWseUq`{)?3i) z^r`@MUNx~qsy61Cj!^II)w%kT5|u!$PC33BO23DQI=YW(x_8x?<+sUyE9xfK<#gV)CXJUPR`x^x0OnYmsq?v}je@59nIMiz{`7g! zOrLV6K;SRIcY;I0vqTxx7vPVp)2br-#nWHF*(3cv0Wh^Vh(}G2SB)(^@_XN?+t}U> z>O_9b2eX(%N4fJs|5GWCagv;y;jmC?6l73ObM#LZ+Musw=Yw8o2TfuJvDq^S;ypB; z?p5?v&>v8pa7caK_;=O%=5bK-ojCM;cGp8s@s+SE@TUet`0cvp$6VKkRTMVHOUGYg zQ6YY^JLG^z692_;H`o{AfKAdT@4VDLk4Q)W+&MI5^u#5!=7%oQ2g(XC(v9(}-L#gN zmgkdYdC+G!z+CS#VZvbuKk~J}u{PBcJpWggC-0xBxrOr|GuQ||i<9k}S_iscL&L)Q z-={!Ka3usj6@m-Ao?I8!cGnl zC&6_n-?l{-YIpl%2$9zx*B4&n53Wcc{q>;lwMUX`L-39eTN5<@=vJU09%67uw*$US zut*g>PRz3=$(lHiUhd{1-j!Q05nRwphi!ZqCz@;i>6x`b)yCp{=M?tiA|t4j8MtP>&b!BCmIh zOUhA>2~xyLA)$1P43NR<}+=rM=@yg!ig>TpuBvN{Pjq+ljK9zCl5p~Zz3Jce>D$tg>GeSRSo(5)E z0K;q8ry0=s*F||{U0TB3(kzxmS*MC?=(Wn7Bh6f^)#++F3!~V}(OK0c1JlQ@AsFIN z=?<$cO4)&2i?nt}tcRe}mneFbI_Q0d27F(~;-6&!G0C>vUgU|~im+Nvl&ZR$E$W5* zuP|=>f9#evzGa;i9wG@q8J{K}zum*ZZm*xoj*l)RGrJT&mZNdr? z4&OELXR=F^rJJ!mko@IMuMx>bg|>O5oYlRG=Eh8J0iz#wu`*?`HI1CXas&494YSoq zLb}qG3&3^oEEypEVo8trGif;N@@+svoFh#_Y)I|*886L6IHcN4pun-suQtSXQ*o)o zTnK$IAL4uGduxshHZn2FKVRXHcHXy;xO0quix7f8p9lj$M5q5`R|EvT2VR^Aw?IZH&$!uekLavRxe8AOkivss_K!Vd}ag zy(l5jgEherMqu{j8X`l9eG*2!w&-51LgZq+UCWdhq3`-V!fVz}Nn(4=xk5#=ztX#? z!{};0(ZQb8Fq*MgIXj)OnL(?i=!_3cbteG8X`$rKbNeI0Qg73uui98fAt%7OfCqI1 zp&NZ#bBY{0va0u&*+p+A`k2b0|HdTEk zHY?fPNByk%-;aZD7M5$*;d03jiCp&)z31`sshC5poNE^BT#+os8nx$LE5*Gi+os{;5;7674eO__kIldzf3r>b_03 z8vR4q0BuN4)IGz%+G+3xMjePelg6ak?rdhUr+bhN?Zz_hhx!cQjmCM0I4<=frSSyM zV8whGsMZLzSb>BG{za9ovJIxAx$G|{!<<5L4}e$9YF#B?-7Ca5YdEaMtFVRo=m7Hgb&`HFZMMV|+E!q;7X4G1Gj*tHs5rwqYMR|z5B@QpgzG8-F*BZNMkRw`p;oBU~0 zOi%{k>_K{1is6{6?e(ToM53J4=4G~XD(oR^J|wTeD0i<&V0{WGh)vZ%_)s4qZw0?U zpiK#?YUhG}-tZ2-c|xq5p<~u%b|LR8adl(q7@lTLvcjFBX%ew_xQ|2RP7Q52=uY*M zpNwO_`KuJs2!XJ?Hd*M$RxIIgL9VoW&t6|jB(1G8>_V1Yn%t{KI$tar>qa;Zem@Z7 zTgSzNQE}a(fj?X)mHvb|*}s;Of?@1lDZ!fVF3o%>yCgV#AGQOQZ9wt&Gf6E@rr44n zO_KIP3|z}<0xJ5Ui_~UTfT>BSu>McC{LOF&P+dAd`p)wFyh}gkDs6-AWKOU3(=b;* zP3%;wc#pe;2Fo4P8KX9de9t~+N;I^2z!F-tRa0q`DdK$+^H z(R$ymq9Dv}U!|f^FhZxO1h0>it=Lt%-VT-2hAL5o>nz;~;Hi5RsbH6%5->PR{<^ne z?EY~YB76O)*;}}q+DS9zqm9M@mm!Hh^hk2HOx}FkfGDR=zwKK-!^`gY>5QPdn6E%` zgYVG&rFuubygw)D7 zk?3#;u@4OWKIS3U=Qhms9tOZK*IBa&r@!7BZPKbYhWpx98L=?m2=y%gn8~ii{TD$8 z)&|<`JMSM$(2tAtLCRQ@n9a?x+;J_2~vkKRtNGI%_jo#BzJ!9o}uWGs= z!9b4?k!)3SyQPBt$C+DWrC9R_YvcnpByT4pxatl)cjixY@hijQsPhRfhNjA zyh#$FW+i{I(m?3$O-2s~%p7SAQh~#0!vb>Ec%tJ9Ir9jb->)^~Si0M~vFiy3hjP%J z{@FG#@595<(hX)qyUHa$d&ad zd-6SN*VzHQ)VWFybjn^HJ>j3_IgvvUvQ*-=ExtZAX`=Xf=Q?|-UPh$>8tBNpccxur zZZ%8#(nv)2i{shJQk^-g;d-;M75HUctKg5fk0phPv(CbVj_?a5$j{_qf+h@(?Sn}; zr|ne88_z&~Zcx6t{LnszkM#ZC8(tkXCR_Qwi#{X+<_#b)E64CWj@O?y_>F(4JcT_H z&t_2TSIs+`Q>%n==L*r(zVHW@MQPq9NoK$Y_YsZICx#0|>TKfA3Goi&0Yb64O{ofy zhKO}g&NnZ%iVime=)Af7MMXaS*a7nDL`SzrV|~*IX6RVr;o!EE%2Jts&Up9jbfkMo zQ+7hT1V34Gp3@U^rBy0z3*?mfR3mt*H&5?c*!WLBjWBRInd8HUbFg@a(k5f_{FZ?3hJODRJ z^b26vCEd+a+h(to36c2gC3nLE4T_A$SKio)o4 z(uBnjsJmInw2T8K08yw9^I-(It}eW5D)6!U(!ep@lrBrrJt4j_tCV=RVbr@DblG@m?^LeQkDg#C4Cjf z(#F>oYl|ZlBHPU%FUhjX)<#+sn_Nr_a~|Lvw)RNHYHFy_C@*)6gy$#=fKA-~VJr&U zRuK?)$At!SSz8 z^W_i^Ob%(jd{F%uDCVLHOB-l}X->ahBgwc4%nQ!uXLv7@IgAuY(ub(2@>IxKx1TBW zA?G4lftU3n>`@(p!@s8IJO1n zbP~%UjUP@)J9NPKMjCyTjO7$RHV^B)njn?fzOufv?QSwpubghCWlO}4DAY|Adg1ZK zZp8LC7fEdHh)xtfqSsM+`L2(E3=3npDfzWZ_0!H231j5BUwLtj1(hiIE3|u+C-^V? z3{&rpRwI=)y#~}kR2I9hbTgS*#S&NFfSAmCrm5HVQ~%C!J8?nUdG33BUWKmB3XkXP z=BI2=B|NgF=qtxAz;!|d)`=M@6I|wu;IJrb8Tg}rBN(^uHRsDw@6%fA zcD)30v$v|M5L9<3K4aG|=nC9=CbL>&2DLv;KRQ)AO|cOuJ>K*ccnXjlGZFx3IYNRs-u7v9o>b zny=ymN;fZkT*nVTKy4p)4~?mL)kIAA3g0wHY`SbYV3}|gM!ynuVNJn*A|Z8Q(_or0 z6u{G<)2IjNzelk?t3o!*OeupEe-WWm1h|1f9tsn*L%#+({{IkRJwzv*!W+0R*0;ZB zHVj?&pry`)Yn&8VKfEn6p;0hk9yP#1S4zgW^kCWPd&%MO&m0WF&_9~KF{XBxK_4&l zO)L*_shUb69HEh4&FQpFT5jAf>*YPj8HYB8<}qpT?^ckV!cV|oW4kn@sSgX;Z>xJl zLqnnZQGlbzanMn1{aRy_rzGh0f-qHG@MGn*_R){~qj`5Ib@0-@y!9Y(*lHA8zB+TE zd3&HVx6`})9RIk6bB-R_Z{_uxDikP}v*EB#EbzLR?Eakb;qhu7O-#?we403)*~I@` zKe<@-_6!f=cRZY1IXO=KT^seugKzunQ}twrJ#2_cYr1N9WqS54k!XK0)3e?4vPBeO z?5+0Zj(B1hV9uQiV2k}U&G~>j*7?xCSs@8^QCbr_QNnh>_muhI}}y z@2lb5g3yKy+DXsk$pc`qkgDa#SHC+S%uyCJMlQ*IH2Q7&hKqJBVnl_$ZEL>|Ppxc4 zquyCOLI-qO(BWK*?@Ee8+^!RPZV4>lp2k>bQk z6m&YUfoV^}zy}qElfa9C7wz6czgLQM)(nB?|ITDYiF`J4y-My!9TpOBabB-6=Kl2wi@n!b7cc5$?ON#G%x4vmv@Ga>Jc3uidBvPT|3|MEc#^}PTiPhYN=Um|P? zQ4+mC(bb7dLHnkZm?s3*F9#IXL^vq}fX@ z8$!z+;LV+W_fa58H>KSsGH}3m`9B|2`|`o_`o-ia*#mlIZ_zIw)djQPOpxOcO2pUp@%e(_F@ZHqhTKjT8E%h*_KTL5>q^(m1IfYCRqf z7C&SrWrG}Rf?`mRfwfG-sHpmX^v5g1#yVXJ!Yh_fKT4$3VxAt9 zy;DT@?GrVZxf4L=H!1ic#D@P|@%5K_3v?sRVxF0+V7`WqwxF3!vJ^AX$bJbPbBZHu z__Qz5&AwgD=o+!rmmb~+NSvs&8V>GI;>%h^dpjf~Bg!cn4nh(RC#&82)re$>EZGLG z8*4vj7ZTmA`FU~R1?g5%3>aMq_(oYVLn(opN7%&66LJ(%+Kd7W85<$}di2m9C-wD*S8_ty+_7ETF{EF9w+T4bH22{J5hLHyECN$31ZAto(q^nwe`g#i@a040rrE17xSTzR@Fm&{H0~FU-$I zeHPdg>B>u>i9#Sk+ba}XABS}}Q57-XR7xLI!{Q{_XI<&((w_(If;w|cdjW5?Gg;)# z%-F1bZ**^lw)DH1jcqhvd$&q~D*w7eJ^s;0TXdp&#ARjSki+J8H0??b;Z6UJV>e=@ z>T3M$Q~rv=Mv2=7q9%qy!O^_&gOf$IE~;Ty1zo^6>AlvGl#H5f4UuiI^)KTKqH$`G z$&P#*vqN#rH}c3&rW=C-yU}mYyQ=n;XeMQhCzJa?S3>J2rn9wjo_!))sTV?9mOppk zSHs^uq33b8uzcm*D;`gu-AW_8#VE#ejSNL(HGdd6#K(aGSInL7kv7Q>BC<9w9Y3K> z(!i~TowEIwtegqyer$YS`Httyb^?;T18D&(P4w3?CyaC@it;l<%RlH1&MY&a`(0i% z^-i-SyhG)~dBm+ba`#jo|K>EfVH%CE{XPFPSs!GNcqQv*FY(4%CN`D2p4mXtq^nJ~ zQXZoIS5~W9qg3gL8^s>44dYgy?#?WNopPCtwAa-{FgiW{ISo=c=B0!9mBRbvb*&WC zwQ<*Ar{ev`vYO&kd>(J3Y=jMIAz;C+)M7Lh8OQf#PyUMwV*&El(v`JvA2?5BS&Skj zbvLheXswsRkC0P|3P4)0S+Lp6XU%g5WRlIQUO6}X6Yia!5tTjRblpk(4RxbB!?Taz z0bHxV7>I1aLNkppGc|ZC$m<_m^2hDC`LF))b^i=<bS2C%mw|G;`|1l z`+b*(XV10T?@eOVN$xbq>Bnaxw?q?KW<~W%n*-b5_b%L8+?xqp$!2--?!CQ3Ep=#( z48!@0zR1aZQf@A;~17RKd8kU zS1Jc~E;+&vzmVm02tJ!dP#xwUI%oxXga24{cDY@T?kVTOZ*rNN-|nqcdsx+|ULWo= zz*81o@;t*gYMEoiC?ob=7OZazeco{eo@L=>ob@oLyKN#Wr<*YcwQ(hrp%+#ExXV-W zew<}D&r09tck%syl?&Pl-Zni6{o%-!9a%CgJ!?+RU|+B3dU*ApMQpyu}0Q; z?2?r(=BFgOTGm>J3rS|TMGc5?(>ygOew^@a?pe6i`lZ2)(pu1sA7Y`&?h4Q~$Ph^Z z{pUF5h5x!c6p3j8YjoNCIFIsOsYcn>V8k2$v`P+bTT_!Gy;?Lj!Iv_J5Hne>;uWc= z8Ow;g#Hwmp-Xy9zt{m|^DjY{$d5_Ob$*?>imi1*F-2@AgXS3m&zb}2$fjon7`!?E@ z1TA)CzSkw6@!V18y_2_qKum$_F75Tv`?SEexrv!>E_09jVe&$4wv3j(sT?9R8B(@h zf$i*4WM;+wSF)~@EBBq@k$TuR4xO>^JoEC2KjaB5V>Csmy6h5GOwEs5#_^}AeRfvn zF(HGgw_IynBF=1t`3}o4?8@w^T>I?urEfe!Y3^I!u_ z!zb!)o8s%EW z{I+k2M(W?yd8sJfb3d0Q0l?{CLVb8nVdN)&Dz726rBdl1hCi`bL#fF3E3vb;^m|B< zel#6!UVh0DnRAgxcHlY;Z=am4wFUxZoc#=2G^B#xxzDrBl+WGB^ekpK#8=^jK#;A% zQ}%z{NNaH9S0&sa-|>#Dgg^*hybRsqzI+8B;O--Fhu@DJ)gf7)LPC}WjK)=1I`9$G zj*ki#H|3fPkHXx+Be>OtYJ;$NcJcQMn)S-`}HWm@91J?vQ5s<7=EIqoW$%sDXa9 zyPV~NokP`BExRD6)6fSkrJP#sO=Lc{242@+SNs4%BXZSsF&savN`DT zuj3jG)4OlV#_X*3Z!b*AOI}7aKxQ`0fd=)5xOTrdsIKhSVeRBW!Cj5g=R;u}AU5vb zjXg@t$`kM#2J4G=qcPRz!!FG3rsR}6*d1`AP)@Ei`;CykS~4BxeOZU$#PP%$g95)9 zC=uIp1PlO8$4v$--T;38;44O>LQS^01@L211)Wm}I=N!h#d4)Bvhl znFwyXfcIcaTfPd=e3@?RNy>_Wds%hnJk;XT%I?PmiTdO7uHz?9jos8ERE9loigvtm>XIn^#Jc-`mpl+{J^nI1YE)E3L(IzJ+@a;8w9dc z;kmfptUINLTvP(lK1a9tS?#@5-%^nTw>?<2AtukjBa2#oDX-ShW5_k@1Fdgt68f%E z40@l;t`>yO;8Q0_#sicPEw8)IOTNwNCRseuIsJF{4L*z5_r4+t@A$1S3r6PSMtFV8 zSmkOGrj66I-80T(+}G|O*_9F~9LLj-77#Vz09!`8nZgy(Edt{;yL^e;pSj)no(LTn zi;>en<^%h+EKjdY*$27FEd%^sqK=CE%Zi^5FP!#*d7+f=tBf6Xiu@VeO&hiS%`aNQ zlMmotjFD+ajjqt2B{4iJvR(~#_Fq1T=r+dl*hXl~&+_Gin*8+xM2~;}N_*YW50&uo z8<%flH(Yn%9P4J4N;pKVM&RBYnnyn{ud@2E`T_oINC@U}Y6VKWY(AuWSP2^JXg21m z0_*_7spqE@Lb#1MOn28^lSm6l(7Y-CZ?VJheTr6O~%o7!d(R<(Qd|tA}f=8{!xnbRQl7kL|rI-wA=&(~J5^f#YG{iJtF) z-6R&Vu%9p$a_B)ibF|JLfSNe!DAW>oYSyYf%n5KRm`QZ-)&+_5Di{UJerdn+U z4{fU6SpbR~*s%fCan&=$4>FCuby|V#4#85r zH4nPM2{nmSm=x&yEJ>C>l@|1%G!RbZE`QI2fUP}3|=Gd^kvw59v zVjr8StfrE0kE_`h0VX6`U`*dEk0z8(VXb`gyUxu-((`-UFifM6aeN6Bl4TI!XZB$dyQddsM;s+Zlns5Yfn zyw%Tt;P>~2B7<%UkZS~I?qW9{LMhG1TtBKW$8#B7884jfF&}iyHCNiKW_z_439H|p z8FPo7Ok~*kav%1g{bve?IS9;&EEB7P$u&MNSb|XnXSd`L3a`!6#d{xdC3-i>yJaPx zEG6%560bIY_gu>LT*~+8#|HuEEZ_FYxhNz*sfVv%h8Fs0CGUV=DR7LSe#!vlbp5lm zis)Eu6uTfBE{lrz*iWn*;@*NvRIWGsvQw(7dx^jBt{V^+f)KM>c9Qc)*;E7+?{z=j zi>|JvhO~1A2hV%1zy7yS>3TQQ%^3kXU)mE`1~h)d!z|&_tWKmt@KWXRQWvSk!TOXv zdGh|liwe_($JI^~4j_H9l~71feRKQAi4`t34`4VDGi(;D++}&GH{3(QWB~W&Jv&z0 zelN@Xx(OMk{zJPi`|`i>SEQThc!D#~LfhXkvDPl;t-t%Z|Gt9|18O^{ztD-xiq zm^Knwc~#IXdO|+ZvtNj9%qNph<7a8_!v?pLcKb?9$g6wz`^Y!@#7o>A)av5sDVN_* z+=k`6Nii2aZ0wX@H($a9kE}&@bW6$43&(0`?&@wqx4_A#FB0BFOyJe(BVc+8yXT^M z-+y9eB4a=Y>}0SiwGAqlP@Z(Z0;flRkUxM)H90;scz-8d;{Sis9-{lbOdOG$i@DIniQY4mq*IpH-XWbh?0M} zTB8VRSKVMy%)4O4r_hj$kxkURjY1!(_)g0BoZh8n-dBqRt?ukoS5-hmaFZQPAHgp) znGvU;=I>@&wsJ~;cn)Tt8!nu;a%xUjPO6ghy!~LunKel4pbk0mhww_-qO96gz1I;O z<9lD@MTSj&g_G)Uy05|_D=o&*3$?#>h<4@Z$Qo&Y6AaDnyPZAv4p>+isOkgRqQvDMXzi>E?JCXKg>UH zg9%tC5yRJc0j<2j9G|XMxm>$KwAV3Zuf*G_84^yz3gY`3BQJi{56iT)gNb9Yl~1F$ z9v2vcA?89d7lnAngJt4;df|@X6q#j%3K!Ds7)?ruZz>g}@!3}7!RY1UQ}U&FL8S_a zuq@>R2M2k0!*ebb@k6>|>^;65^Ko>kR^&C#Wdk4H{PQS$u~y_=w`Jl^e7RO zde*Xh@*$FZrmE+u9uXXl#O&T}%=Fh7n$r@!9oYyaG7A#f9n4V52N#908$Sc7(XUO> z+Oxao?i9V2SH}!S1bpwTyiajuL^`=4)LGNyzy(&yz13OL6nt-C8O)fnJAL{g4w=(R zUZh|r%&zVsYRS10yG2JN;OO$eVMHCk>4DSNrbF9bmi1 zWdKm<)|(t9f`O^%?b%i(SH<#i2fZf2C%6`3^{GQs(#zRBV|1ATxdkV`N&jJ#`y&!=u{`1;5xgl!$FKV0G$AvK zaQXySkBP>?fZPUY(G8Nu<<#|-g5)>Koc<+1w(J=d4f1l!s*F<6w+Vm_`e`B+4_da; zYM&{sRx)+Wf|-{Ny-RDnpqkD?h|snnoyuazC?3@9jjeDb<^~y-gQraYN?z^ z=Wp=(jLbgEBhcut_w~wJRIQmZ0NPPhhfvr`pCe*m-GipEi;supZG@U!so@pP$kaes zu9XX*-%H)-ym883@(sWAwad9m5RNl82GbZZP_1g=$a4oCF|atvS*t%gXi>QHZ$Trj zj6Vu{B=RbUcDiWKsak%&Fq$OTnWe#s%8w~+^-IJFj? zi+w#v?zsUYb`(EaA6hq|&}@xNd*h zKX`wrZ+euY5Sm;2L<#kz!2V64zZpgm3H6-ne01~VIH|NvZ(evkt%l@E^-f<Z$LpftIau^hXp?)vrIu)F zx$>LOoYtv}EsWlz#Ba$bx+28f2$Ldt9(j?cquwVN^jvQ%LRAgKzNt*Ol4tO4$w!># zDb0mrl40|V-S`avBucoTM1OaGaDX?qm967wM^f_sycjCnAhM!KpOB{oCyg9o0M*rd5Of`^;Oa73bZo*DW4(7catiD{od6swnQvf$ezbIP6Zm^Z>xxXH6 zBW6fUJHIpI5TyZA-JM*`J6vfiE?TNvlzX!ZqiFP5t=J$UE4Ko}WaRm7VMmY$7ow8CduNo<$e~&91 zx^Pcltj&csv_+#rWTTcx-RFp$TZ_}ZL3c%I_IdS{O0ry6N>N4E=vo_kD0#J6f_O_6 zx|YY&HBKzS_hzFbOn?~AWKqs@bE8@8gyZbIQHmJHsHU9bom&pwz zCo-ut`M0xHP~%4^Wwq0dW!7QVFT6HB_PSS4Q#8+uOOs`XiDl&Y{g!tbN4(zA?gyos zQoM_l+EK2VvCP*$wd?9~ojFR#ITVc&Jtvma5|_>(@H0GPmW#;imcMa4I8P2F^c`5T zWNc^E<0kf*@h+LqKN8gAX7-if+l(FNjC&R?u{9_vq(G;vAB*Ry-3qvV z=B?8Zw*78DgMd7>*^5hBoLm0!-ZaikD4%c!2UoGkyOSwUdbZJI$-AE|^7#ksQB}QD ztlWEIwc!$Dy0aH)9Q2%ZPA#PvYm>UV|7=FqtMbd{!61$!abuUR632{H*VGwzU>S+a zs#I}5HTHdwSFG}k3VC$!0m zu;dxFA>zS5Y@`@5gD(&?@HqcS5Z+G7ST};@Xa!*(>(m4#_|be?q-|3LGSkozk&Lh6~5oyc_~SmA3wDX9SUOOuh~?Fph}Zj+Hp z5ICwWeZ+JumHMOqO%w-!M4n6;@E`$qJuDhFg{vE$^p_CFtRK*#Y?mr9%ydpUM|_^2 zpVi@nPWWKU8>*DVAGLMy%;w06@2kF{J3-PD4^;9NZyO^C{tAu{P5pK#!abky3-rla z=*jVP;fZ;=dY-t1!#&;)`l{3J!{myLk-@GW3`(k-zl^~kW~5y?1wmyd^9MbX1y^J( zQIqRr;TP7}P_!BY&nPLAlDbaZ=3g86!Wdnu0@?(B1TvvEu@HVO|Bmwz;GV_bKmx^& zF7~rgLt%L+7yc|}BR#06bH%x5?N#xP#TerS;!&c`QKmO)#o^RbH8`3?}@=M&rd&?e|O1+KVujpP#>@Z8vn^a1;xz@3rGD_sb1w__+Puz0E{XTpRvy~7l#EiJeH@qY9C`_Qs`+DAiUJMnlase+_NCsDiq*#L2ARfT zxwR;t$R$H5^-DV&y}wp%M}7Dnl#ym_fF^O=hVJNLZ)6DgsU6^%s3LDYh7Stq4(!6O zFBRu}v`=XJcK&s2We@=pHpHw+0;?9oMTS)~b?pDVQXZZ*EVYw!$*D|GH~i!Lt^^)+ zb{2+wm(4NYf@Y;k4FBD^s5KB)d)Ew{x9+B}g!|$^*tT`KYr=X?*4|W`_;NxVsIutq zISZ5DRng2+d;yAab`#x5fv;*B!VH>v|38F%bx<8a_a*KUAh-t$uE8CGOOW93kYEY! z`fzu5x8M-m9`5e$t`GMI%ePXs_1mr8ny#Aa+tX9?S5No3=bnSIvU_^?1e!NOUom~>H!Pk?vM!|kLSoT~uSZdjuQ0Hu7N%LqAL93w=fbRl zR7!VOzTNcSx3~1Bu~*OR0nD^13?!munZ^VAG>rOZUZhq1QN^vG?{zsE<&sCe;EJpu)I%IqIcwkGkWmJE# zice8+U7|oIx^M^Z$0Cw?0*Ld$5y1cxUBN@7?@Qcov{&5sqgen!`3e^1?M=?DsALK& z(WCp`S9FNV7UKC1{fw6qMJduJx@_m_cY~gfi8yINNnIg4ETc<4bfJsn1ziN7xcc=EIk!l{rY&EDi zx2K#yVS{HnE+JGz`STI61_;xSMFDXnM%8E3?%i##kO(qH$)9-F73Kk!Ovow6B)D=Q z7^92qV{HUgNx$~|PM5Llos6Esme2Zvl#vOh*h;J!8mIaOUse*e_GO^<6<##Gz}_X% z$z@jRK(LS(lhR3lfgasZ#Y+@Hz3IuWx)E-h-dTu`{fwZBj5?HQDBy>3CZCbD_qu>x z`8doZnX`6+?0SQ&PoZ8ffJ@P7?jiG6$UXvh4@X)$?>EeUnF(J0pb6v6_A^7vW6D5< z(sPdsuaj2auVunR+RC*74)~;rF=ti>Z^K%}Wd`T)24GTx==TSaF1c8QvmGRX5_Ye$?jzL>DAVIlTTkm978+6(YT z%`soC){w$W{AEMu;UQX3AxfI^Y{s;@fkl_1Cgk3DrTLf@IXFUbaXJO;1o-=TjbG(u z3(*N|Nc@{01uS|ly6(@9c?Uf=s-la()5c;RBOFR+>-b%9S-se=7Cp|jMFr^$&c9qZgaM{A;}K(ySu-LhQ0g6 zacwHW&ixT^y&mQJ)86Ucf0ukqy!meYRr5x8kzT)Z5aVe}M27buGhXBHr@S zsE))(6{FWL9IbzI>mf1Q653Kkt)V{65Bmo^SR|`hGUGve3D_0{_238W1NJ! z9sAynmKhE1rrf;lE@6@)omh)1rlF1wmjG(Qh3cAK3G4V+_ot zW+0W(6P9z;w#W?ANvHUbd2GXjRdSza|BAu5{(1Vsk17dmF52WDg2e^<+_7)%9b7zk zJLg=&^`gNXseLwguIQ`wSDt(($rhac7zfr7v&7BZSFC;3ueN!wr2A@#l!^6RW75Oo zEm)?MBUzTO7}!EQlUXmUX|wv}JwW!%w2Y(-=evqEwt%YPk+g14Hi13VFtO=6xuI)B zN!x|rKD>W^HvcR0(lBl5qR64!!}&L|_$={f@lprvI*!PeO_FEm)|c2s%ocb1O3FFu z>#y3=V(t7UyqvQcOAWTJiwIM9?~CwU^QgshM$BkYag8(rq~NY-P1huN!atMgBOEoT zp=SZJ2TmG6z8ZL1bpxmPlW$qEIX|*x!CLQai09K)VzXI?pK)VeV@4c$hnnw+ky;%$ ztDn)H=#p5rSKVfRZUjmL<%2Sl+$=hSrY`=baS5Wbb}p`uFL=e-&|8A!fO4iq(bX!< zN`5cg4a1#(ehMn`py$|7M*V!T@cxmOU5HqjfXgq~sHOJA&sqL#>4sZYQLkhvq&5v4 z%xkKX4#8L6FWO)uijhmghyij+m`=b`xvhA2od*=S&+0B2fW0A1O4eVxzg)7o z>pPNjd1T=ISIy37Pc3FIKiPoAgLNV0k$%7n|{a<|uYfwGim_F>jeR-jvDE^ZYrwLVU%!TYm2E0N4KvKFc` zbkz5qvso9(mMEoFFFON`Z$j#=K#yN=#w&G_PD@_x!!SjO2+6(S{!{r*Nm|l?=4zBSm8pKB4t7m#3qn3`=D~X~1}pfacb%DE zZmY4aw)KQ9c(WzOvxYJmb*T_yUL4L!oLxQDikq$A22wcBgq=KhTCHQA5o8500X;)iqDBE*SH9xw2e!YTWp6(Fr5e$Gr!F~8H4;dPbwA2??n<;qTszbA{K7wCCT$956cIl_ieWK zh7+VG(seh>UN1ON?=bO%Z*N-encv?AqN5!TfNGm9Z(#NPc1O+m<9@^vAGTQ>W=?}S zp~n?OWm|$X-ngZg-s2XUB5*dp+h)K<<)p}7+2d%qS~g~9_M4&ccrm%q%j0qD%i9$I z<*dcMRdX!IVeu=%8Q;d4k2BQz8m4}^lca0m`+W_VHM>z03S^V7&<&Ba4 zh?M?p(AjAf?v_XOQY20M{ca%aSU~NFUMY``E-3qn{uZt9QdtK@NDt+?PSOVt|NWo; zdpHZj7?v7kz-3*+Q)f5*D|M|Xp9Ie8q!UU%>8eS zc`-4Qv+6$XjsBhDIBr>3rW3ocPBH}L%zt4sj)SfF*-tgsYi#>K` z69#efAY?PQZNAU5vXEh&7*!9GZ2wMCNF!|!gzWd^rv`J>B0&DVKq-VN23+*Y+=7xV zahLMr#d_|ITn!j^r3Q~rzpi{dQd8OE~kg^BvzGpG)@}{^&=eI zRaM#|x%c{4%U*i$+3NpQ+G=lr${pT3>n&r|dT{f-*=XBn6Rc@uVS-DS^9R}SemZE! z>xJomBIH13&S%J~HjV4wA=c+S)N)O*7=q!(!WJ-2iMU+)0tWC3wR zJhy7$5Yw#}I?u_Qf;uqexhF<1!SnoZUxR3bI6cr5A4Dk)cJ&(A7lPA54=~59VOr|B?%fF5#4SmO?R-gJdwD#nuMLDgrYCb ztLQDV`0THbQhi=QStGXkU|w>_cKKnCpJ5lPoeZc^E&YV8MkQ6fntltDI}nz@oTOAg zp_6^4USk{owV_+aZT***%Eh1S`4B!I=DE|JC_m9CRofpYi!)PG1W0i#wLSCClv;)@ z5|@EQ?cE3>c07S>-?(@@i7D-f+2E04+!yg3x?8^)w*87N&RF)0OjJVmLwhoFYulK@?Sog|B4o#GZljO&z(*pCVI`QxL0+GB z3J~u zJVUGEcR)#gc?@q9!!rqPhxYeQvG2AjA7fq_>bD;OweRoo;cuDY!iwKFi}N@C=G%U} zqx{J0Hc|FjSTf9sI542zgFpU0)nzo|VyMC1VMF}UcGy`Q{mS`kJV9*U-uQQtUPk-S zttQH7%A`=7SG+#I_0MX>f7tehs~ zo|uiJgM#vo$m{1kgatit!jhBDUAf+eSSRc8+~Y4^8Lw1MUBs81Loby62)Zwhxi60L zH(QrmEQ7TDVQ#VR1L00>Ynp#WKx%?|c_Qgo<=m`&q_tRL#WZX`E1_)~S&N|{&Ld~G zeusX~(OPE^h+x)h8V)Z67{XraHolVluDot+*Vc2zv&3e9W!44HQ+7ND9P2GQH)QoZ zYqS(u(=Ix9e1cn_TKGdXT{DkXo|hMq%m2$`bO*QQ#|m`ncIPP@f;mvknRF3CX)k=k zPL&WNf=qn1#+RN}EB~E;HxrJAyRc2_rqtt+0$q-3v|t))wqDk0 zxKk}e#$hBvbueEJOh;YOONxPc^2V(!BYeMAI*{~KFpkb#z*{N2lj`%!ucG8i?%TQ? zGh081H?VE6=&nQ{5!d+`o`0!YRS*=+u-N6mchtv|UmXv>=(uUAqEB{##p-!lE!zFD zS)|glX13LemfWa@a<9oIX>R#G`;|k2JZrknvd>gy`SR#9h>97h_!`J(b|xi}rN?6y zx!wS81wyJraqV+m6esL868iP9rWJHE{RTkM8_eLS4J@oUqdUYNSQXxLC;$3)Xj*vkCh|RBveX zo|0UsW^8^~e3Mbj_)BhU%$;r_fpa7DgAq<3!tkF?s`pr@^x*3$Hs&>H`I&Zf^CL^z zhvZG2(^WoGn-0rqApA6u=K?N;AK`L1;XArDK8F6CbakTVJPiT9N{o=a$fMNZ;gPb! z3AnEC?SyVNM`d=iq@weTSM^lBrJ(qrxGg~((l zbs>242Mj*ikv95zTR+asF_yMX49{d_(o(-Fiw1`x)3*V+fBmXz71Yv9pY|CUC>*+u zoIkNTxKom694^I#_v;I#-tC!}vujW=X~bFZi50-Z{KhRy4wp{+Tl$3(7Ok){ynfuD zTZ?$c6H4E>8ufLGatT#`M_ALgXYYh`>?mnQe;U)N>ImRG4NZF4+MP$+bX2SRy=nd8 z_-}1WK&v=WFDh26$7HcsN9fGOm--`Q(z)RU=5-$Ng-87ht)dR3fgJ=o=D-C9!GH0% zP_4Fz7$v$D_2#hgm@<@Ia-q(q&WT~3c_4KXJCgg*UZ!iywLvtptIXY+f7E*u16<;c z5(Mfnez(60h1jjD0rM_qFxFw3vK&9?C3lTX2~)*!eumO&D(RFP6yOfH3(Q({oS@w` zmnu!do$60p5>#{M9M^k`Wl2RT?M$GXT=ZoM#0jV|Di9wkVOE(QC^7OJC{6OZ7B8d= zE1fdv0ZkGLiDz3+94*EPz&`}$Ep|b<5{kRdRr&nR0Cn>Nx2&J)C0D2?>T?|)q-i=F zSJOhLh5+rBm4=aqmAjhJHi3PhE0<;GsO=^z$Y}W*WwxQoV^EKFf>?%*4gXJX!JIRJ?5Te4AQioY5c{jq^v<@Znu3i9rth4<@dHfzaX-L=(3aWpgWsKv1~<1 z(n!nM`m69U?+ixZQS%4Ur=cadY)F9XzwN=;uHP|Jb%Z%|rY@N$4GAU`KpmB z84!C&v|;jzLl@Im0W}ut8;T_}Vr70eE@Ue3XMu&C=h<8M0HRPmBF^Ba*5_;Wl3Ygwv{i} zc_)%3wY(0D_jL_Sg8pH7 zh~1;|?RcJk{a}t)+#qS;S&u+AE3Al1|KM%S@4X4Uctd+nELTrs3l;**@F#+3n&Ls4 z74hUFgtni*;pe?h$xLe?WkF#s#$LB3UT9M{C3`9u5&!&i94;*gdo|z7n6dQaMh=n z>z%l4`jOfTX>&^2jg)Lbd50d(tE9GmVLkI{zltN7WVS_i!L>i(W5gHV_1#_HXW7rNVs`!A_qgl>@E6T6-07egKzLkn!3Q+V#AjwxJ_^$JEtyAM=AyMw zrig?GxHXiBkEHh36Hq#coCnz#V1@S;}4B|~Ss-Gno_Ds7`>&f<>$(@pBOBIutxflGQJ z87$Nua#aulB?lnjKKxT@6rtTe%&3msKTHnb$9*4Tf1|hitoHDy5F+(*%WEHI9G*~m z&Pi+aneH@+Nj-kjKXq~dkAcNRt5VRtPPld0q-m?$Q~hFZmW}Gyj>9HmDJsW!nOgI_ zeC*`NP=2?o_dkcCRVDhi{Zq}9^NFjrq9O?5h!N^0LPx+a^hO_Y#0t1=)*;!l!LDgd zmScj;dM`IWFEhVm7&Dm_p3v24ZOcR1Yiux<2*Mv(4rV%8*PbTV$e}NhGIWyEA}%Zx zveZ{d%l}E>ycmS^MhZ4t`#ZuubtUc(Gu^9xT}RWPcGJG9yM-}iXW=()sDHY=$;R@g zMorw2&e(%pJ{tNgT20|e44OwL(-iL9>yKeL3d!_7=H_LI9_ktY*dbRJ?IMo*Vy5p& zG;kQPwIPdb-kZi zwVeJYKJ;tX?3ZaO|1diAD+Qs*(h35Lkt-hoX}~|ie07Kr%nai+qNwuXR6rs?eh_yZ z$tfWC4aaQ(I!jN?F2j0kV^am_kYac&B{8RE#-jI6dvay7y9GMuP0Nx;YBo77OP>EW z16}Tg&Pi!VKk8*Xh$iFi@2@fP*`v(D-vFfZAT~|b^5>?cuMpP0tKBtmftCPOtH$KC zx`^>cV@KfdK|RJUUav;SsNN9=ev%|7cX8YJs*^g{?C+(W6s@bgMmp-c1xLE_st^1* zt=lq%tB{@LQ*!^qCqY?M}0$;P$Jstj6xb_O9pUqj#?vjaW`jgoDY zBFxm&MpLX5q^mIQQUZNgeOTX>!xiKiGI5j3NC$qcm3D*Yw|;&$-*m6@bUACYaoLPv z?xbXRJpLQcvJC3E_HVNo4USqzk3H&ubqVQ`V?1lKaG~>E z-#_P~o2{dtxIp4k8@fQKd6Tki1sA|3(N8>+=DRUlcgon$CqMeAE;h*1|zX##-(14}wQMV2%7_rQ&B2Ee>7Yd)gzOu%+9J8%Pu=+P(6l&``0E zMT@huDwt}CAK*r+`Qt|G>#J4=Ob~66sdp}YWK-x|DE>-1j2Jn`jE>2VEtH|6UKq+L zwD;lpUjJeQhd+NQY9@47_sTKd7U#M2`@C5R^)lbcjPCCG2M zPOxhW!1a0}AfR=9Opq!#ii0LBO<5N=Tz3~?ZWkbzy-$kZQ$|?hvW{mzl5T!&mBraC zWzuN?E6+z05_u;=LzwLN0ldl4%#ZJBZy3k!GgrPZJ(4dB)d>z!jAjfuLg235c7q|w zh^V74XdJ>C^;f%#L$10BMg;@g3fb%SD3*K6;ADa^4FJc7AGMSb7T2{^j`38*_TtJr zZ<6_2d3F|>H4)JIeRFoG`ma?_4lV?yBOg#0I|rV+EcB!NMyAlc4DMQL@gL@F16CjBtlJ7o`WO~O0TK{E z#c}3`7-1t1`VznxsqAxG{h(o&-}~BpqLv?@_z;`P6n>dko`?L~cpg8KD=v{U~tq}|%c1Yqk3aAW=7b!zSGOjv%~Sbl`gb&c%Q zG+jW-9*w4KhM{%N#uD)C$jH(Az9By~CN#K<8BGk^#kk1g&k|xynQBbu*7bPMihq71aW4(FUL~$j08p2x{7Ws*x7C*}Ce?Rk7qUzo zZO~R+T^cqu5Xbl9FY)+Wd(yR(nd45fhwQo+tn$2X#5dZmzwo$1WS7A*ZMg;aT!!c-jJ0C(e-tX5%f}5oE)qR1QW=W# zE1LsFWp#z;HgAAL5Nz@f%Beg$qn zZ>o;T5BwQ*-o15L7l2vWtelH7XSg*AYQbRPmS$QW`z;vVV=7gfitmB9Xr9{Z)+&y9 zZWILVGKeYErZ~IwGmjWz5*BP?)kkbR58b%sn%A_F@ga^Z9-=4lymsi}F4F}dk@*S?oUNYluk2PxXdn*k*F7wdy z+CBAa(zkvZLF(KB#jYWNGfmJYmi62h zlo6KN%`JUkgvCwPmky=Pi({Z}~~z*%SJtHWBmLC#hAfTr*tko5o^W8G7~ zXQn&*Cds6V{Tn-eOOZdAX7lWd!XOqmi`KxKw&2>!_iAdH)X;IW=*CoXq;u=uc*F45 zhStPWdZUoz3*FLgbCeK|tANV$ws!Wb|3^cq(wT%+Jcz5g; z1irZ`MkgK4%iqjo`{X8cM~M;1ppd)F7d3fdeY@yGS1{pif3wg6Z+6bblc$jRx;0<; z8Dx0%AY_2mXcuGYnSRUdOb)h!`otaCgqn-{)OMZo|?@i7yK;&Z6L!*@j5@HheN%!5a zHkOoQsSNHS$5ul5=RVGa%9`wrc*V3WS9D|XBtvHxkhN*Y@@j^hu~Sq*4_c!=%-9a< zhJ<%eeFc!(B(`U`n9`2dV?f(xI6E8rF3ry>$Sr*Y>NdzD3GfVlkq0SWE-rb;C_P1A z>)Avz0{L(TI)bn61$sblI4_Avbo;I=lj?}TOf?>iS#RX_Y=?p|{Ys5&O3xvLGScD2 zsqAR3Gy3FP4>5TftN`NW^fF}6D~>Z<^G#@Pd@FZGF;C|*$U{QS6Ubaf$kHoJ?`~SI z!{=wj#`}`g=tSUTbv88sA*K#>lhhFG%`*mEuv0jeeB(bNG@g8VJ>$Dwb?e~l)5z_F z@h-z;m^MG!75w_TasN~*^`lJ87Pl8PXIy}k!zuF!=W(x-=4Y>@3~5v!xhL7ZlzeE$CN~3i z#iOzVFx3(umhqGM#`eGI1Jrbfb;rcLq;kOrGVvc~xWZGN_u+3lt^_+1BixnDZdrEM zIjc9{X7!XB)>ZbM+$u*x%^%FSGAe#SwN$tU(hnV%rL6zjZt&E8hm5sZR(d=Xe`@1d z8aj!amA7EJZjj?kzviiq!@H_Y+?411;@TD(ETEP5El@}vgVi|Vs<|l2CLXoAvcZa; z#(jx+>&iQfk3wzOlt}&C{LsaZla~)TTD*B@a#*H6CA6kJg8MY4$#aN zwyV7}+z)2^t0fY9qx>1+&6GZCfLp(69~iD#mip|D?UK9kchvhG&;1tans;6=QM!M9 z`?WSnzPoN;?IUFzIgL2UYJBE}CH!>yZHZkg79|;Su66n6oei)3hIP;%SA=*p42*M& zF*JE2!Xw1?1_*iEC0Xd!^q+r0wI0eaM;U8QSU+}@F9hvd;dG5odRJ$V^o?A5)tgg`p zjo0%GDqRT$rN@EkS9oyl6P-JS&!nsk)h~70-hqe6Wqx~*q&&xqKV8~i+y?=RZYr4H zIBrmN7@xfY7fo@zMn1T$q24t2e%D^be(pHH_5iBRA6a4#uKC(VjUe*Z$jZ$`3p1du zi0I9=-V1YI9wh%~c{z>={3fSUwX(ChZjS!+p@yW-eUN|ebyX*BCnu)Jhw+Z9C&)tx z^Ie?mP1^m@{Q43w+Pj$&0v_+hJlL*Vy~)bi-#!eBx$z!U4mItuboZ{{(2ChTxIWj<DOs`nc_q=`m{hK_?Ci=!7Y2mBD@VlDew^oCVYJT$p`LvP>20 zP0D6|AelsU2^6!c;hUOSJ%k>_yG%@V+}9*74soi3XENtT<__<=eGlSJ(OA z==9I!>0hVxv#%c#i@VI$m6;jC6SS*f07KCr4JhWl)6Hx}A;Xf>y7<5Ep8vPb{d;)} z;rm(e`$c-MD)J~bQ%a4hcvIj{Y?g!?ogN+JQ|eneE!I3h4=pk*c2z=6Qtq|%rs0Iz zja<1BtFyAzjNM@V6~H_NIn(FmF$)wB5!A$i|zb!-p1|Z{OkUnT?Hfvim ziQ)x)W(YuP4?Js8-nwmwy>9LT_vnqFs%Cou<+pCx3$9sJ&0)-vk-b=ck#y4B8L|v= zm*!kTum{_to8X0G1~y`!S=kSq?)^wfzstXx{)(5a64E-~;*VEYHT=N=2kh)4kH(V$;8J8C}!J zoEB+8T+mT#w-{{4&`7mHqWs-cpeOF{y*h8QPA_6bsjmLb9j?MC&zG6`gviGG+v4j8 zo2ZY9KYsrFj^Tl=KQKKD7=Z{Zr4Cf?1+E7Q7f* zp5mTT&|du>e8B6*r;BJM69wfjL^%ej*xntwC4|!kXEAz8g z`I%b)=BDxQ5lP*t3wE~6o;n!CWau_ytURFp63SI#`R}H_4da;e`!=7ma5SeIF&-L3 zh-jzLz1Q`%9n^SnZ6b@}x&Pt+s_JZFjl-4}wT;3m?jDzXguPV?$+Mp2O)OD=Iffdy_suVMyr>5OtDdD&pzOs^}a2ndDrr!vxpo{kx zTc5T;Gu9C#0n564FF?*fvewc~X-u!oh;;c*MNdqLdwg^v?}813-%I~otyL;BBkoQ; z*iZQKU%V{SiK$gmKYT2^WT2pE|9^1C2X*j&0vduocArg?t3Ay)aFC6BzfMbLHs?|d z!3{Z3g>CF1Y8nMt1!|499*waP8p?1Wry;u(_Hs$Eq*Y>e0 z`lz$k#obbdYDVS5g4J1JGOK;B@pO_qQ0PYvQS!BGV6fOJeDC@Be&AX5>A11(<_hkW z$eV0jS&%tyf%t0lFu+Eb`y~6mJO@*c{C6fy=i<}H9I2H^hpmgd8Yx*)3@NgsQ&6kDcD+pOW*hHL8NvFEd$xki%x}0f@{K|Myp~#;qJK8t zXR`%fSy@>DF86ay0f4v5?^UpS`@-Rn4yd0PT<#(kK=skZlfxIYcAMTmF0Hijl@6?L zX*rEN`8nm*Q|quR+GFfz5^&{d=X1+=HqQBLPC-<6Z{H)5E42KpjF)6j_{C=Z?bq=- zH*pShw9@r5TdSU)oljSL<3wlU!|Pv6b&CJb2I;i?rzog;g`&lOA1?hX<5(tSC*{Hi zVQYpHUV>CPEqaKLmQ?t|Tv!16R`=WU3-PDLA5*hfT%^-i1^)b8I)&Lp%$g!3ccq6? zn59?K>nBgsu5*l<@HGHtRq}XD*7&`G2x?7{k;~Qn`1U%{a7~!L@A3_qmFxIVxJmRB zBj@n(O*sF`wvz$84}Z(nR49_j1AAFm)ulHZF*JI|Nm<~b+<%Tb_ysy)N-4j64|g%> zx+3X*V(@*Tu(AWe!Q4l4^g{b@m5r|3@ZBw#WX z)Ox;CL%eN@#JX!I+~dpk>Fm0U;i|iZD6>3|k0fLbah~^Niy>Nh`;{KF5Pb>kgP_>s zXR#aAjxsVWEQAu$_)Eyq}^Benq{UlBX`gp zXCkA0D(oi9YkVoC5Ot4y2Li0b=dtLRFTT6MjUH@x@f!$(T=+5JBl!YVqBzDSmc$(> z&m8>9@)i2&pO;}Z@IwP92W!XSY2zW8(J6|1__A+K!pCTG5?fu4%^&1YuRKA7p!ml- z_*-Y_K~uLF?5ym`hzo+T1j##?`-mdSQ?_n{D)~azLQbkqIdhYHm-F^g`#$8O7?daD zp3SeqxtKGAiOO1tS7-0O}qt5^=J+L{^RE_`Q-@|zQ1zpwS#7`cD)@6@NHx|se@@$bHPdbSsM7Gx;SKrR5{ zmA-TAJJj~qi=)$ytZ?muO%b*y-QCj`-H5?(i<- z*%LeIIc-?V0$~oXvZsHVPhW86%pcQ(R1ECUDaRH}{Xo(EEh!V0AiwKlP~ISaJRrvP z`0aAMhCv@keP1W8^+Bg)#e2NuUT?MXem1*`R29&9AI)!PttI54Pqyo6?p|SGC6GPJ zIBm2YiH0lP3f~?13Qx!oGelmXl&*qLSL@@6bwU3Sc=NrT=Yo9F$=+#*|HvWWM=P;) zQr0(GZJ?Up6-dj&Q>o)M^cxL=l*qJ&tGBD(muGVKx&apipqf2^9;t5Lb~Ay|_oFmk z8&Na=$r2)m=v3__@h-=TR-tZaVJhO)?0_`E2|Ns$jI|jynBHDDmuafH;R2p0%mwn9 zJ}z@kZ^OEg_MD=(1A$W6p1?3Pa~k)H%lO}^wfYwVI-?F#f2iWu!axpN6M~tY%J({m z>?M9}m=3=}7e|_?)oS%P<`t`+H0_1@VEN+#Ge=U)nTaxHi z7&_9{kv*mZehPSa%8smKtlEzdn@hYmUsqHf+V)Hk@jfq*(})3nrRQb4Kd84=qqjMn zGa>tu?gCy!yRYKzml!lJJ+!H@Qr#c^JN2Hg ze?yX#U1^=oz>7vd79+maYsIJNgyIliWeh7FqZo0z+V39^X}1+Y|S*E zC?ST0`&Ds>79{*%T;$hHoX|G%NncTpR!>TBs|TV9%{;V6MP?0|q<5|0o_ULVhK)S) z``YbkGp3I0!$77@JAhx|uG{&|cuHg*uND0-C@8369xTiBaa7iaSDWOQgJLW zpKlbemy3s%P04c2)1R0qAksSfs@7OD0NUfv4y#7Mzy*IozoaHb-9OtH4f;U0-= zNi`GwXT_Ij#xg>DV(xL{4vK1hlfx~84*C{aE~baRv&O5mu&Qr|a`>_C!)TDZ5}us1 z7}=M8VUp5|t~*7zbJ0ZKCCm!ut<}0wkn31a(y@K7%%5L<+i-P$j#9VXZ4W!K_OYb{aNg5Y*;BoLx@S3i zQ1jL+3w=C;N0q}ZNB5L2f@WuZB4UbQfo zlTSyOGMTrBUD>aQR&?ay@e@fE+Pe5B%Dm(lfW-_E#@Y7l4429MfA4`cko43AoIMIiw;71d7JetJjI z+qG~%{IJHR*c@#HR|n&p^5PBr4jNzYEp8p%yv*4BFXdUb6&Z$`ToQJ9y^)tk&g0}w zZ`$?OH}jcFaKXf2H$MAB*FK2f zEeU*j7u;zo-m&g;t-ap}>L(51G^5Q~m%9t#G%IN#F&`6ibbbl;GhQe1hJaiN^cumR zPUW*1jUhYUZQ3F+ubAFp{nFn@Tf(p2*7qsYqRqf99^DEa(n1e<=BU}a+T%7t!)M)p z?mz23gDb8yn=RoU3PC`||cttaaW`WRV*#;g8aty*WvgcnD7cX|77;a|k zTg~9S&x7v}pN#(@Fm+;@Xby@e!g|LE&9=I~iIdA#;D@9%*Wgt`-7%~-!I)im3hh3T z%_3G8vDtR!CQ3x?y^)sBmzovuOY1;DSjp56h*Da~8a`HmxUHk0U&(JdF_$E{Kw z=rq3{kxzsm-YgxXMNje~;|x1@)99DU+hJGQoOIegR@kpGblt33aO9bA?^D!|FSOmP znFO}NT*z-3vX`rL-OBmoH`FhjFSG+0EQ+|*-exj6@iXR`w^>w5G*I`1REkqw716W6 ze?+$$GCqNHzduOzN#-4q@SB95(c7@Y*zE_Fg3S3Wky{)w&5^_0%K<# z-E(tP_DMeDFrKwQyx^w+b4TwMIYb??gU7f^KH<~6cAehE26c_P`q|%P07cV27gIyu z@e2S6tJcOZHCzYNmA6SkjresBo3}mnhmP49Al8Rv!u5Nf)U9!-Ro=sM@ip?#f%EHlmhL5z>u=y=b%i*#xpz$_6>Ul<^0QBAFy_%2l%DIt6S(9<| zrd=~oy@9i6&j2gZIChN|CbEWJg7ARIV((NG$eTge8QiqntD(SZF<^adDJ$~k zj+7f4^5oN}?O6nmwVdMz7^F7JX$2p-fX=t=p_vy@*^sJ!|5s5;8Yhm{^*W)qQV~~0(1egtsPzKl94xVC6ZFu8vJJT6#!k+7M zL4w~{+AjuBC%-cFoszZV`Q1k^buUgFI`Zp753=sP+RE>GV;%yAo@T+J zr;BBx%Xvf{zc-11khjxdb2T<~ax`l(NfY_pr1u+y$%Dq&&Do{3wW$S|?5=3huh>#^ zR56K6FMhTmlXc7__nr%gIsu@829S)Mw4w7?Y>V}?8$`jF7yr1QR^4UsBQ%V1U7X7( zJ|>R5KUUX>(lVXoDO9H$-5qnk=}nU%2Qp@?imT}s$=>F&uCdWvcmA;gw*TY%Y-w%d zRnsEOR`l(Uf1aqDcOSkbs&bft#1v@pIUPn#YjzDANl{Vp12xqx{yVL4iUEZgh% z!yA4^SuAK%cdQ146PXY+C(K)%hfD-0rjqPqBFbi` zid`to2lYjP4i%_L#5(Xd_Zjftt{7cRXupN8RvlG||68lhe{hihlWghw*sClhf#P#) zYQ6cPC7BIPME^mdF>{WQ{}L%dzJd#i;WY%;n}s)$F*KGTJQFZ(OI=P9^H4f($%?mV zvKS+>2%(f6`)3-~oG<||`&X)dUd|$SW4wAKwHkjt>T4+K$I$9l>!Xw}Yq7hVnVFfZ zv)N|*mt1%CUWj+M|!whdN z@;4rNvm_apG?&&S16Ir14Lhb$$2smT8O*a}e)zuYVYl8NOs%|@+7DD2!-76>KQNd8eeOIy8 z4E=(zuQEFRx6f6q6|*UX;9-&@I1!q;GhY!xJ4XxK&I_j&-<<|QSWn}{rHr|J9X6h= z#N@e+Rk4^4=D?6CmA6QQcQL<~bnwF;xzXrd*Fp?*ePY!H?=T8hyNR%s+}UJtN0tfc9}JuLSGOhd+L4%I9kn zvhSDRfuQ^6s94lRKf`5wQ9s&4F#o7Ta{1c3&ZUN2y>G)NvqjpmihoYUV)af-IfqZC z_`aG~l3`*EX>xw2%r#cE6{_XeCPLjYFLAJVf*H+g0uImQLW0|a$bO}KlzzA+FPgS` z#JB%OsSQzJ8BH*_Hs_mK)){)5VWMZ=I4AR7wf=%nr5RbfdQy~a5q<7g+o&-XM_YvJJXKJef-IPxh`RBKNs&kR2s4t=^U?= z>6o~Epu^Fe!t4%WWd`5N|3xH%JA6Bu6o&gQ8^0~XKG%@xyEtbFF*dahXC90MoVdM2 z#tH!cJn_o@71mhGvL|m>h`v2*7T3j;*z~rc9?^xBYxn8lHt{D!tt`i=RJC5-JLd7! zH#(j+SGLNP`@x+(&U^p8StjbL*=%wUMT+U;&4v>0qHU9kLW@Hxc?yf7vUE`=aJ}5e zeLer(%CUub>(h`r%HA?>iitH+PgdDLPe^$|qvpMuL73@IidM8sFv^=?-awDIX=x9k zMH3A-e0VEjxP+9T;7wQH-O`vJHm9_VVOK^lS!w?W&6{{Zp)-l%9VXKy2Rxwf6Y)@xV4Kr_yAkWK%dN;I9d#>MX%KMq?uNKwai_fPGBT5U(E2KKWra zdIuf6CP3nvr4lheKA!+OpeTVGhQv9XWS;XUm>#DLSxls16ndO;Cd$M+Dq)V(u#{nz zuH?L^SM79n50y4iiNH;muPBJHCBEj<2kG9Nr-D}$19?PPTLj6^_ZHnU=Yv}Xpe>>A z_!C8Krs=O&c)|mpGm^j3m=Vzp!u72 z*YqcKS%}JTlR0sqf?uWhPk4VJP}lJ|ugBc|bnF{{l+#}6C~WSdw~4I7cws7oG5rY7 z9sUO0zD#jrJ;Dpkx0gD^bU3OYQ&2xc)ieEy1`%;W0*+C?(p-;judPp^*DMeTc(Ra` z-S6?b-m&k2@Mw-tALad`t$EUuzvq6);z!RCBawW5i~_Jl7!$vKvhO%K&AA)0dEsk| z{EdKQVeliGl!8DUPl((-Vm|C8$j^%yOrJ1|KgO#`adYlxA2RC496y_2YnP~WwD<Zi7zT(+e{Hh&z)hfGS<&5Gw%ouH`u`PYx#GnV|>8hNkY`TnEQ*v^rO8q4)cx$wN9SUnQzr3#lwg} z0a-NWCv_PCJd<3^$Z{wu6~b76K#{l*q~&+dX3eRGE$dYq&^6IZ_esX{OVQkQrFd3e z%eu5zUK<;{4`!Ck>D(jx6ov96*&kY;%QH`|phlcqlshKtoxE?Tqo&AhGp!9$u);iT zzR0}N<2K~+8dF>)&Z~rzm=KZ`XI?tzDH$>-l_dC%f@no|F@=5pSF9Ce`t!2``wx|BZY3)CSBc1$9Nj+%=^9ntTI#-mq4U))y=B5?^*uh z*%F%4kESYkWrU4NBY6cl4dlyA{ux?X(o6^JmU-*E#w4oha{5GjPlZerbl=g&Va*EM(HkdGkvk?t=*DI+tYJ+N?ot>=_-Rg< zqBIuKCXC3>e3nUEk0t6HR{daBS`S>B+0}Ptzz^A ztR!h_OVgGk@f*r<{4HW1Dx9nkmRlQdam*p2f?M47wJe53$d4Z3sYNIHbd=44lSFWD zD3XVv2KaW^y?a<(uyVo|g;Y`k-xXtZB~IksQ66ok<#wEr4&yX83JfU%1J*%iuW8pM^P-B;RlPc_p z`TOq_U%Vp`ylg#}9yQ6B)`+7`hfT@DJrgy-u!Rx~nw3pew=~JM6QbCysRPOq07hvG z*&e2?lDyL}h)(zsaedv;fV$^gk7K-OCZve?EgOFwV!V-Xb-1O(Jf##?Xse)47eddj zflUZ)+HH%uN*5*UJOh%O!q;}IiDMeby#7ZoD8YsD)2drukEe`JA7v__ivgUAliz0i zDLM11QVd?4EuFq2lM?&$;)@WN$-Yj3!xJY&W7kC8`6jLJjAG+Qh`O$TTQBufsbAxS zkg-twnfP}oahI<%m&X}wmUPf%6k`&^hgT^IGIM|MP-sHz? z+LM>=c`|CfmwE!%jxC?`eJ~Qji2ps0=3z|%(Ai;1X$7hF4}2ifG3~t_tDrTz$C9C{ zGZiorHw`4Bbec4U>J13A2_efJoxHN_P7(?<_l(0y-bxg6`IM8_j&WVADtV1m%K2=96*7JevC#RuN5R3OW2jegHTpE2W(i`l!jtGza- za?lPFyn}$8AtHa~MkONr>KYEtcINRf@!od5k#Hed9dp;uA!cwn@2%QIR4-? zUmI);We9$taYoUN8PZtqaSis1&eai-cDL6hAh(73>fhB<@V~g_JC;6s0B@5Kj2Ma- zEch)Vag$OabldlB+=u1wM(zgFMq2XZv`C?VS7o!#nh>QDy3P0HPVOJRUa$o51Dm%U zNGLKX&QkxU!YM)gxJ>>$J1LTBEBxQ;?UNe%?UgRR$m-`JJLS9eVMm9!lXV5whq&zp z-E5Pw&ca*K@7ol@^?VUG+M(rPVN3g_Pxwl?}8I3!C{US zIJqTVLcdGLb{k0~JH>AJQuT=j*~KmOcITO{_T~#DFnlTB?p7UqgYia-1aNHES?s=2 zK|RWvS%(jFAF(#g`o;KnP5QwSf7&6sWBux~>2~FNL3_f9oM^;lB>^^qr+`&+ZJW5U zJcNU%Y)vfJpfj6){OaDQ$DtejZl5*2oB-2-@ZyfxV#8_@1!Em5xFxeKe0z&n2V`qr z#G`y-K!4*5Wq8BG!{Vm*jo!5C1sVrP$X>BqU}T#GM95yDp}(bmW8~_QN7pqAKuO)s z{pPy~H3!nj-D;RJ+U2@wm3>neFlZslh%p1t_DXxYkdL#^s(Za^%L%&#(f8hhWyA8s z%xSeRjR2)C*3VqkTF-FaQy@#ehS>SsG`uRxJH4irR6>!jQNCvqPUxIeUO#6qo~F)q zZk?;Wk3R=lfyP>=E^C}^U6wT3EjiogPv0-@@0bwytEHRk>jsb9QeICN_!2H0Dk|7x zhV(8CT!dU+HD1{-`&%%o$0$=iPrk*~R9y?yuO6wL`EX=-t6hA%wyu0=d)HA*DVH;x z%*7&LznrMneuQ%OcCo$eA6G3|xJ=6xKdb$_p6o_>-`=TKAp`(Nf7v)q4`)Enf32yE z`~=mqZqv9ev|CcK3%ke+Eb`3>?3HuQXM@@JG@f!I{(GyKJt&Kw?T$l<(`j!gXJ91V zs9abipRv*{w^D07<;v3ln>bjqaw%ViKss2MS5s5z%CW4eNYyAQ0#D7CQ9Ju~M2JYBmm?F< zz`N#cH{?Z znx>c%e1jDFujiY8m`H+OFm~J7F%Ab*0ow397rBabyn$E@{ex8c%Q7pD<)31@e3w`bDuk3&Pr*&VDgJj0vAv#Z{kWUP2?ynqTOvYLHQ zB;4C8-H|0V3`nc~7j*-b?b#daNMJX+Kc36mHWl6d&2&c+Oyb_Y##NhtU1qn;p1AB$ zrnOR~&XQ)y((Tt~UfmiEU(^9ZlSwu0!bPbYYuRk^&@-Fh4~GNs8(bd)ikid`nN#~t ze%;GvXYN(GTt|V6-!gjgZnh}@c!C)R$&$bEz+`1=U8(MRd%Q;>RS;HJ={ddw)@`QyZ*g=x3?|S0_}(G}@W*E%o3m_t4`{-nC^S!!g+78{DC9(u4?kcS+>JGjy+FuPjh3hz>`5QNAI<-GtHlyR(UO3_A$@ z31zcX_S0ed9a2vfE68=?RQLm{5G>&HPkNIpKP+2Uz>X45F!2ue9;KIo7>0Qgo&UIK zbX-j0^K^JopUc~_Z`q0iaquJfGaR`OgzQasYhsvdtibewooMyms2zCQ9;10oJ|Ur_ zwA>;_{yteYH#Kf3MSli{0{&f0Gm4p4r4uRs53;-aObs=)Fu$O@tTHV$4H(`^30d_I zT(imsaHQDMSpY4>IrY;c%ZWzxW1<%|=U7{-L~LkeU<{;O{5R8XJT*7Jwhc{xnj)ea zAa%K;Odc#<+34y$l%nvG(!T)dwvAO6k+W43KgQC+Gq4k82#6$(2Z+c;og?SzcRT|) zNF^6CWH-D0LWQN8@N%5)w%Yhj{ZCEREEJ^%Ji0Qj@=X+QR{U{SvVS;723nDCbP+{1 zXG=d9*rD!iD+?!-I3Q2~Z-Lfay@ZNA-zqdNmmNYI$q?xb00!zQu1w;3ybyk4mdw^; zuCRFpROKHqE}Dts@IDsozhe}8!2gu(BxqqD7;?W!HQ|xuVz!X?hSqJsdDU$Uj~(8Q zmbc5Lcd$3Rct4=TtbH6hgwr=rg zPC&mLVcOKId#;?DN$(FbmL%}hXhbzw0;O$d9oY*_?y?U@eY-!KvZfEpH5>mXb}9)3 z`)pDp(>&t;I0b$|KBOWSPXjSz2^ra)=6{RE?bAdgKWXpU%vw?+NTUI=!{yZk5u0t< zv2S&RJu~X^=iJjB(hXK{p$U@Px;Esy5sykdVSw!2%IA=L*u44Fh|8fhimZphgM6LXN^lV4TpA7UOa z;NWK@mE8^Cpz{sBEpP^X?S!yXH%w=bNGaS9UIWpUMm@NC#Rc4FH%7$R2`QKGlPZh? z=J^;u`-MbY+vy#T52vhtF!;DFdyZX&5<@9_Gr1t zeFOGM{Q3RUNq_ty7SLPXc;DN*;%V`8*Ok8k+tLU5w+|j0!xft`i`mu&M`3e&By?1U z?WQIskj-&vXawhO8D{A1KbM8G7 zBEAu=1M$%(G|L|!g!;6$$K9_5)$64FcGQpkcw;u%EvowAO5(Q?LtdhGXH)G7)HivIkxMr$H&k?yS>CKbIL=n=FToQJZ^%PU6d6n_g7 za_VLXye50k!+|rJ@XlG)wXrTYh(-SpLU(1AVHId~FIZ!^Y4fc&*0NfXIX?5@=Rw@` zAin1g;yoxf#$DvJ2_{L|n>%jZ`%caUm5C&(7E!fM_wdx9dL)Z6iT*K_7sK}okhUs?w>M07OPdj4t7Y7yAii(J(iVGYzjCY!bsc*8?Ecn1E15@QNcjUO=%7>u0l_w zKK+d{C!BM+Ms8y!<<4~2@SWpki}?4UFzguT+C6QUIEq=c(J50Ei_tpFBwmQwQ zCP%@$0uW`50~I-ekl)*X43Z8_8pV9iIU(@yWygd z{e6)wlZ>sWG{3sJhG-EYPavKOVtX>ZMB`g(@9!ZAT(@P@l$gvK(<`4 zVH$5bq{peqO*xkt7}0#$J9k%o^OHo+zFz0N(G`CXniAsGo?T7=mo(u9o5JE&Y!wL0 z_SWl%xd!{pFL&gOFhvDvK;8E;HFUh=oe^^NULLW226(_pwcpb3^QPlssh zAdc_ph-4?JG#-+YD;D!EIU6z+!Z;s$^z) z6c1-k@9L@p(gJk7wbefdTKKge{OD)Ym#gk?Nx8ZRytghfLNP#k)qS;p zOzMK*P0j03z#E?S2hPDfShmu3$qMTEeRQP!+mYJ3m_m5oC<)(>L9(Ak>~D|yw^Eh5 zlX&s5E~2Y|@NEehn{2a+*~0Pa@>ps1O^&d!+cyvP{YECdx7L2U7Mnwhugebm@fBa- zr}3Y3_R~Jkb;lYStWhs}#2Orp8|ZW8r|;Aa%u%B9H#{|6v_iYsD0U@e4BbOxSf<*1 zSF{Iv8WIu~<{d+;YOr|F*z01mNO%|im_LWuu*8O$jgU*jKp2Zr861z9xQvR^W5*(VZI-KB6jgB9|M zy1DPzAWT<+u=FK7ucYTMEDv%NSuS6h3{!8&buA`l3k?XVBZ{O3Po0Z){>Fa5H&C9f z$ZXeuAg~5JS#O5aY9ZCl?gfXycSonQsx}`XGL~f_*VlD7GtQOhR&y1q+#?r(GH3S< z(bF$j4HvohKeg@h@6j7B3SF?O5*j`@s&WeN?aVua7Zqk|g!seot{-qab`aeAB-gnS z);OztxSHg&UrDS{athDs{`!bm?VU4o=Qe`+A4sa+x0WZ(&o`DgT^vO|kgI$cnwl-n zp=PaTRwZgT$`XiX?q#%Jaja`*R*{x{6|G?Z>OtDP`|)u}i3le;Gf^y3C+*Hg>=mF{UW}HcVa?=2$nD@k=pC-0w@8jN{hz=Z) zT`rlMsRhmZb2*`AbOp@{$p-mV5nVwzafD30`0wK*cM1D|KWP&fCzs+nJ=4-Z4FGAI z-yTif9);YviZ;q04wio7uM=;QN!=Qna z7Jw$#x){fNF<;&(|L<6$4gfL9r|f!*FKsJ71Ek0~ckhgYzKZ1)n7NGx=Q(w+5LK65KXc9`wfu-lMn@m4RHe7vB!O4x}tg*5T#qc&avw~hWl+Y$NXQv zT+V5XbHCeUQ%+d_mxZ0`@@4SEZ^X&p)02i2foh2Gh>OhbfIvDN-9AHvKpVuL*z5g< z@n4s-{XKnESM_uyclf5P8*h+o`Lb7nzcur|{Y)Ej^pUge8p4MYeU@sI{`4+TJp{kU zw+tJfhd=p&vSWbbn;u1EUR7yWxd+FMnCtZ!i3B$^72FguW7l9~vGlv7t+cJAkRaYq#6u@Fza+w+nC}3Ltc@E9HripR31- zt;FZZ50nFf&c5skJaQ)FZ|-%%GgTA) zTTRl!`*_K^3b6TezmscVZEfpIzTO+=-{_xy(b!9ud2x;S2*Pt|nVAdo+P9LLebZ7nUo z9mgN$&abX(Zs=mAqQ#VuV8bm)3)1ihb3k@-`kmCO-;4P|1I#5-!#WNysaYVZD`NZ2f1Y*p(XozqnZQ&xR< z28;4R`>06VM>$BJXT~=Bfc@{JD@)qoA?%Ba&&&x1M)dzBT~2n^MvfLv|Ap%L|8=lB z14%?Q;Z^;vl|h9r#Ox69#(!DDvO?&;pT-Qw1p+XDPH@!W3}iSGXtOGUt0BA{Eulkw zRTe9Z0wrc#I?DnFiSY=&VnSkRQ0vSt_(VZf>K4j zOaTx1`%aR7rPfRB_@|QKQt4zEEYi1iO;BU$#;TsY#2^GJ3cGa^yi3T)07Q!3Y$nh_ zLKhI^Jjyvvm@^lx9+49gxM@sdLgNXZOuwuvAP&VPUxh*NGz)r>4S2w0VghKvdnsC7 z&B^7^W55E1z$1cSe2*ZCyGPh?3ggaA%SG(azx=AIU-e9qoqzy*>68HZ-fCe_tViKN zPGD4t6odfgO19i8y~ZiLlY|Ts&qaS`u;EU2Pyy&0jetckO;BHc@Dc}pN zAmQRh)+$JAh;@c*7@-2G?1jXfF;n1;tVj4*Z^RYDNN-B|&Dbx?{wQ?&tTOLlSx_p) zo~gW#ci2kydF|H=7v*Y})?1PvZCXuSnM^Sr_yzsPi`FVT;83!;w!W-LtA!1r zWVtjktDclqD#sRmGkE|A#N7Pq$2zMg_**O%85VrC3N~wu{_#kuj|=&k8T?6(0D$85 z@BSbdx?Kj6Y#7ILVZj`;?-V7c-P^%l=+YeGmV*eM7Bl)aDZBw!f66}MDqm{Mc0eP& zTi}}pq=T3XIWU6Jh6vKnT&WOQgTQZB4rTiykq?emrd`_HLp|$@X*YQOUa~Lexi@$N zsU*)^9M^o{eOga7mBzlnjN7C7W`Bw4&%nAan@QP^v$`u|borM=@GW5DABS>~MRY4b zMhpF_YHYw;_@Le2?ioMf;C98WZeIYUV{IOUuDTTjg7rp2&zvLO*HeI|FxF-2t<<(2 zn?-qIrku%&2SK*Xku!zJQoxK*YJD(lBQMu4(p~{4;-zslYaLHL$@1ERTi2Z6x6=_vmKq~C!l6rTn3{8$Kc-`E>;PIZvI z1EsQIpUB^yFYuKOyTBl; z7WzS$Pght2+M)-9+f=e|D3AtbyIVPY^?*+(@8B|T>=xr*1;`xT>}!Q=A5$OLD`oUg zIjw_caD(5pb`bg&y;^R}#p|nB>x*l%%1$yKD>j>YXN$=LU#RMGs4?Tp;-7{3HNceS zHKKa}m@jbtOhpKeHrGx3WtYWHff+*<-OHr5x~^I>XbCybHAQliw?bX@D;m74wyQ9= zh?q}mUG3zkVyK_ZIMd-|E~t7SVG9*e5bjGE7fBMUwx;FXzth>0&#& z(JFOm>s-ZqyuG%otgU==O;u-Y*YdteU2Unoqq2et|JGnNHcKultMr#<9dz5xVgB_W zw2E&-qNA&;dU3=n$)jAJog6dnrJa1^fun2C2%do?cJo-C-3s!W6Fg=?6p*4b1SRB zO=1hQ(=O|=b=ltX?89Q*rI04ml;*DL>x=Rz#=gYjNxPSD0PfG6L~NIq=(}fUp#~0> zjmh%U;={!Vtfl$(^7I8#YDw}>wgN}Bb zLpi+Ki<>~p5~k7?F|ga2<(8(VNxZZP`g(DF6`ivrO+!&DHiVG&LQp+vAcU+~$n1}V zf~Wvyf&i>*q1yp`)urWAOIzF92G5LDnmKoeZyJZ9vYs0Y?LUCy_)iR-Y!MA=_{2phNVLC~g+HQ{70ztvFpkF+dx^~8_Ihq5`{NlkD%w=;%_55Mt}N?R~4aC_upsE=Nd{@ z$@D|Uq}g$$|K=7A?J@r!{k4NrU5#fXUS3^`J@AOxeuKX)W|D zujy9QEbtlq^}c=&8M@r8uy^jPrD-h%So9-4XHH==eCG$TBVOld@yiD1WHB>xXF{S*&^?^8r8AmBOPd z^DvOGRN8R8e59}Ij`JGeUPveWXJM}=vk>+=Wi7xrrKp4Bq7~u$B9+6>KOCfu3egjH zJ3q*aZn5zSFkk@UG`#%sH2UERD&Z-?P(}8uIMOEs$;#Z9_(jZ%7)9+YT6coD;zK9UD~M@SbeGr*Q&STf)PiU9ZCGCatxU;5uw z8zz-nF3rAYUfGYAV^CRoR14AY%hG-Xyrg-f(T4)03hqd=XtI{8vPUlU;#$-jidyCK z-Zy___p=IvC5Pu&a*e`hjxt#}uUa ze;ptQcBpbl0t8af%>`wBL*aCe93bS9Ml zo&qzi**+;I>_iII+Ebf5*hnj3d`tBj^g%N?{bDPgO?Jpe?8Q`>XE=#qKZnfGBNo|) zAY5Z*J^uL8Q?D~S{!{DA4<57dV@`v6Emv{cYYN{9Gj^dJGPh0RO^V=`14l=88D;FR zYAbE=ksaOJE1Jh<`rOUEb`)v1({=mN6Z2ftDu0brF2kDux1KdIn|zgV5KSV9Zqdz@%+3hUqS;y%|Wb%6b(J~t$F zzyc(>vcwIKc>jS1OhJZu@iOwuaX&U1Jf+)ToOABj^tM9k_HG3^#Q>$;Gwm;KlX{PW zoNAb!8bOt`g_OTWnzuuZ9!+Y@D_6f6hc1ig!z%zEge|B?k)jZK%w5-Z(?pcV9syx=Xm1-^6G$DBQmEV$K7?0|@%)_kmsk^M=Hdhpw9~GMRN&S61omtcnJh@QSo4#k(^seC7w96`bi#n?KHI3F?quje!I77Ie zTh?GDf1Kz^X%$d`#{Be&D*fzEESlF+-11M0b~rT_>CfWiiat!J0kLZQU~g$bi$tss z3_^Zo+LC|hFFt#9-f`3lx>57kPXfVXTz{^k7b9l`Gwu9gBeILG3+a86L#W@wyOC%7 z7@ebJ_np!mk#c&(W4X~Za0KYR9o_SVh$vvAbZ&maf4a6@mGqsWu63ql+eI!`l-+YP zxcftU%eBAre;Yi#pMI7_`0nv(4H5MmTj5&fh)bJ7ZD*k|#f_Ncn@Ob>AUgRir->^2 z!_R6CR5F~uU;5!hB)2>t;=~zvVmWzt1xuOZz{YK=#Mj=*G_|NNuI`m{^-#&Srel9_ zZsa?~h3gGDSLH#a8k9BNvJNh5Y%z20J&t8$a)nQIbf!XZzJISH7WjhQ;+bY^ho#Fd zCe*d}*sE5$owcBMcyD@{b`;B{+6GB0EGnQspN;(*k=hT_w!_}plZUjZQfyQEGI?`b z;|Bsh@^e@y#Y9z*VO~5N#8^Q#=qx}V!6C*!hVzc@io+aug_^JO1FDc3W=(&SJjhHQ zPJ{`Vf9WFysoxH9{vHEL)M0I!Eg-X0o%~Mc;P97wm39f%xxPy(iLbIeA|g3f?xvb9 zt#eL9fu7UX@>;R1E=VUjWu5pq9^ohSDqCeZ9%&~!e>&C7t-D!XNKbSMJGFDTbsj#L zU#L%Pl(jaSUsR>H>o~!(yX724o7)B-x)!&(&E(Yd{Oi#3iS^{4V$^;t)F*vMW6B|0 zW3alE>{QL^VfjOtZNOUgnc1f52=4Q~_;V8v&Hw$J`2MubCy+esjfhDEtl*@ z?C*(6SxTEY&2eWXqo4f=dUeuFWsci6s3#(&4QXTgc9IoMa~x7T>EOq+DQzR~5e`Npbes z>3j_C+FGP#{Xd|i#ix_<-kL#WIp0=F#c8Upn%i*5u5p3jVz%O@g5A6YSJ@@b9N$0B z>P^LlM?LLJ^BdCdgoVyqQ0*v4uQ$7;drB0NNLfOK=|}vX zSXX$`>$ZUahF`I6yZQrju9(2w;NEb0vA~92u!kP*c(zx)Sw0UNNVE#xI+Xnnzg0(;k(6JE;2->GY^;+mPJ9UX)OX1T}|y^g(ejk!g!<6lWiJUwUs z{8gqS{pYS=q^aj$t&aGJ@zGvXa&@~L9K$_tgqK_o7*Xpr)uy<7; z9~3VI$moFmj!p*cYO&@T&z)*4XHz8HR=%%DlVwbyNZsvuBh~s?BH`RAD8=NuNq9x% z-PlG1BO%GPgoMl%62>}bP4?b#J$}p#4vmnm;)2q5%CdVzFZ0U8AG^}?rmJ;3vuG$X z#5cU=MUU0OX*ABrnx|KC+M;O??x_B>wDKGfW0oJq-rI_C%u9`{QyG=BIbXhSn$%$5 z7L^%%4wo7f8~d6CY;pZ26_vZbl_kZ7W6?7EHRrY(`Qj?YHF{B%fT%t~&h#fdK}Y1q=Y*0pX5t#klR`_rq=_0&EF#6nw$Yw3|~1;|gwj&5sDo zHHq0(XwzTYtj+J|<0$kKM`H)p^ZkBUH>LgU?dFh&+RztOH;21bKZF`qfm zO@CTG?dc2u)xe;2US#UQSWRHkrKovL^Ks|19YCKRI(KSh!UYH|37xzx+j7oU@L@{6 zQ%SJ8~pF_NoaF&MV_7WiyG*bwG>_$SMX021I?}T@k-r!0Lj@f{VysNAz5RgP_G`)QN~AIRpGK3~}x0 zUfj^PAN{r<^d*Q4An8MqWx%~=9yILQy!m;!8FFTr_-uI?17_^SS~phEPBMb;(VG$aX1Ay`ybzq26nOdX@Cs5zxPciT>1Xta%vms^!Mtt7#@N2IsntZGi-2k`Y+(Q4@v(#zZ=|{it@0vjP9D-}ta@$JJ`a_0KkXd&_{PjY zH^_xstUxg5 zhu%>q+uZ)PtM-%1*sf1~IxbAnk3W6|dvOgUHhv-Q(G)z!YOAl)dr=QGO#Iy?&T?0g z;Q5`%CqtOI-}E8yWsUOrI0Nh@InWTapJBJ#E^#X4Ik#a55o008_Tm5vi&+FSAz&u# z^2OVwJGg3(27CNP%HkUxrE?$Cl7m)dSM$km2?8zsm$n^BF zBP`~D1y-7ED`eVa`Pqz8aJ3*Z13=9(^7{PahG61~hjHMM7o8K7+0o4Eos!4}!_5RD zGS;Oud6j)UM0)MCDi>9Ftcv7Fh5P9$a(bBfbj!SHU~2IQ1pn_8UvZ$}&=&^`>??Bt zBmKXm`2Su2jl{w)V@%5{{}+*#7(95tCTOTkiiY+O_Q%`FZn5@}m|@YWZL`Qz9) z%3$~jk{)QxibL)ZZSfXdI5=hWtlNnmqUx2nY+rbWos?X-*IvCuy<`PD2e3SkI6tg z%yr40iZC90NLnu*KP{JwV}baufbbXF`?+-BK6Tw-WOV*6(~&CH3L%>s7Q12|%IO3~PHTFtYfb?LMIZH@v)* zux(^&+awU2MpQKdD;|T;{YUa zd!Aol5{UwDxQY&)!0n(xC#ZdA*WDk)^&JzX5V$O>#0vYA$aX_ zCW=-ikZ51l=VCR2crVB=y2;RH*07RvMKfnqQFw9^jkD^+tPPo?P0*!I-Ha^A_%uH? zjj3QBLwT}M_=jmSU@H?^VvRXqkk3|n$O^klt9z}!VQ(bhwSId=SyBX0D?L^Bj`z3( zN|^NtDY5(AV=xi`N{0sgbk9?DsTc$eUX96S+XV0IfuH-hdsVD1y5QYFW@~<S67CS7>KD!}kdIzp!@VAJh2t3?E|UUlz(=G-VzyE;d+xr%=9_;olJOI~o2n z0sjocpA+yep@TOcf^KWFXOnEmkO zXN2KaW)FP-bzMG_hxrAM8!m=7v3B@-a}O^swDYxmO`hF?9$w1NthYFCU$D6lXR-56e?+;p$AAClTUu&vDJ!|%y|J>wU2$7UBq({q zioYZx)zoOz?e=YSclyG<2cU{C?Di-C_!Yptp`N~oyUQQ&r@+rgBHj{jC`7dbP}{4K zl6Hr^+Tp17Hn)|xJm^|ivc}c8zNWFEwYkydZf|U8Z(Os+-PqPDDc&wRGq>0Rq~ z*M$25pL*CA4!ZpTB~q~_^pMN_kXwrQdVF3*@^(dhN-z@g`8uUfc^>k(v3qxmFYNd9 zG-rs^;z~Hc7lCet-%L;ik8t73@Z(u zUSDK3Ro3(bH`D|JN;ue4AMEr=0Z$+p>GAnO(7W3g4hMpu0x0KElrTUoZ4K+)j%sHa zC_Cg1`NEN4z|-SbHf0<|YkOUNQ|qU%Do3a<{D2R|mfXHhkK&QMJ;8`iN}uTIJv2F* z9rZ2lH7>_mw{vX+u$syO6>D5>P?`jj1U4`WEx7yvU!(;F*%zTEWSD7xZ>VS8lu1>r z!AbBaWL6octi47C3A(%irRS=B(g4|r1D(|8-5^IT48Gd0M5;G6^z~M(@w@%*48v|~ zURwvcbyPPtw@U7&=Ej=3+P3C8x69eE&VgBuYFFJ16>D5|An4T%beeIHBBTvrzv7$O zgPL1gYFb;!fv(m~Fysr6BBnLyY-p`(Zf|u=vj)Zw0YV{J z8`I-T#hM=U=2^63n>~70)+gtTu5{x z%%Ipc5Ps_{d&T!?JTt@dK|Gtp;}d>%iCb?x=fty0Jb!DI zw6nh!Y3h8hlzs2SzxOJw(D)$%A7cCLVF8!ed;Wxgr_RMH(s6!{R+R;pMV#?X0?yAt z+AA{4Gq!5#dt81-jLB9pg;(hCWjb7c-fY$3yoAl?q%NUMlxLLDyaaQ#=rO*&kEKHRRuOLTaL4qv3hyLGr(hlg~y{w#N+4lmH*Q5~*- zAKRwGKd!@f>Tvz{^}BSq{#AIl4%dIB{+t7vxc>Zjj}HHY4v*__lMX+u!}aIW zM|Jo-9iCkEp#=shxpXo7-3ckV6!~t<8W~PP(s-;qa+LV_$m5Vk;>6EI9;bTbIpU4T zqevsWiNAO)@Tjtpoy4C(9?fqgO8iOWQLK>=@h6bSsU7Jc{x`_u*E1tc#2-T*!^ub$ z@jpQxhcr@3{66Gysz+?Z{~PjXawAsapGO`|d_*GtpOD8;H!^kw`EMg{rSTL0HRP|M z@e}_y$j_(o6Tcn#Yiaz%e+l^oG=AW@p4()#@bG+tB&)xWx17Y`$?ApV{QLHSX<^>8 zsXX*nmN7$WX>+tX+8p;d*2}R|#j^UJE;Yxco{-f`a`~|Vm%YHLz9*N*9m$ z)k)y@=9%&PY7orjO*mtdNz2v&l;zMNG{(dYn4yk2)hqI$R|#1RNWFK`sh)SJNy_h! zTl!ystmtNYfuYX|Ft%H$Kw%4HGae11{8*w8kl64g^(e}sCikLUBqk=4N$4UWLFVXM zd^!(E!T@Zd04r2lmC}Tfo{Y*8Dj%yXln=$>At@gkL_NFIgv(MlGMeu^bP#!2od<)* zQLDd%&XP|w!ML63K{GSnJT;7^)jElZ*}U;W9x z{POq+UJHmN%lhW-ArJUCvFm^(2lyDV0bt4g7XiB^`DJ(nggjslAx~)t`M|h@$YBj3 zFZdlHD-48?AG}UTyO9v`gh4`{GZ8|*@FPOZ`Gk-+e3OulIfRfusD#A9R{Qezpht{u z-jc)vZeY^=jsX#Arqf!&&eEZX|$ zO~8UQz~VQbqt^m!Et2-2hrNj3gIa0_&+gBlXUmeUXjcv;t4_x)gYvSKw)47Xl9x>)4BPu?J_i)>1p>h)v-1y*auZo?!l7 zqPA}U1}5+&n!sy-F~3B$v>6=YLFbtZ=*`1U^)e4S-@kxC=QKF8L!G4j{&-)hBX$MC z)i(|3)|VaX`GjHK48sM?$uiR0tx>v)I@+PRZEkX|CEefFerCJQRU9q zg&bvR?1D+LI0m3Z{}D_7%c!mDLdjs22y?^=?AFnhg;Gu$U@OGEoer=R;)ZJq0M$9w zH})36t~zmkf>hWk?`X9bP$-_nL74+m7C^Wj&8LUMCV-m&jtx&j zvjw9tG9y0doB0qn^8sjPaAL`hcEhN#{7smPgdGse3;QAZT2rwX@dCgT*G&jPw`(W( zt$BP9Jw#bJK8Rk2ULjGOwbYG`7HXiffSg$NUQCpc=$0qrqlM~VY}gn(ZkRV{`D)zq zLL7QE?@w>PMfUy%Bvvo&Ekc2P|+yUEYD( z@}5*D)nCiB2x@Bo{rO}vddUEm0|s_!G&cqdwux(%D|PTZDK!>1XhSN#FI~hg%aYnz z_VquAfw@2@`=Z`sac#HF6mHXly)oVM|Z^Nq(m;Aw3>mwcifz*=y2?;B3< z6ra?L-Oly%j!?$!I3-}_E5!j{iiLD ze+ROh-h&SH-1uA0oC7jAfXxN6?+6=QITNmdPP^6RJs}UQu*=>*)(-Tbd({4{6w57> zcWkzo%7^yhYjgSe#E;J5J_YeP#h@v<{75~jU*4hEOB3HdhjHezQyq7zze;=;i+bbz z@kg$$U7m2L2giq8-U%3vD`&Ia27*2M9a&vrcd5{Qt%{wh`{nVYGQ^l-`9|ztt(UYt zp9A$CrO6m_5qcIf>eU03_geWe2W00_2u<|9hZ63Ww;Vtn>h?|PTCXB~<5~;zPp*Jr zheCrok8eZcw7w>v*v0Gs%*|ZyeTRCzy!X*AQ&r-vv(Q~Cm|Wf)K|4Hh7UyZ63vAg8 z7NWIPe(XhT4Mg3Qhs_qO=_JP zbhMOKg5$|i0N+&Bj_*eU>9eS>$4(ey7mZ5pK%IHij4N&wp49!ZVN-0v)R(vA2%d(- z-ZsZBnW0cUta|`KECZ%)H8!u@@m!J*DgO^*ZO17mh#*XI*GXYgHPGVvso)P#9h6UO^4k9Igz zwrFHDc8YgebVW<`CR1T`2{wvG55ZDcZ_bMAkN$nFo1*3kdSzMlnOuDHLmN-tT}ojP z;`R|OWYW$LB4LN-LUrY*F~F=qs|8tB=w0b6W630DmSRq+-k&y&X-;+<14;Bw03lY~ zCabx}O(+0(a>$IavPvFM?C>fYJ7H6+td_6t+XnFxT)SxxZa8knC3YKl<{nfF=L%xS ztF$L8=%W@&F93=muo!3+(8WMq7&?JA0foqQO9(o4LF}t6kJFNH0Cq|JQf$J|ca6Nm zWnZDraSSw=M)3OZPV}xth?X++1&MjOW`D4`>|3z|qPM0GXe13~@!STMi9rPvZIyTA=D`egS%Ok|yvy<{Lzm?` zhgpsdT4Qk&EKH}SMq$#8VwkzfWXeq@PQz;(22k{sXYg%6F~Ra`eFpVSj|-lOad6gHTq?>(|lVo8)IOR-eK zVWEn!Q+z;C>3kCb&R`*9+GFZoGp}!EIM2J6AKLQE*k;zAZR$~CcMlEH)LEi<--oB7 z8`Jq47+=EZU5qv``Z-1ej6TBX$I{E4Fk~)QIi6nb*(~ji3FTU{luKqvGmXa+2r3@M zm%4w&&*BTSweBWdywA$z5$}0Ntr{-gSLWr>&hgh@F!~m| zD0rFCPqXoVpV6&MuCFt?naT5YMt{s`HIwHDjEZ(^V-)=hzd^w1K>Ow2OY-j{{o!Cp zxx?1d7qHa?dqe&nUl<>Hg8|#hvXXMlXl!x&%PT8vWu+@u-irS!pjKIT*ZZ#3cfap_ z*YQN&zi^@8`>xZ|2~~n!$_7u^XItwF_`)8=4^`R}e~(}B`y#emAiu{G_D5_kf8aq| zU8i3OhC#ShB`a6nva+PCtP*Cdgsua6Jt3*2b5p?6>-S0}zHWC{*wgECcXx)TIH{!D z6X|9ZdZ2J*Q!iCYQ6{2T;0VQ$lz9O6W#fNjON?21_0Y z0_5B1j&#GoI`Pr#3*pGzVNc)zUj(|Ma`#6fT~bNN?@h5%NssoIH`SvNPk>oki5Il$ z3qbpkZV*=S+%Nq%B(%%4U6{suR?u(zlVO9 zDQS11^hq55i$Z<=yQI?FrHo1J|CPV6K7NB~{tN2AHdWvH=hT0F3QJSXZ0rA)VTM1m z{@d^w7yeHKef>RGiQlI)w23LfE+N$mnPS=VlBRo`fUn}0eXdgfzW`860|W{H00;;G zN02l`Mk?3zlXU<900aR59RL6TY-wV1FL!BfWN&wKFKSa$LoG5cH(z)-HZEjrY~+1= zTvXK-@C*z%;OH3@6$_JUDl9bUQh`ogo{CCcd}c~%w<0So%QI+YHFRc_!)dxxX^(q* zc`I)ZD|?xm4-f+_E3`5bQ+(AKj~c!ZL6~o?wfC6^5@kQX-}lGI@5h{T_E~$awbx#I zt+gL#?;9q04Uqi4w-Zmrg_S^2e?|w1ypLZrc=(sQO?)wsR|1mak#{GBPIk;Q5SR2Ptm}f8)7-J1D zM*loU$6IgcesNc0L?1&1J{bYPV3=x!Tl}xZv{f_cK7#RW$o6J!_ZY)ic~%#5AS93GO7#l(BtwZza-QAs+MBve%SYqRDYK^^TPWLroMWB8D0h zwPkezq)O}KCs%ov;j?GeWq6SeCd);(M5V}sD2NVG0UBw2@;VE0;6x5~tS}hd6|ZAX z@zxqD-s@PNMHNk}W{JM@M}W3A z&m&%HNUiYUgWSl=r```lYe=i$)l_)p`Q*~D@+Sb(iGbIPtRQ~mPd3@q>maApWb9WV ztxu@(@@$>JcSCpt4gFKPnQEbM8^!eT@_wjGtd06rGt`LyJO;{qF=FTrDTrYdVmN4q zbzeKN+fcHu*!I=JPztC z!RGhA5kXRt^S<{u_O9@wY)+ht2#{SP>7`Wa8H7V!S z$~E%lV{a(qzbAZlo0RfANc}fgw%V$`_QDT(T6e0Fr?$_T2 zGkce}1+pv|Pf||?<<2iJvP~_|wcSycYa5_*8-v__5z6g!#;xl!o!k3CcM9b^6I{lX z<@JQi#In3NxD=J;^=kV{SWs2D*HnH(jI`V7oNF6k6c((j++!-wGfVYSOs%xRS(ayx z`Kj!NnBtne?glgbS`87^dA)m7=k@8OlN+RybM#4Vl#^|0J~Gw-ZIKFS8r^4~R9jLf&=k9VI%@Fvr=ZlE#B^^Vaj0S<_Lw zF@{*~CqG3%tDIw1qQfvOAq;4rt)2o~vRq)ZfUwM9C{0-JMU*cNgf(I{;v+tY5}ML5 zOc;v+2_?sh#jg9rU>J?n-W`jgO6%>UIdRaI)Eanq-F^TUO{s^pCCDoo5tj4FC%W$K z(P??GigURa0j{!q%J^FJsF#1jCF!_dt^QaSz{R}KOCV(e ziffkz&CmC``9OKKlh5Xlb@@~U1Zp>A)d+w!rE>2`KeY``l7_#@3q_(*T9E;qa^&xw)hzm+yv%g36fJuPKp&1IF+ z?$c$XV%&|6EA>qN4?}ESf~^cgnZCe#IWsT3BE2W8QEf!PxJlZOSe6rWvKq@HMoV9s z0Q1ReG&Q2hIVvVX`o%0Rf*YgupJH!nh4wZT17o6ey$CyFIw}HK-kWu9#O(61F=e$< zeTqF%SWqD>t7yzIo5V_pzcig?sXt@pT@!b2NxzYrAKpao;3VzOb>1FpX?9@P&N^p6^=Ybi5Q z1vZVaUX$RO0@rl7Cc!lsuIbf4 z()gBDyb`lo|5R3%#VNSm8hF282t72uNqzJ$Ri zV|p5YU%;;j;t0QoX+3%H(H z&e+P!I3c(u8BzO|OqZIhg3F97&ASy0q|}rtxON+n!zo7OFi~*d%KA;|fNvUxAO`U+ z%!y3#$u2?nWpxlp`O= zPxCzsCH6{9Cb6&71d`d#-euCe+4v5g3h;@kw`((`QH`uZ$O5o71-5D0o?F+oF+rubJqPS*vrE10CJXPl}}O3U~9r zQFq1Mo`sBexj_|&D>s;+ph?PjGY~9L7+@Ni5S0I3K<66{_|-SxAuT|K&WHb8_|GKM z;4|RsdF(Yt69w>3CeNU)3Is~ypl-9NZr^xEFTeYNR*GTtfU*c9Ch`LX%!`-4qX(7l zF3q6PSpd)d6>5~S$~HBUa$>K8w6C8bBFxbwfj0utfPy*Ici-mWp5Pvb=B3U9{>3BO zGO1qEP@mwMz_W(-h4E_T0geS!n1yvn7RpmZXcIx0gWPqFLD-xPtX}E$o$z`BJS3N{ zN1jQE&FF)3G})_kK_-Wz%d~Tssq_tm^Lls20NXLiLJ!CZS*-m{XG+8OS$uf0fvapAp$pSANqmd3`DbiAs zR^cE~`i8Gp&G2C7pT#+u_t~H(@37@k2N&cS{~~MPAW-n-HXNxnXuXTQi_AFARw4ck z5)(1eUrS97`_+r7TH@jDPdb|Y3{AYWe&Q`vIEI8R{jKge1Xg>RI5Fp5NmtYHQ#9zL z<5dU8QC6D-rozu(V1pE+)DK>U_dUV&c)b8&0)l3r|4jqxCXPoz-N`?uHl}Wb=W=VN z{GGJ-@$dG=?360IxVFqWB%K;CySua}Q;eaW!gqUOw#wInpsHA0ELY3l$=@nQR7530 z-s~?lyLk6rp^-(_eMTOkDiZjBk$l8oXa&mzpjHHxyDT-bz_-eaF)Q7%aR|F#yils2 zX-^cdDT%XZiZ*+pXtk$^Y3jYK46X`?Ih98FioJHLL)DHJd(rsK`8ONTYkxnD-}C;7 z{7qSvy-;wyjUhH{Uq(4={yBt!9svH zHoNK`icorjEz6X9@cst!jqqme8!r`kgZg3{S;hoy6}*!-q;5&wDF2KuQUSx=OF%#Lg@Fx>+=6-rX|l1r&oF1RUvSNbH^Q<5a%@Cg{iz#6`2UgfZ>%Bg4_V5s48C_JLWY&KJ*-tP0zGD# z!bv$6;!{l)8BFc1)CwPVh`m@s8?ZGoPFPmuZ1Ibeq`h67EfM0#605T%QWTsmCh;mY z>vgD4SDmBLW5H@Ooct;BMvF4R>?s0L(1|+I$_Brs^|r~kq6|Hj9~hjr)$6hJgzGon z)Q#FkCes9v=?^$ugX8Tdw?@viV2zeX50k4{+u4p~tC({LG$v7QQYWC1#L=AtY7O{M zQ2K9C-4m#|q`G?DF`u^tEtku-!9abqP0U5$5JCbprE%OQP27PtX@TuE19jnw(Cjz| zky*NfJOk8o=>jr%bGY%Hi&trttpZh-#;)TdS-Z4j!??nLhoH%I~Ex6ViiRbT;sW>!3nG{;qKSyOqLM5EZ zx^wix^Q5he1*l0-V9cfFwNk7WFD!G}UNQu*sYhBM-DUGK=QfwZ9F_%3`VS;f4aRS- zQQB#c+6=;z6t}y!7+Im9FMlXZSAXF8hl<&eUf$^VM%Hp<>~}-^D2bpdd4Z^>BZD?~ zo#1-Ih;t%>>z@(CC<;N^gArD`;Jzu6x=YtU@OpD3<;DrdMgw$fCg5{sEnu0WWG+Qt z4`RF)`sh%fP8L(x)H|A)ARx_!0vHF+3VRRY7hJhmzNc=5UvTm$MfET!7g+a!pq9zl zxwZ9I2Yuj2bAoVp)ltT)UQ|Cip510iLgEBXY>IGDd|W#q$ytbD-+-1@~$cI;RO*y}BFJHFhcm*AN_~!#e#rRt;JVzkmbXdukAGJmQ_b5CDByw$Fm-SG zo@*tBuZ2>GZ4^6Fb+OX?qm6oGb3){aYO1TW0!kq@7`0^qu5WB%;pbZr0QtT+%^p_a z1I`a|Nk|omzE@6@QO`v|HbD%fcIn1+lH#Q&;Gx(%zVu)jQ;%G3R`U;{Sq-#{0!S)M z8%$<(9L1-A?ec$7{d##t{---BiZ{?UZ2To_H;I9FJ9xXHr|jd@*UsL?f;9?k)p`q6 zXd2pjtS+dJsC@Bup{v3p#4E8N-66zR44%S51Z&JpF^bVF)54SSeu=t!X?)6^?ZkuSPJeK3}dLSU_^S0ja#?rboU^F~@e?OEb2k<}E@N!_?o zJw=i6G$pZKMLZ9?vWh7?i#RXCD zq9z843bD8yrz#TET7k-iMHM$J$F{5s)r#vRaELoi@4kj~XXCAgV1HdPLa<_`$>(0! zl~AyE7sqq%{adiu2cne|k9l-CoD>C4}s zSse;R7XQZEfqX=LuRGJnI2uumXU5!AlSz7_kL4$@GEqN%Zgv+ zMB3vSUPl`C4kJst>8LE&e^Mfe4x5pZfASZ`LBBiXJr682{ukySMB3T-_R znyUKZvwZEu_?x7I<3c;mr`vU$^U}gQ&bwsRah58kQj7~p;`3yAapE&w6~G|+GfgX| z8u_klH{n$<_iA1R*w3=q)S@z01t6cPSl|a^ z>8=p&5+s$zDmE{qpqoUUC1YJryrpUo0#~XrSZ}&s^r8h9PcRo#Y6(Z`77X(Iju264`VG3NRD{%a=cl1n+^Y!5#)LLGA!Tm2% zS*!q3(ypK*Tu*0)>nVbGOmbztDQYT02P;CH-(H3fdYKIG4UyrEr#qA34xpoc7ulhX zR{V=$8;?5r_vikcj$Wpq+#5U6(d2gGO-kqDWqwgz?hfiG7M1Df)JdqLG>`CXP(fdt z#}yR#9)!RvzTUKH)5+bH2cj`1&{Z`qO#e`n+G=n6a&CL716@T}UC!n0m|C>-c_w zuvy94fdt<*Qk`;tmspeYnvt_0=PV^>l=I1=X{>yi5Lo6cUo@RPU5`(zYI5ogS6q=( zld~^p{EfqFa@0GZwj_-_O|SVhsH@Hgy-}Y-4Y~om=Ie+}`$T;J?-7@_jQLyjO0a>7 z6&i?pi0UjiNQC|qaX*vh<&?3ekO37OOX-DUDczMEsvlfSi}QIm84MkbMcdo86?sUwDSGk= zikH}M^Elg7HOI?q7he7bsoJ44a2B+~d8{4Ibik=AL99Z3(>vKT0MpPHqVqk1qw*EEiHCt8$#(YnqYIMY@Xwc~Zo&~RJvWNDDsixn&N z18}jFRWHorPlSxJ)0_}1#CK7=5G<3n@ppRS z+gsW(Z(X9eME-!cDlnAlrAN>%;8?$s*@WZt5UpF)^{9JR&ttVpcjZ=7yH++k3ffVL z#l6LmDyOGb;PAV0<39JsIsH608|7Q~`SVQ~KZ;TEgnBt$8PbJ->3O# zYR|K9(8LOjIjQ5u+=n%0X&r{`>h5`bQ8PB;{mRHVAZQ&S_LENe#Y^Rpar7?BaYC77 zmE*O;5Z8^g%4VMV4alrjs;R9^r__B&8Vm_r?PtNUOB173LrX&pRXi>RX(YNE#VeF( zsD<8MWujH8plEswV3fDH>l_E6N0UfVuW##8I`!@2GB@Na()Z_m>n13jF|^l-Lwj>#Zitv`-abYDY?chP(n%;-6@9}iJb@1i>< zvA6@o@N*!~rG7SY`8vMUzQFiFe|06idL1cgF9g!g*VB9tvc(h~DfWRU(3c#>d5v~u zeX?CyJ(=NcZtkOjvI?$maex!co1vF?Vt9G4nimM(#^@MseC&_Qd{x`;mU(EKUgiZ* zaaez&^)Vo>mGw7?Y3&}KJdqe#mc9%0Yhy#_;Cg+gL#dv6f&h*R0 z3uz)3g!i>f!-_F9&#}&A@Y2`=++^wy?-GP#r?y|mhl;icxEq6z7CvuTV|ZZ*^2Xak zeimyFCe|Jju|2#wpSK4F-;fmoKPMbM9pN4G!`s;TY^cFhxLaRMU{Jq??vc8wiF_5p zT)<(bhrqmzFmC}2=;pLg-F%7Z=2TrbZ^;R7hxHGGI$k#us>dL>j{vIbD10!gyZFSy zb}t4iYZd?|*o z#d=FnG6TwZhkCB9h*nXF`5dJh2d_fV5Q>O!6h9-19rJ1My2v2r2UOJDNDZ89;wE^n zEo+ade>kQm5z{jmJ3PueOVkI&tncXhY=k5+ON~3l0un0k{qUfUoPQ>#gTnFGBmS!p z-DdSWs6cS9mHsifo-r;Yr&Z70H9U9sJ&^nOJT7jw&^-W1jY=m+~nTqh-NVXGeB~oNzOOpGx7$xPC4R^AYHP(>yaxN)F_;Kj>H=mghXk z6Qg|@mUcd+ElX{b8zxbSioI^X>sUkwovN)TkII>gghTfbwE7hNsq+}&1rWd63R?ZW}M#~MyR?R5(4zUWZ7idl* z;kxyZ;y8?^kU@H~Ywa;5mw2+*+WVDUQ0#5YGMgRipyK|iWPwN|H7B*sZWNxY(mn!R z@Jy<8w#3Z7yx8kWsPGj1yEbFn?2iFZSYrjwVlo+D;sx?H23Z2vwa%7)v!7z{8?|q6 zJ(6!2txHU;^G!lUR0`w33gh6`JTx40;^nzEi!wS+xjP<5k24NDI>VDTM!DPSnfs|B zTOJ(;3e>BnOCJPIv&pk@`L0F2-z-0b(?l7{Obe)>@#d7F<7{TZV?`rnAERWUQc-TO zYArN6h8X0TCV8USw+?54Ko2IGwJ%cr+9%2b@wEPEbe#4s#$09JDP_3isq~Z|GKuk? z(Z;d_v+TvRvKO#9vUsZ6=_l*w#Sv3|$s;D=_$t_3zt_wrDHHrov`B1`@J8(;wuVWr zRt&-#v-8w_Vrr`2lLeJCJ6-9M4Wfs(Wpa9Qvufo&!kXA@&&?!Y4P+5`@bGC!MgUE^G-oCTOAHZ?P?7 zoCz(m&{7^@pCTsM?-Q+_9E*zfUKwj~4w)iaX}vzeS7gg$Et9JR;6uaQUg6Z(@0Y0i}UuJUAB)HQ94gzywi+>W&6uTgp`S!4*LFmxkt*Br0P~z{)RvocDKFl*wl3y!=Az{%@uJ zih>TK2#Sw<66`$$EAB+9KOsv;iR2c`DCQ zgUNbU;Jrb;8dn(CK+%^-=@$#G=L`(`JZH;&qPY56MAwohxSod_SDiTDGt#KVdb3=0 zf(r*KAT70m zUSEz&wsFIl1Wb_tk7Fa_mVMmv<^zlPwPnMVW}r-2N%si`K4#Yb{SELX0)0x#Rt z7fy0-e5Gfm85%EGo;>kJ`-r(;+DAS%*j^xBW1r|)gSG~Mue4GFcMi#8^+SvG&_0Z{ z;9Mp^+>9c44t?r2bs!i9tsL$t_an(l;2W1JsWL(yWeG$DIx}5?8SSm0USecnl@1g* zo4c1>1mb?4J}Fa%frg9_TxF13a&=dOUQ8uMZ&2#UV#Kx8>{o5R?^DV2KDwvgwUQKJ3Gv>BCy7>LEVS1 zz5sc?2nfpKQ?Xt_tL8Xcas}5|9Df~K>uin@=KY3w#=h!o?kCLKOFEb%xVF>Hc)_)q zZVCj~2D%w5xJDWA28w+--Ha1lJB?g(uf)aAEjKuNflm^6W0`1_Yh87YGKh0X#&y0} z^8LW#X!=epC;%@8Hp9@eUCX{8qc0F75F;dQJ3*6}pfE;O_Pa2lN*b0D$?L&A2gE~5 zqR^EOOB_1PCU{=J`w0nh!V@5%MBis^oc0e85CfMZ7&dB04Qd>stbtOBmlDoAF$-5- zW%+~nuJ$v0cEU%Z^}UJf{pA|1x;)cp9|wLwbi54MhT%Zxe?HFhJ%jHNQ4`i*gSz^( z-%mBD%`5im#W6sA`R-xukGIQgqkWd(`T*YB?+{$LIA$$bN2vE!Ub*K(nBJ5r#z{@t zq5vAxE29`1byoVlwom0hs8fS#AC@+CRjaj<-m3v81I2K5m`w1VY7S>t+D)x zKJ@?;!f}*quvSvHXuBXe5xWgvLII?8XjUL)JE~_5F113op<-zqOg^A34S)>g?DT{; zE4pKCimWzckCP^AW%8p2l*|!aDMKO`-234kwOJ{$CHUSr9kPzJ3apK=#%O;~ybwbC z{`OH~KTjcO@kn7lbl`cj+q92-P3lH!2;0=UW4LS-WOX+@e5A+xMYsq6E=qEao_%|4;$_p|OcW$J!oUk9?S(21`YJXv+Z5(ZT?=A3YBk z@mXp6>#gxijXV8@n@FzkPf6k(=KTkctk!5`CmTDDoUKK%(6JI zpW?V5x>m^68{D3pT6rmBsTV+M3pmsB+CrJWkU<L7T~g@&sH3d*E(-1d9q?Fd0MHx; zIDi4p(gDAOw;&(V{+JR0qR+09nq}`N%-;}6-ea5k0hcIM1X#zrTS*3)EN;NKmU)qF zYJVN8$Uv?>9-i}-R@RCy!4Q+wRPgj`W%6OFsx9VR--lVN>64IcY96#*T+mUuvOgpA z$#0xcS6tWUa-NS>o93=RylMvP5(rPehf@Ssn<(5=CYX$4QxLOn37|sn?K=)P&W9pE z{Rc!6&IH!H8zG(wM8JZ(ycKN-^*q+LFeN{-av7*)hnJ@BEPb7k8BZ)zJI}m>++%3O&1D=0i5`vk&B$Tp*etU8e&V!RK{98X&*sV#6fR5p(YwX%`DG~V0P;{97`;g zhnb6U(ZO{TMyUcW!X0L!6@xQPSh4Rx3U+aRZlLaGc?6mTBS51@q;3HxNwXRW#d2Y+ z?}_GMiOLNflqfp1L@JgDe|)w!J>R1f&d9UH5R%7=(!(ELLn|9u$YCQ34E8?(Sb3(& zmkuDQejhF(ReCeE_N_qYOo6BG%VuhEQ+Urna8GK+Fx}U!nc|df>O&`VM*{}`6y!_q zBe-kf#`hxpq;By=qVH|=p;1H`tpMd#K+?XC&{bDPvS#Tw;o5ZHR#aMQT)x*%>kRoW zZ)NLELSo%`h!pSP0bHT6Y@BTX__t^lo;h1e1UE+a&~q9<^*B2hJi3TRq*9p>H*`Y$ zqs5+tG0HHI$_vm5!k+zj=zxx{F$_j8y#~;Mhhjr`+I4LG?*PNv3>Nn`4Xl+bD}4iu z8#Xr?@piY#^LFA1FcC8T4(TVxJ$j*BlhN$>KImtQz2zf}=>@g{;sF^{5Ig z1;p`2SA|#rJ(Ya>U@@2aIe#nm28mqG+Ue&9c-V0vc5cA6;b>WZj1}UY&4a~W01|(a zdeG~!O!x_i!lH`?r4R=|qj_SXNg8so<0u#*>0xV(!v{A5$ENV<7;SrC%rJolV#=+a zk+sfK5ad6DEn(!V&QtwLhImGRSU0^f!Jn?LB0=RPTYkYhJ6q~xSTW}BsoE^ZzpR=;f%~z=j&+cs=rA+C zzLlv=79aQ!gSMFWeF(Yln#1({Y9V$Yu@~fUqq33xo^^=4Pua{6Q-xm_;o#9It0(zG zYIukE;I1?FmnB+XO z&a<0xCdkQ8VDRCaL*EmRN`^MnRY`R>inFD*KH`JU<{WXhvpH9Aj|Tv%yHvr2TdoQJ zT0vEsBDjaZD+X^Qt)Y+o z2Mey3QNT@C3NDpW2C+Z`CnG}m!JgxTf-3`And$fdyJMu8O>G^-5Ce#~?+O|Z!kTkF z905{EkD#|FptWX-uX4}$Ja2SqtkE@!^J=1LtD|8i-vRbLx-#BW!#}T(nvH_%DclX< zsrT^%+vW&1?jbctvN5W)U+KmITvq(C#e!>N1e;=Wrr#&Hae@ZB)U!pi{Wihf7prs% zV|pG2f7N=7UqdDp%&Fk|9)}>BX9}(pbaS`ht_QwYGv3-pt^H`L-s`!B$G`ja2=Qu` zVA-$RtTdL;vX5lc1yMb01lA_B>lDzmFhj;W zeLwCcWP49z-bjqhDtuqGw$}+?lAlH9y85kx?7kE~m!UfTG>doEu4}IvxQu%37vZU5 zpS2s>!MWEnyvMPhQ>`~y9KC|B2=#inxGvkPi|Z0FIP>6@s6C0vIt;)k*E}%tUqxum9b+h9pj(Z-JDBhnTf@Y$BT4?x4?rSjy}?H5~dP z=vYsIeSCwk;Ea*_gOBCI`6BTj7_Bwtn2k8J#NtCc#E0o}gJPAMO@a%@TOYJY2hKx# zIPYx(@2=x_XNmbjl6wl~58Qr$Cd7N0_Y#uHO?feCcb;@aWd7mdAdnVF>Eb!{Hx3V#{+Fxi) zjje5S{|&>W;n-wD@-1Q7CLI9J{kPe#uzWB48q6m~N$FRXSjb$^rVYTG6=0^R90q5e zc4)i!d{)CA-0j9FJXMHs-y@dtzApU06CQ@kkKiP;;JyY~nC92@4*CFHuxB^`lI=DK zXK*B>vD|bmR6&g0CPod*I6SvOK0;$d@-D(^JPF7Ko|V)%`u`_SWhoKIjG3~cjGYKX(yxXoShYpBcZ#ekMDpbJ?mf9_XqBS!DhRUnSR zxft9jY_zB&)5FGudNZIY4Cs3t5h{#;nh>OlNOa@P_X#c>+MulA<4!r;{ z%o8*{Atnq>4~FLS4np$)y0%I|9+d)V(|n0B=9nN9 z!M%ok$KFsPjVdHS{BFG06o7fe;`Tefb~ie@t%;zZIie&6zhn6Y5H{0JG?BPYYTIoz zA=yQalxLZ>=UC$HkXS<-XxHSJ*oCcaW8X{qW(E5cY(G2s3;fKMHo4d+rYSUMj{B#g zX;6$()3B1hHEw+(-5cCws>&Ar?|y9Fk48=OWpWHn^P3n@Y^8T zFi&yg1V2l z4E2NS0t9!r0g$24gahOea66nC>V1@H1n%gR;F;~mt)Eltw64Y8oQxL1h3jeMmlHU* zoHFj;kx^u`J+?z}V?ozA4y>9X7uk}ui)rmsW?4};gG{f=UdGA&VWn-e0Yq@+Lg-k2 z!F4pp|6{eA&GEY}tk}Uh{$!hfANiVB2dLxM5(|yh%y^)>K z`zO&;dY?mj(>kN)AbO^ANw1_cdSyiK(YSL+PwtG~i$t%n*Eyv3@6PCbK=kfEm-Lo) zMz50S-F7b9!@ADs?I3#F&t-e~xifkPiQd?AN$*f+^bF)5Hl54%VC>94^dWlRo=bZD zJEJ$4=;faqdKl6?U{)TY$;lM_o`8r!{BuEZhx9Xc3NHNapge8>BfkBw5`Tk5gh==o zoEbc-b;G`MPmIoffCOcv*}c*6n=;a*#CUG@Ni7k={MC@2vB9w&rv;T8_3$tgQhl@F zr!m)-m?*gaeI5(eq=!rfu`4EK3a;b}cw!~)_MRy1c~zmX5qqhsI^rrEx;w!FNX%gp z+{Zy>gTm=`B1Aa-e^)jKkDnXagcWegah*12?I-BXeV&A^K#w=i)RW7p-F@5Roao6y z64f|cdW~B@DTs9EW)rSbGbPdjN&G4w7f9lcEy*&A_lr7+?vp6OkCfE&a7(HA7ue`? z^Qi!5L)9Z^+hjZ?v&V_1noPDC_g<+H8@+ z{JSn9Mr=3YMlSNv$~~q?-15E1W|ybajxaW@H_Csz)C#z-258R>e&|M|cqUopUGi31 zR&eZ1kW>nA8i~gejI_!=eh-+_Z9%;!79sYVvVFYdR38ofit`_;Y z3Cd8Q9D5=ACK-D>!~4_5Lbj1|ZDl>~XPE3MGMeSB%22mo>?vthROX8S=tfL&9Aa=lC}`))WA+>KFH-`&iHOuhhPiXBAPf8a)W8P!zjB`+j()tRruxdVFTGaum{ z%?C`31rVt_1DoXcXt)zQZjalTUjj7epgq3K8&?|q7xM2lljW*E(XCFy0{_yJ6gy2B zX2Or7`cgaG+>`D4hJyMN=` zOH=!XtP^p>R)JI`ZUj~DsW;CCRCV0wtSG_+RwGgAN_%hq59|_XywnE3=K=7I+ArMb z=l;222>BajgwD?P!}PTsFyZJXAK{64iD5$!hYvH&D3%_IH3;ti_96z+l9xVYa{h)2 zFsK*b_)AsG5So;ISO=o@V>#2`DI{KXW zV#Wcjpy2V(L0t;8Kl2l(BIwCG$rymQo(~pIFxMIxwq82Eg zlzz3~x=ujfqYp9q9@dyi$3StCIYcZ2+Y`N{SAggvp+p~%f8tph546j&re`3_qoG+I z^+jrYE5N`g<%$D3BRc#4gB0KP1C0Ihn@wS;?_%7P6-5~sW;Rmjd*?uV#QDjP{Op0i z=%zH}V!?ego^Ub-ifolyj1R_1Ez!cf&q1nEOBcW$hjU0_ zk@`g)_i0L7%87pWb3#9q;P2T3M(@*eV-I*F@|c-Zp3}K+a1!l^h;v38o8{jGml_+^ zEcfDUVes+$U%C21D;QzMp>~YO2MMmryYW>(C*UT~5XTP;8*kqUkG`jW4H$c!$B&>a z9*BT&H=sTFxFVH(bhnE>x)WTt{;Dg0yvvi;6@ApY%(9~1VPo^dGfoPwG=MDUE~@VA z#^twY?{Ue0(OI2;OLCuJIu~+>wq0aP@=g9#Hx+uLyZV=Q&G)u$f#!QlX!E_rmyMve zn#y}g2X3P;roM^A?TUx`6CK8X;D;<125_Y|%0G21yJ0B1ru`thgw#Lb-*M4U2Li;! z8Q|Z!5oaa27m)g1+27t4j>F{n``bI>>o9rd{-7sd$(LgC&1XOh`vZ32cI-s|&R9oj z%U$qR%+~9xQ@!Y{zTOP4B=0`ps%l?*W*@@jCHvZ!_@9`(a9=?8GdoiL{dw(_pA~O} zcr#P{_>T2n9HQ&|uCvwo54nPwEWhU=ckgXq!sqt}5j=Rj8o0ieO5K!d}TDF<@+O7^r@@#H=248i)?-|^$V zj59L-wi|F>6iC)dZNucQdzdCll{)l$7lLsDME(x_IF4hO6h|8UbxSo=w5J7fqO}dV8Om`bbXjd&aUSsh0T0} z#do(Mk<07p3&^!6{Ql4H>m+6q$)BCxbuRjtpZ|n}2K;W!vg;hl6t}@>ZwKDV{C{Q# zw64Er2j1L?TX2Jb0lm0dRUo8mZ6d3HBLt zGIj~`47i}GY2Y@gDY88BF_V3YI6QPX!%=fKUC0&2U>EXf=Oe}`j*p#>M2vU5=b06m zak6A_>PFo4K;EF?C!v+~CS&cej5fiQ-4#E>>BBw_DcR>4xz>3qN|^sKV3Q79t8ERp zV10h3W}U{m{L)I?3SpMw@npAz8kU-pHD(huK%%0~0*n>0udcl1w+_P^5RW)KIH0~RA&>l5rX~;E# z`?H2%OisIEp}nDV&BmB%%sj`Wqy!hXBa1_zG%H`g#V)GaV>Oo>PP6potfc{K?4wbL zb5;;Q5`Xn#9UHj=#jmcTH@cYp{Xxth)uE=nO4V216|3(iUtF86_`YTw)mVt@a5OWX zWt4t}qX(Yc@ze_tfbh*e4uqJu+v@_J^fz;%WR%yU|5!czSZpk-|5mJZqF#)5&j)VoI`Vr=<++LkKhT9{X?sWpL5Hq zriP&Lk5v-*ylqgyf7X_>&5kO3*EXJgp8JlTeV&28o_(IBznp!Z9*538&pUrXZeJZc z`#fK3XP>9*iL=i$jfQp4W}*OsC_bwUD!Ozlc^N$upSaipsk&t>`2^bYeHdd-x21#_ zt@pS7d1kBg3xwm};2_rDU!4eK(LOLeA&a&7s%Vj^XZ$|J42sW|P8&e8#&T#gp^Z~{dINxxE z;P(C+a7*2o7ZR=Lo4VqTREGtZ8WlDw_VxEr&a(^`eTVi3Tv;zuhpx=m8tgY#Ms^Ic zuzim(Hi`jG4g;JV9x?6vu3fG-IS03d<+{ap?7Ohu1?Jv#7{@UZ4}Hv-sW?KysCjRLb`;u!#NJvt@DLG5QXj_w(aG zsz)(g!SU8h#Mq#)x$yy@WeD`*Sy()vvIkf5U|^eh{H*eoHJugsWldrB1E(VbkqJ(P zganEW@VIgTYmZAl3X2qffLFe<&D8P7^M&(0c(5)r!$r0NU)@eJy*$bH>&~;{ani}N z0&haC!4MoY=sCO}4RQ4)UF07}| z3Vcn=S%DwM&rT{)KQc}V^ZFmfPnV|%?l`z%+eZrX&2V4#Rq1Nvo?_#x{uqDz;ES0U zoD0hg?Pklze9s)Ajc93Ag72Cg)K2yF|2;x<=wf*A-S;i^eSCv=oK+!{&rT*a&Cs|9 zF$=L|2~fPz?3;9oC}V7m8(t_@UkS8$=#3rI)7`KCjNS;ISN@MFK%P=hORTuk8O2Rtqx8>Gk)@y?^3y2&ztR3N+B1>%MSRRAu4_BG z4-wr5j7XQ!+}0UQ>N1@{lXaQ?i!?`=e21_%$Y?*@DQ$^ppD>&&+Ki^FQ<~*Ov-I53 z$;?1`7Zdq@_FE`_@-XA9e=fKi-Yofs#AGBNAgU?@8-{89X{VF%|%2LKZ!gi zG@YMQ-&MRMtRntx{JqLFVS85}{4${B|3z*Sy&dxUCh^+iT=A+?KfY0?&e{FUpzF=R zP`w#Q2T>M#!(dwiu$PCzUhcbxV9&+?@mha2 zpOVmBruSv9&@g+cAD1zUH|ay|JM?XF=ba(rG+XGFxcVn^?^8Z;9I0jWVEH*xOWJUE zux3(#G7hOtG~;T7EJv(11>3@8lfF8+!8rksN=N7pey0?p5ttnv5leQ)Mb|i<~$TUZId=B zmNzI*At(9bJ@Su6JnSl7uG0F-UuNM>#q>(wsd$%|Q{IKee}A}BaTtfeQT{mXZIu4n zTTv1NIv`T&zwHp3xckt=>BN5gtHc(C#aLVAZ}FseXluBGZYJ%|ZN?7|a8DXMLcsA` z@PqhF+Ie#9>2`*3piwt=iJ`_WQD4jXUn5(~Y3enZnQr!evLXOkfpE@W-wx-H0M1)N zao!TLZ$c*YH!c-ibFd{**46(!^t*mLue9|JRHwZ1@dkbFtaB%k(6N(9(B3Tewiihz z?vY1qvNDS|u<{NJE$=|zroUhs7oSBQeOAqVwnP@5JrT0=?d6c(tjodYZw=k!RsN9{ zI*FiZjpF6XFf`s5%MCR7BOT}~HCtzQ!EGa;is;;mF3L2s`;fRxd6&$nq?=LwQ*^f9 z!^TtLVi#PK1FDu3D*7avLXIVM!;JCk+Ns-3#*TeE&@%?<-Hh&nI11gDi6HHX;(g() zOb@V<7|Kc_)4m(evWm1sPA5r>wqNe>hQ%A=M~wHRP~MX|mz}4noe0W)C%nuWZV$v9Oh3O-Vx$a{XjNO*;vO=A`c>W{F**aD+{uT!?|63!y;e1;QGN}2<^&D{(~bz;>!v`jtPkmJtm~F9A0U!>NUsrqr{NaSaPe>lq}wiM^BFnYpjw%?>Z%g zcAb(ybL^|b8n$>@M~zydohNBOGq~}YSD$s=Yw!Mn+$oU%{eA*kWZFP_yu6=w+;|dE zRLKjSxPgvmF2*{BGWUGA;=b5h;q6*H(PIVeV7T+xV#oRAIcED{!SyV*8~{=9Y6IC| zwKaw>fR$hN;!bv^uCjXp`YktZAx|5@y!;Zqh>_iI(F@!@o6Mx=6MA0CpKIL919z(e zcNP536X(H;vDRa9<327caFZLUzCqp66{Nl1GuLMEWcN(na`H%Jy-8io5_4@9dG%{p zT=x>(dJaF%!Lv;T=`^BrBg^U!|HYl#757Ua0&%X!U$Dtf0vvu6h}+25e*!<)s>qBoVQ7F&!wB%n!FuUgiqJw^G|GloCfvyO3SR_bVJBxbJDBqbeSWodP-J z>dn9SCb7K*osSqN3GPIAAnowwkSHRB`FNU-XKYt_Ar{^FNY^QjSmz^_NrLNt#^5?7 zcBqJFHOfkEQs*zkLE6+UQd@+uaGkX7dfn+Ysg{K#NWvDD+mjm$HPE4alW|k5|8PDj zaf%q1S{Hb4!S|2QF1<#MBFD&;c9LVOR29MXi}+)k>NTkke~g?ok`5Xjy~69(Hvq@> zh|BT_mL%^4HFx7ah2_~sPd;w=D$K*Z@loqb4S@~g--p}s2>CnvU`Ha#-^e!djq{9( zD9?%TOr)cQa^ zq?PzGPKVlN=$$CjZ3Q^4yn@A?xe(7;z>ef&f8|XBeY0mZuxlK@#`7yXvZTOfUHR6u zRWsE2E$n+oJa~+6$~%SB4|-yBMhc$guFM4ze!=yOq20PTPZo9-^M^*v8i_Dqjo!fZ za}oV0e=Y>BUHHiuA?IM=R)*SZ#a{llm5x^keV+@MOg42@Jk9dv%U5a30JiQ%F$2Kc z+JQs-R{#ayC_Y1zJSkeaevaN;$nQSn_zFDNBFKnb`yA-+4&nxUR_x_MY5Tdih`rGc zt8<%aPquy)Qhghq61N;V(@uB|A%E=rL>r!KDm6ulR`R3#5Jiu8se$I8*g18HWF_!y z-)Qb{8JuSs)xav;Yu`Y7`bpQ}L8lRjSd6WSq6Y(glq~iHhQw@XeHAQUB2w&+>*J&nK6L$U{0iK^BY zGgwf4S2FcYmzqqDD*(idTVSCo{?><7u>h2=5L9#=w4903`l2dM7LOOHMW#H{K_JuF zA%vNnCRPXvNg+B`n)?r;hOENET-ycs5jk83sIETvv4m}aL3|1>7Z}9(BxO9jEu`c+ z(rXw}A8up?MW%oO3k$;A(S&C*(jI$@T%ozmM6BZnlT7}q^2%4QTP@G|5!a3 z2C1nlo<@$FRRNnx!c)6YC|4seevRzLr~Wp4s&hP|bW4J}&y(?SBQqqQBP4zR-((cr z!!d-evXr)9q99?IB`h0jR%V%$uFy&K%rh7Ocm@6nuBQy_V@g*8-IxW}CkBfB@NCB1 zQ*=}(*&lTkv?-#)5YHN{j5X00!Ja1w;WPLf*-NUz)rJXp#);~KEDZX=Ex1q)*Pua7 zCV>H{@-m)#xwkmpS8`~oZ74R{MZ#yx5K}P}{IjhOMVEdFftP>amn*Pcl^(!X#%A2z z(t|r(&c{tb1=_PwSY~x?5tbc=EZ?BTkdHEG$cl0f$utHtZHG!Ktt5bNsTh&z_!7h) zPkR#jR8m^rBH|+RqLu1Oud6c52!WI+pPam~Bpq=aYN!xL@!Isg0n%V3Hl(Kyk3M%%EYP+3VqO|6W`VgLpn$ZC9nov zgI2D~ml&c$ce{}#xMx4^kTL)uo4m`-dv@hF^sa-&kv@MB-2DmbWSSnwOb=^RbsXA zpj%n-sasO(hW^7UEU2p78v%-21gxckuEaqLvnqX+f0)S00(E)2^koFMI9MQXgH2t; zRH@1AxD2%KN+nt`%iY}@9dD;rti)p&!w`%AgSNAroG#mnSH0iL=1tj|#5{J=&o-Wk zP5H&{;b$ASgLxQX96Ai@m0Y!G6_f5jTy;vPP^(jVpkQ&0n3-BD&DF+F_C3z%tFMA= zUdL?OSDen06=CNrmm-~RIvsZYdjI7}r%t?0xtb_8io?}5tePgjc!?a1xhWVF-w0OMYg;zo&kovmtLOiMEvOJoH1%pbJQ`)~$f5G;)T)d~tE zSIc&r`aax^fkv2SQ|sV%v^-ST+MJ-)zTI8d+KbppyQmv_>BcHnxtAQzU*Or3c;kBy z>fft=ds^4$4^=!5isB!T`G8hk&bcUORR}zfqUTwsdHE4J)c&jLZF-6vuRW{I*74Gz zKd5773i9Lin#rJwxM2Y&Fpw~s8VPFJCv+Uvnr9kM^U_U@SNXxbZ%g}tYC>I(AUZUh2f?tHt5@M z!xH}%+S9~9C-|lOnOy!K(bkPdTXz$4$gQY+kx*BWh>Ok#>x_f*&g8(+ZE2LW-mZ`R za@!bqM*FQGFi6>JX7-#U8m(j|(fEg+23;%cnu3uI!a;14N%v;f3In;#^oI5)55Pj+ zk#eCam^Xpr5B(?cr$AVk8Bq4~j7-^uvXh~0;5gEY$DS>Y83o$GXVf%QGdr{qlz(wo zYzNiYhxo25wgb_Y3@vAre8f`(wVM?W?6TTv?~3%^P>3gr7O%qExf6zP|)^@o`{hi+V` zAX@cb&A2EV$In)u(pwa2hx&ft@ke~@bxLnvDZU#<1ByWc=t{5!~E zBii%ln{>PJvidn55cLSN8^1Q`#h68Q;>W=AIC{o28hN=irw4na86XjF2W5MX`x~L` ziBNrciYJTtdKo7{8SBk@86ShvPGDtx7jAFDGU5RNI1Q+bvOqm2s281N#p|cWLq5H1 z!+@pD>N-3$c@H>@MNler=}Enq|5m@pGbu$Vg&Gkk)MR`-c2a-DgYMOdCxb;S4XZaL z2Rg?N0d=?zCCr2yD4L|#=G}oLr|uz!1zLuOwT#1E^b$X54%8}+K~t^rdOXq03Yw^1 z*@fa4y{tolw;w64z_Vw)UaE-?nn=&oiv#7kkLs*f;Q0Z1p4}KM;4@I?N~5mtcv2F^ z9Jt_Tl6he2}E43l_f$ zi@&l97QYCJKNpHW-DZMB$k-qHaa%#~*U)!cXm*1y9sB9k`Dk7Fc$Y4tQA#|Wlg`^O zd#k>3LN9CyR`yyvNEHtaP!FBZrF1Pm?hHKs9UpT}=rW6Er_g&@+=bnmIr^S zX&?n)qmW13fge>V1s*1_V>OcSTyEZP3Jk>o{D~+%W{GCl5qCT!>T;>Uvy^aQv4w~7 zW}MQ{gJr$sTM3O7dYXZFo$-$yb|A9`pbLX>E)K|m(v(H9ptTDZHBArEft*amcX7xI1?qX7zTqBH4JH#P_##n_SxQwEDHaNg8 z-gYtw-xmH4B(mPbC1PQ?SRVwCsq5%qJ}SS7RqTL_S!VJ2VCgN$SY0G9y~7HWX^3)` znRPiZ?{vsJmP9HhT4*pRlF;G`b3`ll&Bo$-M{xd~PCD|Bxrii`znRP$Xj{aW`JSsI0c-*)Nq5w0* z4kqEV3e5V7p4D;frqnu5+_h^H4Sb$Z>*CFkS`x)RPWyRTj^pyFfj`M7zS|$8VyfeS zekMR_U1|m7)2eY@6U`-p)zg=BJ!(GszWn9MEuL(@oLBG3?k?x;@MQlTXXery{QiOK zA*!*gc>y|Vbds^W>)0FBxwi8LfuTNMu2CFj<%arWZ+OIB!iR}VZ<9|Rd&l$emDRa+ zi$NK`qdGUvV$cUMLsesQ1Qklo+lNr5UW4?S#}e-|>Pip70O8`=!O?V9<~)>W9!V4f zL}>Hl>!o#8d3?QGYuvU11%qpwj(&GQ+G7+Ws|{J1BODEgVh|)m_BnY&He=U#>dX>&wZ5+bcKwL(6r++aEO zj&g$)x~(7L@k@DDf35aEum6(t2UpMV!gVUYj^o#9{F=+Jnf&VH*8+Z>%ddCv>qLGn z;@9c?D)Q?bes%HdOnxoY@jbxd_;nM%e$THj@M{IXdiiw&zZy6m6ThlF{WQNW=hr3t zx`1C(ID9g{*7Ed&{Q4xnHu0;4!udLKil|oBEP=H)8qN`KK}d$zi#8#1peO2 zuk}3L%%9`<^);TpgI|yH>w13ugkN9c*QNaG;@AJv-q(OfQDtf0gorW37@f%CP9C39 z0tO~vb^Ui&Rd+&uCSZV=M9e^jPSIUS8mhY6?&=UQIvGKXyICjem(NAT*{G~CJK33x zgHD#OVKQSxR8&UM51p*TIE)hzjl0&oM|s1*?{|pY-TKJ$XUreI9iX86H%oE3`M5PO_dv}? zZYVQTv#){FXCd_^oQ8eB<<+F#1Hk=(<7SAiOiM>;-@)`KR3&YKmzL^F9M8bt z{$?$*M$zIZ6b>1nI}ujfqH4fUgZ>sJ9Msg9Ki&d-=psE7Rt(k2M$tq}C0au-;?HLr`eKz`{zv0Nupd=4T;-Sl^-;h4VZFVcSQrEU9--LU0o5 zZFD>6ivj0rm;zJjNtK0VdK`7>cI54uqF$3xe*t=OzWTBYw=*U0|7O+lMI}{!RH%d_ z;SG8u5hucmuEwKEK%IwB+mfW-I0;<<%n#d`oPH^|klrsVPpWQThW@E|adV0wv=A7l zqG3NoXzQ}1Ub7T-I!mq4i9S0_$vDvgP!dK2+v?!0N&Qk_d=6f4q+N9oDYw>;p;ky8`E~z)youB9P>hN&grQt%d7t(Y06gs0TKEEHPg7@E% zXQiX88-XsmJ*kh{yzbkQ`l{Q`&-0LrkAqB`Ew}s<HZ0(o9< z`r>*p9)f(`5bm(iJqb?+EKKJAX92F8$AgxX#A3<@wCdZa)lk-GD$x{)5okqSokhUL z117da0I#HkQ({3VyZ{*+qG~AI6tP%mV|clk#A*s#3|LYYq;?8MOCr3EFd_sxpA(3* zDzT6fHekD8Bw}Gl65f>&=046+*_=>fu>HW|XKNQzn^beRL>Q_bjl`7L24Xg{(po~n zpbGm0Eu`2QjF=LRD^?o=S|qGa)OCs^f30G`-;zKep(B4q3?xtyfg(|u`wiHZr3eBk zLDUSpt~Nw?K%=^zKnV@OrZfR)f(@Apr6*K0M+;gFi>dKQA_f(Qja5Ly{YMm{g$oxV zqa_qacSe<%J{wt^IpXZ#L}GDbow@-wLb!wM)S!By#5*-bUlUXc(Y)E{&O{gw6(m40 z&^@cH7Q+6>Na}yIu`!X-(0qL=%9s|4&sELEWIj}o)K4!LCNX1+4OzPu+8=x*(8(?p}F{wYb(F&*E zW#P~7hCJU*>TCz30X~HIAjBU8`~>hR$jt4qkoFx5KLh+V$n645yC6A&5UmkSF+!S(tkFSp=YlQk zQB7?lP)JvrVcQC}h&-P8i17PcRXjfU8$xL8(WHLzkuMLOpf6(^%$M8FrH=vfAA@xq zP!HG#kiLI@{(hU64SDYjs!d8lgZ*`^c{aKWrVYZZbJx>g6E=pQngEWEI&dMGlw3U2 z90n5%v?wvOCKA!iKdKT&7H5N@qF^Keo{hjbn+cn4(jp2z$C#mu5>O)SR1^vu6C7v6 zR5Fod#{7PeI7i1>PP`=&b7-=nt&va=t;gl~4XaY;E45hQ!Z79p)L; zg!qNU+qRxmhT=oCMI?YyZFKM&gUgAU%dJr67f*LZ=I3QIT172Njp;|>=Z?W z$+sLXri6^R$@bd(vn5$v!fUAsfd*C4ojPsS=PHHl=LlQ*BX$HKr@qJ+(HC**3r#3= z*6#}`bm*A;7=TSjEIUbssYk)-CoS7eg_}-Vwu0Y){nbfpOjF_Cp0xZH- z9|g@tup+>W54*PJaHI+LRU1*^JQUDT5v;H1ZWLUBZkAC{vG1c+eH(9}(0mlsY!1ZZ zXyJUc5CvP&%}8C2R?A3@pw&pd1IaRKR*@I}8pw;ZS|mv_f@KE&rnYi7-bf%V4hcvz zkc9NbXn~CMDl{MdhR_0}Ye?78f(0n7;C&u!sKY9{K}KNKu>RbD!s{^`MQTtxD2<@@ zb_CARj-plQ8#0P&=o=^s`MvW|G>X0fkvK$<55<@grlUD176%%|LmO4x z5%G<%gGA~xHS zrzhe#{r>{}?rCd&z`Wm0isNrof8Z$ZDA30o`HusA(xLw}&>uVG{{-~dY0E}&`t(tt zM!x1ly^PQH#p+6fu8G-p9gf2BYh#zE=M{8H1CMx z?<3>y)1=$wdmv4Am!KU!t273^6Q63Mv4{}~L^PsR zjm0rzPHdUlkv6MT7LwN;vm-?2rP)V=&HWJ z2;#|p#%u)5cdDcq7m>>O4dPlIOram#yYltYK1#LJ^x+c zG8gH`uU3>bp;niRg=0hnj@&_iBD_8n4wg?!=Yhu84Su{S_v7n~@(YaF)cW0~eiat3 ziI*~;ui>2h6wvX|7plTC_u02c6f@Nurq9vf%+F2z_GwBDwT6qH{0^$ta68_BnQ><_ zj-PFuQ`Zg{{|E&v9KlX?=+M!`G`@jgHv&^@lH-vcKhe<$ed zouoJQ3}L$?lX@L_zJ3jOEjK6iA#!T{_@@*2XA}nj^^W(^!@$pn_!#gT9sCjCw?SP- z09)M?eEhqIGAOGWAl>XJ59~Y&at8q2?(^$`eW@Snysvno{v@<>u=v0KyYW0bOxsw4 z(%LiAzMh?t(TP8y0zCY$X%AmDK3MX_fb-e=DZ|qT#F;_kwWgjE!b=wfPs9*#rzp8%Qs zi}ZnDmvaPU8_ye;s+|1AIYf0%KG18;zoj|ZCHBym^}yw1K07|%WsmvdE$J1Trrnyg z*Q`oBC;wJcOL-1)OV;yy&FYzbi*2hB@^w4gPz`M;H8WZThpi&da#xyhgzfYIpda#Y zwacvsTPufAzyf}gZZPSK*-6n$!Jz&ah!B5}1EUsqmCvRO42C7POG_qPb|9!n#z<-C+k<*jPih%Z&*>n=sQLW`^~ zjf9O@L|YnxlldiNsS1VFcrENJ6Y)zCw-RuZm*Jl)Tr>+D?8BGTLD594`O>;FrYb?R z6E7+mSHrpRiUCpJ4eSraqfAIlK7mbAs z)#}(wdVz(K(KfKz~sL>kHiC9FC>VTXMX?g9(}_5%(A z-UEyPP5{neIq1VIKpxuLd|UwO5)y&qLour66di zrXzud`|Nl{NQ1H-v13aUh6+WYAK39)Rac@da3%%b>!{4UA1wtR3k4LdRyE&tqhqKX z``=_XnweG}4=7O;SE(Wqwz>1uD&XX7HDg`cH&p|ZcpiolzJudf+?<*^&cV$8_#R|AJWaa2cLA+V3`_-sXEjHp+7qLi^FI6 z3#V17O6z%Q&;!$|@!Emgg?>7%23mo~1)j&w&st5EaLVs^kQKbm&_% z`Gxd&G!_aQP3V>MxDhkuUNtE!cPzcN$xcQHuOonxvK1xOi)m_MSydJG^E}}FSHdmj zHOtDYp#FF_iTB08)8nmym|<}wh<_Py>imEC;5*dlGynbXI=*_&f3EA^t>*(eR_@j_ zI(C0$1OL;WTsWgcFrCaL=a3%qVe(D#FaUZ$g}?cGa-A#gderr#>zA%KUB7pI>iV?@ znU{k29wp?;p$6%F^dUNpS;EvXE14m-PG}G!!Ya8%{-OL+d9VDwH_dm2?`q!yAMcZW z6+V2)fxjHGggoW?74<&#Cn}A;n%+ov($CR9r(XhH0@ayWMZOKXO-j>O4VD zuV;s6!1Jo-BTs=SiB)2q7!)^%&xj-92@y%xO3S48`^aa=SIK3rRj#$JyIp_jdc^fJ z*RNe?T!_l0=2A5EAhnZvhx%7)HeEt*q93Ps)4!qXn9tZ;ZjO74`(DpR`4?VOZM(%okHebWv&hHiW3sKKw-ba0?5h1+z z4v??G_?9zwF^@A(x*zrI@mwL!g;qW!9u=oc-;!FTm!uCQx4c6B0gU2x(AN9CPkH|Y z?F;#~_}=k-jN28p?tK@NP2@k4Z8oJOo)Gp3KljWK z=ZlX?yCko?Qr_cx7w7Lls1gE+TuJ@|`7(JEf>*;!tI!KLDpHNwJE4`I|i}@{clKGe!XJ)f5R%DCWrR*AZBiq3~ z$v(^eANCdYSL{(Xjr%&saY620?uXnjxHq}qa36Ei`D^)``9&}`Eqt8+5kJIVCd?A9 z7rr6fCae=$g(rl)!XY8eo#meC&Ud@qqWe~Nz}@P3#Peg%2cFZObn(x`8^kuTQ~Vq8 zDRGxLB)%yU(j1ABR!U9MM(KX($I{!<`_gggDtWH#mR0$$$qz=H510Plv+vY)U(uE z)E}V_uAo`El3oQC`ZE14{UQAc%`mqxtC<&=KQM^BioKp)?taMgea};#A9d{FN5;>%p~qYU`+6$Uw(flxi!KsJ&=vW3*h zC~1&wWINeGZYDcPf-0a2sUnJ`7)qcds+cOHDyb@}nyRDfsRpW%3Q{eUMnx%uYNOhz z4r(*iNp(@(R1dY4+D7$K+o>H?AJq>&K0qa@L28KFNA0H$fL$G;hN;8U5o&}wMx6jF zJ4208h|Zuh>1;Ze&ZB41`7}Wn(1mmnP0|c4&<%7W9i&@mjgHa=-A1?59rR|HNxSH7 zx(DoY8{JE9r+3hObU(e59-x!-AUy>0^?v#QeGq2aVfrwAgdU-ffwhm)XXr5+F&RuI zlg;EZdCV*(pCOn6rjRLONQPkqMq-MYGNzKLVyc-srk-hF8kr!|!e~sC>14W?ZkVsP zGTWG5W;?Tk>4Uj@Co{k#nL%bhbAUO>9Abu@ zJB!U{3ATVOWQ$mmWmtigz^BUCO16rvX6x8`wgG1UAk6<7tN;ev#!F1DNP zVYjl|*j{!!yMygx``MlB0Gnh7*&%ix%p?ccgX|%8m_5uMVMo|w>*jj6t=u-Q7uKO2uoCsdS~S2N;0|(!xMA)vcZ3_^j&UcrQSJ;k#vwj~&*ZcD zTt1JV#pm+`UjVCC5l`|AtX&dc%$M<%d=*~}>sUSCz&G+im?<XNZY#fy@8!4iJNQ1npWn$3@JW6UW|V#We*OS|kUzu^^N0B({0M)HKf#akXZSH5 z2^m7BkS*lGnm7wqMM5YL3WXwp6c|AeB%xR+6Dox&p<1XD>S0B16oNvFpb1gI5ZZ)x zp+nd#bP8QUx6mVO6}Abz!ggVY&?oc@JB0xuDGUlju+Hum4hRQf9vc=83rBQfF(SVt*UF;Ay z!+h5zc8fjYR&kryD{dEeh<#$exKkVulj0!Eg8Rh%;sNoXct{);4~s` gW_NaJ9 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 diff --git a/EasyModbus/pom.xml b/EasyModbus/pom.xml new file mode 100644 index 0000000..f6b586c --- /dev/null +++ b/EasyModbus/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + EasyModbus + EasyModbus + 0.0.1-SNAPSHOT + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.0 + + + org.scream3r + jssc + 2.8.0 + + + + src + + + src + + **/*.java + + + + + + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + + \ No newline at end of file diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/BufferedMessage.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/BufferedMessage.java deleted file mode 100644 index e01e61a..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/BufferedMessage.java +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Initial Contribution for Automatic Reconnect & Offline Buffering - */ -package org.eclipse.paho.client.mqttv3; - -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; - -/** - * A BufferedMessage contains an MqttWire Message and token - * it allows both message and token to be buffered when the client - * is in resting state - */ -public class BufferedMessage { - - private MqttWireMessage message; - private MqttToken token; - - public BufferedMessage(MqttWireMessage message, MqttToken token){ - this.message = message; - this.token = token; - } - - public MqttWireMessage getMessage() { - return message; - } - - public MqttToken getToken() { - return token; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/DisconnectedBufferOptions.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/DisconnectedBufferOptions.java deleted file mode 100644 index 21ea06e..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/DisconnectedBufferOptions.java +++ /dev/null @@ -1,91 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Initial Contribution for Automatic Reconnect & Offline Buffering - */ -package org.eclipse.paho.client.mqttv3; - -/** - * Holds the set of options that govern the behaviour - * of Offline (or Disconnected) buffering of messages - */ -public class DisconnectedBufferOptions { - - /** - * The default size of the disconnected buffer - */ - public static final int DISCONNECTED_BUFFER_SIZE_DEFAULT = 5000; - - public static final boolean DISCONNECTED_BUFFER_ENABLED_DEFAULT = false; - - public static final boolean PERSIST_DISCONNECTED_BUFFER_DEFAULT = false; - - public static final boolean DELETE_OLDEST_MESSAGES_DEFAULT = false; - - private int bufferSize = DISCONNECTED_BUFFER_SIZE_DEFAULT; - private boolean bufferEnabled = DISCONNECTED_BUFFER_ENABLED_DEFAULT; - private boolean persistBuffer = PERSIST_DISCONNECTED_BUFFER_DEFAULT; - private boolean deleteOldestMessages = DELETE_OLDEST_MESSAGES_DEFAULT; - - /** - * Constructs a new DisconnectedBufferOptions object using the - * default values. - * - * The defaults are: - *

    - *
  • The disconnected buffer is disabled
  • - *
  • The buffer holds 5000 messages
  • - *
  • The buffer is not persisted
  • - *
  • Once the buffer is full, old messages are not deleted
  • - *
- * More information about these values can be found in the setter methods. - */ - public DisconnectedBufferOptions() { - // Do Nothing. - } - - public int getBufferSize() { - return bufferSize; - } - - public void setBufferSize(int bufferSize) { - if (bufferSize < 1) { - throw new IllegalArgumentException(); - } - this.bufferSize = bufferSize; - } - - public boolean isBufferEnabled() { - return bufferEnabled; - } - - public void setBufferEnabled(boolean bufferEnabled) { - this.bufferEnabled = bufferEnabled; - } - - public boolean isPersistBuffer() { - return persistBuffer; - } - - public void setPersistBuffer(boolean persistBuffer) { - this.persistBuffer = persistBuffer; - } - - public boolean isDeleteOldestMessages() { - return deleteOldestMessages; - } - - public void setDeleteOldestMessages(boolean deleteOldestMessages) { - this.deleteOldestMessages = deleteOldestMessages; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttActionListener.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttActionListener.java deleted file mode 100644 index 51f1e58..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttActionListener.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.eclipse.paho.client.mqttv3; - -/** - * Implementors of this interface will be notified when an asynchronous action completes. - * - *

A listener is registered on an MqttToken and a token is associated - * with an action like connect or publish. When used with tokens on the MqttAsyncClient - * the listener will be called back on the MQTT client's thread. The listener will be informed - * if the action succeeds or fails. It is important that the listener returns control quickly - * otherwise the operation of the MQTT client will be stalled. - *

- */ -public interface IMqttActionListener { - /** - * This method is invoked when an action has completed successfully. - * @param asyncActionToken associated with the action that has completed - */ - public void onSuccess(IMqttToken asyncActionToken ); - /** - * This method is invoked when an action fails. - * If a client is disconnected while an action is in progress - * onFailure will be called. For connections - * that use cleanSession set to false, any QoS 1 and 2 messages that - * are in the process of being delivered will be delivered to the requested - * quality of service next time the client connects. - * @param asyncActionToken associated with the action that has failed - * @param exception thrown by the action that has failed - */ - public void onFailure(IMqttToken asyncActionToken, Throwable exception); -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java deleted file mode 100644 index b0a1e32..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java +++ /dev/null @@ -1,875 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2013, 2015 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - * Ian Craggs - per subscription message handlers (bug 466579) - * Ian Craggs - ack control (bug 472172) - */ - -package org.eclipse.paho.client.mqttv3; - -/** - * Enables an application to communicate with an MQTT server using non-blocking methods. - *

- * It provides applications a simple programming interface to all features of the MQTT version 3.1 - * specification including: - *

- *
    - *
  • connect - *
  • publish - *
  • subscribe - *
  • unsubscribe - *
  • disconnect - *
- *

- * There are two styles of MQTT client, this one and {@link IMqttClient}.

- *
    - *
  • IMqttAsyncClient provides a set of non-blocking methods that return control to the - * invoking application after initial validation of parameters and state. The main processing is - * performed in the background so as not to block the application program's thread. This non- - * blocking approach is handy when the application needs to carry on processing while the - * MQTT action takes place. For instance connecting to an MQTT server can take time, using - * the non-blocking connect method allows an application to display a busy indicator while the - * connect action takes place in the background. Non blocking methods are particularly useful - * in event oriented programs and graphical programs where invoking methods that take time - * to complete on the the main or GUI thread can cause problems. The non-blocking interface - * can also be used in blocking form.
  • - *
  • IMqttClient provides a set of methods that block and return control to the application - * program once the MQTT action has completed. It is a thin layer that sits on top of the - * IMqttAsyncClient implementation and is provided mainly for compatibility with earlier - * versions of the MQTT client. In most circumstances it is recommended to use IMqttAsyncClient - * based clients which allow an application to mix both non-blocking and blocking calls.
  • - *
- *

- * An application is not restricted to using one style if an IMqttAsyncClient based client is used - * as both blocking and non-blocking methods can be used in the same application. If an IMqttClient - * based client is used then only blocking methods are available to the application. - * For more details on the blocking client see {@link IMqttClient}

- * - *

There are two forms of non-blocking method: - *

    - *
  1. - *
    - *     IMqttToken token = asyncClient.method(parms)
    - *     
    - *

    In this form the method returns a token that can be used to track the - * progress of the action (method). The method provides a waitForCompletion() - * method that once invoked will block until the action completes. Once - * completed there are method on the token that can be used to check if the - * action completed successfully or not. For example - * to wait until a connect completes:

    - *
    - *      IMqttToken conToken;
    - *   	conToken = asyncClient.client.connect(conToken);
    - *     ... do some work...
    - *   	conToken.waitForCompletion();
    - *     
    - *

    To turn a method into a blocking invocation the following form can be used:

    - *
    - *     IMqttToken token;
    - *     token = asyncClient.method(parms).waitForCompletion();
    - *     
    - *
  2. - * - *
  3. - *
    - *     IMqttToken token method(parms, Object userContext, IMqttActionListener callback)
    - *     
    - *

    In this form a callback is registered with the method. The callback will be - * notified when the action succeeds or fails. The callback is invoked on the thread - * managed by the MQTT client so it is important that processing is minimised in the - * callback. If not the operation of the MQTT client will be inhibited. For example - * to be notified (called back) when a connect completes:

    - *
    - *     	IMqttToken conToken;
    - *	    conToken = asyncClient.connect("some context",new new MqttAsyncActionListener() {
    - *			public void onSuccess(IMqttToken asyncActionToken) {
    - *				log("Connected");
    - *			}
    - *
    - *			public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
    - *				log ("connect failed" +exception);
    - *			}
    - *		  });
    - *      
    - *

    An optional context object can be passed into the method which will then be made - * available in the callback. The context is stored by the MQTT client) in the token - * which is then returned to the invoker. The token is provided to the callback methods - * where the context can then be accessed. - *

    - *
  4. - *
- *

To understand when the delivery of a message is complete either of the two methods above - * can be used to either wait on or be notified when the publish completes. An alternative is to - * use the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method which will - * also be notified when a message has been delivered to the requested quality of service.

- * - */ -public interface IMqttAsyncClient { - /** - * Connects to an MQTT server using the default options. - *

The default options are specified in {@link MqttConnectOptions} class. - *

- * - * @throws MqttSecurityException for security related problems - * @throws MqttException for non security related problems - * @return token used to track and wait for the connect to complete. The token - * will be passed to the callback methods if a callback is set. - * @see #connect(MqttConnectOptions, Object, IMqttActionListener) - */ - public IMqttToken connect() throws MqttException, MqttSecurityException; - - /** - * Connects to an MQTT server using the provided connect options. - *

The connection will be established using the options specified in the - * {@link MqttConnectOptions} parameter. - *

- * - * @param options a set of connection parameters that override the defaults. - * @throws MqttSecurityException for security related problems - * @throws MqttException for non security related problems - * @return token used to track and wait for the connect to complete. The token - * will be passed to any callback that has been set. - * @see #connect(MqttConnectOptions, Object, IMqttActionListener) - */ - public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException ; - /** - * Connects to an MQTT server using the default options. - *

The default options are specified in {@link MqttConnectOptions} class. - *

- * - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when the connect completes. Use - * null if not required. - * @throws MqttSecurityException for security related problems - * @throws MqttException for non security related problems - * @return token used to track and wait for the connect to complete. The token - * will be passed to any callback that has been set. - * @see #connect(MqttConnectOptions, Object, IMqttActionListener) - */ - public IMqttToken connect(Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException; - - - /** - * Connects to an MQTT server using the specified options. - *

The server to connect to is specified on the constructor. - * It is recommended to call {@link #setCallback(MqttCallback)} prior to - * connecting in order that messages destined for the client can be accepted - * as soon as the client is connected. - *

- *

The method returns control before the connect completes. Completion can - * be tracked by: - *

- *
    - *
  • Waiting on the returned token {@link IMqttToken#waitForCompletion()} or
  • - *
  • Passing in a callback {@link IMqttActionListener}
  • - *
- * - * @param options a set of connection parameters that override the defaults. - * @param userContext optional object for used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when the connect completes. Use - * null if not required. - * @return token used to track and wait for the connect to complete. The token - * will be passed to any callback that has been set. - * @throws MqttSecurityException for security related problems - * @throws MqttException for non security related problems including communication errors - */ - public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException; - - /** - * Disconnects from the server. - *

An attempt is made to quiesce the client allowing outstanding - * work to complete before disconnecting. It will wait - * for a maximum of 30 seconds for work to quiesce before disconnecting. - * This method must not be called from inside {@link MqttCallback} methods. - *

- * - * @return token used to track and wait for disconnect to complete. The token - * will be passed to any callback that has been set. - * @throws MqttException for problems encountered while disconnecting - * @see #disconnect(long, Object, IMqttActionListener) - */ - public IMqttToken disconnect( ) throws MqttException; - - /** - * Disconnects from the server. - *

An attempt is made to quiesce the client allowing outstanding - * work to complete before disconnecting. It will wait - * for a maximum of the specified quiesce time for work to complete before disconnecting. - * This method must not be called from inside {@link MqttCallback} methods. - *

- * @param quiesceTimeout the amount of time in milliseconds to allow for - * existing work to finish before disconnecting. A value of zero or less - * means the client will not quiesce. - * @return token used to track and wait for disconnect to complete. The token - * will be passed to the callback methods if a callback is set. - * @throws MqttException for problems encountered while disconnecting - * @see #disconnect(long, Object, IMqttActionListener) - */ - public IMqttToken disconnect(long quiesceTimeout) throws MqttException; - - /** - * Disconnects from the server. - *

An attempt is made to quiesce the client allowing outstanding - * work to complete before disconnecting. It will wait - * for a maximum of 30 seconds for work to quiesce before disconnecting. - * This method must not be called from inside {@link MqttCallback} methods. - *

- * - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when the disconnect completes. Use - * null if not required. - * @return token used to track and wait for the disconnect to complete. The token - * will be passed to any callback that has been set. - * @throws MqttException for problems encountered while disconnecting - * @see #disconnect(long, Object, IMqttActionListener) - */ - public IMqttToken disconnect( Object userContext, IMqttActionListener callback) throws MqttException; - - /** - * Disconnects from the server. - *

- * The client will wait for {@link MqttCallback} methods to - * complete. It will then wait for up to the quiesce timeout to allow for - * work which has already been initiated to complete. For instance when a QoS 2 - * message has started flowing to the server but the QoS 2 flow has not completed.It - * prevents new messages being accepted and does not send any messages that have - * been accepted but not yet started delivery across the network to the server. When - * work has completed or after the quiesce timeout, the client will disconnect from - * the server. If the cleanSession flag was set to false and is set to false the - * next time a connection is made QoS 1 and 2 messages that - * were not previously delivered will be delivered.

- *

This method must not be called from inside {@link MqttCallback} methods.

- *

The method returns control before the disconnect completes. Completion can - * be tracked by: - *

- *
    - *
  • Waiting on the returned token {@link IMqttToken#waitForCompletion()} or
  • - *
  • Passing in a callback {@link IMqttActionListener}
  • - *
- * - * @param quiesceTimeout the amount of time in milliseconds to allow for - * existing work to finish before disconnecting. A value of zero or less - * means the client will not quiesce. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when the disconnect completes. Use - * null if not required. - * @return token used to track and wait for the connect to complete. The token - * will be passed to any callback that has been set. - * @throws MqttException for problems encountered while disconnecting - */ - public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException; - - /** - * Disconnects from the server forcibly to reset all the states. Could be useful when disconnect attempt failed. - *

- * Because the client is able to establish the TCP/IP connection to a none MQTT server and it will certainly fail to - * send the disconnect packet. It will wait for a maximum of 30 seconds for work to quiesce before disconnecting and - * wait for a maximum of 10 seconds for sending the disconnect packet to server. - * - * @throws MqttException if any unexpected error - * @since 0.4.1 - */ - public void disconnectForcibly() throws MqttException; - - /** - * Disconnects from the server forcibly to reset all the states. Could be useful when disconnect attempt failed. - *

- * Because the client is able to establish the TCP/IP connection to a none MQTT server and it will certainly fail to - * send the disconnect packet. It will wait for a maximum of 30 seconds for work to quiesce before disconnecting. - * - * @param disconnectTimeout the amount of time in milliseconds to allow send disconnect packet to server. - * @throws MqttException if any unexpected error - * @since 0.4.1 - */ - public void disconnectForcibly(long disconnectTimeout) throws MqttException; - - /** - * Disconnects from the server forcibly to reset all the states. Could be useful when disconnect attempt failed. - *

- * Because the client is able to establish the TCP/IP connection to a none MQTT server and it will certainly fail to - * send the disconnect packet. - * - * @param quiesceTimeout the amount of time in milliseconds to allow for existing work to finish before - * disconnecting. A value of zero or less means the client will not quiesce. - * @param disconnectTimeout the amount of time in milliseconds to allow send disconnect packet to server. - * @throws MqttException if any unexpected error - * @since 0.4.1 - */ - public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException; - - /** - * Determines if this client is currently connected to the server. - * - * @return true if connected, false otherwise. - */ - public boolean isConnected(); - - /** - * Returns the client ID used by this client. - *

All clients connected to the - * same server or server farm must have a unique ID. - *

- * - * @return the client ID used by this client. - */ - public String getClientId(); - - /** - * Returns the address of the server used by this client. - *

The format of the returned String is the same as that used on the constructor. - *

- * - * @return the server's address, as a URI String. - * @see MqttAsyncClient#MqttAsyncClient(String, String) - */ - public String getServerURI(); - - /** - * Publishes a message to a topic on the server. - *

A convenience method, which will - * create a new {@link MqttMessage} object with a byte array payload and the - * specified QoS, and then publish it. - *

- * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param payload the byte array to use as the payload - * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2. - * @param retained whether or not this message should be retained by the server. - * @return token used to track and wait for the publish to complete. The token - * will be passed to any callback that has been set. - * @throws MqttPersistenceException when a problem occurs storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance if too many messages are being processed. - * @see #publish(String, MqttMessage, Object, IMqttActionListener) - * @see MqttMessage#setQos(int) - * @see MqttMessage#setRetained(boolean) - */ - public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, - boolean retained ) throws MqttException, MqttPersistenceException; - - /** - * Publishes a message to a topic on the server. - *

A convenience method, which will - * create a new {@link MqttMessage} object with a byte array payload and the - * specified QoS, and then publish it. - *

- * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param payload the byte array to use as the payload - * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2. - * @param retained whether or not this message should be retained by the server. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when message delivery - * hsa completed to the requested quality of service - * @return token used to track and wait for the publish to complete. The token - * will be passed to any callback that has been set. - * @throws MqttPersistenceException when a problem occurs storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - * @see #publish(String, MqttMessage, Object, IMqttActionListener) - * @see MqttMessage#setQos(int) - * @see MqttMessage#setRetained(boolean) - */ - public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, - boolean retained, Object userContext, IMqttActionListener callback ) throws MqttException, MqttPersistenceException; - - /** - * Publishes a message to a topic on the server. - * Takes an {@link MqttMessage} message and delivers it to the server at the - * requested quality of service. - * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param message to deliver to the server - * @return token used to track and wait for the publish to complete. The token - * will be passed to any callback that has been set. - * @throws MqttPersistenceException when a problem occurs storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - * @see #publish(String, MqttMessage, Object, IMqttActionListener) - */ - public IMqttDeliveryToken publish(String topic, MqttMessage message ) throws MqttException, MqttPersistenceException; - - /** - * Publishes a message to a topic on the server. - *

- * Once this method has returned cleanly, the message has been accepted for publication by the - * client and will be delivered on a background thread. - * In the event the connection fails or the client stops. Messages will be delivered to the - * requested quality of service once the connection is re-established to the server on condition that: - *

- *
    - *
  • The connection is re-established with the same clientID - *
  • The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} - * set to false - *
  • The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} - * set to false - *
  • Depending when the failure occurs QoS 0 messages may not be delivered. - *
- * - *

When building an application, - * the design of the topic tree should take into account the following principles - * of topic name syntax and semantics:

- * - *
    - *
  • A topic must be at least one character long.
  • - *
  • Topic names are case sensitive. For example, ACCOUNTS and Accounts are - * two different topics.
  • - *
  • Topic names can include the space character. For example, Accounts - * payable is a valid topic.
  • - *
  • A leading "/" creates a distinct topic. For example, /finance is - * different from finance. /finance matches "+/+" and "/+", but - * not "+".
  • - *
  • Do not include the null character (Unicode
    \x0000
    ) in - * any topic.
  • - *
- * - *

The following principles apply to the construction and content of a topic - * tree:

- *
    - *
  • The length is limited to 64k but within that there are no limits to the - * number of levels in a topic tree.
  • - *
  • There can be any number of root nodes; that is, there can be any number - * of topic trees.
  • - *
- * - *

The method returns control before the publish completes. Completion can - * be tracked by: - *

- *
    - *
  • Setting an {@link IMqttAsyncClient#setCallback(MqttCallback)} where the - * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} - * method will be called.
  • - *
  • Waiting on the returned token {@link MqttToken#waitForCompletion()} or
  • - *
  • Passing in a callback {@link IMqttActionListener} to this method
  • - *
- * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param message to deliver to the server - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when message delivery - * has completed to the requested quality of service - * @return token used to track and wait for the publish to complete. The token - * will be passed to callback methods if set. - * @throws MqttPersistenceException when a problem occurs storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - * @see MqttMessage - */ - public IMqttDeliveryToken publish(String topic, MqttMessage message, - Object userContext, IMqttActionListener callback) throws MqttException, MqttPersistenceException; - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String topicFilter, int qos) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when subscribe - * has completed - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback) - throws MqttException; - - /** - * Subscribe to multiple topics, each of which may include wildcards. - * - *

Provides an optimized way to subscribe to multiple topics compared to - * subscribing to each one individually.

- * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilters one or more topics to subscribe to, which can include wildcards - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException; - - /** - * Subscribes to multiple topics, each of which may include wildcards. - *

Provides an optimized way to subscribe to multiple topics compared to - * subscribing to each one individually.

- *

The {@link #setCallback(MqttCallback)} method - * should be called before this method, otherwise any received messages - * will be discarded. - *

- *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true - * when when connecting to the server then the subscription remains in place - * until either:

- * - *
    - *
  • The client disconnects
  • - *
  • An unsubscribe method is called to un-subscribe the topic
  • - *
- * - *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false - * when connecting to the server then the subscription remains in place - * until either:

- *
    - *
  • An unsubscribe method is called to unsubscribe the topic
  • - *
  • The next time the client connects with cleanSession set to true
  • - *
- *

- * With cleanSession set to false the MQTT server will store messages on - * behalf of the client when the client is not connected. The next time the - * client connects with the same client ID the server will - * deliver the stored messages to the client. - *

- * - *

The "topic filter" string used when subscribing - * may contain special characters, which allow you to subscribe to multiple topics - * at once.

- *

The topic level separator is used to introduce structure into the topic, and - * can therefore be specified within the topic for that purpose. The multi-level - * wildcard and single-level wildcard can be used for subscriptions, but they - * cannot be used within a topic by the publisher of a message. - *

- *
Topic level separator
- *
The forward slash (/) is used to separate each level within - * a topic tree and provide a hierarchical structure to the topic space. The - * use of the topic level separator is significant when the two wildcard characters - * are encountered in topics specified by subscribers.
- * - *
Multi-level wildcard
- *

The number sign (#) is a wildcard character that matches - * any number of levels within a topic. For example, if you subscribe to - * finance/stock/ibm/#, you receive - * messages on these topics:

- *
    - *
  • finance/stock/ibm
  • - *
  • finance/stock/ibm/closingprice
  • - *
  • finance/stock/ibm/currentprice
  • - *
- *

The multi-level wildcard - * can represent zero or more levels. Therefore, finance/# can also match - * the singular finance, where # represents zero levels. The topic - * level separator is meaningless in this context, because there are no levels - * to separate.

- * - *

The multi-level wildcard can - * be specified only on its own or next to the topic level separator character. - * Therefore, # and finance/# are both valid, but finance# is - * not valid. The multi-level wildcard must be the last character - * used within the topic tree. For example, finance/# is valid but - * finance/#/closingprice is not valid.

- * - *
Single-level wildcard
- *

The plus sign (+) is a wildcard character that matches only one topic - * level. For example, finance/stock/+ matches - * finance/stock/ibm and finance/stock/xyz, - * but not finance/stock/ibm/closingprice. Also, because the single-level - * wildcard matches only a single level, finance/+ does not match finance.

- * - *

Use - * the single-level wildcard at any level in the topic tree, and in conjunction - * with the multilevel wildcard. Specify the single-level wildcard next to the - * topic level separator, except when it is specified on its own. Therefore, - * + and finance/+ are both valid, but finance+ is - * not valid. The single-level wildcard can be used at the end of the - * topic tree or within the topic tree. - * For example, finance/+ and finance/+/ibm are both valid.

- *
- *
- *

The method returns control before the subscribe completes. Completion can - * be tracked by:

- *
    - *
  • Waiting on the supplied token {@link MqttToken#waitForCompletion()} or
  • - *
  • Passing in a callback {@link IMqttActionListener} to this method
  • - *
- * - * @param topicFilters one or more topics to subscribe to, which can include wildcards - * @param qos the maximum quality of service to subscribe each topic at.Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when subscribe - * has completed - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error registering the subscription. - * @throws IllegalArgumentException if the two supplied arrays are not the same size. - */ - public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) - throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when subscribe - * has completed - * @param messageListener a callback to handle incoming messages - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback, IMqttMessageListener messageListener) throws MqttException; - - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param messageListener a callback to handle incoming messages - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String topicFilter, int qos, IMqttMessageListener messageListener) throws MqttException; - - - /** - * Subscribe to multiple topics, each of which may include wildcards. - * - *

Provides an optimized way to subscribe to multiple topics compared to - * subscribing to each one individually.

- * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilters one or more topics to subscribe to, which can include wildcards - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param messageListeners one or more callbacks to handle incoming messages - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners) throws MqttException; - - - /** - * Subscribe to multiple topics, each of which may include wildcards. - * - *

Provides an optimized way to subscribe to multiple topics compared to - * subscribing to each one individually.

- * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilters one or more topics to subscribe to, which can include wildcards - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when subscribe - * has completed - * @param messageListeners one or more callbacks to handle incoming messages - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback, IMqttMessageListener[] messageListeners) throws MqttException; - - - /** - * Requests the server unsubscribe the client from a topic. - * - * @see #unsubscribe(String[], Object, IMqttActionListener) - * @param topicFilter the topic to unsubscribe from. It must match a topicFilter - * specified on an earlier subscribe. - * @return token used to track and wait for the unsubscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error unregistering the subscription. - */ - public IMqttToken unsubscribe(String topicFilter) throws MqttException; - - /** - * Requests the server unsubscribe the client from one or more topics. - * - * @see #unsubscribe(String[], Object, IMqttActionListener) - * - * @param topicFilters one or more topics to unsubscribe from. Each topicFilter - * must match one specified on an earlier subscribe. * - * @return token used to track and wait for the unsubscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error unregistering the subscription. - */ - public IMqttToken unsubscribe(String[] topicFilters) throws MqttException; - - /** - * Requests the server unsubscribe the client from a topics. - * - * @see #unsubscribe(String[], Object, IMqttActionListener) - * - * @param topicFilter the topic to unsubscribe from. It must match a topicFilter - * specified on an earlier subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when unsubscribe - * has completed - * @return token used to track and wait for the unsubscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error unregistering the subscription. - */ - public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback) - throws MqttException; - - /** - * Requests the server unsubscribe the client from one or more topics. - *

- * Unsubcribing is the opposite of subscribing. When the server receives the - * unsubscribe request it looks to see if it can find a matching subscription for the - * client and then removes it. After this point the server will send no more - * messages to the client for this subscription. - *

- *

The topic(s) specified on the unsubscribe must match the topic(s) - * specified in the original subscribe request for the unsubscribe to succeed - *

- *

The method returns control before the unsubscribe completes. Completion can - * be tracked by: - *

- * - *
    - *
  • Waiting on the returned token {@link MqttToken#waitForCompletion()} or
  • - *
  • Passing in a callback {@link IMqttActionListener} to this method
  • - *
- * - * @param topicFilters one or more topics to unsubscribe from. Each topicFilter - * must match one specified on an earlier subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when unsubscribe - * has completed - * @return token used to track and wait for the unsubscribe to complete. The token - * will be passed to callback methods if set. - * @throws MqttException if there was an error unregistering the subscription. - */ - public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback) - throws MqttException; - - - /** - * Sets a callback listener to use for events that happen asynchronously. - *

There are a number of events that the listener will be notified about. - * These include: - *

- *
    - *
  • A new message has arrived and is ready to be processed
  • - *
  • The connection to the server has been lost
  • - *
  • Delivery of a message to the server has completed
  • - *
- *

Other events that track the progress of an individual operation such - * as connect and subscribe can be tracked using the {@link MqttToken} returned from - * each non-blocking method or using setting a {@link IMqttActionListener} on the - * non-blocking method.

- * @see MqttCallback - * @param callback which will be invoked for certain asynchronous events - */ - public void setCallback(MqttCallback callback); - - /** - * Returns the delivery tokens for any outstanding publish operations. - *

If a client has been restarted and there are messages that were in the - * process of being delivered when the client stopped this method - * returns a token for each in-flight message enabling the delivery to be tracked - * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} - * callback can be used to track the delivery of outstanding messages. - *

- *

If a client connects with cleanSession true then there will be no - * delivery tokens as the cleanSession option deletes all earlier state. - * For state to be remembered the client must connect with cleanSession - * set to false

- * @return zero or more delivery tokens - */ - public IMqttDeliveryToken[] getPendingDeliveryTokens(); - - /** - * If manualAcks is set to true, then on completion of the messageArrived callback - * the MQTT acknowledgements are not sent. You must call messageArrivedComplete - * to send those acknowledgements. This allows finer control over when the acks are - * sent. The default behaviour, when manualAcks is false, is to send the MQTT - * acknowledgements automatically at the successful completion of the messageArrived - * callback method. - * @param manualAcks if set to true MQTT acknowledgements are not sent - */ - public void setManualAcks(boolean manualAcks); - - /** - * Indicate that the application has completed processing the message with id messageId. - * This will cause the MQTT acknowledgement to be sent to the server. - * @param messageId the MQTT message id to be acknowledged - * @param qos the MQTT QoS of the message to be acknowledged - * @throws MqttException if there was a problem sending the acknowledgement - */ - public void messageArrivedComplete(int messageId, int qos) throws MqttException; - - /** - * Close the client - * Releases all resource associated with the client. After the client has - * been closed it cannot be reused. For instance attempts to connect will fail. - * @throws MqttException if the client is not disconnected. - */ - public void close() throws MqttException; -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttClient.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttClient.java deleted file mode 100644 index 810dfed..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttClient.java +++ /dev/null @@ -1,966 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2015 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - * Ian Craggs - per subscription message handlers (bug 466579) - * Ian Craggs - ack control (bug 472172) - */ - -package org.eclipse.paho.client.mqttv3; - -/** - * Enables an application to communicate with an MQTT server using blocking methods. - *

- * This interface allows applications to utilize all features of the MQTT version 3.1 - * specification including:

- *
    - *
  • connect - *
  • publish - *
  • subscribe - *
  • unsubscribe - *
  • disconnect - *
- *

- * There are two styles of MQTT client, this one and {@link IMqttAsyncClient}.

- *
    - *
  • IMqttClient provides a set of methods that block and return control to the application - * program once the MQTT action has completed.
  • - *
  • IMqttAsyncClient provides a set of non-blocking methods that return control to the - * invoking application after initial validation of parameters and state. The main processing is - * performed in the background so as not to block the application programs thread. This non - * blocking approach is handy when the application wants to carry on processing while the - * MQTT action takes place. For instance connecting to an MQTT server can take time, using - * the non-blocking connect method allows an application to display a busy indicator while the - * connect action is occurring. Non-blocking methods are particularly useful in event-oriented - * programs and graphical programs where issuing methods that take time to complete on the the - * main or GUI thread can cause problems.
  • - *
- *

- * The non-blocking client can also be used in a blocking form by turning a non-blocking - * method into a blocking invocation using the following pattern:

- *
- *     IMqttToken token;
- *     token = asyncClient.method(parms).waitForCompletion();
- *     
- *

- * Using the non-blocking client allows an application to use a mixture of blocking and - * non-blocking styles. Using the blocking client only allows an application to use one - * style. The blocking client provides compatibility with earlier versions - * of the MQTT client.

- */ -public interface IMqttClient { //extends IMqttAsyncClient { - /** - * Connects to an MQTT server using the default options. - *

The default options are specified in {@link MqttConnectOptions} class. - *

- * - * @throws MqttSecurityException when the server rejects the connect for security - * reasons - * @throws MqttException for non security related problems - * @see #connect(MqttConnectOptions) - */ - public void connect() throws MqttSecurityException, MqttException; - - /** - * Connects to an MQTT server using the specified options. - *

The server to connect to is specified on the constructor. - * It is recommended to call {@link #setCallback(MqttCallback)} prior to - * connecting in order that messages destined for the client can be accepted - * as soon as the client is connected. - *

- *

This is a blocking method that returns once connect completes

- * - * @param options a set of connection parameters that override the defaults. - * @throws MqttSecurityException when the server rejects the connect for security - * reasons - * @throws MqttException for non security related problems including communication errors - */ - public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException; - - /** - * Connects to an MQTT server using the specified options. - *

The server to connect to is specified on the constructor. - * It is recommended to call {@link #setCallback(MqttCallback)} prior to - * connecting in order that messages destined for the client can be accepted - * as soon as the client is connected. - *

- *

This is a blocking method that returns once connect completes

- * - * @param options a set of connection parameters that override the defaults. - * @return the MqttToken used for the call. Can be used to obtain the session present flag - * @throws MqttSecurityException when the server rejects the connect for security - * reasons - * @throws MqttException for non security related problems including communication errors - */ -public IMqttToken connectWithResult(MqttConnectOptions options) throws MqttSecurityException, MqttException; - - /** - * Disconnects from the server. - *

An attempt is made to quiesce the client allowing outstanding - * work to complete before disconnecting. It will wait - * for a maximum of 30 seconds for work to quiesce before disconnecting. - * This method must not be called from inside {@link MqttCallback} methods. - *

- * - *

This is a blocking method that returns once disconnect completes

- * - * @throws MqttException if a problem is encountered while disconnecting - */ - public void disconnect() throws MqttException; - - /** - * Disconnects from the server. - *

- * The client will wait for all {@link MqttCallback} methods to - * complete. It will then wait for up to the quiesce timeout to allow for - * work which has already been initiated to complete - for example, it will - * wait for the QoS 2 flows from earlier publications to complete. When work has - * completed or after the quiesce timeout, the client will disconnect from - * the server. If the cleanSession flag was set to false and is set to false the - * next time a connection is made QoS 1 and 2 messages that - * were not previously delivered will be delivered.

- * - *

This is a blocking method that returns once disconnect completes

- * - * @param quiesceTimeout the amount of time in milliseconds to allow for - * existing work to finish before disconnecting. A value of zero or less - * means the client will not quiesce. - * @throws MqttException if a problem is encountered while disconnecting - */ - public void disconnect(long quiesceTimeout) throws MqttException; - - /** - * Disconnects from the server forcibly to reset all the states. Could be useful when disconnect attempt failed. - *

- * Because the client is able to establish the TCP/IP connection to a none MQTT server and it will certainly fail to - * send the disconnect packet. It will wait for a maximum of 30 seconds for work to quiesce before disconnecting and - * wait for a maximum of 10 seconds for sending the disconnect packet to server. - * - * @throws MqttException if any unexpected error - * @since 0.4.1 - */ - public void disconnectForcibly() throws MqttException; - - /** - * Disconnects from the server forcibly to reset all the states. Could be useful when disconnect attempt failed. - *

- * Because the client is able to establish the TCP/IP connection to a none MQTT server and it will certainly fail to - * send the disconnect packet. It will wait for a maximum of 30 seconds for work to quiesce before disconnecting. - * - * @param disconnectTimeout the amount of time in milliseconds to allow send disconnect packet to server. - * @throws MqttException if any unexpected error - * @since 0.4.1 - */ - public void disconnectForcibly(long disconnectTimeout) throws MqttException; - - /** - * Disconnects from the server forcibly to reset all the states. Could be useful when disconnect attempt failed. - *

- * Because the client is able to establish the TCP/IP connection to a none MQTT server and it will certainly fail to - * send the disconnect packet. - * - * @param quiesceTimeout the amount of time in milliseconds to allow for existing work to finish before - * disconnecting. A value of zero or less means the client will not quiesce. - * @param disconnectTimeout the amount of time in milliseconds to allow send disconnect packet to server. - * @throws MqttException if any unexpected error - * @since 0.4.1 - */ - public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards using a QoS of 1. - * - * @see #subscribe(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @throws MqttException if there was an error registering the subscription. - * @throws MqttSecurityException if the client is not authorized to register the subscription - */ - public void subscribe(String topicFilter) throws MqttException, MqttSecurityException; - - /** - * Subscribes to a one or more topics, which may include wildcards using a QoS of 1. - * - * @see #subscribe(String[], int[]) - * - * @param topicFilters the topic to subscribe to, which can include wildcards. - * @throws MqttException if there was an error registering the subscription. - */ - public void subscribe(String[] topicFilters) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribe(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @throws MqttException if there was an error registering the subscription. - */ - public void subscribe(String topicFilter, int qos) throws MqttException; - - /** - * Subscribes to multiple topics, each of which may include wildcards. - *

The {@link #setCallback(MqttCallback)} method - * should be called before this method, otherwise any received messages - * will be discarded. - *

- *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true - * when when connecting to the server then the subscription remains in place - * until either: - *

- *
    - *
  • The client disconnects
  • - *
  • An unsubscribe method is called to un-subscribe the topic
  • - *
- *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false - * when when connecting to the server then the subscription remains in place - * until either:

- *
    - *
  • An unsubscribe method is called to unsubscribe the topic
  • - *
  • The client connects with cleanSession set to true
  • - *
- *

- * With cleanSession set to false the MQTT server will store messages on - * behalf of the client when the client is not connected. The next time the - * client connects with the same client ID the server will - * deliver the stored messages to the client. - *

- * - *

The "topic filter" string used when subscribing - * may contain special characters, which allow you to subscribe to multiple topics - * at once.

- *

The topic level separator is used to introduce structure into the topic, and - * can therefore be specified within the topic for that purpose. The multi-level - * wildcard and single-level wildcard can be used for subscriptions, but they - * cannot be used within a topic by the publisher of a message. - *

- *
Topic level separator
- *
The forward slash (/) is used to separate each level within - * a topic tree and provide a hierarchical structure to the topic space. The - * use of the topic level separator is significant when the two wildcard characters - * are encountered in topics specified by subscribers.
- * - *
Multi-level wildcard
- *

The number sign (#) is a wildcard character that matches - * any number of levels within a topic. For example, if you subscribe to - * finance/stock/ibm/#, you receive - * messages on these topics:

- *
    - *
  • finance/stock/ibm
  • - *
  • finance/stock/ibm/closingprice
  • - *
  • finance/stock/ibm/currentprice
  • - *
- * - *

The multi-level wildcard - * can represent zero or more levels. Therefore, finance/# can also match - * the singular finance, where # represents zero levels. The topic - * level separator is meaningless in this context, because there are no levels - * to separate.

- * - *

The multi-level wildcard can - * be specified only on its own or next to the topic level separator character. - * Therefore, # and finance/# are both valid, but finance# is - * not valid. The multi-level wildcard must be the last character - * used within the topic tree. For example, finance/# is valid but - * finance/#/closingprice is not valid.

- * - *
Single-level wildcard
- *

The plus sign (+) is a wildcard character that matches only one topic - * level. For example, finance/stock/+ matches - * finance/stock/ibm and finance/stock/xyz, - * but not finance/stock/ibm/closingprice. Also, because the single-level - * wildcard matches only a single level, finance/+ does not match finance.

- * - *

Use - * the single-level wildcard at any level in the topic tree, and in conjunction - * with the multilevel wildcard. Specify the single-level wildcard next to the - * topic level separator, except when it is specified on its own. Therefore, - * + and finance/+ are both valid, but finance+ is - * not valid. The single-level wildcard can be used at the end of the - * topic tree or within the topic tree. - * For example, finance/+ and finance/+/ibm are both valid.

- *
- *
- * - *

This is a blocking method that returns once subscribe completes

- * - * @param topicFilters one or more topics to subscribe to, which can include wildcards. - * @param qos the maximum quality of service to subscribe each topic at.Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @throws MqttException if there was an error registering the subscription. - * @throws IllegalArgumentException if the two supplied arrays are not the same size. - */ - public void subscribe(String[] topicFilters, int[] qos) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards using a QoS of 1. - * - * @see #subscribe(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param messageListener a callback to handle incoming messages - * @throws MqttException if there was an error registering the subscription. - * @throws MqttSecurityException if the client is not authorized to register the subscription - */ -public void subscribe(String topicFilter, IMqttMessageListener messageListener) throws MqttException, MqttSecurityException; - - /** - * Subscribes to a one or more topics, which may include wildcards using a QoS of 1. - * - * @see #subscribe(String[], int[]) - * - * @param topicFilters the topic to subscribe to, which can include wildcards. - * @param messageListeners one or more callbacks to handle incoming messages - * @throws MqttException if there was an error registering the subscription. - */ -public void subscribe(String[] topicFilters, IMqttMessageListener[] messageListeners) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribe(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param messageListener a callback to handle incoming messages - * @throws MqttException if there was an error registering the subscription. - */ -public void subscribe(String topicFilter, int qos, IMqttMessageListener messageListener) throws MqttException; - - /** - * Subscribes to multiple topics, each of which may include wildcards. - *

The {@link #setCallback(MqttCallback)} method - * should be called before this method, otherwise any received messages - * will be discarded. - *

- *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true - * when when connecting to the server then the subscription remains in place - * until either:

- *
    - *
  • The client disconnects
  • - *
  • An unsubscribe method is called to un-subscribe the topic
  • - *
- *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false - * when when connecting to the server then the subscription remains in place - * until either:

- *
    - *
  • An unsubscribe method is called to unsubscribe the topic
  • - *
  • The client connects with cleanSession set to true
  • - *
- *

- * With cleanSession set to false the MQTT server will store messages on - * behalf of the client when the client is not connected. The next time the - * client connects with the same client ID the server will - * deliver the stored messages to the client. - *

- * - *

The "topic filter" string used when subscribing - * may contain special characters, which allow you to subscribe to multiple topics - * at once.

- *

The topic level separator is used to introduce structure into the topic, and - * can therefore be specified within the topic for that purpose. The multi-level - * wildcard and single-level wildcard can be used for subscriptions, but they - * cannot be used within a topic by the publisher of a message. - *

- *
Topic level separator
- *
The forward slash (/) is used to separate each level within - * a topic tree and provide a hierarchical structure to the topic space. The - * use of the topic level separator is significant when the two wildcard characters - * are encountered in topics specified by subscribers.
- * - *
Multi-level wildcard
- *

The number sign (#) is a wildcard character that matches - * any number of levels within a topic. For example, if you subscribe to - * finance/stock/ibm/#, you receive - * messages on these topics:

- *
    - *
  • finance/stock/ibm
  • - *
  • finance/stock/ibm/closingprice
  • - *
  • finance/stock/ibm/currentprice
  • - *
- *

The multi-level wildcard - * can represent zero or more levels. Therefore, finance/# can also match - * the singular finance, where # represents zero levels. The topic - * level separator is meaningless in this context, because there are no levels - * to separate.

- * - *

The multi-level wildcard can - * be specified only on its own or next to the topic level separator character. - * Therefore, # and finance/# are both valid, but finance# is - * not valid. The multi-level wildcard must be the last character - * used within the topic tree. For example, finance/# is valid but - * finance/#/closingprice is not valid.

- * - *
Single-level wildcard
- *

The plus sign (+) is a wildcard character that matches only one topic - * level. For example, finance/stock/+ matches - * finance/stock/ibm and finance/stock/xyz, - * but not finance/stock/ibm/closingprice. Also, because the single-level - * wildcard matches only a single level, finance/+ does not match finance.

- * - *

Use - * the single-level wildcard at any level in the topic tree, and in conjunction - * with the multilevel wildcard. Specify the single-level wildcard next to the - * topic level separator, except when it is specified on its own. Therefore, - * + and finance/+ are both valid, but finance+ is - * not valid. The single-level wildcard can be used at the end of the - * topic tree or within the topic tree. - * For example, finance/+ and finance/+/ibm are both valid.

- *
- *
- * - *

This is a blocking method that returns once subscribe completes

- * - * @param topicFilters one or more topics to subscribe to, which can include wildcards. - * @param qos the maximum quality of service to subscribe each topic at.Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param messageListeners one or more callbacks to handle incoming messages - * @throws MqttException if there was an error registering the subscription. - * @throws IllegalArgumentException if the two supplied arrays are not the same size. - */ - public void subscribe(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards using a QoS of 1. - * - * @see #subscribeWithResponse(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @return token used to track the subscribe after it has completed. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribeWithResponse(String topicFilter) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards using a QoS of 1. - * - * @see #subscribeWithResponse(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param messageListener a callback to handle incoming messages - * @return token used to track the subscribe after it has completed. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribeWithResponse(String topicFilter, IMqttMessageListener messageListener) throws MqttException; - - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribeWithResponse(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @return token used to track the subscribe after it has completed. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribeWithResponse(String topicFilter, int qos) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribeWithResponse(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param messageListener a callback to handle incoming messages - * @return token used to track the subscribe after it has completed. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribeWithResponse(String topicFilter, int qos, IMqttMessageListener messageListener) throws MqttException; - - /** - * Subscribes to a one or more topics, which may include wildcards using a QoS of 1. - * - * @see #subscribeWithResponse(String[], int[]) - * - * @param topicFilters the topic to subscribe to, which can include wildcards. - * @return token used to track the subscribe after it has completed. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribeWithResponse(String[] topicFilters) throws MqttException; - - /** - * Subscribes to a one or more topics, which may include wildcards using a QoS of 1. - * - * @see #subscribeWithResponse(String[], int[]) - * - * @param topicFilters the topic to subscribe to, which can include wildcards. - * @param messageListeners one or more callbacks to handle incoming messages - * @return token used to track the subscribe after it has completed. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribeWithResponse(String[] topicFilters, IMqttMessageListener[] messageListeners) throws MqttException; - - /** - * Subscribes to multiple topics, each of which may include wildcards. - *

The {@link #setCallback(MqttCallback)} method - * should be called before this method, otherwise any received messages - * will be discarded. - *

- *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true - * when when connecting to the server then the subscription remains in place - * until either:

- *
    - *
  • The client disconnects
  • - *
  • An unsubscribe method is called to un-subscribe the topic
  • - *
- *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false - * when when connecting to the server then the subscription remains in place - * until either:

- *
    - *
  • An unsubscribe method is called to unsubscribe the topic
  • - *
  • The client connects with cleanSession set to true
  • - *
- *

- * With cleanSession set to false the MQTT server will store messages on - * behalf of the client when the client is not connected. The next time the - * client connects with the same client ID the server will - * deliver the stored messages to the client. - *

- * - *

The "topic filter" string used when subscribing - * may contain special characters, which allow you to subscribe to multiple topics - * at once.

- *

The topic level separator is used to introduce structure into the topic, and - * can therefore be specified within the topic for that purpose. The multi-level - * wildcard and single-level wildcard can be used for subscriptions, but they - * cannot be used within a topic by the publisher of a message. - *

- *
Topic level separator
- *
The forward slash (/) is used to separate each level within - * a topic tree and provide a hierarchical structure to the topic space. The - * use of the topic level separator is significant when the two wildcard characters - * are encountered in topics specified by subscribers.
- * - *
Multi-level wildcard
- *

The number sign (#) is a wildcard character that matches - * any number of levels within a topic. For example, if you subscribe to - * finance/stock/ibm/#, you receive - * messages on these topics:

- *
    - *
  • finance/stock/ibm
  • - *
  • finance/stock/ibm/closingprice
  • - *
  • finance/stock/ibm/currentprice
  • - *
- *

The multi-level wildcard - * can represent zero or more levels. Therefore, finance/# can also match - * the singular finance, where # represents zero levels. The topic - * level separator is meaningless in this context, because there are no levels - * to separate.

- * - *

The multi-level wildcard can - * be specified only on its own or next to the topic level separator character. - * Therefore, # and finance/# are both valid, but finance# is - * not valid. The multi-level wildcard must be the last character - * used within the topic tree. For example, finance/# is valid but - * finance/#/closingprice is not valid.

- * - *
Single-level wildcard
- *

The plus sign (+) is a wildcard character that matches only one topic - * level. For example, finance/stock/+ matches - * finance/stock/ibm and finance/stock/xyz, - * but not finance/stock/ibm/closingprice. Also, because the single-level - * wildcard matches only a single level, finance/+ does not match finance.

- * - *

Use - * the single-level wildcard at any level in the topic tree, and in conjunction - * with the multilevel wildcard. Specify the single-level wildcard next to the - * topic level separator, except when it is specified on its own. Therefore, - * + and finance/+ are both valid, but finance+ is - * not valid. The single-level wildcard can be used at the end of the - * topic tree or within the topic tree. - * For example, finance/+ and finance/+/ibm are both valid.

- *
- *
- * - *

This is a blocking method that returns once subscribe completes

- * - * @param topicFilters one or more topics to subscribe to, which can include wildcards. - * @param qos the maximum quality of service to subscribe each topic at.Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @throws MqttException if there was an error registering the subscription. - * @return token used to track the subscribe after it has completed. - * @throws IllegalArgumentException if the two supplied arrays are not the same size. - */ - public IMqttToken subscribeWithResponse(String[] topicFilters, int[] qos) throws MqttException; - - /** - * Subscribes to multiple topics, each of which may include wildcards. - *

The {@link #setCallback(MqttCallback)} method - * should be called before this method, otherwise any received messages - * will be discarded. - *

- *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true - * when when connecting to the server then the subscription remains in place - * until either:

- *
    - *
  • The client disconnects
  • - *
  • An unsubscribe method is called to un-subscribe the topic
  • - *
- * - *

- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false - * when when connecting to the server then the subscription remains in place - * until either:

- *
    - *
  • An unsubscribe method is called to unsubscribe the topic
  • - *
  • The client connects with cleanSession set to true
  • - *
- *

- * With cleanSession set to false the MQTT server will store messages on - * behalf of the client when the client is not connected. The next time the - * client connects with the same client ID the server will - * deliver the stored messages to the client. - *

- * - *

The "topic filter" string used when subscribing - * may contain special characters, which allow you to subscribe to multiple topics - * at once.

- *

The topic level separator is used to introduce structure into the topic, and - * can therefore be specified within the topic for that purpose. The multi-level - * wildcard and single-level wildcard can be used for subscriptions, but they - * cannot be used within a topic by the publisher of a message. - *

- *
Topic level separator
- *
The forward slash (/) is used to separate each level within - * a topic tree and provide a hierarchical structure to the topic space. The - * use of the topic level separator is significant when the two wildcard characters - * are encountered in topics specified by subscribers.
- * - *
Multi-level wildcard
- *

The number sign (#) is a wildcard character that matches - * any number of levels within a topic. For example, if you subscribe to - * finance/stock/ibm/#, you receive - * messages on these topics:

- *
    - *
  • finance/stock/ibm
  • - *
  • finance/stock/ibm/closingprice
  • - *
  • finance/stock/ibm/currentprice
  • - *
- *

The multi-level wildcard - * can represent zero or more levels. Therefore, finance/# can also match - * the singular finance, where # represents zero levels. The topic - * level separator is meaningless in this context, because there are no levels - * to separate.

- * - *

The multi-level wildcard can - * be specified only on its own or next to the topic level separator character. - * Therefore, # and finance/# are both valid, but finance# is - * not valid. The multi-level wildcard must be the last character - * used within the topic tree. For example, finance/# is valid but - * finance/#/closingprice is not valid.

- * - *
Single-level wildcard
- *

The plus sign (+) is a wildcard character that matches only one topic - * level. For example, finance/stock/+ matches - * finance/stock/ibm and finance/stock/xyz, - * but not finance/stock/ibm/closingprice. Also, because the single-level - * wildcard matches only a single level, finance/+ does not match finance.

- * - *

Use - * the single-level wildcard at any level in the topic tree, and in conjunction - * with the multilevel wildcard. Specify the single-level wildcard next to the - * topic level separator, except when it is specified on its own. Therefore, - * + and finance/+ are both valid, but finance+ is - * not valid. The single-level wildcard can be used at the end of the - * topic tree or within the topic tree. - * For example, finance/+ and finance/+/ibm are both valid.

- *
- *
- - * - *

This is a blocking method that returns once subscribe completes

- * - * @param topicFilters one or more topics to subscribe to, which can include wildcards. - * @param qos the maximum quality of service to subscribe each topic at.Messages - * published at a lower quality of service will be received at the published - * QoS. Messages published at a higher quality of service will be received using - * the QoS specified on the subscribe. - * @param messageListeners one or more callbacks to handle incoming messages - * @throws MqttException if there was an error registering the subscription. - * @return token used to track the subscribe after it has completed. - * @throws IllegalArgumentException if the two supplied arrays are not the same size. - */ - public IMqttToken subscribeWithResponse(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners) throws MqttException; - - /** - * Requests the server unsubscribe the client from a topic. - * - * @see #unsubscribe(String[]) - * @param topicFilter the topic to unsubscribe from. It must match a topicFilter - * specified on the subscribe. - * @throws MqttException if there was an error unregistering the subscription. - */ - public void unsubscribe(String topicFilter) throws MqttException; - - /** - * Requests the server unsubscribe the client from one or more topics. - *

- * Unsubcribing is the opposite of subscribing. When the server receives the - * unsubscribe request it looks to see if it can find a subscription for the - * client and then removes it. After this point the server will send no more - * messages to the client for this subscription. - *

- *

The topic(s) specified on the unsubscribe must match the topic(s) - * specified in the original subscribe request for the subscribe to succeed - *

- * - *

This is a blocking method that returns once unsubscribe completes

- * - * @param topicFilters one or more topics to unsubscribe from. Each topicFilter - * must match one specified on a subscribe - * @throws MqttException if there was an error unregistering the subscription. - */ - public void unsubscribe(String[] topicFilters) throws MqttException; - - - /** - * Publishes a message to a topic on the server and return once it is delivered. - *

This is a convenience method, which will - * create a new {@link MqttMessage} object with a byte array payload and the - * specified QoS, and then publish it. All other values in the - * message will be set to the defaults. - *

- * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param payload the byte array to use as the payload - * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2. - * @param retained whether or not this message should be retained by the server. - * @throws MqttPersistenceException when a problem with storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - * @see #publish(String, MqttMessage) - * @see MqttMessage#setQos(int) - * @see MqttMessage#setRetained(boolean) - */ - public void publish(String topic, byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException; - - /** - * Publishes a message to a topic on the server. - *

- * Delivers a message to the server at the requested quality of service and returns control - * once the message has been delivered. In the event the connection fails or the client - * stops, any messages that are in the process of being delivered will be delivered once - * a connection is re-established to the server on condition that:

- *
    - *
  • The connection is re-established with the same clientID
  • - *
  • The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} - * set to false
  • - *
  • The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} - * set to false
  • - *
- *

In the event that the connection breaks or the client stops it is still possible to determine - * when the delivery of the message completes. Prior to re-establishing the connection to the server:

- *
    - *
  • Register a {@link #setCallback(MqttCallback)} callback on the client and the delivery complete - * callback will be notified once a delivery of a message completes - *
  • or call {@link #getPendingDeliveryTokens()} which will return a token for each message that - * is in-flight. The token can be used to wait for delivery to complete. - *
- * - *

When building an application, - * the design of the topic tree should take into account the following principles - * of topic name syntax and semantics:

- * - *
    - *
  • A topic must be at least one character long.
  • - *
  • Topic names are case sensitive. For example, ACCOUNTS and Accounts are - * two different topics.
  • - *
  • Topic names can include the space character. For example, Accounts - * payable is a valid topic.
  • - *
  • A leading "/" creates a distinct topic. For example, /finance is - * different from finance. /finance matches "+/+" and "/+", but - * not "+".
  • - *
  • Do not include the null character (Unicode
     \x0000
    ) in - * any topic.
  • - *
- * - *

The following principles apply to the construction and content of a topic - * tree:

- * - *
    - *
  • The length is limited to 64k but within that there are no limits to the - * number of levels in a topic tree.
  • - *
  • There can be any number of root nodes; that is, there can be any number - * of topic trees.
  • - *
- * - * - *

This is a blocking method that returns once publish completes

* - * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param message to delivery to the server - * @throws MqttPersistenceException when a problem with storing the message - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - */ - public void publish(String topic, MqttMessage message) throws MqttException, MqttPersistenceException; - - /** - * Sets the callback listener to use for events that happen asynchronously. - *

There are a number of events that listener will be notified about. These include:

- *
    - *
  • A new message has arrived and is ready to be processed
  • - *
  • The connection to the server has been lost
  • - *
  • Delivery of a message to the server has completed.
  • - *
- *

Other events that track the progress of an individual operation such - * as connect and subscribe can be tracked using the {@link MqttToken} passed to the - * operation

- * @see MqttCallback - * @param callback the class to callback when for events related to the client - */ - public void setCallback(MqttCallback callback); - - /** - * Get a topic object which can be used to publish messages. - *

An alternative method that should be used in preference to this one when publishing a message is:

- *
    - *
  • {@link MqttClient#publish(String, MqttMessage)} to publish a message in a blocking manner - *
  • or use publish methods on the non-blocking client like {@link IMqttAsyncClient#publish(String, MqttMessage, Object, IMqttActionListener)} - *
- *

When building an application, - * the design of the topic tree should take into account the following principles - * of topic name syntax and semantics:

- * - *
    - *
  • A topic must be at least one character long.
  • - *
  • Topic names are case sensitive. For example, ACCOUNTS and Accounts are - * two different topics.
  • - *
  • Topic names can include the space character. For example, Accounts - * payable is a valid topic.
  • - *
  • A leading "/" creates a distinct topic. For example, /finance is - * different from finance. /finance matches "+/+" and "/+", but - * not "+".
  • - *
  • Do not include the null character (Unicode
     \x0000
    ) in - * any topic.
  • - *
- * - *

The following principles apply to the construction and content of a topic - * tree:

- * - *
    - *
  • The length is limited to 64k but within that there are no limits to the - * number of levels in a topic tree.
  • - *
  • There can be any number of root nodes; that is, there can be any number - * of topic trees.
  • - *
- * - * @param topic the topic to use, for example "finance/stock/ibm". - * @return an MqttTopic object, which can be used to publish messages to - * the topic. - * @throws IllegalArgumentException if the topic contains a '+' or '#' - * wildcard character. - */ - public MqttTopic getTopic(String topic); - - /** - * Determines if this client is currently connected to the server. - * - * @return true if connected, false otherwise. - */ - public boolean isConnected(); - - /** - * Returns the client ID used by this client. - *

All clients connected to the - * same server or server farm must have a unique ID. - *

- * - * @return the client ID used by this client. - */ - public String getClientId(); - - /** - * Returns the address of the server used by this client, as a URI. - *

The format is the same as specified on the constructor. - *

- * - * @return the server's address, as a URI String. - * @see MqttAsyncClient#MqttAsyncClient(String, String) - */ - public String getServerURI(); - - /** - * Returns the delivery tokens for any outstanding publish operations. - *

If a client has been restarted and there are messages that were in the - * process of being delivered when the client stopped this method will - * return a token for each message enabling the delivery to be tracked - * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} - * callback can be used to track the delivery of outstanding messages. - *

- *

If a client connects with cleanSession true then there will be no - * delivery tokens as the cleanSession option deletes all earlier state. - * For state to be remembered the client must connect with cleanSession - * set to false

- * @return zero or more delivery tokens - */ - public IMqttDeliveryToken[] getPendingDeliveryTokens(); - - /** - * If manualAcks is set to true, then on completion of the messageArrived callback - * the MQTT acknowledgements are not sent. You must call messageArrivedComplete - * to send those acknowledgements. This allows finer control over when the acks are - * sent. The default behaviour, when manualAcks is false, is to send the MQTT - * acknowledgements automatically at the successful completion of the messageArrived - * callback method. - * @param manualAcks if set to true, MQTT acknowledgements are not sent. - */ - public void setManualAcks(boolean manualAcks); - - /** - * Indicate that the application has completed processing the message with id messageId. - * This will cause the MQTT acknowledgement to be sent to the server. - * @param messageId the MQTT message id to be acknowledged - * @param qos the MQTT QoS of the message to be acknowledged - * @throws MqttException if there was a problem sending the acknowledgement - */ - public void messageArrivedComplete(int messageId, int qos) throws MqttException; - - /** - * Close the client - * Releases all resource associated with the client. After the client has - * been closed it cannot be reused. For instance attempts to connect will fail. - * @throws MqttException if the client is not disconnected. - */ - public void close() throws MqttException; -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java deleted file mode 100644 index 8d74de9..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.eclipse.paho.client.mqttv3; -/** - * Provides a mechanism for tracking the delivery of a message. - * - *

A subclass of IMqttToken that allows the delivery of a message to be tracked. - * Unlike instances of IMqttToken delivery tokens can be used across connection - * and client restarts. This enables the delivery of a messages to be tracked - * after failures. There are two approaches - *

    - *
  • A list of delivery tokens for in-flight messages can be obtained using - * {@link IMqttAsyncClient#getPendingDeliveryTokens()}. The waitForCompletion - * method can then be used to block until the delivery is complete. - *
  • A {@link MqttCallback} can be set on the client. Once a message has been - * delivered the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method will - * be called withe delivery token being passed as a parameter. - *
- *

- * An action is in progress until either:

- *
    - *
  • isComplete() returns true or
  • - *
  • getException() is not null. If a client shuts down before delivery is complete - * an exception is returned. As long as the Java Runtime is not stopped a delivery token - * is valid across a connection disconnect and reconnect. In the event the client - * is shut down the getPendingDeliveryTokens method can be used once the client is - * restarted to obtain a list of delivery tokens for inflight messages.
  • - *
- * - */ - -public interface IMqttDeliveryToken extends IMqttToken { - /** - * Returns the message associated with this token. - *

Until the message has been delivered, the message being delivered will - * be returned. Once the message has been delivered null will be - * returned. - * @return the message associated with this token or null if already delivered. - * @throws MqttException if there was a problem completing retrieving the message - */ - public MqttMessage getMessage() throws MqttException; -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttMessageListener.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttMessageListener.java deleted file mode 100644 index 10ae31a..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttMessageListener.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - */ - -package org.eclipse.paho.client.mqttv3; - -/** - * Implementers of this interface will be notified when a message arrives. - * - */ -public interface IMqttMessageListener { - /** - * This method is called when a message arrives from the server. - * - *

- * This method is invoked synchronously by the MQTT client. An - * acknowledgment is not sent back to the server until this - * method returns cleanly.

- *

- * If an implementation of this method throws an Exception, then the - * client will be shut down. When the client is next re-connected, any QoS - * 1 or 2 messages will be redelivered by the server.

- *

- * Any additional messages which arrive while an - * implementation of this method is running, will build up in memory, and - * will then back up on the network.

- *

- * If an application needs to persist data, then it - * should ensure the data is persisted prior to returning from this method, as - * after returning from this method, the message is considered to have been - * delivered, and will not be reproducible.

- *

- * It is possible to send a new message within an implementation of this callback - * (for example, a response to this message), but the implementation must not - * disconnect the client, as it will be impossible to send an acknowledgment for - * the message being processed, and a deadlock will occur.

- * - * @param topic name of the topic on the message was published to - * @param message the actual message. - * @throws Exception if a terminal error has occurred, and the client should be - * shut down. - */ - public void messageArrived(String topic, MqttMessage message) throws Exception; -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttToken.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttToken.java deleted file mode 100644 index d19ccde..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/IMqttToken.java +++ /dev/null @@ -1,163 +0,0 @@ -/************************************************************************** - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - */ -package org.eclipse.paho.client.mqttv3; - -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; - -/** - * Provides a mechanism for tracking the completion of an asynchronous task. - * - *

When using the asynchronous/non-blocking MQTT programming interface all - * methods/operations that take any time (and in particular those that involve - * any network operation) return control to the caller immediately. The operation - * then proceeds to run in the background so as not to block the invoking thread. - * An IMqttToken is used to track the state of the operation. An application can use the - * token to wait for an operation to complete. A token is passed to callbacks - * once the operation completes and provides context linking it to the original - * request. A token is associated with a single operation.

- *

- * An action is in progress until either:

- *
    - *
  • isComplete() returns true or
  • - *
  • getException() is not null.
  • - *
- * - */ -public interface IMqttToken { - - /** - * Blocks the current thread until the action this token is associated with has - * completed. - * - * @throws MqttException if there was a problem with the action associated with the token. - * @see #waitForCompletion(long) - */ - public void waitForCompletion() throws MqttException; - - /** - * Blocks the current thread until the action this token is associated with has - * completed. - *

The timeout specifies the maximum time it will block for. If the action - * completes before the timeout then control returns immediately, if not - * it will block until the timeout expires.

- *

If the action being tracked fails or the timeout expires an exception will - * be thrown. In the event of a timeout the action may complete after timeout. - *

- * - * @param timeout the maximum amount of time to wait for, in milliseconds. - * @throws MqttException if there was a problem with the action associated with the token. - */ - public void waitForCompletion(long timeout) throws MqttException; - - /** - * Returns whether or not the action has finished. - *

True will be returned both in the case where the action finished successfully - * and in the case where it failed. If the action failed {@link #getException()} will - * be non null. - *

- * @return whether or not the action has finished. - */ - public boolean isComplete(); - - /** - * Returns an exception providing more detail if an operation failed. - *

While an action in in progress and when an action completes successfully - * null will be returned. Certain errors like timeout or shutting down will not - * set the exception as the action has not failed or completed at that time - *

- * @return exception may return an exception if the operation failed. Null will be - * returned while action is in progress and if action completes successfully. - */ - public MqttException getException(); - - /** - * Register a listener to be notified when an action completes. - *

Once a listener is registered it will be invoked when the action the token - * is associated with either succeeds or fails. - *

- * @param listener to be invoked once the action completes - */ - public void setActionCallback(IMqttActionListener listener); - - /** - * Return the async listener for this token. - * @return listener that is set on the token or null if a listener is not registered. - */ - public IMqttActionListener getActionCallback(); - - /** - * Returns the MQTT client that is responsible for processing the asynchronous - * action - * @return the client - */ - public IMqttAsyncClient getClient(); - - /** - * Returns the topic string(s) for the action being tracked by this - * token. If the action has not been initiated or the action has not - * topic associated with it such as connect then null will be returned. - * - * @return the topic string(s) for the subscribe being tracked by this token or null - */ - public String[] getTopics(); - - /** - * Store some context associated with an action. - *

Allows the caller of an action to store some context that can be - * accessed from within the ActionListener associated with the action. This - * can be useful when the same ActionListener is associated with multiple - * actions

- * @param userContext to associate with an action - */ - public void setUserContext(Object userContext); - - /** - * Retrieve the context associated with an action. - *

Allows the ActionListener associated with an action to retrieve any context - * that was associated with the action when the action was invoked. If not - * context was provided null is returned.

- - * @return Object context associated with an action or null if there is none. - */ - public Object getUserContext(); - - /** - * Returns the message ID of the message that is associated with the token. - * A message id of zero will be returned for tokens associated with - * connect, disconnect and ping operations as there can only ever - * be one of these outstanding at a time. For other operations - * the MQTT message id flowed over the network. - * @return the message ID of the message that is associated with the token - */ - public int getMessageId(); - - /** - * @return the granted QoS list from a suback - */ - public int[] getGrantedQos(); - - /** - * @return the session present flag from a connack - */ - public boolean getSessionPresent(); - - /** - * @return the response wire message - */ - public MqttWireMessage getResponse(); - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java deleted file mode 100644 index ae5150b..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java +++ /dev/null @@ -1,1610 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - * Ian Craggs - per subscription message handlers (bug 466579) - * Ian Craggs - ack control (bug 472172) - * James Sutton - Bug 459142 - WebSocket support for the Java client. - * James Sutton - Automatic Reconnect & Offline Buffering. - */ - -package org.eclipse.paho.client.mqttv3; - -import java.lang.reflect.Field; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Hashtable; -import java.util.Properties; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLSocketFactory; - -import org.eclipse.paho.client.mqttv3.internal.ClientComms; -import org.eclipse.paho.client.mqttv3.internal.ConnectActionListener; -import org.eclipse.paho.client.mqttv3.internal.DisconnectedMessageBuffer; -import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper; -import org.eclipse.paho.client.mqttv3.internal.NetworkModule; -import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule; -import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule; -import org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory; -import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketNetworkModule; -import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketSecureNetworkModule; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttSubscribe; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttUnsubscribe; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; -import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; -import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; -import org.eclipse.paho.client.mqttv3.util.Debug; - -/** - * Lightweight client for talking to an MQTT server using non-blocking methods - * that allow an operation to run in the background. - * - *

- * This class implements the non-blocking {@link IMqttAsyncClient} client - * interface allowing applications to initiate MQTT actions and then carry on - * working while the MQTT action completes on a background thread. This - * implementation is compatible with all Java SE runtimes from 1.7 and up. - *

- *

- * An application can connect to an MQTT server using: - *

- *
    - *
  • A plain TCP socket - *
  • A secure SSL/TLS socket - *
- * - *

- * To enable messages to be delivered even across network and client restarts - * messages need to be safely stored until the message has been delivered at the - * requested quality of service. A pluggable persistence mechanism is provided - * to store the messages. - *

- *

- * By default {@link MqttDefaultFilePersistence} is used to store messages to a - * file. If persistence is set to null then messages are stored in memory and - * hence can be lost if the client, Java runtime or device shuts down. - *

- *

- * If connecting with {@link MqttConnectOptions#setCleanSession(boolean)} set to - * true it is safe to use memory persistence as all state is cleared when a - * client disconnects. If connecting with cleanSession set to false in order to - * provide reliable message delivery then a persistent message store such as the - * default one should be used. - *

- *

- * The message store interface is pluggable. Different stores can be used by - * implementing the {@link MqttClientPersistence} interface and passing it to - * the clients constructor. - *

- * - * @see IMqttAsyncClient - */ -public class MqttAsyncClient implements IMqttAsyncClient { - private static final String CLASS_NAME = MqttAsyncClient.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - private static final String CLIENT_ID_PREFIX = "paho"; - private static final long QUIESCE_TIMEOUT = 30000; // ms - private static final long DISCONNECT_TIMEOUT = 10000; // ms - private static final char MIN_HIGH_SURROGATE = '\uD800'; - private static final char MAX_HIGH_SURROGATE = '\uDBFF'; - private String clientId; - private String serverURI; - protected ClientComms comms; - private Hashtable topics; - private MqttClientPersistence persistence; - private MqttCallback mqttCallback; - private MqttConnectOptions connOpts; - private Object userContext; - private Timer reconnectTimer; // Automatic reconnect timer - private static int reconnectDelay = 1000; // Reconnect delay, starts at 1 - // second - private boolean reconnecting = false; - private static Object clientLock = new Object(); // Simple lock - - private ScheduledExecutorService executorService; - - /** - * Create an MqttAsyncClient that is used to communicate with an MQTT - * server. - *

- * The address of a server can be specified on the constructor. - * Alternatively a list containing one or more servers can be specified - * using the {@link MqttConnectOptions#setServerURIs(String[]) - * setServerURIs} method on MqttConnectOptions. - * - *

- * The serverURI parameter is typically used with the the - * clientId parameter to form a key. The key is used to store - * and reference messages while they are being delivered. Hence the - * serverURI specified on the constructor must still be specified even if a - * list of servers is specified on an MqttConnectOptions object. The - * serverURI on the constructor must remain the same across restarts of the - * client for delivery of messages to be maintained from a given client to a - * given server or set of servers. - * - *

- * The address of the server to connect to is specified as a URI. Two types - * of connection are supported tcp:// for a TCP connection and - * ssl:// for a TCP connection secured by SSL/TLS. For example: - *

- *
    - *
  • tcp://localhost:1883
  • - *
  • ssl://localhost:8883
  • - *
- *

- * If the port is not specified, it will default to 1883 for - * tcp://" URIs, and 8883 for ssl:// URIs. - *

- * - *

- * A client identifier clientId must be specified and be less - * that 65535 characters. It must be unique across all clients connecting to - * the same server. The clientId is used by the server to store data related - * to the client, hence it is important that the clientId remain the same - * when connecting to a server if durable subscriptions or reliable - * messaging are required. - *

- * A convenience method is provided to generate a random client id that - * should satisfy this criteria - {@link #generateClientId()}. As the client - * identifier is used by the server to identify a client when it reconnects, - * the client must use the same identifier between connections if durable - * subscriptions or reliable delivery of messages is required. - *

- *

- * In Java SE, SSL can be configured in one of several ways, which the - * client will use in the following order: - *

- *
    - *
  • Supplying an SSLSocketFactory - - * applications can use - * {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a - * factory with the appropriate SSL settings.
  • - *
  • SSL Properties - applications can supply SSL - * settings as a simple Java Properties using - * {@link MqttConnectOptions#setSSLProperties(Properties)}.
  • - *
  • Use JVM settings - There are a number of standard - * Java system properties that can be used to configure key and trust - * stores.
  • - *
- * - *

- * In Java ME, the platform settings are used for SSL connections. - *

- * - *

- * An instance of the default persistence mechanism - * {@link MqttDefaultFilePersistence} is used by the client. To specify a - * different persistence mechanism or to turn off persistence, use the - * {@link #MqttAsyncClient(String, String, MqttClientPersistence)} - * constructor. - * - * @param serverURI - * the address of the server to connect to, specified as a URI. - * Can be overridden using - * {@link MqttConnectOptions#setServerURIs(String[])} - * @param clientId - * a client identifier that is unique on the server being - * connected to - * @throws IllegalArgumentException - * if the URI does not start with "tcp://", "ssl://" or - * "local://". - * @throws IllegalArgumentException - * if the clientId is null or is greater than 65535 characters - * in length - * @throws MqttException - * if any other problem was encountered - */ - public MqttAsyncClient(String serverURI, String clientId) throws MqttException { - this(serverURI, clientId, new MqttDefaultFilePersistence()); - } - - /** - * Create an MqttAsyncClient that is used to communicate with an MQTT - * server. - *

- * The address of a server can be specified on the constructor. - * Alternatively a list containing one or more servers can be specified - * using the {@link MqttConnectOptions#setServerURIs(String[]) - * setServerURIs} method on MqttConnectOptions. - * - *

- * The serverURI parameter is typically used with the the - * clientId parameter to form a key. The key is used to store - * and reference messages while they are being delivered. Hence the - * serverURI specified on the constructor must still be specified even if a - * list of servers is specified on an MqttConnectOptions object. The - * serverURI on the constructor must remain the same across restarts of the - * client for delivery of messages to be maintained from a given client to a - * given server or set of servers. - * - *

- * The address of the server to connect to is specified as a URI. Two types - * of connection are supported tcp:// for a TCP connection and - * ssl:// for a TCP connection secured by SSL/TLS. For example: - *

- *
    - *
  • tcp://localhost:1883
  • - *
  • ssl://localhost:8883
  • - *
- *

- * If the port is not specified, it will default to 1883 for - * tcp://" URIs, and 8883 for ssl:// URIs. - *

- * - *

- * A client identifier clientId must be specified and be less - * that 65535 characters. It must be unique across all clients connecting to - * the same server. The clientId is used by the server to store data related - * to the client, hence it is important that the clientId remain the same - * when connecting to a server if durable subscriptions or reliable - * messaging are required. - *

- * A convenience method is provided to generate a random client id that - * should satisfy this criteria - {@link #generateClientId()}. As the client - * identifier is used by the server to identify a client when it reconnects, - * the client must use the same identifier between connections if durable - * subscriptions or reliable delivery of messages is required. - *

- *

- * In Java SE, SSL can be configured in one of several ways, which the - * client will use in the following order: - *

- *
    - *
  • Supplying an SSLSocketFactory - - * applications can use - * {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a - * factory with the appropriate SSL settings.
  • - *
  • SSL Properties - applications can supply SSL - * settings as a simple Java Properties using - * {@link MqttConnectOptions#setSSLProperties(Properties)}.
  • - *
  • Use JVM settings - There are a number of standard - * Java system properties that can be used to configure key and trust - * stores.
  • - *
- * - *

- * In Java ME, the platform settings are used for SSL connections. - *

- *

- * A persistence mechanism is used to enable reliable messaging. For - * messages sent at qualities of service (QoS) 1 or 2 to be reliably - * delivered, messages must be stored (on both the client and server) until - * the delivery of the message is complete. If messages are not safely - * stored when being delivered then a failure in the client or server can - * result in lost messages. A pluggable persistence mechanism is supported - * via the {@link MqttClientPersistence} interface. An implementer of this - * interface that safely stores messages must be specified in order for - * delivery of messages to be reliable. In addition - * {@link MqttConnectOptions#setCleanSession(boolean)} must be set to false. - * In the event that only QoS 0 messages are sent or received or - * cleanSession is set to true then a safe store is not needed. - *

- *

- * An implementation of file-based persistence is provided in class - * {@link MqttDefaultFilePersistence} which will work in all Java SE based - * systems. If no persistence is needed, the persistence parameter can be - * explicitly set to null. - *

- * - * @param serverURI - * the address of the server to connect to, specified as a URI. - * Can be overridden using - * {@link MqttConnectOptions#setServerURIs(String[])} - * @param clientId - * a client identifier that is unique on the server being - * connected to - * @param persistence - * the persistence class to use to store in-flight message. If - * null then the default persistence mechanism is used - * @throws MqttException - * if any other problem was encountered - */ - public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException { - this(serverURI, clientId, persistence, new TimerPingSender()); - } - - public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence, - MqttPingSender pingSender) throws MqttException { - this(serverURI, clientId, persistence, pingSender, null); - } - - /** - * Create an MqttAsyncClient that is used to communicate with an MQTT - * server. - *

- * The address of a server can be specified on the constructor. - * Alternatively a list containing one or more servers can be specified - * using the {@link MqttConnectOptions#setServerURIs(String[]) - * setServerURIs} method on MqttConnectOptions. - * - *

- * The serverURI parameter is typically used with the the - * clientId parameter to form a key. The key is used to store - * and reference messages while they are being delivered. Hence the - * serverURI specified on the constructor must still be specified even if a - * list of servers is specified on an MqttConnectOptions object. The - * serverURI on the constructor must remain the same across restarts of the - * client for delivery of messages to be maintained from a given client to a - * given server or set of servers. - * - *

- * The address of the server to connect to is specified as a URI. Two types - * of connection are supported tcp:// for a TCP connection and - * ssl:// for a TCP connection secured by SSL/TLS. For example: - *

- *
    - *
  • tcp://localhost:1883
  • - *
  • ssl://localhost:8883
  • - *
- *

- * If the port is not specified, it will default to 1883 for - * tcp://" URIs, and 8883 for ssl:// URIs. - *

- * - *

- * A client identifier clientId must be specified and be less - * that 65535 characters. It must be unique across all clients connecting to - * the same server. The clientId is used by the server to store data related - * to the client, hence it is important that the clientId remain the same - * when connecting to a server if durable subscriptions or reliable - * messaging are required. - *

- * A convenience method is provided to generate a random client id that - * should satisfy this criteria - {@link #generateClientId()}. As the client - * identifier is used by the server to identify a client when it reconnects, - * the client must use the same identifier between connections if durable - * subscriptions or reliable delivery of messages is required. - *

- *

- * In Java SE, SSL can be configured in one of several ways, which the - * client will use in the following order: - *

- *
    - *
  • Supplying an SSLSocketFactory - - * applications can use - * {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a - * factory with the appropriate SSL settings.
  • - *
  • SSL Properties - applications can supply SSL - * settings as a simple Java Properties using - * {@link MqttConnectOptions#setSSLProperties(Properties)}.
  • - *
  • Use JVM settings - There are a number of standard - * Java system properties that can be used to configure key and trust - * stores.
  • - *
- * - *

- * In Java ME, the platform settings are used for SSL connections. - *

- *

- * A persistence mechanism is used to enable reliable messaging. For - * messages sent at qualities of service (QoS) 1 or 2 to be reliably - * delivered, messages must be stored (on both the client and server) until - * the delivery of the message is complete. If messages are not safely - * stored when being delivered then a failure in the client or server can - * result in lost messages. A pluggable persistence mechanism is supported - * via the {@link MqttClientPersistence} interface. An implementer of this - * interface that safely stores messages must be specified in order for - * delivery of messages to be reliable. In addition - * {@link MqttConnectOptions#setCleanSession(boolean)} must be set to false. - * In the event that only QoS 0 messages are sent or received or - * cleanSession is set to true then a safe store is not needed. - *

- *

- * An implementation of file-based persistence is provided in class - * {@link MqttDefaultFilePersistence} which will work in all Java SE based - * systems. If no persistence is needed, the persistence parameter can be - * explicitly set to null. - *

- * - * @param serverURI - * the address of the server to connect to, specified as a URI. - * Can be overridden using - * {@link MqttConnectOptions#setServerURIs(String[])} - * @param clientId - * a client identifier that is unique on the server being - * connected to - * @param persistence - * the persistence class to use to store in-flight message. If - * null then the default persistence mechanism is used - * @param pingSender - * Custom {@link MqttPingSender} implementation. - * @param executorService - * used for managing threads. If null then a newFixedThreadPool - * is used. - * @throws IllegalArgumentException - * if the URI does not start with "tcp://", "ssl://" or - * "local://" - * @throws IllegalArgumentException - * if the clientId is null or is greater than 65535 characters - * in length - * @throws MqttException - * if any other problem was encountered - */ - public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence, - MqttPingSender pingSender, ScheduledExecutorService executorService) throws MqttException { - final String methodName = "MqttAsyncClient"; - - log.setResourceName(clientId); - - if (clientId == null) { // Support empty client Id, 3.1.1 standard - throw new IllegalArgumentException("Null clientId"); - } - // Count characters, surrogate pairs count as one character. - int clientIdLength = 0; - for (int i = 0; i < clientId.length() - 1; i++) { - if (Character_isHighSurrogate(clientId.charAt(i))) - i++; - clientIdLength++; - } - if (clientIdLength > 65535) { - throw new IllegalArgumentException("ClientId longer than 65535 characters"); - } - - MqttConnectOptions.validateURI(serverURI); - - this.serverURI = serverURI; - this.clientId = clientId; - - this.persistence = persistence; - if (this.persistence == null) { - this.persistence = new MemoryPersistence(); - } - - this.executorService = executorService; - if (this.executorService == null) { - this.executorService = Executors.newScheduledThreadPool(10); - } - - // @TRACE 101= ClientID={0} ServerURI={1} PersistenceType={2} - log.fine(CLASS_NAME, methodName, "101", new Object[] { clientId, serverURI, persistence }); - - this.persistence.open(clientId, serverURI); - this.comms = new ClientComms(this, this.persistence, pingSender, this.executorService); - this.persistence.close(); - this.topics = new Hashtable(); - - } - - /** - * @param ch - * the character to check. - * @return returns 'true' if the character is a high-surrogate code unit - */ - protected static boolean Character_isHighSurrogate(char ch) { - return (ch >= MIN_HIGH_SURROGATE) && (ch <= MAX_HIGH_SURROGATE); - } - - /** - * Factory method to create an array of network modules, one for each of the - * supplied URIs - * - * @param address - * the URI for the server. - * @param options - * the {@link MqttConnectOptions} for the connection. - * @return a network module appropriate to the specified address. - * @throws MqttException - * if an exception occurs creating the network Modules - * @throws MqttSecurityException - * if an issue occurs creating an SSL / TLS Socket - */ - protected NetworkModule[] createNetworkModules(String address, MqttConnectOptions options) - throws MqttException, MqttSecurityException { - final String methodName = "createNetworkModules"; - // @TRACE 116=URI={0} - log.fine(CLASS_NAME, methodName, "116", new Object[] { address }); - - NetworkModule[] networkModules = null; - String[] serverURIs = options.getServerURIs(); - String[] array = null; - if (serverURIs == null) { - array = new String[] { address }; - } else if (serverURIs.length == 0) { - array = new String[] { address }; - } else { - array = serverURIs; - } - - networkModules = new NetworkModule[array.length]; - for (int i = 0; i < array.length; i++) { - networkModules[i] = createNetworkModule(array[i], options); - } - - log.fine(CLASS_NAME, methodName, "108"); - return networkModules; - } - - /** - * Factory method to create the correct network module, based on the - * supplied address URI. - * - * @param address the URI for the server. - * @param options Connect options - * @return a network module appropriate to the specified address. - */ - private NetworkModule createNetworkModule(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException { - final String methodName = "createNetworkModule"; - // @TRACE 115=URI={0} - log.fine(CLASS_NAME,methodName, "115", new Object[] {address}); - - NetworkModule netModule; - SocketFactory factory = options.getSocketFactory(); - - int serverURIType = MqttConnectOptions.validateURI(address); - - URI uri; - try { - uri = new URI(address); - // If the returned uri contains no host and the address contains underscores, - // then it's likely that Java did not parse the URI - if(uri.getHost() == null && address.contains("_")){ - try { - final Field hostField = URI.class.getDeclaredField("host"); - hostField.setAccessible(true); - // Get everything after the scheme:// - String shortAddress = address.substring(uri.getScheme().length() + 3); - hostField.set(uri, getHostName(shortAddress)); - - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - throw ExceptionHelper.createMqttException(e.getCause()); - } - - } - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Malformed URI: " + address + ", " + e.getMessage()); - } - - String host = uri.getHost(); - int port = uri.getPort(); // -1 if not defined - - switch (serverURIType) { - case MqttConnectOptions.URI_TYPE_TCP : - if (port == -1) { - port = 1883; - } - if (factory == null) { - factory = SocketFactory.getDefault(); - } - else if (factory instanceof SSLSocketFactory) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); - } - netModule = new TCPNetworkModule(factory, host, port, clientId); - ((TCPNetworkModule)netModule).setConnectTimeout(options.getConnectionTimeout()); - break; - case MqttConnectOptions.URI_TYPE_SSL: - if (port == -1) { - port = 8883; - } - SSLSocketFactoryFactory factoryFactory = null; - if (factory == null) { -// try { - factoryFactory = new SSLSocketFactoryFactory(); - Properties sslClientProps = options.getSSLProperties(); - if (null != sslClientProps) - factoryFactory.initialize(sslClientProps, null); - factory = factoryFactory.createSocketFactory(null); -// } -// catch (MqttDirectException ex) { -// throw ExceptionHelper.createMqttException(ex.getCause()); -// } - } - else if ((factory instanceof SSLSocketFactory) == false) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); - } - - // Create the network module... - netModule = new SSLNetworkModule((SSLSocketFactory) factory, host, port, clientId); - ((SSLNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout()); - ((SSLNetworkModule)netModule).setSSLHostnameVerifier(options.getSSLHostnameVerifier()); - // Ciphers suites need to be set, if they are available - if (factoryFactory != null) { - String[] enabledCiphers = factoryFactory.getEnabledCipherSuites(null); - if (enabledCiphers != null) { - ((SSLNetworkModule) netModule).setEnabledCiphers(enabledCiphers); - } - } - break; - case MqttConnectOptions.URI_TYPE_WS: - if (port == -1) { - port = 80; - } - if (factory == null) { - factory = SocketFactory.getDefault(); - } - else if (factory instanceof SSLSocketFactory) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); - } - netModule = new WebSocketNetworkModule(factory, address, host, port, clientId); - ((WebSocketNetworkModule)netModule).setConnectTimeout(options.getConnectionTimeout()); - break; - case MqttConnectOptions.URI_TYPE_WSS: - if (port == -1) { - port = 443; - } - SSLSocketFactoryFactory wSSFactoryFactory = null; - if (factory == null) { - wSSFactoryFactory = new SSLSocketFactoryFactory(); - Properties sslClientProps = options.getSSLProperties(); - if (null != sslClientProps) - wSSFactoryFactory.initialize(sslClientProps, null); - factory = wSSFactoryFactory.createSocketFactory(null); - - } - else if ((factory instanceof SSLSocketFactory) == false) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); - } - - // Create the network module... - netModule = new WebSocketSecureNetworkModule((SSLSocketFactory) factory, address, host, port, clientId); - ((WebSocketSecureNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout()); - // Ciphers suites need to be set, if they are available - if (wSSFactoryFactory != null) { - String[] enabledCiphers = wSSFactoryFactory.getEnabledCipherSuites(null); - if (enabledCiphers != null) { - ((SSLNetworkModule) netModule).setEnabledCiphers(enabledCiphers); - } - } - break; - default: - // This shouldn't happen, as long as validateURI() has been called. - log.fine(CLASS_NAME,methodName, "119", new Object[] {address}); - netModule = null; - } - return netModule; - } - - private String getHostName(String uri) { - int portIndex = uri.indexOf(':'); - if (portIndex == -1) { - portIndex = uri.indexOf('/'); - } - if (portIndex == -1) { - portIndex = uri.length(); - } - return uri.substring(0, portIndex); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(java.lang.Object, - * org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttToken connect(Object userContext, IMqttActionListener callback) - throws MqttException, MqttSecurityException { - return this.connect(new MqttConnectOptions(), userContext, callback); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect() - */ - public IMqttToken connect() throws MqttException, MqttSecurityException { - return this.connect(null, null); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho. - * client.mqttv3.MqttConnectOptions) - */ - public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException { - return this.connect(options, null, null); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho. - * client.mqttv3.MqttConnectOptions, java.lang.Object, - * org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback) - throws MqttException, MqttSecurityException { - final String methodName = "connect"; - if (comms.isConnected()) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED); - } - if (comms.isConnecting()) { - throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS); - } - if (comms.isDisconnecting()) { - throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); - } - if (comms.isClosed()) { - throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED); - } - if (options == null) { - options = new MqttConnectOptions(); - } - this.connOpts = options; - this.userContext = userContext; - final boolean automaticReconnect = options.isAutomaticReconnect(); - - // @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} - // userName={3} password={4} will={5} userContext={6} callback={7} - log.fine(CLASS_NAME, methodName, "103", - new Object[] { Boolean.valueOf(options.isCleanSession()), new Integer(options.getConnectionTimeout()), - new Integer(options.getKeepAliveInterval()), options.getUserName(), - ((null == options.getPassword()) ? "[null]" : "[notnull]"), - ((null == options.getWillMessage()) ? "[null]" : "[notnull]"), userContext, callback }); - comms.setNetworkModules(createNetworkModules(serverURI, options)); - comms.setReconnectCallback(new MqttReconnectCallback(automaticReconnect)); - - // Insert our own callback to iterate through the URIs till the connect - // succeeds - MqttToken userToken = new MqttToken(getClientId()); - ConnectActionListener connectActionListener = new ConnectActionListener(this, persistence, comms, options, - userToken, userContext, callback, reconnecting); - userToken.setActionCallback(connectActionListener); - userToken.setUserContext(this); - - // If we are using the MqttCallbackExtended, set it on the - // connectActionListener - if (this.mqttCallback instanceof MqttCallbackExtended) { - connectActionListener.setMqttCallbackExtended((MqttCallbackExtended) this.mqttCallback); - } - - comms.setNetworkModuleIndex(0); - connectActionListener.connect(); - - return userToken; - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(java.lang. - * Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttToken disconnect(Object userContext, IMqttActionListener callback) throws MqttException { - return this.disconnect(QUIESCE_TIMEOUT, userContext, callback); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect() - */ - public IMqttToken disconnect() throws MqttException { - return this.disconnect(null, null); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long) - */ - public IMqttToken disconnect(long quiesceTimeout) throws MqttException { - return this.disconnect(quiesceTimeout, null, null); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long, - * java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) - throws MqttException { - final String methodName = "disconnect"; - // @TRACE 104=> quiesceTimeout={0} userContext={1} callback={2} - log.fine(CLASS_NAME, methodName, "104", new Object[] { new Long(quiesceTimeout), userContext, callback }); - - MqttToken token = new MqttToken(getClientId()); - token.setActionCallback(callback); - token.setUserContext(userContext); - - MqttDisconnect disconnect = new MqttDisconnect(); - try { - comms.disconnect(disconnect, quiesceTimeout, token); - } catch (MqttException ex) { - // @TRACE 105=< exception - log.fine(CLASS_NAME, methodName, "105", null, ex); - throw ex; - } - // @TRACE 108=< - log.fine(CLASS_NAME, methodName, "108"); - - return token; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly() - */ - public void disconnectForcibly() throws MqttException { - disconnectForcibly(QUIESCE_TIMEOUT, DISCONNECT_TIMEOUT); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long) - */ - public void disconnectForcibly(long disconnectTimeout) throws MqttException { - disconnectForcibly(QUIESCE_TIMEOUT, disconnectTimeout); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long, - * long) - */ - public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException { - comms.disconnectForcibly(quiesceTimeout, disconnectTimeout); - } - - /** - * Disconnects from the server forcibly to reset all the states. Could be - * useful when disconnect attempt failed. - *

- * Because the client is able to establish the TCP/IP connection to a none - * MQTT server and it will certainly fail to send the disconnect packet. - * - * @param quiesceTimeout - * the amount of time in milliseconds to allow for existing work - * to finish before disconnecting. A value of zero or less means - * the client will not quiesce. - * @param disconnectTimeout - * the amount of time in milliseconds to allow send disconnect - * packet to server. - * @param sendDisconnectPacket - * if true, will send the disconnect packet to the server - * @throws MqttException - * if any unexpected error - */ - public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout, boolean sendDisconnectPacket) - throws MqttException { - comms.disconnectForcibly(quiesceTimeout, disconnectTimeout, sendDisconnectPacket); - } - - /* - * (non-Javadoc) - * - * @see IMqttAsyncClient#isConnected() - */ - public boolean isConnected() { - return comms.isConnected(); - } - - /* - * (non-Javadoc) - * - * @see IMqttAsyncClient#getClientId() - */ - public String getClientId() { - return clientId; - } - - /* - * (non-Javadoc) - * - * @see IMqttAsyncClient#getServerURI() - */ - public String getServerURI() { - return serverURI; - } - - /** - * Returns the currently connected Server URI Implemented due to: - * https://bugs.eclipse.org/bugs/show_bug.cgi?id=481097 - * - * Where getServerURI only returns the URI that was provided in - * MqttAsyncClient's constructor, getCurrentServerURI returns the URI of the - * Server that the client is currently connected to. This would be different - * in scenarios where multiple server URIs have been provided to the - * MqttConnectOptions. - * - * @return the currently connected server URI - */ - public String getCurrentServerURI() { - return comms.getNetworkModules()[comms.getNetworkModuleIndex()].getServerURI(); - } - - /** - * Get a topic object which can be used to publish messages. - *

- * There are two alternative methods that should be used in preference to - * this one when publishing a message: - *

- *
    - *
  • {@link MqttAsyncClient#publish(String, MqttMessage)} to publish a - * message in a non-blocking manner or
  • - *
  • {@link MqttClient#publish(String, MqttMessage)} to publish a message - * in a blocking manner
  • - *
- *

- * When you build an application, the design of the topic tree should take - * into account the following principles of topic name syntax and semantics: - *

- * - *
    - *
  • A topic must be at least one character long.
  • - *
  • Topic names are case sensitive. For example, ACCOUNTS and - * Accounts are two different topics.
  • - *
  • Topic names can include the space character. For example, - * Accounts payable is a valid topic.
  • - *
  • A leading "/" creates a distinct topic. For example, - * /finance is different from finance. /finance - * matches "+/+" and "/+", but not "+".
  • - *
  • Do not include the null character (Unicode \x0000) in any topic.
  • - *
- * - *

- * The following principles apply to the construction and content of a topic - * tree: - *

- * - *
    - *
  • The length is limited to 64k but within that there are no limits to - * the number of levels in a topic tree.
  • - *
  • There can be any number of root nodes; that is, there can be any - * number of topic trees.
  • - *
- * - * @param topic - * the topic to use, for example "finance/stock/ibm". - * @return an MqttTopic object, which can be used to publish messages to the - * topic. - * @throws IllegalArgumentException - * if the topic contains a '+' or '#' wildcard character. - */ - protected MqttTopic getTopic(String topic) { - MqttTopic.validate(topic, false/* wildcards NOT allowed */); - - MqttTopic result = (MqttTopic) topics.get(topic); - if (result == null) { - result = new MqttTopic(topic, comms); - topics.put(topic, result); - } - return result; - } - - /* - * (non-Javadoc) Check and send a ping if needed.

By default, client - * sends PingReq to server to keep the connection to server. For some - * platforms which cannot use this mechanism, such as Android, developer - * needs to handle the ping request manually with this method.

- * - * @throws MqttException for other errors encountered while publishing the - * message. - */ - public IMqttToken checkPing(Object userContext, IMqttActionListener callback) throws MqttException { - final String methodName = "ping"; - MqttToken token; - // @TRACE 117=> - log.fine(CLASS_NAME, methodName, "117"); - - token = comms.checkForActivity(); - // @TRACE 118=< - log.fine(CLASS_NAME, methodName, "118"); - - return token; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang. - * String, int, java.lang.Object, - * org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback) - throws MqttException { - return this.subscribe(new String[] { topicFilter }, new int[] { qos }, userContext, callback); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang. - * String, int) - */ - public IMqttToken subscribe(String topicFilter, int qos) throws MqttException { - return this.subscribe(new String[] { topicFilter }, new int[] { qos }, null, null); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang. - * String[], int[]) - */ - public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException { - return this.subscribe(topicFilters, qos, null, null); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang. - * String[], int[], java.lang.Object, - * org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) - throws MqttException { - final String methodName = "subscribe"; - - if (topicFilters.length != qos.length) { - throw new IllegalArgumentException(); - } - - // remove any message handlers for individual topics - for (int i = 0; i < topicFilters.length; ++i) { - this.comms.removeMessageListener(topicFilters[i]); - } - - // Only Generate Log string if we are logging at FINE level - if (log.isLoggable(Logger.FINE)) { - StringBuffer subs = new StringBuffer(); - for (int i = 0; i < topicFilters.length; i++) { - if (i > 0) { - subs.append(", "); - } - subs.append("topic=").append(topicFilters[i]).append(" qos=").append(qos[i]); - - // Check if the topic filter is valid before subscribing - MqttTopic.validate(topicFilters[i], true/* allow wildcards */); - } - // @TRACE 106=Subscribe topicFilter={0} userContext={1} callback={2} - log.fine(CLASS_NAME, methodName, "106", new Object[] { subs.toString(), userContext, callback }); - } - - MqttToken token = new MqttToken(getClientId()); - token.setActionCallback(callback); - token.setUserContext(userContext); - token.internalTok.setTopics(topicFilters); - - MqttSubscribe register = new MqttSubscribe(topicFilters, qos); - - comms.sendNoWait(register, token); - // @TRACE 109=< - log.fine(CLASS_NAME, methodName, "109"); - - return token; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang. - * String, int, java.lang.Object, - * org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback, - IMqttMessageListener messageListener) throws MqttException { - - return this.subscribe(new String[] { topicFilter }, new int[] { qos }, userContext, callback, - new IMqttMessageListener[] { messageListener }); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang. - * String, int) - */ - public IMqttToken subscribe(String topicFilter, int qos, IMqttMessageListener messageListener) - throws MqttException { - return this.subscribe(new String[] { topicFilter }, new int[] { qos }, null, null, - new IMqttMessageListener[] { messageListener }); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang. - * String[], int[]) - */ - public IMqttToken subscribe(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners) - throws MqttException { - return this.subscribe(topicFilters, qos, null, null, messageListeners); - } - - public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback, - IMqttMessageListener[] messageListeners) throws MqttException { - - if ((messageListeners.length != qos.length) || (qos.length != topicFilters.length)) { - throw new IllegalArgumentException(); - } - - IMqttToken token = this.subscribe(topicFilters, qos, userContext, callback); - - // add message handlers to the list for this client - for (int i = 0; i < topicFilters.length; ++i) { - this.comms.setMessageListener(topicFilters[i], messageListeners[i]); - } - - return token; - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang. - * String, java.lang.Object, - * org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback) - throws MqttException { - return unsubscribe(new String[] { topicFilter }, userContext, callback); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang. - * String) - */ - public IMqttToken unsubscribe(String topicFilter) throws MqttException { - return unsubscribe(new String[] { topicFilter }, null, null); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang. - * String[]) - */ - public IMqttToken unsubscribe(String[] topicFilters) throws MqttException { - return unsubscribe(topicFilters, null, null); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang. - * String[], java.lang.Object, - * org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback) - throws MqttException { - final String methodName = "unsubscribe"; - - // Only Generate Log string if we are logging at FINE level - if (log.isLoggable(Logger.FINE)) { - String subs = ""; - for (int i = 0; i < topicFilters.length; i++) { - if (i > 0) { - subs += ", "; - } - subs += topicFilters[i]; - } - - // @TRACE 107=Unsubscribe topic={0} userContext={1} callback={2} - log.fine(CLASS_NAME, methodName, "107", new Object[] { subs, userContext, callback }); - } - - for (int i = 0; i < topicFilters.length; i++) { - // Check if the topic filter is valid before unsubscribing - // Although we already checked when subscribing, but invalid - // topic filter is meanless for unsubscribing, just prohibit it - // to reduce unnecessary control packet send to broker. - MqttTopic.validate(topicFilters[i], true/* allow wildcards */); - } - - // remove message handlers from the list for this client - for (int i = 0; i < topicFilters.length; ++i) { - this.comms.removeMessageListener(topicFilters[i]); - } - - MqttToken token = new MqttToken(getClientId()); - token.setActionCallback(callback); - token.setUserContext(userContext); - token.internalTok.setTopics(topicFilters); - - MqttUnsubscribe unregister = new MqttUnsubscribe(topicFilters); - - comms.sendNoWait(unregister, token); - // @TRACE 110=< - log.fine(CLASS_NAME, methodName, "110"); - - return token; - } - - /* - * (non-Javadoc) - * - * @see IMqttAsyncClient#setCallback(MqttCallback) - */ - public void setCallback(MqttCallback callback) { - this.mqttCallback = callback; - comms.setCallback(callback); - } - - /* - * (non-Javadoc) - * - * @see IMqttAsyncClient#setManualAcks(manualAcks) - */ - public void setManualAcks(boolean manualAcks) { - comms.setManualAcks(manualAcks); - } - - public void messageArrivedComplete(int messageId, int qos) throws MqttException { - comms.messageArrivedComplete(messageId, qos); - } - - /** - * Returns a randomly generated client identifier based on the the fixed - * prefix (paho) and the system time. - *

- * When cleanSession is set to false, an application must ensure it uses the - * same client identifier when it reconnects to the server to resume state - * and maintain assured message delivery. - *

- * - * @return a generated client identifier - * @see MqttConnectOptions#setCleanSession(boolean) - */ - public static String generateClientId() { - // length of nanoTime = 15, so total length = 19 < 65535(defined in - // spec) - return CLIENT_ID_PREFIX + System.nanoTime(); - - } - - /* - * (non-Javadoc) - * - * @see IMqttAsyncClient#getPendingDeliveryTokens() - */ - public IMqttDeliveryToken[] getPendingDeliveryTokens() { - return comms.getPendingDeliveryTokens(); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, - * byte[], int, boolean, java.lang.Object, - * org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean retained, Object userContext, - IMqttActionListener callback) throws MqttException, MqttPersistenceException { - MqttMessage message = new MqttMessage(payload); - message.setQos(qos); - message.setRetained(retained); - return this.publish(topic, message, userContext, callback); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, - * byte[], int, boolean) - */ - public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean retained) - throws MqttException, MqttPersistenceException { - return this.publish(topic, payload, qos, retained, null, null); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, - * org.eclipse.paho.client.mqttv3.MqttMessage) - */ - public IMqttDeliveryToken publish(String topic, MqttMessage message) - throws MqttException, MqttPersistenceException { - return this.publish(topic, message, null, null); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, - * org.eclipse.paho.client.mqttv3.MqttMessage, java.lang.Object, - * org.eclipse.paho.client.mqttv3.IMqttActionListener) - */ - public IMqttDeliveryToken publish(String topic, MqttMessage message, Object userContext, - IMqttActionListener callback) throws MqttException, MqttPersistenceException { - final String methodName = "publish"; - // @TRACE 111=< topic={0} message={1}userContext={1} callback={2} - log.fine(CLASS_NAME, methodName, "111", new Object[] { topic, userContext, callback }); - - // Checks if a topic is valid when publishing a message. - MqttTopic.validate(topic, false/* wildcards NOT allowed */); - - MqttDeliveryToken token = new MqttDeliveryToken(getClientId()); - token.setActionCallback(callback); - token.setUserContext(userContext); - token.setMessage(message); - token.internalTok.setTopics(new String[] { topic }); - - MqttPublish pubMsg = new MqttPublish(topic, message); - comms.sendNoWait(pubMsg, token); - - // @TRACE 112=< - log.fine(CLASS_NAME, methodName, "112"); - - return token; - } - - /** - * User triggered attempt to reconnect - * - * @throws MqttException - * if there is an issue with reconnecting - */ - public void reconnect() throws MqttException { - final String methodName = "reconnect"; - // @Trace 500=Attempting to reconnect client: {0} - log.fine(CLASS_NAME, methodName, "500", new Object[] { this.clientId }); - // Some checks to make sure that we're not attempting to reconnect an - // already connected client - if (comms.isConnected()) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED); - } - if (comms.isConnecting()) { - throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS); - } - if (comms.isDisconnecting()) { - throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); - } - if (comms.isClosed()) { - throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED); - } - // We don't want to spam the server - stopReconnectCycle(); - - attemptReconnect(); - } - - /** - * Attempts to reconnect the client to the server. If successful it will - * make sure that there are no further reconnects scheduled. However if the - * connect fails, the delay will double up to 128 seconds and will - * re-schedule the reconnect for after the delay. - * - * Any thrown exceptions are logged but not acted upon as it is assumed that - * they are being thrown due to the server being offline and so reconnect - * attempts will continue. - */ - private void attemptReconnect() { - final String methodName = "attemptReconnect"; - // @Trace 500=Attempting to reconnect client: {0} - log.fine(CLASS_NAME, methodName, "500", new Object[] { this.clientId }); - try { - connect(this.connOpts, this.userContext, new MqttReconnectActionListener(methodName)); - } catch (MqttSecurityException ex) { - // @TRACE 804=exception - log.fine(CLASS_NAME, methodName, "804", null, ex); - } catch (MqttException ex) { - // @TRACE 804=exception - log.fine(CLASS_NAME, methodName, "804", null, ex); - } - } - - private void startReconnectCycle() { - String methodName = "startReconnectCycle"; - // @Trace 503=Start reconnect timer for client: {0}, delay: {1} - log.fine(CLASS_NAME, methodName, "503", new Object[] { this.clientId, new Long(reconnectDelay) }); - reconnectTimer = new Timer("MQTT Reconnect: " + clientId); - reconnectTimer.schedule(new ReconnectTask(), reconnectDelay); - } - - private void stopReconnectCycle() { - String methodName = "stopReconnectCycle"; - // @Trace 504=Stop reconnect timer for client: {0} - log.fine(CLASS_NAME, methodName, "504", new Object[] { this.clientId }); - synchronized (clientLock) { - if (this.connOpts.isAutomaticReconnect()) { - if (reconnectTimer != null) { - reconnectTimer.cancel(); - reconnectTimer = null; - } - reconnectDelay = 1000; // Reset Delay Timer - } - } - } - - private class ReconnectTask extends TimerTask { - private static final String methodName = "ReconnectTask.run"; - - public void run() { - // @Trace 506=Triggering Automatic Reconnect attempt. - log.fine(CLASS_NAME, methodName, "506"); - attemptReconnect(); - } - } - - class MqttReconnectCallback implements MqttCallbackExtended { - - final boolean automaticReconnect; - - MqttReconnectCallback(boolean isAutomaticReconnect) { - automaticReconnect = isAutomaticReconnect; - } - - public void connectionLost(Throwable cause) { - if (automaticReconnect) { - // Automatic reconnect is set so make sure comms is in resting - // state - comms.setRestingState(true); - reconnecting = true; - startReconnectCycle(); - } - } - - public void messageArrived(String topic, MqttMessage message) throws Exception { - } - - public void deliveryComplete(IMqttDeliveryToken token) { - } - - public void connectComplete(boolean reconnect, String serverURI) { - } - - } - - class MqttReconnectActionListener implements IMqttActionListener { - - final String methodName; - - MqttReconnectActionListener(String methodName) { - this.methodName = methodName; - } - - public void onSuccess(IMqttToken asyncActionToken) { - // @Trace 501=Automatic Reconnect Successful: {0} - log.fine(CLASS_NAME, methodName, "501", new Object[] { asyncActionToken.getClient().getClientId() }); - comms.setRestingState(false); - stopReconnectCycle(); - } - - public void onFailure(IMqttToken asyncActionToken, Throwable exception) { - // @Trace 502=Automatic Reconnect failed, rescheduling: {0} - log.fine(CLASS_NAME, methodName, "502", new Object[] { asyncActionToken.getClient().getClientId() }); - if (reconnectDelay < 128000) { - reconnectDelay = reconnectDelay * 2; - } - rescheduleReconnectCycle(reconnectDelay); - } - - private void rescheduleReconnectCycle(int delay) { - String reschedulemethodName = methodName + ":rescheduleReconnectCycle"; - // @Trace 505=Rescheduling reconnect timer for client: {0}, delay: - // {1} - log.fine(CLASS_NAME, reschedulemethodName, "505", - new Object[] { MqttAsyncClient.this.clientId, String.valueOf(reconnectDelay) }); - synchronized (clientLock) { - if (MqttAsyncClient.this.connOpts.isAutomaticReconnect()) { - if (reconnectTimer != null) { - reconnectTimer.schedule(new ReconnectTask(), delay); - } else { - // The previous reconnect timer was cancelled - reconnectDelay = delay; - startReconnectCycle(); - } - } - } - } - - } - - /** - * Sets the DisconnectedBufferOptions for this client - * - * @param bufferOpts - * the {@link DisconnectedBufferOptions} - */ - public void setBufferOpts(DisconnectedBufferOptions bufferOpts) { - this.comms.setDisconnectedMessageBuffer(new DisconnectedMessageBuffer(bufferOpts)); - } - - /** - * Returns the number of messages in the Disconnected Message Buffer - * - * @return Count of messages in the buffer - */ - public int getBufferedMessageCount() { - return this.comms.getBufferedMessageCount(); - } - - /** - * Returns a message from the Disconnected Message Buffer - * - * @param bufferIndex - * the index of the message to be retrieved. - * @return the message located at the bufferIndex - */ - public MqttMessage getBufferedMessage(int bufferIndex) { - return this.comms.getBufferedMessage(bufferIndex); - } - - /** - * Deletes a message from the Disconnected Message Buffer - * - * @param bufferIndex - * the index of the message to be deleted. - */ - public void deleteBufferedMessage(int bufferIndex) { - this.comms.deleteBufferedMessage(bufferIndex); - } - - /** - * Returns the current number of outgoing in-flight messages being sent by - * the client. Note that this number cannot be guaranteed to be 100% - * accurate as some messages may have been sent or queued in the time taken - * for this method to return. - * - * @return the current number of in-flight messages. - */ - public int getInFlightMessageCount() { - return this.comms.getActualInFlight(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close() - */ - public void close() throws MqttException { - close(false); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close() - */ - public void close(boolean force) throws MqttException { - final String methodName = "close"; - // @TRACE 113=< - log.fine(CLASS_NAME, methodName, "113"); - comms.close(force); - // @TRACE 114=> - log.fine(CLASS_NAME, methodName, "114"); - - } - - /** - * Return a debug object that can be used to help solve problems. - * - * @return the {@link Debug} object - */ - public Debug getDebug() { - return new Debug(clientId, comms); - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttCallback.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttCallback.java deleted file mode 100644 index 21d9c10..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttCallback.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - - -/** - * Enables an application to be notified when asynchronous - * events related to the client occur. - * Classes implementing this interface - * can be registered on both types of client: {@link IMqttClient#setCallback(MqttCallback)} - * and {@link IMqttAsyncClient#setCallback(MqttCallback)} - */ -public interface MqttCallback { - /** - * This method is called when the connection to the server is lost. - * - * @param cause the reason behind the loss of connection. - */ - public void connectionLost(Throwable cause); - - /** - * This method is called when a message arrives from the server. - * - *

- * This method is invoked synchronously by the MQTT client. An - * acknowledgment is not sent back to the server until this - * method returns cleanly.

- *

- * If an implementation of this method throws an Exception, then the - * client will be shut down. When the client is next re-connected, any QoS - * 1 or 2 messages will be redelivered by the server.

- *

- * Any additional messages which arrive while an - * implementation of this method is running, will build up in memory, and - * will then back up on the network.

- *

- * If an application needs to persist data, then it - * should ensure the data is persisted prior to returning from this method, as - * after returning from this method, the message is considered to have been - * delivered, and will not be reproducible.

- *

- * It is possible to send a new message within an implementation of this callback - * (for example, a response to this message), but the implementation must not - * disconnect the client, as it will be impossible to send an acknowledgment for - * the message being processed, and a deadlock will occur.

- * - * @param topic name of the topic on the message was published to - * @param message the actual message. - * @throws Exception if a terminal error has occurred, and the client should be - * shut down. - */ - public void messageArrived(String topic, MqttMessage message) throws Exception; - - /** - * Called when delivery for a message has been completed, and all - * acknowledgments have been received. For QoS 0 messages it is - * called once the message has been handed to the network for - * delivery. For QoS 1 it is called when PUBACK is received and - * for QoS 2 when PUBCOMP is received. The token will be the same - * token as that returned when the message was published. - * - * @param token the delivery token associated with the message. - */ - public void deliveryComplete(IMqttDeliveryToken token); - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttCallbackExtended.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttCallbackExtended.java deleted file mode 100644 index 7c7adf2..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttCallbackExtended.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Initial Contribution for Automatic Reconnect & Offline Buffering - */ -package org.eclipse.paho.client.mqttv3; - -/** - * Extension of {@link MqttCallback} to allow new callbacks - * without breaking the API for existing applications. - * Classes implementing this interface can be registered on - * both types of client: {@link IMqttClient#setCallback(MqttCallback)} - * and {@link IMqttAsyncClient#setCallback(MqttCallback)} - */ -public interface MqttCallbackExtended extends MqttCallback { - - /** - * Called when the connection to the server is completed successfully. - * @param reconnect If true, the connection was the result of automatic reconnect. - * @param serverURI The server URI that the connection was made to. - */ - public void connectComplete(boolean reconnect, String serverURI); - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttClient.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttClient.java deleted file mode 100644 index c878441..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttClient.java +++ /dev/null @@ -1,735 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2015 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - * Ian Craggs - per subscription message handlers (bug 466579) - * Ian Craggs - ack control (bug 472172) - */ -package org.eclipse.paho.client.mqttv3; - -import java.util.Properties; -import java.util.concurrent.ScheduledExecutorService; - -import javax.net.SocketFactory; - -import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; -import org.eclipse.paho.client.mqttv3.util.Debug; - -/** - * Lightweight client for talking to an MQTT server using methods that block - * until an operation completes. - * - *

This class implements the blocking {@link IMqttClient} client interface where all - * actions block until they have completed (or timed out). - * This implementation is compatible with all Java SE runtimes from 1.7 and up. - *

- *

An application can connect to an MQTT server using:

- *
    - *
  • A plain TCP socket - *
  • An secure SSL/TLS socket - *
- * - *

To enable messages to be delivered even across network and client restarts - * messages need to be safely stored until the message has been delivered at the requested - * quality of service. A pluggable persistence mechanism is provided to store the messages. - *

- *

By default {@link MqttDefaultFilePersistence} is used to store messages to a file. - * If persistence is set to null then messages are stored in memory and hence can be lost - * if the client, Java runtime or device shuts down. - *

- *

If connecting with {@link MqttConnectOptions#setCleanSession(boolean)} set to true it - * is safe to use memory persistence as all state it cleared when a client disconnects. If - * connecting with cleanSession set to false, to provide reliable message delivery - * then a persistent message store should be used such as the default one.

- *

The message store interface is pluggable. Different stores can be used by implementing - * the {@link MqttClientPersistence} interface and passing it to the clients constructor. - *

- * - * @see IMqttClient - */ -public class MqttClient implements IMqttClient { //), DestinationProvider { - //private static final String CLASS_NAME = MqttClient.class.getName(); - //private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME); - - protected MqttAsyncClient aClient = null; // Delegate implementation to MqttAsyncClient - protected long timeToWait = -1; // How long each method should wait for action to complete - - /** - * Create an MqttClient that can be used to communicate with an MQTT server. - *

- * The address of a server can be specified on the constructor. Alternatively - * a list containing one or more servers can be specified using the - * {@link MqttConnectOptions#setServerURIs(String[]) setServerURIs} method - * on MqttConnectOptions. - * - *

The serverURI parameter is typically used with the - * the clientId parameter to form a key. The key - * is used to store and reference messages while they are being delivered. - * Hence the serverURI specified on the constructor must still be specified even if a list - * of servers is specified on an MqttConnectOptions object. - * The serverURI on the constructor must remain the same across - * restarts of the client for delivery of messages to be maintained from a given - * client to a given server or set of servers. - * - *

The address of the server to connect to is specified as a URI. Two types of - * connection are supported tcp:// for a TCP connection and - * ssl:// for a TCP connection secured by SSL/TLS. - * For example:

- *
    - *
  • tcp://localhost:1883
  • - *
  • ssl://localhost:8883
  • - *
- *

- * If the port is not specified, it will - * default to 1883 for tcp://" URIs, and 8883 for ssl:// URIs. - *

- * - *

- * A client identifier clientId must be specified and be less that 65535 characters. - * It must be unique across all clients connecting to the same - * server. The clientId is used by the server to store data related to the client, - * hence it is important that the clientId remain the same when connecting to a server - * if durable subscriptions or reliable messaging are required. - *

A convenience method is provided to generate a random client id that - * should satisfy this criteria - {@link #generateClientId()}. As the client identifier - * is used by the server to identify a client when it reconnects, the client must use the - * same identifier between connections if durable subscriptions or reliable - * delivery of messages is required. - *

- *

- * In Java SE, SSL can be configured in one of several ways, which the - * client will use in the following order: - *

- *
    - *
  • Supplying an SSLSocketFactory - applications can - * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply - * a factory with the appropriate SSL settings.
  • - *
  • SSL Properties - applications can supply SSL settings as a - * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.
  • - *
  • Use JVM settings - There are a number of standard - * Java system properties that can be used to configure key and trust stores.
  • - *
- * - *

In Java ME, the platform settings are used for SSL connections.

- * - *

An instance of the default persistence mechanism {@link MqttDefaultFilePersistence} - * is used by the client. To specify a different persistence mechanism or to turn - * off persistence, use the {@link #MqttClient(String, String, MqttClientPersistence)} - * constructor. - * - * @param serverURI the address of the server to connect to, specified as a URI. Can be overridden using - * {@link MqttConnectOptions#setServerURIs(String[])} - * @param clientId a client identifier that is unique on the server being connected to - * @throws IllegalArgumentException if the URI does not start with - * "tcp://", "ssl://" or "local://". - * @throws IllegalArgumentException if the clientId is null or is greater than 65535 characters in length - * @throws MqttException if any other problem was encountered - */ - public MqttClient(String serverURI, String clientId) throws MqttException { - this(serverURI,clientId, new MqttDefaultFilePersistence()); - } - - /** - * Create an MqttClient that can be used to communicate with an MQTT server. - *

- * The address of a server can be specified on the constructor. Alternatively - * a list containing one or more servers can be specified using the - * {@link MqttConnectOptions#setServerURIs(String[]) setServerURIs} method - * on MqttConnectOptions. - * - *

The serverURI parameter is typically used with the - * the clientId parameter to form a key. The key - * is used to store and reference messages while they are being delivered. - * Hence the serverURI specified on the constructor must still be specified even if a list - * of servers is specified on an MqttConnectOptions object. - * The serverURI on the constructor must remain the same across - * restarts of the client for delivery of messages to be maintained from a given - * client to a given server or set of servers. - * - *

The address of the server to connect to is specified as a URI. Two types of - * connection are supported tcp:// for a TCP connection and - * ssl:// for a TCP connection secured by SSL/TLS. - * For example:

- *
    - *
  • tcp://localhost:1883
  • - *
  • ssl://localhost:8883
  • - *
- *

- * If the port is not specified, it will - * default to 1883 for tcp://" URIs, and 8883 for ssl:// URIs. - *

- * - *

- * A client identifier clientId must be specified and be less that 65535 characters. - * It must be unique across all clients connecting to the same - * server. The clientId is used by the server to store data related to the client, - * hence it is important that the clientId remain the same when connecting to a server - * if durable subscriptions or reliable messaging are required. - *

A convenience method is provided to generate a random client id that - * should satisfy this criteria - {@link #generateClientId()}. As the client identifier - * is used by the server to identify a client when it reconnects, the client must use the - * same identifier between connections if durable subscriptions or reliable - * delivery of messages is required. - *

- *

- * In Java SE, SSL can be configured in one of several ways, which the - * client will use in the following order: - *

- *
    - *
  • Supplying an SSLSocketFactory - applications can - * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply - * a factory with the appropriate SSL settings.
  • - *
  • SSL Properties - applications can supply SSL settings as a - * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.
  • - *
  • Use JVM settings - There are a number of standard - * Java system properties that can be used to configure key and trust stores.
  • - *
- * - *

In Java ME, the platform settings are used for SSL connections.

- *

- * A persistence mechanism is used to enable reliable messaging. - * For messages sent at qualities of service (QoS) 1 or 2 to be reliably delivered, - * messages must be stored (on both the client and server) until the delivery of the message - * is complete. If messages are not safely stored when being delivered then - * a failure in the client or server can result in lost messages. A pluggable - * persistence mechanism is supported via the {@link MqttClientPersistence} - * interface. An implementer of this interface that safely stores messages - * must be specified in order for delivery of messages to be reliable. In - * addition {@link MqttConnectOptions#setCleanSession(boolean)} must be set - * to false. In the event that only QoS 0 messages are sent or received or - * cleanSession is set to true then a safe store is not needed. - *

- *

An implementation of file-based persistence is provided in - * class {@link MqttDefaultFilePersistence} which will work in all Java SE based - * systems. If no persistence is needed, the persistence parameter - * can be explicitly set to null.

- * - * @param serverURI the address of the server to connect to, specified as a URI. Can be overridden using - * {@link MqttConnectOptions#setServerURIs(String[])} - * @param clientId a client identifier that is unique on the server being connected to - * @param persistence the persistence class to use to store in-flight message. If null then the - * default persistence mechanism is used - * @throws IllegalArgumentException if the URI does not start with - * "tcp://", "ssl://" or "local://" - * @throws IllegalArgumentException if the clientId is null or is greater than 65535 characters in length - * @throws MqttException if any other problem was encountered - */ - public MqttClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException { - aClient = new MqttAsyncClient(serverURI, clientId, persistence); - } - - /** - * Create an MqttClient that can be used to communicate with an MQTT server. - *

- * The address of a server can be specified on the constructor. Alternatively - * a list containing one or more servers can be specified using the - * {@link MqttConnectOptions#setServerURIs(String[]) setServerURIs} method - * on MqttConnectOptions. - * - *

The serverURI parameter is typically used with the - * the clientId parameter to form a key. The key - * is used to store and reference messages while they are being delivered. - * Hence the serverURI specified on the constructor must still be specified even if a list - * of servers is specified on an MqttConnectOptions object. - * The serverURI on the constructor must remain the same across - * restarts of the client for delivery of messages to be maintained from a given - * client to a given server or set of servers. - * - *

The address of the server to connect to is specified as a URI. Two types of - * connection are supported tcp:// for a TCP connection and - * ssl:// for a TCP connection secured by SSL/TLS. - * For example: - *

- *
    - *
  • tcp://localhost:1883
  • - *
  • ssl://localhost:8883
  • - *
- *

If the port is not specified, it will - * default to 1883 for tcp://" URIs, and 8883 for ssl:// URIs. - *

- * - *

- * A client identifier clientId must be specified and be less that 65535 characters. - * It must be unique across all clients connecting to the same - * server. The clientId is used by the server to store data related to the client, - * hence it is important that the clientId remain the same when connecting to a server - * if durable subscriptions or reliable messaging are required. - *

A convenience method is provided to generate a random client id that - * should satisfy this criteria - {@link #generateClientId()}. As the client identifier - * is used by the server to identify a client when it reconnects, the client must use the - * same identifier between connections if durable subscriptions or reliable - * delivery of messages is required. - *

- *

- * In Java SE, SSL can be configured in one of several ways, which the - * client will use in the following order: - *

- *
    - *
  • Supplying an SSLSocketFactory - applications can - * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply - * a factory with the appropriate SSL settings.
  • - *
  • SSL Properties - applications can supply SSL settings as a - * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.
  • - *
  • Use JVM settings - There are a number of standard - * Java system properties that can be used to configure key and trust stores.
  • - *
- * - *

In Java ME, the platform settings are used for SSL connections.

- *

- * A persistence mechanism is used to enable reliable messaging. - * For messages sent at qualities of service (QoS) 1 or 2 to be reliably delivered, - * messages must be stored (on both the client and server) until the delivery of the message - * is complete. If messages are not safely stored when being delivered then - * a failure in the client or server can result in lost messages. A pluggable - * persistence mechanism is supported via the {@link MqttClientPersistence} - * interface. An implementer of this interface that safely stores messages - * must be specified in order for delivery of messages to be reliable. In - * addition {@link MqttConnectOptions#setCleanSession(boolean)} must be set - * to false. In the event that only QoS 0 messages are sent or received or - * cleanSession is set to true then a safe store is not needed. - *

- *

An implementation of file-based persistence is provided in - * class {@link MqttDefaultFilePersistence} which will work in all Java SE based - * systems. If no persistence is needed, the persistence parameter - * can be explicitly set to null.

- * - * @param serverURI the address of the server to connect to, specified as a URI. Can be overridden using - * {@link MqttConnectOptions#setServerURIs(String[])} - * @param clientId a client identifier that is unique on the server being connected to - * @param persistence the persistence class to use to store in-flight message. If null then the - * default persistence mechanism is used - * @param executorService used for managing threads. If null then a newFixedThreadPool is used. - * @throws IllegalArgumentException if the URI does not start with - * "tcp://", "ssl://" or "local://" - * @throws IllegalArgumentException if the clientId is null or is greater than 65535 characters in length - * @throws MqttException if any other problem was encountered - */ - public MqttClient(String serverURI, String clientId, MqttClientPersistence persistence, ScheduledExecutorService executorService) throws MqttException { - aClient = new MqttAsyncClient(serverURI, clientId, persistence, new ScheduledExecutorPingSender(executorService), executorService); - } - - /* - * @see IMqttClient#connect() - */ - public void connect() throws MqttSecurityException, MqttException { - this.connect(new MqttConnectOptions()); - } - - /* - * @see IMqttClient#connect(MqttConnectOptions) - */ - public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException { - aClient.connect(options, null, null).waitForCompletion(getTimeToWait()); - } - - /* - * @see IMqttClient#connect(MqttConnectOptions) - */ - public IMqttToken connectWithResult(MqttConnectOptions options) throws MqttSecurityException, MqttException { - IMqttToken tok = aClient.connect(options, null, null); - tok.waitForCompletion(getTimeToWait()); - return tok; - } - - /* - * @see IMqttClient#disconnect() - */ - public void disconnect() throws MqttException { - aClient.disconnect().waitForCompletion(); - } - - /* - * @see IMqttClient#disconnect(long) - */ - public void disconnect(long quiesceTimeout) throws MqttException { - aClient.disconnect(quiesceTimeout, null, null).waitForCompletion(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly() - */ - public void disconnectForcibly() throws MqttException { - aClient.disconnectForcibly(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long) - */ - public void disconnectForcibly(long disconnectTimeout) throws MqttException { - aClient.disconnectForcibly(disconnectTimeout); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long, long) - */ - public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException { - aClient.disconnectForcibly(quiesceTimeout, disconnectTimeout); - } - - /** - * Disconnects from the server forcibly to reset all the states. Could be useful when disconnect attempt failed. - *

- * Because the client is able to establish the TCP/IP connection to a none MQTT server and it will certainly fail to - * send the disconnect packet. - * - * @param quiesceTimeout the amount of time in milliseconds to allow for existing work to finish before - * disconnecting. A value of zero or less means the client will not quiesce. - * @param disconnectTimeout the amount of time in milliseconds to allow send disconnect packet to server. - * @param sendDisconnectPacket if true, will send the disconnect packet to the server - * @throws MqttException if any unexpected error - */ - public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout, boolean sendDisconnectPacket) throws MqttException { - aClient.disconnectForcibly(quiesceTimeout, disconnectTimeout, sendDisconnectPacket); - } - - /* - * @see IMqttClient#subscribe(String) - */ - public void subscribe(String topicFilter) throws MqttException { - this.subscribe(new String[] {topicFilter}, new int[] {1}); - } - - /* - * @see IMqttClient#subscribe(String[]) - */ - public void subscribe(String[] topicFilters) throws MqttException { - int[] qos = new int[topicFilters.length]; - for (int i=0; iSet the maximum time to wait for an action to complete before - * returning control to the invoking application. Control is returned - * when:

- *
    - *
  • the action completes
  • - *
  • or when the timeout if exceeded
  • - *
  • or when the client is disconnect/shutdown
  • - *
- *

- * The default value is -1 which means the action will not timeout. - * In the event of a timeout the action carries on running in the - * background until it completes. The timeout is used on methods that - * block while the action is in progress. - *

- * @param timeToWaitInMillis before the action times out. A value or 0 or -1 will wait until - * the action finishes and not timeout. - * @throws IllegalArgumentException if timeToWaitInMillis is invalid - */ - public void setTimeToWait(long timeToWaitInMillis) throws IllegalArgumentException{ - if (timeToWaitInMillis < -1) { - throw new IllegalArgumentException(); - } - this.timeToWait = timeToWaitInMillis; - } - - /** - * Return the maximum time to wait for an action to complete. - * @return the time to wait - * @see MqttClient#setTimeToWait(long) - */ - public long getTimeToWait() { - return this.timeToWait; - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#close() - */ - public void close() throws MqttException { - aClient.close(false); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#close() - */ - public void close(boolean force) throws MqttException { - aClient.close(force); - } - - - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#getClientId() - */ - public String getClientId() { - return aClient.getClientId(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#getPendingDeliveryTokens() - */ - public IMqttDeliveryToken[] getPendingDeliveryTokens() { - return aClient.getPendingDeliveryTokens(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#getServerURI() - */ - public String getServerURI() { - return aClient.getServerURI(); - } - - /** - * Returns the currently connected Server URI - * Implemented due to: https://bugs.eclipse.org/bugs/show_bug.cgi?id=481097 - * - * Where getServerURI only returns the URI that was provided in - * MqttAsyncClient's constructor, getCurrentServerURI returns the URI of the - * Server that the client is currently connected to. This would be different in scenarios - * where multiple server URIs have been provided to the MqttConnectOptions. - * - * @return the currently connected server URI - */ - public String getCurrentServerURI(){ - return aClient.getCurrentServerURI(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#getTopic(java.lang.String) - */ - public MqttTopic getTopic(String topic) { - return aClient.getTopic(topic); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#isConnected() - */ - public boolean isConnected() { - return aClient.isConnected(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#setCallback(org.eclipse.paho.client.mqttv3.MqttCallback) - */ - public void setCallback(MqttCallback callback) { - aClient.setCallback(callback); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#setCallback(org.eclipse.paho.client.mqttv3.MqttCallback) - */ - public void setManualAcks(boolean manualAcks) { - aClient.setManualAcks(manualAcks); - } - - public void messageArrivedComplete(int messageId, int qos) throws MqttException { - aClient.messageArrivedComplete(messageId, qos); - } - - /** - * Returns a randomly generated client identifier based on the current user's login - * name and the system time. - *

When cleanSession is set to false, an application must ensure it uses the - * same client identifier when it reconnects to the server to resume state and maintain - * assured message delivery.

- * @return a generated client identifier - * @see MqttConnectOptions#setCleanSession(boolean) - */ - public static String generateClientId() { - return MqttAsyncClient.generateClientId(); - } - - /** - * Will attempt to reconnect to the server after the client has lost connection. - * @throws MqttException if an error occurs attempting to reconnect - */ - public void reconnect() throws MqttException { - aClient.reconnect(); - } - - /** - * Return a debug object that can be used to help solve problems. - * @return the {@link Debug} Object. - */ - public Debug getDebug() { - return (aClient.getDebug()); - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java deleted file mode 100644 index 071846c..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -import java.util.Enumeration; - -/** - * Represents a persistent data store, used to store outbound and inbound messages while they - * are in flight, enabling delivery to the QoS specified. You can specify an implementation - * of this interface using {@link MqttClient#MqttClient(String, String, MqttClientPersistence)}, - * which the {@link MqttClient} will use to persist QoS 1 and 2 messages. - *

- * If the methods defined throw the MqttPersistenceException then the state of the data persisted - * should remain as prior to the method being called. For example, if {@link #put(String, MqttPersistable)} - * throws an exception at any point then the data will be assumed to not be in the persistent store. - * Similarly if {@link #remove(String)} throws an exception then the data will be - * assumed to still be held in the persistent store.

- *

- * It is up to the persistence interface to log any exceptions or error information - * which may be required when diagnosing a persistence failure.

- */ -public interface MqttClientPersistence { - /** - * Initialise the persistent store. - * If a persistent store exists for this client ID then open it, otherwise - * create a new one. If the persistent store is already open then just return. - * An application may use the same client ID to connect to many different - * servers, so the client ID in conjunction with the - * connection will uniquely identify the persistence store required. - * - * @param clientId The client for which the persistent store should be opened. - * @param serverURI The connection string as specified when the MQTT client instance was created. - * @throws MqttPersistenceException if there was a problem opening the persistent store. - */ - public void open(String clientId, String serverURI) throws MqttPersistenceException; - - /** - * Close the persistent store that was previously opened. - * This will be called when a client application disconnects from the broker. - * @throws MqttPersistenceException if an error occurs closing the persistence store. - */ - public void close() throws MqttPersistenceException; - - /** - * Puts the specified data into the persistent store. - * @param key the key for the data, which will be used later to retrieve it. - * @param persistable the data to persist - * @throws MqttPersistenceException if there was a problem putting the data - * into the persistent store. - */ - public void put(String key, MqttPersistable persistable) throws MqttPersistenceException; - - /** - * Gets the specified data out of the persistent store. - * @param key the key for the data, which was used when originally saving it. - * @return the un-persisted data - * @throws MqttPersistenceException if there was a problem getting the data - * from the persistent store. - */ - public MqttPersistable get(String key) throws MqttPersistenceException; - - /** - * Remove the data for the specified key. - * @param key The key for the data to remove - * @throws MqttPersistenceException if there was a problem removing the data. - */ - public void remove(String key) throws MqttPersistenceException; - - /** - * Returns an Enumeration over the keys in this persistent data store. - * @return an enumeration of {@link String} objects. - * @throws MqttPersistenceException if there was a problem getting they keys - */ - public Enumeration keys() throws MqttPersistenceException; - - /** - * Clears persistence, so that it no longer contains any persisted data. - * @throws MqttPersistenceException if there was a problem clearing all data from the persistence store - */ - public void clear() throws MqttPersistenceException; - - /** - * Returns whether or not data is persisted using the specified key. - * @param key the key for data, which was used when originally saving it. - * @return True if the persistence store contains the key - * @throws MqttPersistenceException if there was a problem checking whether they key existed. - */ - public boolean containsKey(String key) throws MqttPersistenceException; -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java deleted file mode 100644 index 2e16b7a..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java +++ /dev/null @@ -1,633 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - * James Sutton - Automatic Reconnect & Offline Buffering - */ -package org.eclipse.paho.client.mqttv3; - -import java.util.Properties; - -import javax.net.SocketFactory; -import javax.net.ssl.HostnameVerifier; - -import org.eclipse.paho.client.mqttv3.util.Debug; - -import java.net.URI; -import java.net.URISyntaxException; - -/** - * Holds the set of options that control how the client connects to a server. - */ -public class MqttConnectOptions { - /** - * The default keep alive interval in seconds if one is not specified - */ - public static final int KEEP_ALIVE_INTERVAL_DEFAULT = 60; - /** - * The default connection timeout in seconds if one is not specified - */ - public static final int CONNECTION_TIMEOUT_DEFAULT = 30; - /** - * The default max inflight if one is not specified - */ - public static final int MAX_INFLIGHT_DEFAULT = 10; - /** - * The default clean session setting if one is not specified - */ - public static final boolean CLEAN_SESSION_DEFAULT = true; - /** - * The default MqttVersion is 3.1.1 first, dropping back to 3.1 if that fails - */ - public static final int MQTT_VERSION_DEFAULT = 0; - /** - * Mqtt Version 3.1 - */ - public static final int MQTT_VERSION_3_1 = 3; - /** - * Mqtt Version 3.1.1 - */ - public static final int MQTT_VERSION_3_1_1 = 4; - - protected static final int URI_TYPE_TCP = 0; - protected static final int URI_TYPE_SSL = 1; - protected static final int URI_TYPE_LOCAL = 2; - protected static final int URI_TYPE_WS = 3; - protected static final int URI_TYPE_WSS = 4; - - private int keepAliveInterval = KEEP_ALIVE_INTERVAL_DEFAULT; - private int maxInflight = MAX_INFLIGHT_DEFAULT; - private String willDestination = null; - private MqttMessage willMessage = null; - private String userName; - private char[] password; - private SocketFactory socketFactory; - private Properties sslClientProps = null; - private HostnameVerifier sslHostnameVerifier = null; - private boolean cleanSession = CLEAN_SESSION_DEFAULT; - private int connectionTimeout = CONNECTION_TIMEOUT_DEFAULT; - private String[] serverURIs = null; - private int MqttVersion = MQTT_VERSION_DEFAULT; - private boolean automaticReconnect = false; - - /** - * Constructs a new MqttConnectOptions object using the - * default values. - * - * The defaults are: - *
    - *
  • The keepalive interval is 60 seconds
  • - *
  • Clean Session is true
  • - *
  • The message delivery retry interval is 15 seconds
  • - *
  • The connection timeout period is 30 seconds
  • - *
  • No Will message is set
  • - *
  • A standard SocketFactory is used
  • - *
- * More information about these values can be found in the setter methods. - */ - public MqttConnectOptions() { - } - - /** - * Returns the password to use for the connection. - * @return the password to use for the connection. - */ - public char[] getPassword() { - return password; - } - - /** - * Sets the password to use for the connection. - * @param password A Char Array of the password - */ - public void setPassword(char[] password) { - this.password = password; - } - - /** - * Returns the user name to use for the connection. - * @return the user name to use for the connection. - */ - public String getUserName() { - return userName; - } - - /** - * Sets the user name to use for the connection. - * @param userName The Username as a String - * @throws IllegalArgumentException if the user name is blank or only - * contains whitespace characters. - */ - public void setUserName(String userName) { - if ((userName != null) && (userName.trim().equals(""))) { - throw new IllegalArgumentException(); - } - this.userName = userName; - } - - /** - * Sets the "Last Will and Testament" (LWT) for the connection. - * In the event that this client unexpectedly loses its connection to the - * server, the server will publish a message to itself using the supplied - * details. - * - * @param topic the topic to publish to. - * @param payload the byte payload for the message. - * @param qos the quality of service to publish the message at (0, 1 or 2). - * @param retained whether or not the message should be retained. - */ - public void setWill(MqttTopic topic, byte[] payload, int qos, boolean retained) { - String topicS = topic.getName(); - validateWill(topicS, payload); - this.setWill(topicS, new MqttMessage(payload), qos, retained); - } - - /** - * Sets the "Last Will and Testament" (LWT) for the connection. - * In the event that this client unexpectedly loses its connection to the - * server, the server will publish a message to itself using the supplied - * details. - * - * @param topic the topic to publish to. - * @param payload the byte payload for the message. - * @param qos the quality of service to publish the message at (0, 1 or 2). - * @param retained whether or not the message should be retained. - */ - public void setWill(String topic, byte[] payload, int qos, boolean retained) { - validateWill(topic, payload); - this.setWill(topic, new MqttMessage(payload), qos, retained); - } - - - /** - * Validates the will fields. - */ - private void validateWill(String dest, Object payload) { - if ((dest == null) || (payload == null)) { - throw new IllegalArgumentException(); - } - - MqttTopic.validate(dest, false/*wildcards NOT allowed*/); - } - - /** - * Sets up the will information, based on the supplied parameters. - * - * @param topic the topic to send the LWT message to - * @param msg the {@link MqttMessage} to send - * @param qos the QoS Level to send the message at - * @param retained whether the message should be retained or not - */ - protected void setWill(String topic, MqttMessage msg, int qos, boolean retained) { - willDestination = topic; - willMessage = msg; - willMessage.setQos(qos); - willMessage.setRetained(retained); - // Prevent any more changes to the will message - willMessage.setMutable(false); - } - - /** - * Returns the "keep alive" interval. - * @see #setKeepAliveInterval(int) - * @return the keep alive interval. - */ - public int getKeepAliveInterval() { - return keepAliveInterval; - } - - /** - * Returns the MQTT version. - * @see #setMqttVersion(int) - * @return the MQTT version. - */ - public int getMqttVersion() { - return MqttVersion; - } - - /** - * Sets the "keep alive" interval. - * This value, measured in seconds, defines the maximum time interval - * between messages sent or received. It enables the client to - * detect if the server is no longer available, without - * having to wait for the TCP/IP timeout. The client will ensure - * that at least one message travels across the network within each - * keep alive period. In the absence of a data-related message during - * the time period, the client sends a very small "ping" message, which - * the server will acknowledge. - * A value of 0 disables keepalive processing in the client. - *

The default value is 60 seconds

- * - * @param keepAliveInterval the interval, measured in seconds, must be >= 0. - * @throws IllegalArgumentException if the keepAliveInterval was invalid - */ - public void setKeepAliveInterval(int keepAliveInterval)throws IllegalArgumentException { - if (keepAliveInterval <0 ) { - throw new IllegalArgumentException(); - } - this.keepAliveInterval = keepAliveInterval; - } - - /** - * Returns the "max inflight". - * The max inflight limits to how many messages we can send without receiving acknowledgments. - * @see #setMaxInflight(int) - * @return the max inflight - */ - public int getMaxInflight() { - return maxInflight; - } - - /** - * Sets the "max inflight". - * please increase this value in a high traffic environment. - *

The default value is 10

- * @param maxInflight the number of maxInfligt messages - */ - public void setMaxInflight(int maxInflight) { - if (maxInflight < 0) { - throw new IllegalArgumentException(); - } - this.maxInflight = maxInflight; - } - - /** - * Returns the connection timeout value. - * @see #setConnectionTimeout(int) - * @return the connection timeout value. - */ - public int getConnectionTimeout() { - return connectionTimeout; - } - - /** - * Sets the connection timeout value. - * This value, measured in seconds, defines the maximum time interval - * the client will wait for the network connection to the MQTT server to be established. - * The default timeout is 30 seconds. - * A value of 0 disables timeout processing meaning the client will wait until the - * network connection is made successfully or fails. - * @param connectionTimeout the timeout value, measured in seconds. It must be >0; - */ - public void setConnectionTimeout(int connectionTimeout) { - if (connectionTimeout <0 ) { - throw new IllegalArgumentException(); - } - this.connectionTimeout = connectionTimeout; - } - - /** - * Returns the socket factory that will be used when connecting, or - * null if one has not been set. - * @return The Socket Factory - */ - public SocketFactory getSocketFactory() { - return socketFactory; - } - - /** - * Sets the SocketFactory to use. This allows an application - * to apply its own policies around the creation of network sockets. If - * using an SSL connection, an SSLSocketFactory can be used - * to supply application-specific security settings. - * @param socketFactory the factory to use. - */ - public void setSocketFactory(SocketFactory socketFactory) { - this.socketFactory = socketFactory; - } - - /** - * Returns the topic to be used for last will and testament (LWT). - * @return the MqttTopic to use, or null if LWT is not set. - * @see #setWill(MqttTopic, byte[], int, boolean) - */ - public String getWillDestination() { - return willDestination; - } - - /** - * Returns the message to be sent as last will and testament (LWT). - * The returned object is "read only". Calling any "setter" methods on - * the returned object will result in an - * IllegalStateException being thrown. - * @return the message to use, or null if LWT is not set. - */ - public MqttMessage getWillMessage() { - return willMessage; - } - - /** - * Returns the SSL properties for the connection. - * @return the properties for the SSL connection - */ - public Properties getSSLProperties() { - return sslClientProps; - } - - /** - * Sets the SSL properties for the connection. - *

Note that these - * properties are only valid if an implementation of the Java - * Secure Socket Extensions (JSSE) is available. These properties are - * not used if a SocketFactory has been set using - * {@link #setSocketFactory(SocketFactory)}. - * The following properties can be used:

- *
- *
com.ibm.ssl.protocol
- *
One of: SSL, SSLv3, TLS, TLSv1, SSL_TLS.
- *
com.ibm.ssl.contextProvider - *
Underlying JSSE provider. For example "IBMJSSE2" or "SunJSSE"
- * - *
com.ibm.ssl.keyStore
- *
The name of the file that contains the KeyStore object that you - * want the KeyManager to use. For example /mydir/etc/key.p12
- * - *
com.ibm.ssl.keyStorePassword
- *
The password for the KeyStore object that you want the KeyManager to use. - * The password can either be in plain-text, - * or may be obfuscated using the static method: - * com.ibm.micro.security.Password.obfuscate(char[] password). - * This obfuscates the password using a simple and insecure XOR and Base64 - * encoding mechanism. Note that this is only a simple scrambler to - * obfuscate clear-text passwords.
- * - *
com.ibm.ssl.keyStoreType
- *
Type of key store, for example "PKCS12", "JKS", or "JCEKS".
- * - *
com.ibm.ssl.keyStoreProvider
- *
Key store provider, for example "IBMJCE" or "IBMJCEFIPS".
- * - *
com.ibm.ssl.trustStore
- *
The name of the file that contains the KeyStore object that you - * want the TrustManager to use.
- * - *
com.ibm.ssl.trustStorePassword
- *
The password for the TrustStore object that you want the - * TrustManager to use. The password can either be in plain-text, - * or may be obfuscated using the static method: - * com.ibm.micro.security.Password.obfuscate(char[] password). - * This obfuscates the password using a simple and insecure XOR and Base64 - * encoding mechanism. Note that this is only a simple scrambler to - * obfuscate clear-text passwords.
- * - *
com.ibm.ssl.trustStoreType
- *
The type of KeyStore object that you want the default TrustManager to use. - * Same possible values as "keyStoreType".
- * - *
com.ibm.ssl.trustStoreProvider
- *
Trust store provider, for example "IBMJCE" or "IBMJCEFIPS".
- * - *
com.ibm.ssl.enabledCipherSuites
- *
A list of which ciphers are enabled. Values are dependent on the provider, - * for example: SSL_RSA_WITH_AES_128_CBC_SHA;SSL_RSA_WITH_3DES_EDE_CBC_SHA.
- * - *
com.ibm.ssl.keyManager
- *
Sets the algorithm that will be used to instantiate a KeyManagerFactory object - * instead of using the default algorithm available in the platform. Example values: - * "IbmX509" or "IBMJ9X509". - *
- * - *
com.ibm.ssl.trustManager
- *
Sets the algorithm that will be used to instantiate a TrustManagerFactory object - * instead of using the default algorithm available in the platform. Example values: - * "PKIX" or "IBMJ9X509". - *
- *
- * @param props The SSL {@link Properties} - */ - public void setSSLProperties(Properties props) { - this.sslClientProps = props; - } - - /** - * Returns the HostnameVerifier for the SSL connection. - * @return the HostnameVerifier for the SSL connection - */ - public HostnameVerifier getSSLHostnameVerifier() { - return sslHostnameVerifier; - } - - /** - * Sets the HostnameVerifier for the SSL connection. Note that it will be - * used after handshake on a connection and you should do actions by - * yourserlf when hostname is verified error. - *

- * There is no default HostnameVerifier - *

- * @param hostnameVerifier the {@link HostnameVerifier} - */ - public void setSSLHostnameVerifier(HostnameVerifier hostnameVerifier) { - this.sslHostnameVerifier = hostnameVerifier; - } - - /** - * Returns whether the client and server should remember state for the client across reconnects. - * @return the clean session flag - */ - public boolean isCleanSession() { - return this.cleanSession; - } - - /** - * Sets whether the client and server should remember state across restarts and reconnects. - *
    - *
  • If set to false both the client and server will maintain state across - * restarts of the client, the server and the connection. As state is maintained: - *
      - *
    • Message delivery will be reliable meeting - * the specified QOS even if the client, server or connection are restarted. - *
    • The server will treat a subscription as durable. - *
    - *
  • If set to true the client and server will not maintain state across - * restarts of the client, the server or the connection. This means - *
      - *
    • Message delivery to the specified QOS cannot be maintained if the - * client, server or connection are restarted - *
    • The server will treat a subscription as non-durable - *
    - *
- * @param cleanSession Set to True to enable cleanSession - */ - public void setCleanSession(boolean cleanSession) { - this.cleanSession = cleanSession; - } - - /** - * Return a list of serverURIs the client may connect to - * @return the serverURIs or null if not set - */ - public String[] getServerURIs() { - return serverURIs; - } - - /** - * Set a list of one or more serverURIs the client may connect to. - *

- * Each serverURI specifies the address of a server that the client may - * connect to. Two types of - * connection are supported tcp:// for a TCP connection and - * ssl:// for a TCP connection secured by SSL/TLS. - * For example: - *

    - *
  • tcp://localhost:1883
  • - *
  • ssl://localhost:8883
  • - *
- * If the port is not specified, it will - * default to 1883 for tcp://" URIs, and 8883 for ssl:// URIs. - *

- * If serverURIs is set then it overrides the serverURI parameter passed in on the - * constructor of the MQTT client. - *

- * When an attempt to connect is initiated the client will start with the first - * serverURI in the list and work through - * the list until a connection is established with a server. If a connection cannot be made to - * any of the servers then the connect attempt fails. - *

- * Specifying a list of servers that a client may connect to has several uses: - *

    - *
  1. High Availability and reliable message delivery - *

    Some MQTT servers support a high availability feature where two or more - * "equal" MQTT servers share state. An MQTT client can connect to any of the "equal" - * servers and be assured that messages are reliably delivered and durable subscriptions - * are maintained no matter which server the client connects to.

    - *

    The cleansession flag must be set to false if durable subscriptions and/or reliable - * message delivery is required.

  2. - *
  3. Hunt List - *

    A set of servers may be specified that are not "equal" (as in the high availability - * option). As no state is shared across the servers reliable message delivery and - * durable subscriptions are not valid. The cleansession flag must be set to true if the - * hunt list mode is used

  4. - *
- * @param array of serverURIs - */ - public void setServerURIs(String[] array) { - for (int i = 0; i < array.length; i++) { - validateURI(array[i]); - } - this.serverURIs = array; - } - - /** - * Validate a URI - * @param srvURI The Server URI - * @return the URI type - */ - public static int validateURI(String srvURI) { - try { - URI vURI = new URI(srvURI); - if ("ws".equals(vURI.getScheme())){ - return URI_TYPE_WS; - } - else if ("wss".equals(vURI.getScheme())) { - return URI_TYPE_WSS; - } - - if ((vURI.getPath() == null) || vURI.getPath().isEmpty()) { - // No op path must be empty - } - else { - throw new IllegalArgumentException(srvURI); - } - if ("tcp".equals(vURI.getScheme())) { - return URI_TYPE_TCP; - } - else if ("ssl".equals(vURI.getScheme())) { - return URI_TYPE_SSL; - } - else if ("local".equals(vURI.getScheme())) { - return URI_TYPE_LOCAL; - } - else { - throw new IllegalArgumentException(srvURI); - } - } catch (URISyntaxException ex) { - throw new IllegalArgumentException(srvURI); - } - } - - /** - * Sets the MQTT version. - * The default action is to connect with version 3.1.1, - * and to fall back to 3.1 if that fails. - * Version 3.1.1 or 3.1 can be selected specifically, with no fall back, - * by using the MQTT_VERSION_3_1_1 or MQTT_VERSION_3_1 options respectively. - * - * @param MqttVersion the version of the MQTT protocol. - * @throws IllegalArgumentException If the MqttVersion supplied is invalid - */ - public void setMqttVersion(int MqttVersion)throws IllegalArgumentException { - if (MqttVersion != MQTT_VERSION_DEFAULT && - MqttVersion != MQTT_VERSION_3_1 && - MqttVersion != MQTT_VERSION_3_1_1) { - throw new IllegalArgumentException(); - } - this.MqttVersion = MqttVersion; - } - - /** - * Returns whether the client will automatically attempt to reconnect to the - * server if the connection is lost - * @return the automatic reconnection flag. - */ - public boolean isAutomaticReconnect() { - return automaticReconnect; - } - - /** - * Sets whether the client will automatically attempt to reconnect to the - * server if the connection is lost. - *
    - *
  • If set to false, the client will not attempt to automatically - * reconnect to the server in the event that the connection is lost.
  • - *
  • If set to true, in the event that the connection is lost, the client - * will attempt to reconnect to the server. It will initially wait 1 second before - * it attempts to reconnect, for every failed reconnect attempt, the delay will double - * until it is at 2 minutes at which point the delay will stay at 2 minutes.
  • - *
- * @param automaticReconnect If set to True, Automatic Reconnect will be enabled - */ - public void setAutomaticReconnect(boolean automaticReconnect) { - this.automaticReconnect = automaticReconnect; - } - - - /** - * @return The Debug Properties - */ - public Properties getDebug() { - final String strNull="null"; - Properties p = new Properties(); - p.put("MqttVersion", new Integer(getMqttVersion())); - p.put("CleanSession", Boolean.valueOf(isCleanSession())); - p.put("ConTimeout", new Integer(getConnectionTimeout())); - p.put("KeepAliveInterval", new Integer(getKeepAliveInterval())); - p.put("UserName", (getUserName() == null) ? strNull : getUserName()); - p.put("WillDestination", (getWillDestination() == null) ? strNull : getWillDestination()); - if (getSocketFactory()==null) { - p.put("SocketFactory", strNull); - } else { - p.put("SocketFactory", getSocketFactory()); - } - if (getSSLProperties()==null) { - p.put("SSLProperties", strNull); - } else { - p.put("SSLProperties", getSSLProperties()); - } - return p; - } - - public String toString() { - return Debug.dumpProperties(getDebug(), "Connection options"); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java deleted file mode 100644 index f2bb574..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -/** - * Provides a mechanism to track the delivery progress of a message. - * - *

- * Used to track the the delivery progress of a message when a publish is - * executed in a non-blocking manner (run in the background)

- * - * @see MqttToken - */ -public class MqttDeliveryToken extends MqttToken implements IMqttDeliveryToken { - - - public MqttDeliveryToken() { - super(); - } - - public MqttDeliveryToken(String logContext) { - super(logContext); - } - - /** - * Returns the message associated with this token. - *

Until the message has been delivered, the message being delivered will - * be returned. Once the message has been delivered null will be - * returned. - * @return the message associated with this token or null if already delivered. - * @throws MqttException if there was a problem completing retrieving the message - */ - public MqttMessage getMessage() throws MqttException { - return internalTok.getMessage(); - } - - protected void setMessage(MqttMessage msg) { - internalTok.setMessage(msg); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttException.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttException.java deleted file mode 100644 index f9f88f4..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttException.java +++ /dev/null @@ -1,239 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - * James Sutton - Automatic Reconnect & Offline Buffering - */ -package org.eclipse.paho.client.mqttv3; - -import org.eclipse.paho.client.mqttv3.internal.MessageCatalog; - -/** - * Thrown if an error occurs communicating with the server. - */ -public class MqttException extends Exception { - private static final long serialVersionUID = 300L; - - /** - * Client encountered an exception. Use the {@link #getCause()} - * method to get the underlying reason. - */ - public static final short REASON_CODE_CLIENT_EXCEPTION = 0x00; - - // CONNACK return codes - /** The protocol version requested is not supported by the server. */ - public static final short REASON_CODE_INVALID_PROTOCOL_VERSION = 0x01; - /** The server has rejected the supplied client ID */ - public static final short REASON_CODE_INVALID_CLIENT_ID = 0x02; - /** The broker was not available to handle the request. */ - public static final short REASON_CODE_BROKER_UNAVAILABLE = 0x03; - /** Authentication with the server has failed, due to a bad user name or password. */ - public static final short REASON_CODE_FAILED_AUTHENTICATION = 0x04; - /** Not authorized to perform the requested operation */ - public static final short REASON_CODE_NOT_AUTHORIZED = 0x05; - - /** An unexpected error has occurred. */ - public static final short REASON_CODE_UNEXPECTED_ERROR = 0x06; - - /** Error from subscribe - returned from the server. */ - public static final short REASON_CODE_SUBSCRIBE_FAILED = 0x80; - - /** - * Client timed out while waiting for a response from the server. - * The server is no longer responding to keep-alive messages. - */ - public static final short REASON_CODE_CLIENT_TIMEOUT = 32000; - - /** - * Internal error, caused by no new message IDs being available. - */ - public static final short REASON_CODE_NO_MESSAGE_IDS_AVAILABLE = 32001; - - /** - * Client timed out while waiting to write messages to the server. - */ - public static final short REASON_CODE_WRITE_TIMEOUT = 32002; - - /** - * The client is already connected. - */ - public static final short REASON_CODE_CLIENT_CONNECTED = 32100; - - /** - * The client is already disconnected. - */ - public static final short REASON_CODE_CLIENT_ALREADY_DISCONNECTED = 32101; - /** - * The client is currently disconnecting and cannot accept any new work. - * This can occur when waiting on a token, and then disconnecting the client. - * If the message delivery does not complete within the quiesce timeout - * period, then the waiting token will be notified with an exception. - */ - public static final short REASON_CODE_CLIENT_DISCONNECTING = 32102; - - /** Unable to connect to server */ - public static final short REASON_CODE_SERVER_CONNECT_ERROR = 32103; - - /** - * The client is not connected to the server. The {@link MqttClient#connect()} - * or {@link MqttClient#connect(MqttConnectOptions)} method must be called - * first. It is also possible that the connection was lost - see - * {@link MqttClient#setCallback(MqttCallback)} for a way to track lost - * connections. - */ - public static final short REASON_CODE_CLIENT_NOT_CONNECTED = 32104; - - /** - * Server URI and supplied SocketFactory do not match. - * URIs beginning tcp:// must use a javax.net.SocketFactory, - * and URIs beginning ssl:// must use a javax.net.ssl.SSLSocketFactory. - */ - public static final short REASON_CODE_SOCKET_FACTORY_MISMATCH = 32105; - - /** - * SSL configuration error. - */ - public static final short REASON_CODE_SSL_CONFIG_ERROR = 32106; - - /** - * Thrown when an attempt to call {@link MqttClient#disconnect()} has been - * made from within a method on {@link MqttCallback}. These methods are invoked - * by the client's thread, and must not be used to control disconnection. - * - * @see MqttCallback#messageArrived(String, MqttMessage) - */ - public static final short REASON_CODE_CLIENT_DISCONNECT_PROHIBITED = 32107; - - /** - * Protocol error: the message was not recognized as a valid MQTT packet. - * Possible reasons for this include connecting to a non-MQTT server, or - * connecting to an SSL server port when the client isn't using SSL. - */ - public static final short REASON_CODE_INVALID_MESSAGE = 32108; - - /** - * The client has been unexpectedly disconnected from the server. The {@link #getCause() cause} - * will provide more details. - */ - public static final short REASON_CODE_CONNECTION_LOST = 32109; - - /** - * A connect operation in already in progress, only one connect can happen - * at a time. - */ - public static final short REASON_CODE_CONNECT_IN_PROGRESS = 32110; - - /** - * The client is closed - no operations are permitted on the client in this - * state. New up a new client to continue. - */ - public static final short REASON_CODE_CLIENT_CLOSED = 32111; - - /** - * A request has been made to use a token that is already associated with - * another action. If the action is complete the reset() can ve called on the - * token to allow it to be reused. - */ - public static final short REASON_CODE_TOKEN_INUSE = 32201; - - /** - * A request has been made to send a message but the maximum number of inflight - * messages has already been reached. Once one or more messages have been moved - * then new messages can be sent. - */ - public static final short REASON_CODE_MAX_INFLIGHT = 32202; - - /** - * The Client has attempted to publish a message whilst in the 'resting' / offline - * state with Disconnected Publishing enabled, however the buffer is full and - * deleteOldestMessages is disabled, therefore no more messages can be published - * until the client reconnects, or the application deletes buffered message - * manually. - */ - public static final short REASON_CODE_DISCONNECTED_BUFFER_FULL = 32203; - - private int reasonCode; - private Throwable cause; - - /** - * Constructs a new MqttException with the specified code - * as the underlying reason. - * @param reasonCode the reason code for the exception. - */ - public MqttException(int reasonCode) { - super(); - this.reasonCode = reasonCode; - } - - /** - * Constructs a new MqttException with the specified - * Throwable as the underlying reason. - * @param cause the underlying cause of the exception. - */ - public MqttException(Throwable cause) { - super(); - this.reasonCode = REASON_CODE_CLIENT_EXCEPTION; - this.cause = cause; - } - - /** - * Constructs a new MqttException with the specified - * Throwable as the underlying reason. - * @param reason the reason code for the exception. - * @param cause the underlying cause of the exception. - */ - public MqttException(int reason, Throwable cause) { - super(); - this.reasonCode = reason; - this.cause = cause; - } - - - /** - * Returns the reason code for this exception. - * @return the code representing the reason for this exception. - */ - public int getReasonCode() { - return reasonCode; - } - - /** - * Returns the underlying cause of this exception, if available. - * @return the Throwable that was the root cause of this exception, - * which may be null. - */ - public Throwable getCause() { - return cause; - } - - /** - * Returns the detail message for this exception. - * @return the detail message, which may be null. - */ - public String getMessage() { - return MessageCatalog.getMessage(reasonCode); - } - - /** - * Returns a String representation of this exception. - * @return a String representation of this exception. - */ - public String toString() { - String result = getMessage() + " (" + reasonCode + ")"; - if (cause != null) { - result = result + " - " + cause.toString(); - } - return result; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttMessage.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttMessage.java deleted file mode 100644 index 06926e4..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttMessage.java +++ /dev/null @@ -1,246 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - ack control (bug 472172) - */ -package org.eclipse.paho.client.mqttv3; - -/** - * An MQTT message holds the application payload and options - * specifying how the message is to be delivered - * The message includes a "payload" (the body of the message) - * represented as a byte[]. - */ -public class MqttMessage { - - private boolean mutable = true; - private byte[] payload; - private int qos = 1; - private boolean retained = false; - private boolean dup = false; - private int messageId; - - /** - * Utility method to validate the supplied QoS value. - * @param qos The QoS Level - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - */ - public static void validateQos(int qos) { - if ((qos < 0) || (qos > 2)) { - throw new IllegalArgumentException(); - } - } - - /** - * Constructs a message with an empty payload, and all other values - * set to defaults. - * - * The defaults are: - *

    - *
  • Message QoS set to 1
  • - *
  • Message will not be "retained" by the server
  • - *
- */ - public MqttMessage() { - setPayload(new byte[]{}); - } - - /** - * Constructs a message with the specified byte array as a payload, - * and all other values set to defaults. - * @param payload The Bytearray of the payload - */ - public MqttMessage(byte[] payload) { - setPayload(payload); - } - - /** - * Returns the payload as a byte array. - * - * @return the payload as a byte array. - */ - public byte[] getPayload() { - return payload; - } - - /** - * Clears the payload, resetting it to be empty. - * @throws IllegalStateException if this message cannot be edited - */ - public void clearPayload() { - checkMutable(); - this.payload = new byte[] {}; - } - - /** - * Sets the payload of this message to be the specified byte array. - * - * @param payload the payload for this message. - * @throws IllegalStateException if this message cannot be edited - * @throws NullPointerException if no payload is provided - */ - public void setPayload(byte[] payload) { - checkMutable(); - if (payload == null) { - throw new NullPointerException(); - } - this.payload = payload; - } - - /** - * Returns whether or not this message should be/was retained by the server. - * For messages received from the server, this method returns whether or not - * the message was from a current publisher, or was "retained" by the server as - * the last message published on the topic. - * - * @return true if the message should be, or was, retained by - * the server. - * @see #setRetained(boolean) - */ - public boolean isRetained() { - return retained; - } - - /** - * Whether or not the publish message should be retained by the messaging engine. - * Sending a message with retained set to true and with an empty - * byte array as the payload e.g. new byte[0] will clear the - * retained message from the server. The default value is false - * - * @param retained whether or not the messaging engine should retain the message. - * @throws IllegalStateException if this message cannot be edited - */ - public void setRetained(boolean retained) { - checkMutable(); - this.retained = retained; - } - - /** - * Returns the quality of service for this message. - * @return the quality of service to use, either 0, 1, or 2. - * @see #setQos(int) - */ - public int getQos() { - return qos; - } - - /** - * Sets the quality of service for this message. - *
    - *
  • Quality of Service 0 - indicates that a message should - * be delivered at most once (zero or one times). The message will not be persisted to disk, - * and will not be acknowledged across the network. This QoS is the fastest, - * but should only be used for messages which are not valuable - note that - * if the server cannot process the message (for example, there - * is an authorization problem), then an - * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)}. - * Also known as "fire and forget".
  • - * - *
  • Quality of Service 1 - indicates that a message should - * be delivered at least once (one or more times). The message can only be delivered safely if - * it can be persisted, so the application must supply a means of - * persistence using MqttConnectOptions. - * If a persistence mechanism is not specified, the message will not be - * delivered in the event of a client failure. - * The message will be acknowledged across the network. - * This is the default QoS.
  • - * - *
  • Quality of Service 2 - indicates that a message should - * be delivered once. The message will be persisted to disk, and will - * be subject to a two-phase acknowledgement across the network. - * The message can only be delivered safely if - * it can be persisted, so the application must supply a means of - * persistence using MqttConnectOptions. - * If a persistence mechanism is not specified, the message will not be - * delivered in the event of a client failure.
  • - * - *
- * If persistence is not configured, QoS 1 and 2 messages will still be delivered - * in the event of a network or server problem as the client will hold state in memory. - * If the MQTT client is shutdown or fails and persistence is not configured then - * delivery of QoS 1 and 2 messages can not be maintained as client-side state will - * be lost. - * - * @param qos the "quality of service" to use. Set to 0, 1, 2. - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws IllegalStateException if this message cannot be edited - */ - public void setQos(int qos) { - checkMutable(); - validateQos(qos); - this.qos = qos; - } - - /** - * Returns a string representation of this message's payload. - * Makes an attempt to return the payload as a string. As the - * MQTT client has no control over the content of the payload - * it may fail. - * @return a string representation of this message. - */ - public String toString() { - return new String(payload); - } - - /** - * Sets the mutability of this object (whether or not its values can be - * changed. - * @param mutable true if the values can be changed, - * false to prevent them from being changed. - */ - protected void setMutable(boolean mutable) { - this.mutable = mutable; - } - - protected void checkMutable() throws IllegalStateException { - if (!mutable) { - throw new IllegalStateException(); - } - } - - protected void setDuplicate(boolean dup) { - this.dup = dup; - } - - /** - * Returns whether or not this message might be a duplicate of one which has - * already been received. This will only be set on messages received from - * the server. - * @return true if the message might be a duplicate. - */ - public boolean isDuplicate() { - return this.dup; - } - - /** - * This is only to be used internally to provide the MQTT id of a message - * received from the server. Has no effect when publishing messages. - * @param messageId The Message ID - */ - public void setId(int messageId) { - this.messageId = messageId; - } - - /** - * Returns the MQTT id of the message. This is only applicable to messages - * received from the server. - * @return the MQTT id of the message - */ - public int getId() { - return this.messageId; - } - - - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPersistable.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPersistable.java deleted file mode 100644 index 95120a1..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPersistable.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -/** - * Represents an object used to pass data to be persisted across the - * {@link org.eclipse.paho.client.mqttv3.MqttClientPersistence MqttClientPersistence} - * interface. - *

- * When data is passed across the interface the header and payload are - * separated, so that unnecessary message copies may be avoided. - * For example, if a 10 MB payload was published it would be inefficient - * to create a byte array a few bytes larger than 10 MB and copy the - * MQTT message header and payload into a contiguous byte array.

- *

- * When the request to persist data is made a separate byte array and offset - * is passed for the header and payload. Only the data between - * offset and length need be persisted. - * So for example, a message to be persisted consists of a header byte - * array starting at offset 1 and length 4, plus a payload byte array - * starting at offset 30 and length 40000. There are three ways in which - * the persistence implementation may return data to the client on - * recovery:

- *
    - *
  • It could return the data as it was passed in - * originally, with the same byte arrays and offsets.
  • - *
  • It could safely just persist and return the bytes from the offset - * for the specified length. For example, return a header byte array with - * offset 0 and length 4, plus a payload byte array with offset 0 and length - * 40000
  • - *
  • It could return the header and payload as a contiguous byte array - * with the header bytes preceeding the payload. The contiguous byte array - * should be set as the header byte array, with the payload byte array being - * null. For example, return a single byte array with offset 0 - * and length 40004. - * This is useful when recovering from a file where the header and payload - * could be written as a contiguous stream of bytes.
  • - *
- */ -public interface MqttPersistable { - - /** - * Returns the header bytes in an array. - * The bytes start at {@link #getHeaderOffset()} - * and continue for {@link #getHeaderLength()}. - * @return the header bytes. - * @throws MqttPersistenceException if an error occurs getting the Header Bytes - */ - public byte[] getHeaderBytes() throws MqttPersistenceException; - - /** - * Returns the length of the header. - * @return the header length - * @throws MqttPersistenceException if an error occurs getting the Header length - */ - public int getHeaderLength() throws MqttPersistenceException; - - /** - * Returns the offset of the header within the byte array returned by {@link #getHeaderBytes()}. - * @return the header offset. - * @throws MqttPersistenceException if an error occurs getting the Header offset - * - */ - public int getHeaderOffset() throws MqttPersistenceException; - - /** - * Returns the payload bytes in an array. - * The bytes start at {@link #getPayloadOffset()} - * and continue for {@link #getPayloadLength()}. - * @return the payload bytes. - * @throws MqttPersistenceException if an error occurs getting the Payload Bytes - */ - public byte[] getPayloadBytes() throws MqttPersistenceException; - - /** - * Returns the length of the payload. - * @return the payload length. - * @throws MqttPersistenceException if an error occurs getting the Payload length - */ - public int getPayloadLength() throws MqttPersistenceException; - - /** - * Returns the offset of the payload within the byte array returned by {@link #getPayloadBytes()}. - * @return the payload offset. - * @throws MqttPersistenceException if an error occurs getting the Payload Offset - * - */ - public int getPayloadOffset() throws MqttPersistenceException; -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java deleted file mode 100644 index e5c142a..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -/** - * This exception is thrown by the implementor of the persistence - * interface if there is a problem reading or writing persistent data. - */ -public class MqttPersistenceException extends MqttException { - private static final long serialVersionUID = 300L; - - /** Persistence is already being used by another client. */ - public static final short REASON_CODE_PERSISTENCE_IN_USE = 32200; - - /** - * Constructs a new MqttPersistenceException - */ - public MqttPersistenceException() { - super(REASON_CODE_CLIENT_EXCEPTION); - } - - /** - * Constructs a new MqttPersistenceException with the specified code - * as the underlying reason. - * @param reasonCode the reason code for the exception. - */ - public MqttPersistenceException(int reasonCode) { - super(reasonCode); - } - /** - * Constructs a new MqttPersistenceException with the specified - * Throwable as the underlying reason. - * @param cause the underlying cause of the exception. - */ - public MqttPersistenceException(Throwable cause) { - super(cause); - } - /** - * Constructs a new MqttPersistenceException with the specified - * Throwable as the underlying reason. - * @param reason the reason code for the exception. - * @param cause the underlying cause of the exception. - */ - public MqttPersistenceException(int reason, Throwable cause) { - super(reason, cause); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPingSender.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPingSender.java deleted file mode 100644 index 27c090c..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttPingSender.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - */ - -package org.eclipse.paho.client.mqttv3; - -import org.eclipse.paho.client.mqttv3.internal.ClientComms; - -/** - * Represents an object used to send ping packet to MQTT broker - * every keep alive interval. - */ -public interface MqttPingSender { - - /** - * Initial method. Pass interal state of current client in. - * @param comms The core of the client, which holds the state information for pending and in-flight messages. - */ - public void init(ClientComms comms); - - /** - * Start ping sender. It will be called after connection is success. - */ - public void start(); - - /** - * Stop ping sender. It is called if there is any errors or connection shutdowns. - */ - public void stop(); - - /** - * Schedule next ping in certain delay. - * @param delayInMilliseconds delay in milliseconds. - */ - public void schedule(long delayInMilliseconds); - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttSecurityException.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttSecurityException.java deleted file mode 100644 index a5791ce..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttSecurityException.java +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -/** - * Thrown when a client is not authorized to perform an operation, or - * if there is a problem with the security configuration. - */ -public class MqttSecurityException extends MqttException { - private static final long serialVersionUID = 300L; - - /** - * Constructs a new MqttSecurityException with the specified code - * as the underlying reason. - * @param reasonCode the reason code for the exception. - */ - public MqttSecurityException(int reasonCode) { - super(reasonCode); - } - - /** - * Constructs a new MqttSecurityException with the specified - * Throwable as the underlying reason. - * @param cause the underlying cause of the exception. - */ - public MqttSecurityException(Throwable cause) { - super(cause); - } - /** - * Constructs a new MqttSecurityException with the specified - * code and Throwable as the underlying reason. - * @param reasonCode the reason code for the exception. - * @param cause the underlying cause of the exception. - */ - public MqttSecurityException(int reasonCode, Throwable cause) { - super(reasonCode, cause); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttToken.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttToken.java deleted file mode 100644 index 3352835..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttToken.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributions: - * Ian Craggs - MQTT 3.1.1 support - */ - -package org.eclipse.paho.client.mqttv3; - -import org.eclipse.paho.client.mqttv3.internal.Token; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; - -/** - * Provides a mechanism for tracking the completion of an asynchronous action. - *

- * A token that implements the ImqttToken interface is returned from all non-blocking - * method with the exception of publish. - *

- * - * @see IMqttToken - */ - -public class MqttToken implements IMqttToken { - /** - * A reference to the the class that provides most of the implementation of the - * MqttToken. MQTT application programs must not use the internal class. - */ - public Token internalTok = null; - - public MqttToken() { - } - - public MqttToken(String logContext) { - internalTok = new Token(logContext); - } - - public MqttException getException() { - return internalTok.getException(); - } - - public boolean isComplete() { - return internalTok.isComplete(); - } - - public void setActionCallback(IMqttActionListener listener) { - internalTok.setActionCallback(listener); - - } - public IMqttActionListener getActionCallback() { - return internalTok.getActionCallback(); - } - - public void waitForCompletion() throws MqttException { - internalTok.waitForCompletion(-1); - } - - public void waitForCompletion(long timeout) throws MqttException { - internalTok.waitForCompletion(timeout); - } - - public IMqttAsyncClient getClient() { - return internalTok.getClient(); - } - - public String[] getTopics() { - return internalTok.getTopics(); - } - - public Object getUserContext() { - return internalTok.getUserContext(); - } - - public void setUserContext(Object userContext) { - internalTok.setUserContext(userContext); - } - - public int getMessageId() { - return internalTok.getMessageID(); - } - - public int[] getGrantedQos() { - return internalTok.getGrantedQos(); - } - - public boolean getSessionPresent() { - return internalTok.getSessionPresent(); - } - - public MqttWireMessage getResponse() { - return internalTok.getResponse(); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttTopic.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttTopic.java deleted file mode 100644 index 404e961..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/MqttTopic.java +++ /dev/null @@ -1,284 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -import java.io.UnsupportedEncodingException; - -import org.eclipse.paho.client.mqttv3.internal.ClientComms; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; -import org.eclipse.paho.client.mqttv3.util.Strings; - -/** - * Represents a topic destination, used for publish/subscribe messaging. - */ -public class MqttTopic { - - /** - * The forward slash (/) is used to separate each level within a topic tree - * and provide a hierarchical structure to the topic space. The use of the - * topic level separator is significant when the two wildcard characters are - * encountered in topics specified by subscribers. - */ - public static final String TOPIC_LEVEL_SEPARATOR = "/"; - - /** - * Multi-level wildcard The number sign (#) is a wildcard character that - * matches any number of levels within a topic. - */ - public static final String MULTI_LEVEL_WILDCARD = "#"; - - /** - * Single-level wildcard The plus sign (+) is a wildcard character that - * matches only one topic level. - */ - public static final String SINGLE_LEVEL_WILDCARD = "+"; - - /** - * Multi-level wildcard pattern(/#) - */ - public static final String MULTI_LEVEL_WILDCARD_PATTERN = TOPIC_LEVEL_SEPARATOR + MULTI_LEVEL_WILDCARD; - - /** - * Topic wildcards (#+) - */ - public static final String TOPIC_WILDCARDS = MULTI_LEVEL_WILDCARD + SINGLE_LEVEL_WILDCARD; - - //topic name and topic filter length range defined in the spec - private static final int MIN_TOPIC_LEN = 1; - private static final int MAX_TOPIC_LEN = 65535; - private static final char NUL = '\u0000'; - - private ClientComms comms; - private String name; - - /** - * @param name The Name of the topic - * @param comms The {@link ClientComms} - */ - public MqttTopic(String name, ClientComms comms) { - this.comms = comms; - this.name = name; - } - - /** - * Publishes a message on the topic. This is a convenience method, which will - * create a new {@link MqttMessage} object with a byte array payload and the - * specified QoS, and then publish it. All other values in the - * message will be set to the defaults. - - * @param payload the byte array to use as the payload - * @param qos the Quality of Service. Valid values are 0, 1 or 2. - * @param retained whether or not this message should be retained by the server. - * @return {@link MqttDeliveryToken} - * @throws MqttException If an error occurs publishing the message - * @throws MqttPersistenceException If an error occurs persisting the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @see #publish(MqttMessage) - * @see MqttMessage#setQos(int) - * @see MqttMessage#setRetained(boolean) - */ - public MqttDeliveryToken publish(byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException { - MqttMessage message = new MqttMessage(payload); - message.setQos(qos); - message.setRetained(retained); - return this.publish(message); - } - - /** - * Publishes the specified message to this topic, but does not wait for delivery - * of the message to complete. The returned {@link MqttDeliveryToken token} can be used - * to track the delivery status of the message. Once this method has - * returned cleanly, the message has been accepted for publication by the - * client. Message delivery will be completed in the background when a connection - * is available. - * - * @param message the message to publish - * @return an MqttDeliveryToken for tracking the delivery of the message - * @throws MqttException if an error occurs publishing the message - * @throws MqttPersistenceException if an error occurs persisting the message - */ - public MqttDeliveryToken publish(MqttMessage message) throws MqttException, MqttPersistenceException { - MqttDeliveryToken token = new MqttDeliveryToken(comms.getClient().getClientId()); - token.setMessage(message); - comms.sendNoWait(createPublish(message), token); - token.internalTok.waitUntilSent(); - return token; - } - - /** - * Returns the name of the queue or topic. - * - * @return the name of this destination. - */ - public String getName() { - return name; - } - - /** - * Create a PUBLISH packet from the specified message. - */ - private MqttPublish createPublish(MqttMessage message) { - return new MqttPublish(this.getName(), message); - } - - /** - * Returns a string representation of this topic. - * @return a string representation of this topic. - */ - public String toString() { - return getName(); - } - - /** - * Validate the topic name or topic filter - * - * @param topicString topic name or filter - * @param wildcardAllowed true if validate topic filter, false otherwise - * @throws IllegalArgumentException if the topic is invalid - */ - public static void validate(String topicString, boolean wildcardAllowed) - throws IllegalArgumentException{ - int topicLen = 0; - try { - topicLen = topicString.getBytes("UTF-8").length; - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException(e.getMessage()); - } - - // Spec: length check - // - All Topic Names and Topic Filters MUST be at least one character - // long - // - Topic Names and Topic Filters are UTF-8 encoded strings, they MUST - // NOT encode to more than 65535 bytes - if (topicLen < MIN_TOPIC_LEN || topicLen > MAX_TOPIC_LEN) { - throw new IllegalArgumentException(String.format("Invalid topic length, should be in range[%d, %d]!", - new Object[] { new Integer(MIN_TOPIC_LEN), new Integer(MAX_TOPIC_LEN) })); - } - - // ******************************************************************************* - // 1) This is a topic filter string that can contain wildcard characters - // ******************************************************************************* - if (wildcardAllowed) { - // Only # or + - if (Strings.equalsAny(topicString, new String[] { MULTI_LEVEL_WILDCARD, SINGLE_LEVEL_WILDCARD })) { - return; - } - - // 1) Check multi-level wildcard - // Rule: - // The multi-level wildcard can be specified only on its own or next - // to the topic level separator character. - - // - Can only contains one multi-level wildcard character - // - The multi-level wildcard must be the last character used within - // the topic tree - if (Strings.countMatches(topicString, MULTI_LEVEL_WILDCARD) > 1 - || (topicString.contains(MULTI_LEVEL_WILDCARD) && !topicString - .endsWith(MULTI_LEVEL_WILDCARD_PATTERN))) { - throw new IllegalArgumentException( - "Invalid usage of multi-level wildcard in topic string: " - + topicString); - } - - // 2) Check single-level wildcard - // Rule: - // The single-level wildcard can be used at any level in the topic - // tree, and in conjunction with the - // multilevel wildcard. It must be used next to the topic level - // separator, except when it is specified on - // its own. - validateSingleLevelWildcard(topicString); - - return; - } - - // ******************************************************************************* - // 2) This is a topic name string that MUST NOT contains any wildcard characters - // ******************************************************************************* - if (Strings.containsAny(topicString, TOPIC_WILDCARDS)) { - throw new IllegalArgumentException( - "The topic name MUST NOT contain any wildcard characters (#+)"); - } - } - - private static void validateSingleLevelWildcard(String topicString) { - char singleLevelWildcardChar = SINGLE_LEVEL_WILDCARD.charAt(0); - char topicLevelSeparatorChar = TOPIC_LEVEL_SEPARATOR.charAt(0); - - char[] chars = topicString.toCharArray(); - int length = chars.length; - char prev = NUL, next = NUL; - for (int i = 0; i < length; i++) { - prev = (i - 1 >= 0) ? chars[i - 1] : NUL; - next = (i + 1 < length) ? chars[i + 1] : NUL; - - if (chars[i] == singleLevelWildcardChar) { - // prev and next can be only '/' or none - if (prev != topicLevelSeparatorChar && prev != NUL || next != topicLevelSeparatorChar && next != NUL) { - throw new IllegalArgumentException(String.format( - "Invalid usage of single-level wildcard in topic string '%s'!", - new Object[] { topicString })); - - } - } - } - } - - /** - * Check the supplied topic name and filter match - * - * @param topicFilter topic filter: wildcards allowed - * @param topicName topic name: wildcards not allowed - * @return true if the topic matches the filter - * @throws IllegalArgumentException if the topic name or filter is invalid - */ - public static boolean isMatched(String topicFilter, String topicName) - throws IllegalArgumentException { - int curn = 0, - curf = 0; - int curn_end = topicName.length(); - int curf_end = topicFilter.length(); - - MqttTopic.validate(topicFilter, true); - MqttTopic.validate(topicName, false); - - if (topicFilter.equals(topicName)) { - return true; - } - - while (curf < curf_end && curn < curn_end) - { - if (topicName.charAt(curn) == '/' && topicFilter.charAt(curf) != '/') - break; - if (topicFilter.charAt(curf) != '+' && topicFilter.charAt(curf) != '#' && - topicFilter.charAt(curf) != topicName.charAt(curn)) - break; - if (topicFilter.charAt(curf) == '+') - { // skip until we meet the next separator, or end of string - int nextpos = curn + 1; - while (nextpos < curn_end && topicName.charAt(nextpos) != '/') - nextpos = ++curn + 1; - } - else if (topicFilter.charAt(curf) == '#') - curn = curn_end - 1; // skip until end of string - curf++; - curn++; - }; - - return (curn == curn_end) && (curf == curf_end); - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/ScheduledExecutorPingSender.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/ScheduledExecutorPingSender.java deleted file mode 100644 index 511909c..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/ScheduledExecutorPingSender.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * Copyright (c) 2017 BMW Car IT GmbH. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - */ - -package org.eclipse.paho.client.mqttv3; - -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import org.eclipse.paho.client.mqttv3.internal.ClientComms; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Default ping sender implementation - * - *

This class implements the {@link IMqttPingSender} pinger interface - * allowing applications to send ping packet to server every keep alive interval. - *

- * - * @see MqttPingSender - */ -public class ScheduledExecutorPingSender implements MqttPingSender { - private static final String CLASS_NAME = ScheduledExecutorPingSender.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - private ClientComms comms; - private ScheduledExecutorService executorService; - private ScheduledFuture scheduledFuture; - private String clientid; - - public ScheduledExecutorPingSender(ScheduledExecutorService executorService) { - if (executorService == null) { - throw new IllegalArgumentException("ExecutorService cannot be null."); - } - this.executorService = executorService; - } - - public void init(ClientComms comms) { - if (comms == null) { - throw new IllegalArgumentException("ClientComms cannot be null."); - } - this.comms = comms; - clientid = comms.getClient().getClientId(); - } - - public void start() { - final String methodName = "start"; - - //@Trace 659=start timer for client:{0} - log.fine(CLASS_NAME, methodName, "659", new Object[]{ clientid }); - //Check ping after first keep alive interval. - schedule(comms.getKeepAlive()); - } - - public void stop() { - final String methodName = "stop"; - //@Trace 661=stop - log.fine(CLASS_NAME, methodName, "661", null); - if (scheduledFuture != null) { - scheduledFuture.cancel(true); - } - } - - public void schedule(long delayInMilliseconds) { - scheduledFuture = executorService.schedule(new PingRunnable(), delayInMilliseconds, TimeUnit.MILLISECONDS); - } - - private class PingRunnable implements Runnable { - private static final String methodName = "PingTask.run"; - - public void run() { - String originalThreadName = Thread.currentThread().getName(); - Thread.currentThread().setName("MQTT Ping: " + clientid); - //@Trace 660=Check schedule at {0} - log.fine(CLASS_NAME, methodName, "660", new Object[]{ new Long(System.currentTimeMillis()) }); - comms.checkForActivity(); - Thread.currentThread().setName(originalThreadName); - } - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/TimerPingSender.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/TimerPingSender.java deleted file mode 100644 index 4c93212..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/TimerPingSender.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - */ - -package org.eclipse.paho.client.mqttv3; - -import java.util.Timer; -import java.util.TimerTask; - -import org.eclipse.paho.client.mqttv3.internal.ClientComms; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Default ping sender implementation - * - *

This class implements the {@link MqttPingSender} pinger interface - * allowing applications to send ping packet to server every keep alive interval. - *

- * - * @see MqttPingSender - */ -public class TimerPingSender implements MqttPingSender { - private static final String CLASS_NAME = TimerPingSender.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME); - - private ClientComms comms; - private Timer timer; - - public void init(ClientComms comms) { - if (comms == null) { - throw new IllegalArgumentException("ClientComms cannot be null."); - } - this.comms = comms; - } - - public void start() { - final String methodName = "start"; - String clientid = comms.getClient().getClientId(); - - //@Trace 659=start timer for client:{0} - log.fine(CLASS_NAME, methodName, "659", new Object[]{clientid}); - - timer = new Timer("MQTT Ping: " + clientid); - //Check ping after first keep alive interval. - timer.schedule(new PingTask(), comms.getKeepAlive()); - } - - public void stop() { - final String methodName = "stop"; - //@Trace 661=stop - log.fine(CLASS_NAME, methodName, "661", null); - if(timer != null){ - timer.cancel(); - } - } - - public void schedule(long delayInMilliseconds) { - timer.schedule(new PingTask(), delayInMilliseconds); - } - - private class PingTask extends TimerTask { - private static final String methodName = "PingTask.run"; - - public void run() { - //@Trace 660=Check schedule at {0} - log.fine(CLASS_NAME, methodName, "660", new Object[]{new Long(System.currentTimeMillis())}); - comms.checkForActivity(); - } - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientComms.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientComms.java deleted file mode 100644 index 542d9df..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientComms.java +++ /dev/null @@ -1,881 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - per subscription message handlers (bug 466579) - * Ian Craggs - ack control (bug 472172) - * James Sutton - checkForActivity Token (bug 473928) - * James Sutton - Automatic Reconnect & Offline Buffering. - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.util.Enumeration; -import java.util.Properties; -import java.util.Vector; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; - -import org.eclipse.paho.client.mqttv3.BufferedMessage; -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; -import org.eclipse.paho.client.mqttv3.IMqttMessageListener; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttCallbackExtended; -import org.eclipse.paho.client.mqttv3.MqttClientPersistence; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; -import org.eclipse.paho.client.mqttv3.MqttPingSender; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.MqttTopic; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Handles client communications with the server. Sends and receives MQTT V3 - * messages. - */ -public class ClientComms { - public static String VERSION = "${project.version}"; - public static String BUILD_LEVEL = "L${build.level}"; - private static final String CLASS_NAME = ClientComms.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME); - - private static final byte CONNECTED = 0; - private static final byte CONNECTING = 1; - private static final byte DISCONNECTING = 2; - private static final byte DISCONNECTED = 3; - private static final byte CLOSED = 4; - - private IMqttAsyncClient client; - private int networkModuleIndex; - private NetworkModule[] networkModules; - private CommsReceiver receiver; - private CommsSender sender; - private CommsCallback callback; - private ClientState clientState; - private MqttConnectOptions conOptions; - private MqttClientPersistence persistence; - private MqttPingSender pingSender; - private CommsTokenStore tokenStore; - private boolean stoppingComms = false; - - private byte conState = DISCONNECTED; - private Object conLock = new Object(); // Used to synchronize connection state - private boolean closePending = false; - private boolean resting = false; - private DisconnectedMessageBuffer disconnectedMessageBuffer; - - private ExecutorService executorService; - - /** - * Creates a new ClientComms object, using the specified module to handle - * the network calls. - * @param client The {@link IMqttAsyncClient} - * @param persistence the {@link MqttClientPersistence} layer. - * @param pingSender the {@link MqttPingSender} - * @param executorService the {@link ExecutorService} - * @throws MqttException if an exception occurs whilst communicating with the server - */ - public ClientComms(IMqttAsyncClient client, MqttClientPersistence persistence, MqttPingSender pingSender, ExecutorService executorService) throws MqttException { - this.conState = DISCONNECTED; - this.client = client; - this.persistence = persistence; - this.pingSender = pingSender; - this.pingSender.init(this); - this.executorService = executorService; - - this.tokenStore = new CommsTokenStore(getClient().getClientId()); - this.callback = new CommsCallback(this); - this.clientState = new ClientState(persistence, tokenStore, this.callback, this, pingSender); - - callback.setClientState(clientState); - log.setResourceName(getClient().getClientId()); - } - - CommsReceiver getReceiver() { - return receiver; - } - - private void shutdownExecutorService() { - String methodName = "shutdownExecutorService"; - executorService.shutdown(); - try { - if (!executorService.awaitTermination(1, TimeUnit.SECONDS)) { - executorService.shutdownNow(); - if (!executorService.awaitTermination(1, TimeUnit.SECONDS)) { - log.fine(CLASS_NAME, methodName, "executorService did not terminate"); - } - } - } catch (InterruptedException ie) { - executorService.shutdownNow(); - Thread.currentThread().interrupt(); - } - } - - /** - * Sends a message to the server. Does not check if connected this validation must be done - * by invoking routines. - * @param message - * @param token - * @throws MqttException - */ - void internalSend(MqttWireMessage message, MqttToken token) throws MqttException { - final String methodName = "internalSend"; - //@TRACE 200=internalSend key={0} message={1} token={2} - log.fine(CLASS_NAME, methodName, "200", new Object[]{message.getKey(), message, token}); - - if (token.getClient() == null ) { - // Associate the client with the token - also marks it as in use. - token.internalTok.setClient(getClient()); - } else { - // Token is already in use - cannot reuse - //@TRACE 213=fail: token in use: key={0} message={1} token={2} - log.fine(CLASS_NAME, methodName, "213", new Object[]{message.getKey(), message, token}); - - throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE); - } - - try { - // Persist if needed and send the message - this.clientState.send(message, token); - } catch(MqttException e) { - if (message instanceof MqttPublish) { - this.clientState.undo((MqttPublish)message); - } - throw e; - } - } - - /** - * Sends a message to the broker if in connected state, but only waits for the message to be - * stored, before returning. - * @param message The {@link MqttWireMessage} to send - * @param token The {@link MqttToken} to send. - * @throws MqttException if an error occurs sending the message - */ - public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttException { - final String methodName = "sendNoWait"; - if (isConnected() || - (!isConnected() && message instanceof MqttConnect) || - (isDisconnecting() && message instanceof MqttDisconnect)) { - if(disconnectedMessageBuffer != null && disconnectedMessageBuffer.getMessageCount() != 0){ - //@TRACE 507=Client Connected, Offline Buffer available, but not empty. Adding message to buffer. message={0} - log.fine(CLASS_NAME, methodName, "507", new Object[] {message.getKey()}); - if(disconnectedMessageBuffer.isPersistBuffer()){ - this.clientState.persistBufferedMessage(message); - } - disconnectedMessageBuffer.putMessage(message, token); - } else { - this.internalSend(message, token); - } - } else if(disconnectedMessageBuffer != null) { - //@TRACE 508=Offline Buffer available. Adding message to buffer. message={0} - log.fine(CLASS_NAME, methodName, "508", new Object[] {message.getKey()}); - if(disconnectedMessageBuffer.isPersistBuffer()){ - this.clientState.persistBufferedMessage(message); - } - disconnectedMessageBuffer.putMessage(message, token); - } else { - //@TRACE 208=failed: not connected - log.fine(CLASS_NAME, methodName, "208"); - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED); - } - } - - /** - * Close and tidy up. - * - * Call each main class and let it tidy up e.g. releasing the token - * store which normally survives a disconnect. - * @throws MqttException if not disconnected - */ - public void close(boolean force) throws MqttException { - final String methodName = "close"; - synchronized (conLock) { - if (!isClosed()) { - // Must be disconnected before close can take place or if we are being forced - if (!isDisconnected() || force) { - //@TRACE 224=failed: not disconnected - log.fine(CLASS_NAME, methodName, "224"); - - if (isConnecting()) { - throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS); - } else if (isConnected()) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED); - } else if (isDisconnecting()) { - closePending = true; - return; - } - } - - conState = CLOSED; - shutdownExecutorService(); - // ShutdownConnection has already cleaned most things - clientState.close(); - clientState = null; - callback = null; - persistence = null; - sender = null; - pingSender = null; - receiver = null; - networkModules = null; - conOptions = null; - tokenStore = null; - } - } - } - - /** - * Sends a connect message and waits for an ACK or NACK. - * Connecting is a special case which will also start up the - * network connection, receive thread, and keep alive thread. - * @param options The {@link MqttConnectOptions} for the connection - * @param token The {@link MqttToken} to track the connection - * @throws MqttException if an error occurs when connecting - */ - public void connect(MqttConnectOptions options, MqttToken token) throws MqttException { - final String methodName = "connect"; - synchronized (conLock) { - if (isDisconnected() && !closePending) { - //@TRACE 214=state=CONNECTING - log.fine(CLASS_NAME,methodName,"214"); - - conState = CONNECTING; - - conOptions = options; - - MqttConnect connect = new MqttConnect(client.getClientId(), - conOptions.getMqttVersion(), - conOptions.isCleanSession(), - conOptions.getKeepAliveInterval(), - conOptions.getUserName(), - conOptions.getPassword(), - conOptions.getWillMessage(), - conOptions.getWillDestination()); - - this.clientState.setKeepAliveSecs(conOptions.getKeepAliveInterval()); - this.clientState.setCleanSession(conOptions.isCleanSession()); - this.clientState.setMaxInflight(conOptions.getMaxInflight()); - - tokenStore.open(); - ConnectBG conbg = new ConnectBG(this, token, connect, executorService); - conbg.start(); - } - else { - // @TRACE 207=connect failed: not disconnected {0} - log.fine(CLASS_NAME,methodName,"207", new Object[] {new Byte(conState)}); - if (isClosed() || closePending) { - throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED); - } else if (isConnecting()) { - throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS); - } else if (isDisconnecting()) { - throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); - } else { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED); - } - } - } - } - - public void connectComplete( MqttConnack cack, MqttException mex) throws MqttException { - final String methodName = "connectComplete"; - int rc = cack.getReturnCode(); - synchronized (conLock) { - if (rc == 0) { - // We've successfully connected - // @TRACE 215=state=CONNECTED - log.fine(CLASS_NAME,methodName,"215"); - - conState = CONNECTED; - return; - } - } - - // @TRACE 204=connect failed: rc={0} - log.fine(CLASS_NAME,methodName,"204", new Object[]{new Integer(rc)}); - throw mex; - } - - /** - * Shuts down the connection to the server. - * This may have been invoked as a result of a user calling disconnect or - * an abnormal disconnection. The method may be invoked multiple times - * in parallel as each thread when it receives an error uses this method - * to ensure that shutdown completes successfully. - * @param token the {@link MqttToken} To track closing the connection - * @param reason the {@link MqttException} thrown requiring the connection to be shut down. - */ - public void shutdownConnection(MqttToken token, MqttException reason) { - final String methodName = "shutdownConnection"; - boolean wasConnected; - MqttToken endToken = null; //Token to notify after disconnect completes - - // This method could concurrently be invoked from many places only allow it - // to run once. - synchronized(conLock) { - if (stoppingComms || closePending || isClosed()) { - return; - } - stoppingComms = true; - - //@TRACE 216=state=DISCONNECTING - log.fine(CLASS_NAME,methodName,"216"); - - wasConnected = (isConnected() || isDisconnecting()); - conState = DISCONNECTING; - } - - // Update the token with the reason for shutdown if it - // is not already complete. - if (token != null && !token.isComplete()) { - token.internalTok.setException(reason); - } - - // Stop the thread that is used to call the user back - // when actions complete - if (callback!= null) {callback.stop(); } - - // Stop the thread that handles inbound work from the network - if (receiver != null) {receiver.stop();} - - // Stop the network module, send and receive now not possible - try { - if (networkModules != null) { - NetworkModule networkModule = networkModules[networkModuleIndex]; - if (networkModule != null) { - networkModule.stop(); - } - } - } catch (Exception ioe) { - // Ignore as we are shutting down - } - - // Stop any new tokens being saved by app and throwing an exception if they do - tokenStore.quiesce(new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING)); - - // Notify any outstanding tokens with the exception of - // con or discon which may be returned and will be notified at - // the end - endToken = handleOldTokens(token, reason); - - try { - // Clean session handling and tidy up - clientState.disconnected(reason); - if (clientState.getCleanSession()) - callback.removeMessageListeners(); - }catch(Exception ex) { - // Ignore as we are shutting down - } - - if (sender != null) { sender.stop(); } - - if (pingSender != null){ - pingSender.stop(); - } - - try { - if(disconnectedMessageBuffer == null && persistence != null){ - persistence.close(); - } - - }catch(Exception ex) { - // Ignore as we are shutting down - } - // All disconnect logic has been completed allowing the - // client to be marked as disconnected. - synchronized(conLock) { - //@TRACE 217=state=DISCONNECTED - log.fine(CLASS_NAME,methodName,"217"); - - conState = DISCONNECTED; - stoppingComms = false; - } - - // Internal disconnect processing has completed. If there - // is a disconnect token or a connect in error notify - // it now. This is done at the end to allow a new connect - // to be processed and now throw a currently disconnecting error. - // any outstanding tokens and unblock any waiters - if (endToken != null & callback != null) { - callback.asyncOperationComplete(endToken); - } - - if (wasConnected && callback != null) { - // Let the user know client has disconnected either normally or abnormally - callback.connectionLost(reason); - } - - // While disconnecting, close may have been requested - try it now - synchronized(conLock) { - if (closePending) { - try { - close(true); - } catch (Exception e) { // ignore any errors as closing - } - } - } - } - - // Tidy up. There may be tokens outstanding as the client was - // not disconnected/quiseced cleanly! Work out what tokens still - // need to be notified and waiters unblocked. Store the - // disconnect or connect token to notify after disconnect is - // complete. - private MqttToken handleOldTokens(MqttToken token, MqttException reason) { - final String methodName = "handleOldTokens"; - //@TRACE 222=> - log.fine(CLASS_NAME,methodName,"222"); - - MqttToken tokToNotifyLater = null; - try { - // First the token that was related to the disconnect / shutdown may - // not be in the token table - temporarily add it if not - if (token != null) { - if (tokenStore.getToken(token.internalTok.getKey())==null) { - tokenStore.saveToken(token, token.internalTok.getKey()); - } - } - - Vector toksToNot = clientState.resolveOldTokens(reason); - Enumeration toksToNotE = toksToNot.elements(); - while(toksToNotE.hasMoreElements()) { - MqttToken tok = (MqttToken)toksToNotE.nextElement(); - - if (tok.internalTok.getKey().equals(MqttDisconnect.KEY) || - tok.internalTok.getKey().equals(MqttConnect.KEY)) { - // Its con or discon so remember and notify @ end of disc routine - tokToNotifyLater = tok; - } else { - // notify waiters and callbacks of outstanding tokens - // that a problem has occurred and disconnect is in - // progress - callback.asyncOperationComplete(tok); - } - } - }catch(Exception ex) { - // Ignore as we are shutting down - } - return tokToNotifyLater; - } - - public void disconnect(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token) throws MqttException { - final String methodName = "disconnect"; - synchronized (conLock){ - if (isClosed()) { - //@TRACE 223=failed: in closed state - log.fine(CLASS_NAME,methodName,"223"); - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CLOSED); - } else if (isDisconnected()) { - //@TRACE 211=failed: already disconnected - log.fine(CLASS_NAME,methodName,"211"); - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_ALREADY_DISCONNECTED); - } else if (isDisconnecting()) { - //@TRACE 219=failed: already disconnecting - log.fine(CLASS_NAME,methodName,"219"); - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); - } else if (Thread.currentThread() == callback.getThread()) { - //@TRACE 210=failed: called on callback thread - log.fine(CLASS_NAME,methodName,"210"); - // Not allowed to call disconnect() from the callback, as it will deadlock. - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECT_PROHIBITED); - } - - //@TRACE 218=state=DISCONNECTING - log.fine(CLASS_NAME,methodName,"218"); - conState = DISCONNECTING; - DisconnectBG discbg = new DisconnectBG(disconnect,quiesceTimeout,token, executorService); - discbg.start(); - } - } - - public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException { - disconnectForcibly(quiesceTimeout, disconnectTimeout, true); - } - - /** - * Disconnect the connection and reset all the states. - * @param quiesceTimeout How long to wait whilst quiesing before messages are deleted. - * @param disconnectTimeout How long to wait whilst disconnecting - * @param sendDisconnectPacket If true, will send a disconnect packet - * @throws MqttException if an error occurs whilst disconnecting - */ - public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout, boolean sendDisconnectPacket) throws MqttException { - // Allow current inbound and outbound work to complete - if (clientState != null) { - clientState.quiesce(quiesceTimeout); - } - MqttToken token = new MqttToken(client.getClientId()); - try { - // Send disconnect packet - if(sendDisconnectPacket) { - internalSend(new MqttDisconnect(), token); - - // Wait util the disconnect packet sent with timeout - token.waitForCompletion(disconnectTimeout); - } - } - catch (Exception ex) { - // ignore, probably means we failed to send the disconnect packet. - } - finally { - token.internalTok.markComplete(null, null); - shutdownConnection(token, null); - } - } - - public boolean isConnected() { - synchronized (conLock) { - return conState == CONNECTED; - } - } - - public boolean isConnecting() { - synchronized (conLock) { - return conState == CONNECTING; - } - } - - public boolean isDisconnected() { - synchronized (conLock) { - return conState == DISCONNECTED; - } - } - - public boolean isDisconnecting() { - synchronized (conLock) { - return conState == DISCONNECTING; - } - } - - public boolean isClosed() { - synchronized (conLock) { - return conState == CLOSED; - } - } - - public boolean isResting() { - synchronized (conLock) { - return resting; - } - } - - - public void setCallback(MqttCallback mqttCallback) { - this.callback.setCallback(mqttCallback); - } - - public void setReconnectCallback(MqttCallbackExtended callback){ - this.callback.setReconnectCallback(callback); - } - - public void setManualAcks(boolean manualAcks) { - this.callback.setManualAcks(manualAcks); - } - - public void messageArrivedComplete(int messageId, int qos) throws MqttException { - this.callback.messageArrivedComplete(messageId, qos); - } - - public void setMessageListener(String topicFilter, IMqttMessageListener messageListener) { - this.callback.setMessageListener(topicFilter, messageListener); - } - - public void removeMessageListener(String topicFilter) { - this.callback.removeMessageListener(topicFilter); - } - - protected MqttTopic getTopic(String topic) { - return new MqttTopic(topic, this); - } - public void setNetworkModuleIndex(int index) { - this.networkModuleIndex = index; - } - public int getNetworkModuleIndex() { - return networkModuleIndex; - } - public NetworkModule[] getNetworkModules() { - return networkModules; - } - public void setNetworkModules(NetworkModule[] networkModules) { - this.networkModules = networkModules; - } - public MqttDeliveryToken[] getPendingDeliveryTokens() { - return tokenStore.getOutstandingDelTokens(); - } - - protected void deliveryComplete(MqttPublish msg) throws MqttPersistenceException { - this.clientState.deliveryComplete(msg); - } - - protected void deliveryComplete(int messageId) throws MqttPersistenceException { - this.clientState.deliveryComplete(messageId); - } - - public IMqttAsyncClient getClient() { - return client; - } - - public long getKeepAlive() { - return this.clientState.getKeepAlive(); - } - - public ClientState getClientState() { - return clientState; - } - - public MqttConnectOptions getConOptions() { - return conOptions; - } - - public Properties getDebug() { - Properties props = new Properties(); - props.put("conState", new Integer(conState)); - props.put("serverURI", getClient().getServerURI()); - props.put("callback", callback); - props.put("stoppingComms", new Boolean(stoppingComms)); - return props; - } - - - - // Kick off the connect processing in the background so that it does not block. For instance - // the socket could take time to create. - private class ConnectBG implements Runnable { - ClientComms clientComms = null; - MqttToken conToken; - MqttConnect conPacket; - private String threadName; - - ConnectBG(ClientComms cc, MqttToken cToken, MqttConnect cPacket, ExecutorService executorService) { - clientComms = cc; - conToken = cToken; - conPacket = cPacket; - threadName = "MQTT Con: "+getClient().getClientId(); - } - - void start() { - executorService.execute(this); - } - - public void run() { - Thread.currentThread().setName(threadName); - final String methodName = "connectBG:run"; - MqttException mqttEx = null; - //@TRACE 220=> - log.fine(CLASS_NAME, methodName, "220"); - - try { - // Reset an exception on existing delivery tokens. - // This will have been set if disconnect occured before delivery was - // fully processed. - MqttDeliveryToken[] toks = tokenStore.getOutstandingDelTokens(); - for (int i=0; i - log.fine(CLASS_NAME, methodName, "221"); - - // Allow current inbound and outbound work to complete - clientState.quiesce(quiesceTimeout); - try { - internalSend(disconnect, token); - token.internalTok.waitUntilSent(); - } - catch (MqttException ex) { - } - finally { - token.internalTok.markComplete(null, null); - shutdownConnection(token, null); - } - } - } - - /* - * Check and send a ping if needed and check for ping timeout. - * Need to send a ping if nothing has been sent or received - * in the last keepalive interval. - */ - public MqttToken checkForActivity(){ - return this.checkForActivity(null); - } - - /* - * Check and send a ping if needed and check for ping timeout. - * Need to send a ping if nothing has been sent or received - * in the last keepalive interval. - * Passes an IMqttActionListener to ClientState.checkForActivity - * so that the callbacks are attached as soon as the token is created - * (Bug 473928) - */ - public MqttToken checkForActivity(IMqttActionListener pingCallback){ - MqttToken token = null; - try{ - token = clientState.checkForActivity(pingCallback); - }catch(MqttException e){ - handleRunException(e); - }catch(Exception e){ - handleRunException(e); - } - return token; - } - - private void handleRunException(Exception ex) { - final String methodName = "handleRunException"; - //@TRACE 804=exception - log.fine(CLASS_NAME,methodName,"804",null, ex); - MqttException mex; - if ( !(ex instanceof MqttException)) { - mex = new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ex); - } else { - mex = (MqttException)ex; - } - - shutdownConnection(null, mex); - } - - /** - * When Automatic reconnect is enabled, we want ClientComs to enter the - * 'resting' state if disconnected. This will allow us to publish messages - * @param resting if true, resting is enabled - */ - public void setRestingState(boolean resting) { - this.resting = resting; - } - - public void setDisconnectedMessageBuffer(DisconnectedMessageBuffer disconnectedMessageBuffer) { - this.disconnectedMessageBuffer = disconnectedMessageBuffer; - } - - public int getBufferedMessageCount(){ - return this.disconnectedMessageBuffer.getMessageCount(); - } - - public MqttMessage getBufferedMessage(int bufferIndex){ - MqttPublish send = (MqttPublish) this.disconnectedMessageBuffer.getMessage(bufferIndex).getMessage(); - return send.getMessage(); - } - - public void deleteBufferedMessage(int bufferIndex){ - this.disconnectedMessageBuffer.deleteMessage(bufferIndex); - } - - - /** - * When the client connects, we want to send all messages from the - * buffer first before allowing the user to send any messages - */ - public void notifyConnect() { - final String methodName = "notifyConnect"; - if(disconnectedMessageBuffer != null){ - //@TRACE 509=Client Connected, Offline Buffer Available. Sending Buffered Messages. - log.fine(CLASS_NAME, methodName, "509"); - - disconnectedMessageBuffer.setPublishCallback(new ReconnectDisconnectedBufferCallback(methodName)); - executorService.execute(disconnectedMessageBuffer); - } - } - - class ReconnectDisconnectedBufferCallback implements IDisconnectedBufferCallback{ - - final String methodName; - - ReconnectDisconnectedBufferCallback(String methodName) { - this.methodName = methodName; - } - - public void publishBufferedMessage(BufferedMessage bufferedMessage) throws MqttException { - if (isConnected()) { - while(clientState.getActualInFlight() >= (clientState.getMaxInFlight()-1)){ - // We need to Yield to the other threads to allow the in flight messages to clear - Thread.yield(); - - } - //@TRACE 510=Publising Buffered message message={0} - log.fine(CLASS_NAME, methodName, "510", new Object[] {bufferedMessage.getMessage().getKey()}); - internalSend(bufferedMessage.getMessage(), bufferedMessage.getToken()); - // Delete from persistence if in there - clientState.unPersistBufferedMessage(bufferedMessage.getMessage()); - } else { - //@TRACE 208=failed: not connected - log.fine(CLASS_NAME, methodName, "208"); - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED); - } - } - } - - public int getActualInFlight() { - return this.clientState.getActualInFlight(); - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java deleted file mode 100644 index 9b876c7..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java +++ /dev/null @@ -1,20 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -public class ClientDefaults { - public static final int MAX_MSG_SIZE = 1024 * 1024 * 256; // 256 MB -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientState.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientState.java deleted file mode 100644 index 5376937..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ClientState.java +++ /dev/null @@ -1,1401 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2017 IBM Corp and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - fix duplicate message id (Bug 466853) - * Ian Craggs - ack control (bug 472172) - * James Sutton - Ping Callback (bug 473928) - * Ian Craggs - fix for NPE bug 470718 - * James Sutton - Automatic Reconnect & Offline Buffering - * Jens Reimann - Fix issue #370 - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.EOFException; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Properties; -import java.util.Vector; - -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.MqttClientPersistence; -import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.MqttPersistable; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; -import org.eclipse.paho.client.mqttv3.MqttPingSender; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingReq; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingResp; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRec; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRel; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttSuback; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttSubscribe; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttUnsubAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttUnsubscribe; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * The core of the client, which holds the state information for pending and - * in-flight messages. - * - * Messages that have been accepted for delivery are moved between several objects - * while being delivered. - * - * 1) When the client is not running messages are stored in a persistent store that - * implements the MqttClientPersistent Interface. The default is MqttDefaultFilePersistencew - * which stores messages safely across failures and system restarts. If no persistence - * is specified there is a fall back to MemoryPersistence which will maintain the messages - * while the Mqtt client is instantiated. - * - * 2) When the client or specifically ClientState is instantiated the messages are - * read from the persistent store into: - * - outboundqos2 hashtable if a QoS 2 PUBLISH or PUBREL - * - outboundqos1 hashtable if a QoS 1 PUBLISH - * (see restoreState) - * - * 3) On Connect, copy messages from the outbound hashtables to the pendingMessages or - * pendingFlows vector in messageid order. - * - Initial message publish goes onto the pendingmessages buffer. - * - PUBREL goes onto the pendingflows buffer - * (see restoreInflightMessages) - * - * 4) Sender thread reads messages from the pendingflows and pendingmessages buffer - * one at a time. The message is removed from the pendingbuffer but remains on the - * outbound* hashtable. The hashtable is the place where the full set of outstanding - * messages are stored in memory. (Persistence is only used at start up) - * - * 5) Receiver thread - receives wire messages: - * - if QoS 1 then remove from persistence and outboundqos1 - * - if QoS 2 PUBREC send PUBREL. Updating the outboundqos2 entry with the PUBREL - * and update persistence. - * - if QoS 2 PUBCOMP remove from persistence and outboundqos2 - * - * Notes: - * because of the multithreaded nature of the client it is vital that any changes to this - * class take concurrency into account. For instance as soon as a flow / message is put on - * the wire it is possible for the receiving thread to receive the ack and to be processing - * the response before the sending side has finished processing. For instance a connect may - * be sent, the conack received before the connect notify send has been processed! - * - */ -public class ClientState { - private static final String CLASS_NAME = ClientState.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME); - private static final String PERSISTENCE_SENT_PREFIX = "s-"; - private static final String PERSISTENCE_SENT_BUFFERED_PREFIX = "sb-"; - private static final String PERSISTENCE_CONFIRMED_PREFIX = "sc-"; - private static final String PERSISTENCE_RECEIVED_PREFIX = "r-"; - - private static final int MIN_MSG_ID = 1; // Lowest possible MQTT message ID to use - private static final int MAX_MSG_ID = 65535; // Highest possible MQTT message ID to use - private int nextMsgId = MIN_MSG_ID - 1; // The next available message ID to use - private Hashtable inUseMsgIds; // Used to store a set of in-use message IDs - - volatile private Vector pendingMessages; - volatile private Vector pendingFlows; - - private CommsTokenStore tokenStore; - private ClientComms clientComms = null; - private CommsCallback callback = null; - private long keepAlive; - private boolean cleanSession; - private MqttClientPersistence persistence; - - private int maxInflight = 0; - private int actualInFlight = 0; - private int inFlightPubRels = 0; - - private Object queueLock = new Object(); - private Object quiesceLock = new Object(); - private boolean quiescing = false; - - private long lastOutboundActivity = 0; - private long lastInboundActivity = 0; - private long lastPing = 0; - private MqttWireMessage pingCommand; - private Object pingOutstandingLock = new Object(); - private int pingOutstanding = 0; - - private boolean connected = false; - - private Hashtable outboundQoS2 = null; - private Hashtable outboundQoS1 = null; - private Hashtable outboundQoS0 = null; - private Hashtable inboundQoS2 = null; - - private MqttPingSender pingSender = null; - - protected ClientState(MqttClientPersistence persistence, CommsTokenStore tokenStore, - CommsCallback callback, ClientComms clientComms, MqttPingSender pingSender) throws MqttException { - - log.setResourceName(clientComms.getClient().getClientId()); - log.finer(CLASS_NAME, "", "" ); - - inUseMsgIds = new Hashtable(); - pendingFlows = new Vector(); - outboundQoS2 = new Hashtable(); - outboundQoS1 = new Hashtable(); - outboundQoS0 = new Hashtable(); - inboundQoS2 = new Hashtable(); - pingCommand = new MqttPingReq(); - inFlightPubRels = 0; - actualInFlight = 0; - - this.persistence = persistence; - this.callback = callback; - this.tokenStore = tokenStore; - this.clientComms = clientComms; - this.pingSender = pingSender; - - restoreState(); - } - - protected void setMaxInflight(int maxInflight) { - this.maxInflight = maxInflight; - pendingMessages = new Vector(this.maxInflight); - } - protected void setKeepAliveSecs(long keepAliveSecs) { - this.keepAlive = keepAliveSecs*1000; - } - protected long getKeepAlive() { - return this.keepAlive; - } - protected void setCleanSession(boolean cleanSession) { - this.cleanSession = cleanSession; - } - protected boolean getCleanSession() { - return this.cleanSession; - } - - private String getSendPersistenceKey(MqttWireMessage message) { - return PERSISTENCE_SENT_PREFIX + message.getMessageId(); - } - - private String getSendConfirmPersistenceKey(MqttWireMessage message) { - return PERSISTENCE_CONFIRMED_PREFIX + message.getMessageId(); - } - - private String getReceivedPersistenceKey(MqttWireMessage message) { - return PERSISTENCE_RECEIVED_PREFIX + message.getMessageId(); - } - - private String getReceivedPersistenceKey(int messageId) { - return PERSISTENCE_RECEIVED_PREFIX + messageId; - } - - private String getSendBufferedPersistenceKey(MqttWireMessage message){ - return PERSISTENCE_SENT_BUFFERED_PREFIX + message.getMessageId(); - } - - protected void clearState() throws MqttException { - final String methodName = "clearState"; - //@TRACE 603=clearState - log.fine(CLASS_NAME, methodName,">"); - - persistence.clear(); - inUseMsgIds.clear(); - pendingMessages.clear(); - pendingFlows.clear(); - outboundQoS2.clear(); - outboundQoS1.clear(); - outboundQoS0.clear(); - inboundQoS2.clear(); - tokenStore.clear(); - } - - private MqttWireMessage restoreMessage(String key, MqttPersistable persistable) throws MqttException { - final String methodName = "restoreMessage"; - MqttWireMessage message = null; - - try { - message = MqttWireMessage.createWireMessage(persistable); - } - catch (MqttException ex) { - //@TRACE 602=key={0} exception - log.fine(CLASS_NAME, methodName, "602", new Object[] {key}, ex); - if (ex.getCause() instanceof EOFException) { - // Premature end-of-file means that the message is corrupted - if (key != null) { - persistence.remove(key); - } - } - else { - throw ex; - } - } - //@TRACE 601=key={0} message={1} - log.fine(CLASS_NAME, methodName, "601", new Object[]{key,message}); - return message; - } - - /** - * Inserts a new message to the list, ensuring that list is ordered from lowest to highest in terms of the message id's. - * @param list the list to insert the message into - * @param newMsg the message to insert into the list - */ - private void insertInOrder(Vector list, MqttWireMessage newMsg) { - int newMsgId = newMsg.getMessageId(); - for (int i = 0; i < list.size(); i++) { - MqttWireMessage otherMsg = (MqttWireMessage) list.elementAt(i); - int otherMsgId = otherMsg.getMessageId(); - if (otherMsgId > newMsgId) { - list.insertElementAt(newMsg, i); - return; - } - } - list.addElement(newMsg); - } - - /** - * Produces a new list with the messages properly ordered according to their message id's. - * @param list the list containing the messages to produce a new reordered list for - * - this will not be modified or replaced, i.e., be read-only to this method - * @return a new reordered list - */ - private Vector reOrder(Vector list) { - - // here up the new list - Vector newList = new Vector(); - - if (list.size() == 0) { - return newList; // nothing to reorder - } - - int previousMsgId = 0; - int largestGap = 0; - int largestGapMsgIdPosInList = 0; - for (int i = 0; i < list.size(); i++) { - int currentMsgId = ((MqttWireMessage) list.elementAt(i)).getMessageId(); - if (currentMsgId - previousMsgId > largestGap) { - largestGap = currentMsgId - previousMsgId; - largestGapMsgIdPosInList = i; - } - previousMsgId = currentMsgId; - } - int lowestMsgId = ((MqttWireMessage) list.elementAt(0)).getMessageId(); - int highestMsgId = previousMsgId; // last in the sorted list - - // we need to check that the gap after highest msg id to the lowest msg id is not beaten - if (MAX_MSG_ID - highestMsgId + lowestMsgId > largestGap) { - largestGapMsgIdPosInList = 0; - } - - // starting message has been located, let's start from this point on - for (int i = largestGapMsgIdPosInList; i < list.size(); i++) { - newList.addElement(list.elementAt(i)); - } - - // and any wrapping back to the beginning - for (int i = 0; i < largestGapMsgIdPosInList; i++) { - newList.addElement(list.elementAt(i)); - } - - return newList; - } - - /** - * Restores the state information from persistence. - * @throws MqttException if an exception occurs whilst restoring state - */ - protected void restoreState() throws MqttException { - final String methodName = "restoreState"; - Enumeration messageKeys = persistence.keys(); - MqttPersistable persistable; - String key; - int highestMsgId = nextMsgId; - Vector orphanedPubRels = new Vector(); - //@TRACE 600=> - log.fine(CLASS_NAME, methodName, "600"); - - while (messageKeys.hasMoreElements()) { - key = (String) messageKeys.nextElement(); - persistable = persistence.get(key); - MqttWireMessage message = restoreMessage(key, persistable); - if (message != null) { - if (key.startsWith(PERSISTENCE_RECEIVED_PREFIX)) { - //@TRACE 604=inbound QoS 2 publish key={0} message={1} - log.fine(CLASS_NAME,methodName,"604", new Object[]{key,message}); - - // The inbound messages that we have persisted will be QoS 2 - inboundQoS2.put(new Integer(message.getMessageId()),message); - } else if (key.startsWith(PERSISTENCE_SENT_PREFIX)) { - MqttPublish sendMessage = (MqttPublish) message; - highestMsgId = Math.max(sendMessage.getMessageId(), highestMsgId); - if (persistence.containsKey(getSendConfirmPersistenceKey(sendMessage))) { - MqttPersistable persistedConfirm = persistence.get(getSendConfirmPersistenceKey(sendMessage)); - // QoS 2, and CONFIRM has already been sent... - // NO DUP flag is allowed for 3.1.1 spec while it's not clear for 3.1 spec - // So we just remove DUP - MqttPubRel confirmMessage = (MqttPubRel) restoreMessage(key, persistedConfirm); - if (confirmMessage != null) { - // confirmMessage.setDuplicate(true); // REMOVED - //@TRACE 605=outbound QoS 2 pubrel key={0} message={1} - log.fine(CLASS_NAME,methodName, "605", new Object[]{key,message}); - - outboundQoS2.put(new Integer(confirmMessage.getMessageId()), confirmMessage); - } else { - //@TRACE 606=outbound QoS 2 completed key={0} message={1} - log.fine(CLASS_NAME,methodName, "606", new Object[]{key,message}); - } - } else { - // QoS 1 or 2, with no CONFIRM sent... - // Put the SEND to the list of pending messages, ensuring message ID ordering... - sendMessage.setDuplicate(true); - if (sendMessage.getMessage().getQos() == 2) { - //@TRACE 607=outbound QoS 2 publish key={0} message={1} - log.fine(CLASS_NAME,methodName, "607", new Object[]{key,message}); - - outboundQoS2.put(new Integer(sendMessage.getMessageId()),sendMessage); - } else { - //@TRACE 608=outbound QoS 1 publish key={0} message={1} - log.fine(CLASS_NAME,methodName, "608", new Object[]{key,message}); - - outboundQoS1.put(new Integer(sendMessage.getMessageId()),sendMessage); - } - } - MqttDeliveryToken tok = tokenStore.restoreToken(sendMessage); - tok.internalTok.setClient(clientComms.getClient()); - inUseMsgIds.put(new Integer(sendMessage.getMessageId()),new Integer(sendMessage.getMessageId())); - } else if(key.startsWith(PERSISTENCE_SENT_BUFFERED_PREFIX)){ - - // Buffered outgoing messages that have not yet been sent at all - MqttPublish sendMessage = (MqttPublish) message; - highestMsgId = Math.max(sendMessage.getMessageId(), highestMsgId); - if(sendMessage.getMessage().getQos() == 2){ - //@TRACE 607=outbound QoS 2 publish key={0} message={1} - log.fine(CLASS_NAME,methodName, "607", new Object[]{key,message}); - outboundQoS2.put(new Integer(sendMessage.getMessageId()),sendMessage); - } else if(sendMessage.getMessage().getQos() == 1){ - //@TRACE 608=outbound QoS 1 publish key={0} message={1} - log.fine(CLASS_NAME,methodName, "608", new Object[]{key,message}); - - outboundQoS1.put(new Integer(sendMessage.getMessageId()),sendMessage); - - } else { - //@TRACE 511=outbound QoS 0 publish key={0} message={1} - log.fine(CLASS_NAME,methodName, "511", new Object[]{key,message}); - outboundQoS0.put(new Integer(sendMessage.getMessageId()), sendMessage); - // Because there is no Puback, we have to trust that this is enough to send the message - persistence.remove(key); - - } - - MqttDeliveryToken tok = tokenStore.restoreToken(sendMessage); - tok.internalTok.setClient(clientComms.getClient()); - inUseMsgIds.put(new Integer(sendMessage.getMessageId()),new Integer(sendMessage.getMessageId())); - - - } else if (key.startsWith(PERSISTENCE_CONFIRMED_PREFIX)) { - MqttPubRel pubRelMessage = (MqttPubRel) message; - if (!persistence.containsKey(getSendPersistenceKey(pubRelMessage))) { - orphanedPubRels.addElement(key); - } - } - } - } - - messageKeys = orphanedPubRels.elements(); - while(messageKeys.hasMoreElements()) { - key = (String) messageKeys.nextElement(); - //@TRACE 609=removing orphaned pubrel key={0} - log.fine(CLASS_NAME,methodName, "609", new Object[]{key}); - - persistence.remove(key); - } - - nextMsgId = highestMsgId; - } - - private void restoreInflightMessages() { - final String methodName = "restoreInflightMessages"; - pendingMessages = new Vector(this.maxInflight); - pendingFlows = new Vector(); - - Enumeration keys = outboundQoS2.keys(); - while (keys.hasMoreElements()) { - Object key = keys.nextElement(); - MqttWireMessage msg = (MqttWireMessage) outboundQoS2.get(key); - if (msg instanceof MqttPublish) { - //@TRACE 610=QoS 2 publish key={0} - log.fine(CLASS_NAME,methodName, "610", new Object[]{key}); - // set DUP flag only for PUBLISH, but NOT for PUBREL (spec 3.1.1) - msg.setDuplicate(true); - insertInOrder(pendingMessages, (MqttPublish)msg); - } else if (msg instanceof MqttPubRel) { - //@TRACE 611=QoS 2 pubrel key={0} - log.fine(CLASS_NAME,methodName, "611", new Object[]{key}); - - insertInOrder(pendingFlows, (MqttPubRel)msg); - } - } - keys = outboundQoS1.keys(); - while (keys.hasMoreElements()) { - Object key = keys.nextElement(); - MqttPublish msg = (MqttPublish)outboundQoS1.get(key); - msg.setDuplicate(true); - //@TRACE 612=QoS 1 publish key={0} - log.fine(CLASS_NAME,methodName, "612", new Object[]{key}); - - insertInOrder(pendingMessages, msg); - } - keys = outboundQoS0.keys(); - while(keys.hasMoreElements()){ - Object key = keys.nextElement(); - MqttPublish msg = (MqttPublish)outboundQoS0.get(key); - //@TRACE 512=QoS 0 publish key={0} - log.fine(CLASS_NAME,methodName, "512", new Object[]{key}); - insertInOrder(pendingMessages, msg); - - } - - this.pendingFlows = reOrder(pendingFlows); - this.pendingMessages = reOrder(pendingMessages); - } - - /** - * Submits a message for delivery. This method will block until there is - * room in the inFlightWindow for the message. The message is put into - * persistence before returning. - * - * @param message the message to send - * @param token the token that can be used to track delivery of the message - * @throws MqttException if an exception occurs whilst sending the message - */ - public void send(MqttWireMessage message, MqttToken token) throws MqttException { - final String methodName = "send"; - if (message.isMessageIdRequired() && (message.getMessageId() == 0)) { - if(message instanceof MqttPublish && (((MqttPublish) message).getMessage().getQos() != 0)){ - message.setMessageId(getNextMessageId()); - }else if(message instanceof MqttPubAck || - message instanceof MqttPubRec || - message instanceof MqttPubRel || - message instanceof MqttPubComp || - message instanceof MqttSubscribe || - message instanceof MqttSuback || - message instanceof MqttUnsubscribe || - message instanceof MqttUnsubAck){ - message.setMessageId(getNextMessageId()); - } - } - if (token != null ) { - try { - token.internalTok.setMessageID(message.getMessageId()); - } catch (Exception e) { - } - } - - if (message instanceof MqttPublish) { - synchronized (queueLock) { - if (actualInFlight >= this.maxInflight) { - //@TRACE 613= sending {0} msgs at max inflight window - log.fine(CLASS_NAME, methodName, "613", new Object[]{new Integer(actualInFlight)}); - - throw new MqttException(MqttException.REASON_CODE_MAX_INFLIGHT); - } - - MqttMessage innerMessage = ((MqttPublish) message).getMessage(); - //@TRACE 628=pending publish key={0} qos={1} message={2} - log.fine(CLASS_NAME,methodName,"628", new Object[]{new Integer(message.getMessageId()), new Integer(innerMessage.getQos()), message}); - - switch(innerMessage.getQos()) { - case 2: - outboundQoS2.put(new Integer(message.getMessageId()), message); - persistence.put(getSendPersistenceKey(message), (MqttPublish) message); - break; - case 1: - outboundQoS1.put(new Integer(message.getMessageId()), message); - persistence.put(getSendPersistenceKey(message), (MqttPublish) message); - break; - } - tokenStore.saveToken(token, message); - pendingMessages.addElement(message); - queueLock.notifyAll(); - } - } else { - //@TRACE 615=pending send key={0} message {1} - log.fine(CLASS_NAME,methodName,"615", new Object[]{new Integer(message.getMessageId()), message}); - - if (message instanceof MqttConnect) { - synchronized (queueLock) { - // Add the connect action at the head of the pending queue ensuring it jumps - // ahead of any of other pending actions. - tokenStore.saveToken(token, message); - pendingFlows.insertElementAt(message,0); - queueLock.notifyAll(); - } - } else { - if (message instanceof MqttPingReq) { - this.pingCommand = message; - } - else if (message instanceof MqttPubRel) { - outboundQoS2.put(new Integer(message.getMessageId()), message); - persistence.put(getSendConfirmPersistenceKey(message), (MqttPubRel) message); - } - else if (message instanceof MqttPubComp) { - persistence.remove(getReceivedPersistenceKey(message)); - } - - synchronized (queueLock) { - if ( !(message instanceof MqttAck )) { - tokenStore.saveToken(token, message); - } - pendingFlows.addElement(message); - queueLock.notifyAll(); - } - } - } - } - - /** - * Persists a buffered message to the persistence layer - * - * @param message The {@link MqttWireMessage} to persist - */ - public void persistBufferedMessage(MqttWireMessage message) { - final String methodName = "persistBufferedMessage"; - String key = getSendBufferedPersistenceKey(message); - - // Because the client will have disconnected, we will want to re-open persistence - try { - message.setMessageId(getNextMessageId()); - key = getSendBufferedPersistenceKey(message); - try { - persistence.put(key, (MqttPublish) message); - } catch (MqttPersistenceException mpe){ - //@TRACE 515=Could not Persist, attempting to Re-Open Persistence Store - log.fine(CLASS_NAME,methodName, "515"); - persistence.open(this.clientComms.getClient().getClientId(), this.clientComms.getClient().getServerURI()); - persistence.put(key, (MqttPublish) message); - } - //@TRACE 513=Persisted Buffered Message key={0} - log.fine(CLASS_NAME,methodName, "513", new Object[]{key}); - } catch (MqttException ex){ - //@TRACE 514=Failed to persist buffered message key={0} - log.warning(CLASS_NAME,methodName, "513", new Object[]{key}); - } - } - - /** - * @param message The {@link MqttWireMessage} to un-persist - */ - public void unPersistBufferedMessage(MqttWireMessage message){ - final String methodName = "unPersistBufferedMessage"; - try{ - //@TRACE 517=Un-Persisting Buffered message key={0} - log.fine(CLASS_NAME,methodName, "517", new Object[]{message.getKey()}); - persistence.remove(getSendBufferedPersistenceKey(message)); - } catch (MqttPersistenceException mpe){ - //@TRACE 518=Failed to Un-Persist Buffered message key={0} - log.fine(CLASS_NAME,methodName, "518", new Object[]{message.getKey()}); - } - - } - - /** - * This removes the MqttSend message from the outbound queue and persistence. - * @param message the {@link MqttPublish} message to be removed - * @throws MqttPersistenceException if an exception occurs whilst removing the message - */ - protected void undo(MqttPublish message) throws MqttPersistenceException { - final String methodName = "undo"; - synchronized (queueLock) { - //@TRACE 618=key={0} QoS={1} - log.fine(CLASS_NAME,methodName,"618", new Object[]{new Integer(message.getMessageId()), new Integer(message.getMessage().getQos())}); - - if (message.getMessage().getQos() == 1) { - outboundQoS1.remove(new Integer(message.getMessageId())); - } else { - outboundQoS2.remove(new Integer(message.getMessageId())); - } - pendingMessages.removeElement(message); - persistence.remove(getSendPersistenceKey(message)); - tokenStore.removeToken(message); - if(message.getMessage().getQos() > 0){ - //Free this message Id so it can be used again - releaseMessageId(message.getMessageId()); - //Set the messageId to 0 so if it's ever retried, it will get a new messageId - message.setMessageId(0); - } - - checkQuiesceLock(); - } - } - - /** - * Check and send a ping if needed and check for ping timeout. - * Need to send a ping if nothing has been sent or received - * in the last keepalive interval. It is important to check for - * both sent and received packets in order to catch the case where an - * app is solely sending QoS 0 messages or receiving QoS 0 messages. - * QoS 0 message are not good enough for checking a connection is - * alive as they are one way messages. - * - * If a ping has been sent but no data has been received in the - * last keepalive interval then the connection is deamed to be broken. - * @param pingCallback The {@link IMqttActionListener} to be called - * @return token of ping command, null if no ping command has been sent. - * @throws MqttException if an exception occurs during the Ping - */ - public MqttToken checkForActivity(IMqttActionListener pingCallback) throws MqttException { - final String methodName = "checkForActivity"; - //@TRACE 616=checkForActivity entered - log.fine(CLASS_NAME,methodName,"616", new Object[]{}); - - synchronized (quiesceLock) { - // ref bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=440698 - // No ping while quiescing - if (quiescing) { - return null; - } - } - - MqttToken token = null; - long nextPingTime = getKeepAlive(); - - if (connected && this.keepAlive > 0) { - long time = System.currentTimeMillis(); - //Reduce schedule frequency since System.currentTimeMillis is no accurate, add a buffer - //It is 1/10 in minimum keepalive unit. - int delta = 100; - - // ref bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=446663 - synchronized (pingOutstandingLock) { - - // Is the broker connection lost because the broker did not reply to my ping? - if (pingOutstanding > 0 && (time - lastInboundActivity >= keepAlive + delta)) { - // lastInboundActivity will be updated once receiving is done. - // Add a delta, since the timer and System.currentTimeMillis() is not accurate. - // A ping is outstanding but no packet has been received in KA so connection is deemed broken - //@TRACE 619=Timed out as no activity, keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} time={3} lastPing={4} - log.severe(CLASS_NAME,methodName,"619", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity), new Long(time), new Long(lastPing)}); - - // A ping has already been sent. At this point, assume that the - // broker has hung and the TCP layer hasn't noticed. - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT); - } - - // Is the broker connection lost because I could not get any successful write for 2 keepAlive intervals? - if (pingOutstanding == 0 && (time - lastOutboundActivity >= 2*keepAlive)) { - - // I am probably blocked on a write operations as I should have been able to write at least a ping message - log.severe(CLASS_NAME,methodName,"642", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity), new Long(time), new Long(lastPing)}); - - // A ping has not been sent but I am not progressing on the current write operation. - // At this point, assume that the broker has hung and the TCP layer hasn't noticed. - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_WRITE_TIMEOUT); - } - - // 1. Is a ping required by the client to verify whether the broker is down? - // Condition: ((pingOutstanding == 0 && (time - lastInboundActivity >= keepAlive + delta))) - // In this case only one ping is sent. If not confirmed, client will assume a lost connection to the broker. - // 2. Is a ping required by the broker to keep the client alive? - // Condition: (time - lastOutboundActivity >= keepAlive - delta) - // In this case more than one ping outstanding may be necessary. - // This would be the case when receiving a large message; - // the broker needs to keep receiving a regular ping even if the ping response are queued after the long message - // If lacking to do so, the broker will consider my connection lost and cut my socket. - if ((pingOutstanding == 0 && (time - lastInboundActivity >= keepAlive - delta)) || - (time - lastOutboundActivity >= keepAlive - delta)) { - - //@TRACE 620=ping needed. keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} - log.fine(CLASS_NAME,methodName,"620", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity)}); - - // pingOutstanding++; // it will be set after the ping has been written on the wire - // lastPing = time; // it will be set after the ping has been written on the wire - token = new MqttToken(clientComms.getClient().getClientId()); - if(pingCallback != null){ - token.setActionCallback(pingCallback); - } - tokenStore.saveToken(token, pingCommand); - pendingFlows.insertElementAt(pingCommand, 0); - - nextPingTime = getKeepAlive(); - - //Wake sender thread since it may be in wait state (in ClientState.get()) - notifyQueueLock(); - } - else { - log.fine(CLASS_NAME, methodName, "634", null); - nextPingTime = Math.max(1, getKeepAlive() - (time - lastOutboundActivity)); - } - } - //@TRACE 624=Schedule next ping at {0} - log.fine(CLASS_NAME, methodName,"624", new Object[]{new Long(nextPingTime)}); - pingSender.schedule(nextPingTime); - } - - return token; - } - - /** - * This returns the next piece of work, ie message, for the CommsSender - * to send over the network. - * Calls to this method block until either: - * - there is a message to be sent - * - the keepAlive interval is exceeded, which triggers a ping message - * to be returned - * - {@link ClientState#disconnected(MqttException)} is called - * @return the next message to send, or null if the client is disconnected - * @throws MqttException if an exception occurs whilst returning the next piece of work - */ - protected MqttWireMessage get() throws MqttException { - final String methodName = "get"; - MqttWireMessage result = null; - - synchronized (queueLock) { - while (result == null) { - - // If there is no work wait until there is work. - // If the inflight window is full and no flows are pending wait until space is freed. - // In both cases queueLock will be notified. - if ((pendingMessages.isEmpty() && pendingFlows.isEmpty()) || - (pendingFlows.isEmpty() && actualInFlight >= this.maxInflight)) { - try { - //@TRACE 644=wait for new work or for space in the inflight window - log.fine(CLASS_NAME,methodName, "644"); - - queueLock.wait(); - - //@TRACE 647=new work or ping arrived - log.fine(CLASS_NAME,methodName, "647"); - } catch (InterruptedException e) { - } - } - - // Handle the case where not connected. This should only be the case if: - // - in the process of disconnecting / shutting down - // - in the process of connecting - if (!connected && - (pendingFlows.isEmpty() || !((MqttWireMessage)pendingFlows.elementAt(0) instanceof MqttConnect))) { - //@TRACE 621=no outstanding flows and not connected - log.fine(CLASS_NAME,methodName,"621"); - - return null; - } - - // Check if there is a need to send a ping to keep the session alive. - // Note this check is done before processing messages. If not done first - // an app that only publishes QoS 0 messages will prevent keepalive processing - // from functioning. -// checkForActivity(); //Use pinger, don't check here - - // Now process any queued flows or messages - if (!pendingFlows.isEmpty()) { - // Process the first "flow" in the queue - result = (MqttWireMessage)pendingFlows.remove(0); - if (result instanceof MqttPubRel) { - inFlightPubRels++; - - //@TRACE 617=+1 inflightpubrels={0} - log.fine(CLASS_NAME,methodName,"617", new Object[]{new Integer(inFlightPubRels)}); - } - - checkQuiesceLock(); - } else if (!pendingMessages.isEmpty()) { - - // If the inflight window is full then messages are not - // processed until the inflight window has space. - if (actualInFlight < this.maxInflight) { - // The in flight window is not full so process the - // first message in the queue - result = (MqttWireMessage)pendingMessages.elementAt(0); - pendingMessages.removeElementAt(0); - actualInFlight++; - - //@TRACE 623=+1 actualInFlight={0} - log.fine(CLASS_NAME,methodName,"623",new Object[]{new Integer(actualInFlight)}); - } else { - //@TRACE 622=inflight window full - log.fine(CLASS_NAME,methodName,"622"); - } - } - } - } - return result; - } - - public void setKeepAliveInterval(long interval) { - this.keepAlive = interval; - } - - public void notifySentBytes(int sentBytesCount) { - final String methodName = "notifySentBytes"; - if (sentBytesCount > 0) { - this.lastOutboundActivity = System.currentTimeMillis(); - } - // @TRACE 643=sent bytes count={0} - log.fine(CLASS_NAME, methodName, "643", new Object[] { - new Integer(sentBytesCount) }); - } - - - /** - * Called by the CommsSender when a message has been sent - * @param message the {@link MqttWireMessage} to notify - */ - protected void notifySent(MqttWireMessage message) { - final String methodName = "notifySent"; - - this.lastOutboundActivity = System.currentTimeMillis(); - //@TRACE 625=key={0} - log.fine(CLASS_NAME,methodName,"625",new Object[]{message.getKey()}); - - MqttToken token = tokenStore.getToken(message); - token.internalTok.notifySent(); - if (message instanceof MqttPingReq) { - synchronized (pingOutstandingLock) { - long time = System.currentTimeMillis(); - synchronized (pingOutstandingLock) { - lastPing = time; - pingOutstanding++; - } - //@TRACE 635=ping sent. pingOutstanding: {0} - log.fine(CLASS_NAME,methodName,"635",new Object[]{ new Integer(pingOutstanding)}); - } - } - else if (message instanceof MqttPublish) { - if (((MqttPublish)message).getMessage().getQos() == 0) { - // once a QoS 0 message is sent we can clean up its records straight away as - // we won't be hearing about it again - token.internalTok.markComplete(null, null); - callback.asyncOperationComplete(token); - decrementInFlight(); - releaseMessageId(message.getMessageId()); - tokenStore.removeToken(message); - checkQuiesceLock(); - } - } - } - - private void decrementInFlight() { - final String methodName = "decrementInFlight"; - synchronized (queueLock) { - actualInFlight--; - //@TRACE 646=-1 actualInFlight={0} - log.fine(CLASS_NAME,methodName,"646",new Object[]{new Integer(actualInFlight)}); - - if (!checkQuiesceLock()) { - queueLock.notifyAll(); - } - } - } - - protected boolean checkQuiesceLock() { - final String methodName = "checkQuiesceLock"; -// if (quiescing && actualInFlight == 0 && pendingFlows.size() == 0 && inFlightPubRels == 0 && callback.isQuiesced()) { - int tokC = tokenStore.count(); - if (quiescing && tokC == 0 && pendingFlows.size() == 0 && callback.isQuiesced()) { - //@TRACE 626=quiescing={0} actualInFlight={1} pendingFlows={2} inFlightPubRels={3} callbackQuiesce={4} tokens={5} - log.fine(CLASS_NAME,methodName,"626",new Object[]{new Boolean(quiescing), new Integer(actualInFlight), new Integer(pendingFlows.size()), new Integer(inFlightPubRels), Boolean.valueOf(callback.isQuiesced()), new Integer(tokC)}); - synchronized (quiesceLock) { - quiesceLock.notifyAll(); - } - return true; - } - return false; - } - - public void notifyReceivedBytes(int receivedBytesCount) { - final String methodName = "notifyReceivedBytes"; - if (receivedBytesCount > 0) { - this.lastInboundActivity = System.currentTimeMillis(); - } - // @TRACE 630=received bytes count={0} - log.fine(CLASS_NAME, methodName, "630", new Object[] { - new Integer(receivedBytesCount) }); - } - - /** - * Called by the CommsReceiver when an ack has arrived. - * - * @param ack The {@link MqttAck} that has arrived - * @throws MqttException if an exception occurs when sending / notifying - */ - protected void notifyReceivedAck(MqttAck ack) throws MqttException { - final String methodName = "notifyReceivedAck"; - this.lastInboundActivity = System.currentTimeMillis(); - - // @TRACE 627=received key={0} message={1} - log.fine(CLASS_NAME, methodName, "627", new Object[] { - new Integer(ack.getMessageId()), ack }); - - MqttToken token = tokenStore.getToken(ack); - MqttException mex = null; - - if (token == null) { - // @TRACE 662=no message found for ack id={0} - log.fine(CLASS_NAME, methodName, "662", new Object[] { - new Integer(ack.getMessageId())}); - } else if (ack instanceof MqttPubRec) { - // Complete the QoS 2 flow. Unlike all other - // flows, QoS is a 2 phase flow. The second phase sends a - // PUBREL - the operation is not complete until a PUBCOMP - // is received - MqttPubRel rel = new MqttPubRel((MqttPubRec) ack); - this.send(rel, token); - } else if (ack instanceof MqttPubAck || ack instanceof MqttPubComp) { - // QoS 1 & 2 notify users of result before removing from - // persistence - notifyResult(ack, token, mex); - // Do not remove publish / delivery token at this stage - // do this when the persistence is removed later - } else if (ack instanceof MqttPingResp) { - synchronized (pingOutstandingLock) { - pingOutstanding = Math.max(0, pingOutstanding-1); - notifyResult(ack, token, mex); - if (pingOutstanding == 0) { - tokenStore.removeToken(ack); - } - } - //@TRACE 636=ping response received. pingOutstanding: {0} - log.fine(CLASS_NAME,methodName,"636",new Object[]{ new Integer(pingOutstanding)}); - } else if (ack instanceof MqttConnack) { - int rc = ((MqttConnack) ack).getReturnCode(); - if (rc == 0) { - synchronized (queueLock) { - if (cleanSession) { - clearState(); - // Add the connect token back in so that users can be - // notified when connect completes. - tokenStore.saveToken(token,ack); - } - inFlightPubRels = 0; - actualInFlight = 0; - restoreInflightMessages(); - connected(); - } - } else { - mex = ExceptionHelper.createMqttException(rc); - throw mex; - } - - clientComms.connectComplete((MqttConnack) ack, mex); - notifyResult(ack, token, mex); - tokenStore.removeToken(ack); - - // Notify the sender thread that there maybe work for it to do now - synchronized (queueLock) { - queueLock.notifyAll(); - } - } else { - notifyResult(ack, token, mex); - releaseMessageId(ack.getMessageId()); - tokenStore.removeToken(ack); - } - - checkQuiesceLock(); - } - - /** - * Called by the CommsReceiver when a message has been received. - * Handles inbound messages and other flows such as PUBREL. - * - * @param message The {@link MqttWireMessage} that has been received - * @throws MqttException when an exception occurs whilst notifying - */ - protected void notifyReceivedMsg(MqttWireMessage message) throws MqttException { - final String methodName = "notifyReceivedMsg"; - this.lastInboundActivity = System.currentTimeMillis(); - - // @TRACE 651=received key={0} message={1} - log.fine(CLASS_NAME, methodName, "651", new Object[] { - new Integer(message.getMessageId()), message }); - - if (!quiescing) { - if (message instanceof MqttPublish) { - MqttPublish send = (MqttPublish) message; - switch (send.getMessage().getQos()) { - case 0: - case 1: - if (callback != null) { - callback.messageArrived(send); - } - break; - case 2: - persistence.put(getReceivedPersistenceKey(message), - (MqttPublish) message); - inboundQoS2.put(new Integer(send.getMessageId()), send); - this.send(new MqttPubRec(send), null); - break; - - default: - //should NOT reach here - } - } else if (message instanceof MqttPubRel) { - MqttPublish sendMsg = (MqttPublish) inboundQoS2 - .get(new Integer(message.getMessageId())); - if (sendMsg != null) { - if (callback != null) { - callback.messageArrived(sendMsg); - } - } else { - // Original publish has already been delivered. - MqttPubComp pubComp = new MqttPubComp(message - .getMessageId()); - this.send(pubComp, null); - } - } - } - } - - - /** - * Called when waiters and callbacks have processed the message. For - * messages where delivery is complete the message can be removed from - * persistence and counters adjusted accordingly. Also tidy up by removing - * token from store... - * - * @param token The {@link MqttToken} that will be used to notify - * @throws MqttException if an exception occurs during notification - */ - protected void notifyComplete(MqttToken token) throws MqttException { - - final String methodName = "notifyComplete"; - - MqttWireMessage message = token.internalTok.getWireMessage(); - - if (message != null && message instanceof MqttAck) { - - // @TRACE 629=received key={0} token={1} message={2} - log.fine(CLASS_NAME, methodName, "629", new Object[] { - new Integer(message.getMessageId()), token, message }); - - MqttAck ack = (MqttAck) message; - - if (ack instanceof MqttPubAck) { - - // QoS 1 - user notified now remove from persistence... - persistence.remove(getSendPersistenceKey(message)); - persistence.remove(getSendBufferedPersistenceKey(message)); - outboundQoS1.remove(new Integer(ack.getMessageId())); - decrementInFlight(); - releaseMessageId(message.getMessageId()); - tokenStore.removeToken(message); - // @TRACE 650=removed Qos 1 publish. key={0} - log.fine(CLASS_NAME, methodName, "650", - new Object[] { new Integer(ack.getMessageId()) }); - } else if (ack instanceof MqttPubComp) { - // QoS 2 - user notified now remove from persistence... - persistence.remove(getSendPersistenceKey(message)); - persistence.remove(getSendConfirmPersistenceKey(message)); - persistence.remove(getSendBufferedPersistenceKey(message)); - outboundQoS2.remove(new Integer(ack.getMessageId())); - - inFlightPubRels--; - decrementInFlight(); - releaseMessageId(message.getMessageId()); - tokenStore.removeToken(message); - - // @TRACE 645=removed QoS 2 publish/pubrel. key={0}, -1 inFlightPubRels={1} - log.fine(CLASS_NAME, methodName, "645", new Object[] { - new Integer(ack.getMessageId()), - new Integer(inFlightPubRels) }); - } - - checkQuiesceLock(); - } - } - - protected void notifyResult(MqttWireMessage ack, MqttToken token, MqttException ex) { - final String methodName = "notifyResult"; - // unblock any threads waiting on the token - token.internalTok.markComplete(ack, ex); - token.internalTok.notifyComplete(); - - // Let the user know an async operation has completed and then remove the token - if (ack != null && ack instanceof MqttAck && !(ack instanceof MqttPubRec)) { - //@TRACE 648=key{0}, msg={1}, excep={2} - log.fine(CLASS_NAME,methodName, "648", new Object [] {token.internalTok.getKey(), ack, ex}); - callback.asyncOperationComplete(token); - } - // There are cases where there is no ack as the operation failed before - // an ack was received - if (ack == null ) { - //@TRACE 649=key={0},excep={1} - log.fine(CLASS_NAME,methodName, "649", new Object [] { token.internalTok.getKey(), ex}); - callback.asyncOperationComplete(token); - } - } - - /** - * Called when the client has successfully connected to the broker - */ - public void connected() { - final String methodName = "connected"; - //@TRACE 631=connected - log.fine(CLASS_NAME, methodName, "631"); - this.connected = true; - - pingSender.start(); //Start ping thread when client connected to server. - } - - /** - * Called during shutdown to work out if there are any tokens still - * to be notified and waiters to be unblocked. Notifying and unblocking - * takes place after most shutdown processing has completed. The tokenstore - * is tidied up so it only contains outstanding delivery tokens which are - * valid after reconnect (if clean session is false) - * @param reason The root cause of the disconnection, or null if it is a clean disconnect - * @return {@link Vector} - */ - public Vector resolveOldTokens(MqttException reason) { - final String methodName = "resolveOldTokens"; - //@TRACE 632=reason {0} - log.fine(CLASS_NAME,methodName,"632", new Object[] {reason}); - - // If any outstanding let the user know the reason why it is still - // outstanding by putting the reason shutdown is occurring into the - // token. - MqttException shutReason = reason; - if (reason == null) { - shutReason = new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); - } - - // Set the token up so it is ready to be notified after disconnect - // processing has completed. Do not - // remove the token from the store if it is a delivery token, it is - // valid after a reconnect. - Vector outT = tokenStore.getOutstandingTokens(); - Enumeration outTE = outT.elements(); - while (outTE.hasMoreElements()) { - MqttToken tok = (MqttToken)outTE.nextElement(); - synchronized (tok) { - if (!tok.isComplete() && !tok.internalTok.isCompletePending() && tok.getException() == null) { - tok.internalTok.setException(shutReason); - } - } - if (!(tok instanceof MqttDeliveryToken)) { - // If not a delivery token it is not valid on - // restart so remove - tokenStore.removeToken(tok.internalTok.getKey()); - } - } - return outT; - } - - /** - * Called when the client has been disconnected from the broker. - * @param reason The root cause of the disconnection, or null if it is a clean disconnect - */ - public void disconnected(MqttException reason) { - final String methodName = "disconnected"; - //@TRACE 633=disconnected - log.fine(CLASS_NAME,methodName,"633", new Object[] {reason}); - - this.connected = false; - - try { - if (cleanSession) { - clearState(); - } - - pendingMessages.clear(); - pendingFlows.clear(); - synchronized (pingOutstandingLock) { - // Reset pingOutstanding to allow reconnects to assume no previous ping. - pingOutstanding = 0; - } - } catch (MqttException e) { - // Ignore as we have disconnected at this point - } - } - - /** - * Releases a message ID back into the pool of available message IDs. - * If the supplied message ID is not in use, then nothing will happen. - * - * @param msgId A message ID that can be freed up for re-use. - */ - private synchronized void releaseMessageId(int msgId) { - inUseMsgIds.remove(new Integer(msgId)); - } - - /** - * Get the next MQTT message ID that is not already in use, and marks - * it as now being in use. - * - * @return the next MQTT message ID to use - */ - private synchronized int getNextMessageId() throws MqttException { - int startingMessageId = nextMsgId; - // Allow two complete passes of the message ID range. This gives - // any asynchronous releases a chance to occur - int loopCount = 0; - do { - nextMsgId++; - if ( nextMsgId > MAX_MSG_ID ) { - nextMsgId = MIN_MSG_ID; - } - if (nextMsgId == startingMessageId) { - loopCount++; - if (loopCount == 2) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_NO_MESSAGE_IDS_AVAILABLE); - } - } - } while( inUseMsgIds.containsKey( new Integer(nextMsgId) ) ); - Integer id = new Integer(nextMsgId); - inUseMsgIds.put(id, id); - return nextMsgId; - } - - /** - * Quiesce the client state, preventing any new messages getting sent, - * and preventing the callback on any newly received messages. - * After the timeout expires, delete any pending messages except for - * outbound ACKs, and wait for those ACKs to complete. - * @param timeout How long to wait during Quiescing - */ - public void quiesce(long timeout) { - final String methodName = "quiesce"; - // If the timeout is greater than zero t - if (timeout > 0 ) { - //@TRACE 637=timeout={0} - log.fine(CLASS_NAME,methodName, "637",new Object[]{new Long(timeout)}); - synchronized (queueLock) { - this.quiescing = true; - } - // We don't want to handle any new inbound messages - callback.quiesce(); - notifyQueueLock(); - - synchronized (quiesceLock) { - try { - // If token count is not zero there is outbound work to process and - // if pending flows is not zero there is outstanding work to complete and - // if call back is not quiseced there it needs to complete. - int tokc = tokenStore.count(); - if (tokc > 0 || pendingFlows.size() >0 || !callback.isQuiesced()) { - //@TRACE 639=wait for outstanding: actualInFlight={0} pendingFlows={1} inFlightPubRels={2} tokens={3} - log.fine(CLASS_NAME, methodName,"639", new Object[]{new Integer(actualInFlight), new Integer(pendingFlows.size()), new Integer(inFlightPubRels), new Integer(tokc)}); - - // wait for outstanding in flight messages to complete and - // any pending flows to complete - quiesceLock.wait(timeout); - } - } - catch (InterruptedException ex) { - // Don't care, as we're shutting down anyway - } - } - - // Quiesce time up or inflight messages delivered. Ensure pending delivery - // vectors are cleared ready for disconnect to be sent as the final flow. - synchronized (queueLock) { - pendingMessages.clear(); - pendingFlows.clear(); - quiescing = false; - actualInFlight = 0; - } - //@TRACE 640=finished - log.fine(CLASS_NAME, methodName, "640"); - } - } - - public void notifyQueueLock() { - final String methodName = "notifyQueueLock"; - synchronized (queueLock) { - //@TRACE 638=notifying queueLock holders - log.fine(CLASS_NAME,methodName,"638"); - queueLock.notifyAll(); - } - } - - protected void deliveryComplete(MqttPublish message) throws MqttPersistenceException { - final String methodName = "deliveryComplete"; - - //@TRACE 641=remove publish from persistence. key={0} - log.fine(CLASS_NAME,methodName,"641", new Object[]{new Integer(message.getMessageId())}); - - persistence.remove(getReceivedPersistenceKey(message)); - inboundQoS2.remove(new Integer(message.getMessageId())); - } - - protected void deliveryComplete(int messageId) throws MqttPersistenceException { - final String methodName = "deliveryComplete"; - - //@TRACE 641=remove publish from persistence. key={0} - log.fine(CLASS_NAME,methodName,"641", new Object[]{new Integer(messageId)}); - - persistence.remove(getReceivedPersistenceKey(messageId)); - inboundQoS2.remove(new Integer(messageId)); - } - - public int getActualInFlight(){ - return actualInFlight; - } - - public int getMaxInFlight(){ - return maxInflight; - } - - /** - * Tidy up - * - ensure that tokens are released as they are maintained over a - * disconnect / connect cycle. - */ - protected void close() { - inUseMsgIds.clear(); - if (pendingMessages != null) { - pendingMessages.clear(); - } - pendingFlows.clear(); - outboundQoS2.clear(); - outboundQoS1.clear(); - outboundQoS0.clear(); - inboundQoS2.clear(); - tokenStore.clear(); - inUseMsgIds = null; - pendingMessages = null; - pendingFlows = null; - outboundQoS2 = null; - outboundQoS1 = null; - outboundQoS0 = null; - inboundQoS2 = null; - tokenStore = null; - callback = null; - clientComms = null; - persistence = null; - pingCommand = null; - } - - public Properties getDebug() { - Properties props = new Properties(); - props.put("In use msgids", inUseMsgIds); - props.put("pendingMessages", pendingMessages); - props.put("pendingFlows", pendingFlows); - props.put("maxInflight", new Integer(maxInflight)); - props.put("nextMsgID", new Integer(nextMsgId)); - props.put("actualInFlight", new Integer(actualInFlight)); - props.put("inFlightPubRels", new Integer(inFlightPubRels)); - props.put("quiescing", Boolean.valueOf(quiescing)); - props.put("pingoutstanding", new Integer(pingOutstanding)); - props.put("lastOutboundActivity", new Long(lastOutboundActivity)); - props.put("lastInboundActivity", new Long(lastInboundActivity)); - props.put("outboundQoS2", outboundQoS2); - props.put("outboundQoS1", outboundQoS1); - props.put("outboundQoS0", outboundQoS0); - props.put("inboundQoS2", inboundQoS2); - props.put("tokens", tokenStore); - return props; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java deleted file mode 100644 index bb6d77c..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java +++ /dev/null @@ -1,506 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - per subscription message handlers (bug 466579) - * Ian Craggs - ack control (bug 472172) - * James Sutton - Automatic Reconnect & Offline Buffering - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Vector; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.Semaphore; - -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.IMqttMessageListener; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttCallbackExtended; -import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.MqttTopic; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Bridge between Receiver and the external API. This class gets called by - * Receiver, and then converts the comms-centric MQTT message objects into ones - * understood by the external API. - */ -public class CommsCallback implements Runnable { - private static final String CLASS_NAME = CommsCallback.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - private static final int INBOUND_QUEUE_SIZE = 10; - private MqttCallback mqttCallback; - private MqttCallbackExtended reconnectInternalCallback; - private Hashtable callbacks; // topicFilter -> messageHandler - private ClientComms clientComms; - private Vector messageQueue; - private Vector completeQueue; - public boolean running = false; - private boolean quiescing = false; - private Object lifecycle = new Object(); - private Thread callbackThread; - private Object workAvailable = new Object(); - private Object spaceAvailable = new Object(); - private ClientState clientState; - private boolean manualAcks = false; - private String threadName; - private final Semaphore runningSemaphore = new Semaphore(1); - private Future callbackFuture; - - CommsCallback(ClientComms clientComms) { - this.clientComms = clientComms; - this.messageQueue = new Vector(INBOUND_QUEUE_SIZE); - this.completeQueue = new Vector(INBOUND_QUEUE_SIZE); - this.callbacks = new Hashtable(); - log.setResourceName(clientComms.getClient().getClientId()); - } - - public void setClientState(ClientState clientState) { - this.clientState = clientState; - } - - /** - * Starts up the Callback thread. - * @param threadName The name of the thread - * @param executorService the {@link ExecutorService} - */ - public void start(String threadName, ExecutorService executorService) { - this.threadName = threadName; - synchronized (lifecycle) { - if (!running) { - // Preparatory work before starting the background thread. - // For safety ensure any old events are cleared. - messageQueue.clear(); - completeQueue.clear(); - - running = true; - quiescing = false; - callbackFuture = executorService.submit(this); - } - } - } - - /** - * Stops the callback thread. - * This call will block until stop has completed. - */ - public void stop() { - final String methodName = "stop"; - synchronized (lifecycle) { - if (callbackFuture != null) { - callbackFuture.cancel(true); - } - if (running) { - // @TRACE 700=stopping - log.fine(CLASS_NAME, methodName, "700"); - running = false; - if (!Thread.currentThread().equals(callbackThread)) { - try { - synchronized (workAvailable) { - // @TRACE 701=notify workAvailable and wait for run - // to finish - log.fine(CLASS_NAME, methodName, "701"); - workAvailable.notifyAll(); - } - // Wait for the thread to finish. - runningSemaphore.acquire(); - } catch (InterruptedException ex) { - } finally { - runningSemaphore.release(); - } - } - } - callbackThread = null; - // @TRACE 703=stopped - log.fine(CLASS_NAME, methodName, "703"); - } - } - - public void setCallback(MqttCallback mqttCallback) { - this.mqttCallback = mqttCallback; - } - - public void setReconnectCallback(MqttCallbackExtended callback){ - this.reconnectInternalCallback = callback; - } - - public void setManualAcks(boolean manualAcks) { - this.manualAcks = manualAcks; - } - - public void run() { - final String methodName = "run"; - callbackThread = Thread.currentThread(); - callbackThread.setName(threadName); - - try { - runningSemaphore.acquire(); - } catch (InterruptedException e) { - running = false; - return; - } - - while (running) { - try { - // If no work is currently available, then wait until there is some... - try { - synchronized (workAvailable) { - if (running && messageQueue.isEmpty() - && completeQueue.isEmpty()) { - // @TRACE 704=wait for workAvailable - log.fine(CLASS_NAME, methodName, "704"); - workAvailable.wait(); - } - } - } catch (InterruptedException e) { - } - - if (running) { - // Check for deliveryComplete callbacks... - MqttToken token = null; - synchronized (completeQueue) { - if (!completeQueue.isEmpty()) { - // First call the delivery arrived callback if needed - token = (MqttToken) completeQueue.elementAt(0); - completeQueue.removeElementAt(0); - } - } - if (null != token) { - handleActionComplete(token); - } - - // Check for messageArrived callbacks... - MqttPublish message = null; - synchronized (messageQueue) { - if (!messageQueue.isEmpty()) { - // Note, there is a window on connect where a publish - // could arrive before we've - // finished the connect logic. - message = (MqttPublish) messageQueue.elementAt(0); - - messageQueue.removeElementAt(0); - } - } - if (null != message) { - handleMessage(message); - } - } - - if (quiescing) { - clientState.checkQuiesceLock(); - } - - } catch (Throwable ex) { - // Users code could throw an Error or Exception e.g. in the case - // of class NoClassDefFoundError - // @TRACE 714=callback threw exception - log.fine(CLASS_NAME, methodName, "714", null, ex); - running = false; - clientComms.shutdownConnection(null, new MqttException(ex)); - } finally { - runningSemaphore.release(); - synchronized (spaceAvailable) { - // Notify the spaceAvailable lock, to say that there's now - // some space on the queue... - - // @TRACE 706=notify spaceAvailable - log.fine(CLASS_NAME, methodName, "706"); - spaceAvailable.notifyAll(); - } - } - } - } - - private void handleActionComplete(MqttToken token) - throws MqttException { - final String methodName = "handleActionComplete"; - synchronized (token) { - // @TRACE 705=callback and notify for key={0} - log.fine(CLASS_NAME, methodName, "705", new Object[] { token.internalTok.getKey() }); - if (token.isComplete()) { - // Finish by doing any post processing such as delete - // from persistent store but only do so if the action - // is complete - clientState.notifyComplete(token); - } - - // Unblock any waiters and if pending complete now set completed - token.internalTok.notifyComplete(); - - if (!token.internalTok.isNotified()) { - // If a callback is registered and delivery has finished - // call delivery complete callback. - if ( mqttCallback != null - && token instanceof MqttDeliveryToken - && token.isComplete()) { - mqttCallback.deliveryComplete((MqttDeliveryToken) token); - } - // Now call async action completion callbacks - fireActionEvent(token); - } - - // Set notified so we don't tell the user again about this action. - if ( token.isComplete() ){ - if ( token instanceof MqttDeliveryToken || token.getActionCallback() instanceof IMqttActionListener ) { - token.internalTok.setNotified(true); - } - } - - - - } - } - - /** - * This method is called when the connection to the server is lost. If there - * is no cause then it was a clean disconnect. The connectionLost callback - * will be invoked if registered and run on the thread that requested - * shutdown e.g. receiver or sender thread. If the request was a user - * initiated disconnect then the disconnect token will be notified. - * - * @param cause the reason behind the loss of connection. - */ - public void connectionLost(MqttException cause) { - final String methodName = "connectionLost"; - // If there was a problem and a client callback has been set inform - // the connection lost listener of the problem. - try { - if (mqttCallback != null && cause != null) { - // @TRACE 708=call connectionLost - log.fine(CLASS_NAME, methodName, "708", new Object[] { cause }); - mqttCallback.connectionLost(cause); - } - if(reconnectInternalCallback != null && cause != null){ - reconnectInternalCallback.connectionLost(cause); - } - } catch (java.lang.Throwable t) { - // Just log the fact that a throwable has caught connection lost - // is called during shutdown processing so no need to do anything else - // @TRACE 720=exception from connectionLost {0} - log.fine(CLASS_NAME, methodName, "720", new Object[] { t }); - } - } - - /** - * An action has completed - if a completion listener has been set on the - * token then invoke it with the outcome of the action. - * - * @param token The {@link MqttToken} that has completed - */ - public void fireActionEvent(MqttToken token) { - final String methodName = "fireActionEvent"; - - if (token != null) { - IMqttActionListener asyncCB = token.getActionCallback(); - if (asyncCB != null) { - if (token.getException() == null) { - // @TRACE 716=call onSuccess key={0} - log.fine(CLASS_NAME, methodName, "716", - new Object[] { token.internalTok.getKey() }); - asyncCB.onSuccess(token); - } else { - // @TRACE 717=call onFailure key {0} - log.fine(CLASS_NAME, methodName, "716", - new Object[] { token.internalTok.getKey() }); - asyncCB.onFailure(token, token.getException()); - } - } - } - } - - /** - * This method is called when a message arrives on a topic. Messages are - * only added to the queue for inbound messages if the client is not - * quiescing. - * - * @param sendMessage - * the MQTT SEND message. - */ - public void messageArrived(MqttPublish sendMessage) { - final String methodName = "messageArrived"; - if (mqttCallback != null || callbacks.size() > 0) { - // If we already have enough messages queued up in memory, wait - // until some more queue space becomes available. This helps - // the client protect itself from getting flooded by messages - // from the server. - synchronized (spaceAvailable) { - while (running && !quiescing && messageQueue.size() >= INBOUND_QUEUE_SIZE) { - try { - // @TRACE 709=wait for spaceAvailable - log.fine(CLASS_NAME, methodName, "709"); - spaceAvailable.wait(200); - } catch (InterruptedException ex) { - } - } - } - if (!quiescing) { - messageQueue.addElement(sendMessage); - // Notify the CommsCallback thread that there's work to do... - synchronized (workAvailable) { - // @TRACE 710=new msg avail, notify workAvailable - log.fine(CLASS_NAME, methodName, "710"); - workAvailable.notifyAll(); - } - } - } - } - - /** - * Let the call back thread quiesce. Prevent new inbound messages being - * added to the process queue and let existing work quiesce. (until the - * thread is told to shutdown). - */ - public void quiesce() { - final String methodName = "quiesce"; - this.quiescing = true; - synchronized (spaceAvailable) { - // @TRACE 711=quiesce notify spaceAvailable - log.fine(CLASS_NAME, methodName, "711"); - // Unblock anything waiting for space... - spaceAvailable.notifyAll(); - } - } - - public boolean isQuiesced() { - if (quiescing && completeQueue.size() == 0 && messageQueue.size() == 0) { - return true; - } - return false; - } - - private void handleMessage(MqttPublish publishMessage) - throws MqttException, Exception { - final String methodName = "handleMessage"; - // If quisecing process any pending messages. - - String destName = publishMessage.getTopicName(); - - // @TRACE 713=call messageArrived key={0} topic={1} - log.fine(CLASS_NAME, methodName, "713", new Object[] { - new Integer(publishMessage.getMessageId()), destName }); - deliverMessage(destName, publishMessage.getMessageId(), - publishMessage.getMessage()); - - if (!this.manualAcks) { - if (publishMessage.getMessage().getQos() == 1) { - this.clientComms.internalSend(new MqttPubAck(publishMessage), - new MqttToken(clientComms.getClient().getClientId())); - } else if (publishMessage.getMessage().getQos() == 2) { - this.clientComms.deliveryComplete(publishMessage); - MqttPubComp pubComp = new MqttPubComp(publishMessage); - this.clientComms.internalSend(pubComp, new MqttToken( - clientComms.getClient().getClientId())); - } - } - } - - public void messageArrivedComplete(int messageId, int qos) - throws MqttException { - if (qos == 1) { - this.clientComms.internalSend(new MqttPubAck(messageId), - new MqttToken(clientComms.getClient().getClientId())); - } else if (qos == 2) { - this.clientComms.deliveryComplete(messageId); - MqttPubComp pubComp = new MqttPubComp(messageId); - this.clientComms.internalSend(pubComp, new MqttToken( - clientComms.getClient().getClientId())); - } - } - - public void asyncOperationComplete(MqttToken token) { - final String methodName = "asyncOperationComplete"; - - if (running) { - // invoke callbacks on callback thread - completeQueue.addElement(token); - synchronized (workAvailable) { - // @TRACE 715=new workAvailable. key={0} - log.fine(CLASS_NAME, methodName, "715", new Object[] { token.internalTok.getKey() }); - workAvailable.notifyAll(); - } - } else { - // invoke async callback on invokers thread - try { - handleActionComplete(token); - } catch (Throwable ex) { - // Users code could throw an Error or Exception e.g. in the case - // of class NoClassDefFoundError - // @TRACE 719=callback threw ex: - log.fine(CLASS_NAME, methodName, "719", null, ex); - - // Shutdown likely already in progress but no harm to confirm - clientComms.shutdownConnection(null, new MqttException(ex)); - } - - } - } - - /** - * Returns the thread used by this callback. - * @return The {@link Thread} - */ - protected Thread getThread() { - return callbackThread; - } - - - public void setMessageListener(String topicFilter, IMqttMessageListener messageListener) { - this.callbacks.put(topicFilter, messageListener); - } - - - public void removeMessageListener(String topicFilter) { - this.callbacks.remove(topicFilter); // no exception thrown if the filter was not present - } - - public void removeMessageListeners() { - this.callbacks.clear(); - } - - - protected boolean deliverMessage(String topicName, int messageId, MqttMessage aMessage) throws Exception - { - boolean delivered = false; - - Enumeration keys = callbacks.keys(); - while (keys.hasMoreElements()) { - String topicFilter = (String)keys.nextElement(); - if (MqttTopic.isMatched(topicFilter, topicName)) { - aMessage.setId(messageId); - ((IMqttMessageListener)(callbacks.get(topicFilter))).messageArrived(topicName, aMessage); - delivered = true; - } - } - - /* if the message hasn't been delivered to a per subscription handler, give it to the default handler */ - if (mqttCallback != null && !delivered) { - aMessage.setId(messageId); - mqttCallback.messageArrived(topicName, aMessage); - delivered = true; - } - - return delivered; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java deleted file mode 100644 index 5bd2db4..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java +++ /dev/null @@ -1,206 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.Semaphore; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRec; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Receives MQTT packets from the server. - */ -public class CommsReceiver implements Runnable { - private static final String CLASS_NAME = CommsReceiver.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - private boolean running = false; - private Object lifecycle = new Object(); - private ClientState clientState = null; - private ClientComms clientComms = null; - private MqttInputStream in; - private CommsTokenStore tokenStore = null; - private Thread recThread = null; - private volatile boolean receiving; - private final Semaphore runningSemaphore = new Semaphore(1); - private String threadName; - private Future receiverFuture; - - - public CommsReceiver(ClientComms clientComms, ClientState clientState,CommsTokenStore tokenStore, InputStream in) { - this.in = new MqttInputStream(clientState, in); - this.clientComms = clientComms; - this.clientState = clientState; - this.tokenStore = tokenStore; - log.setResourceName(clientComms.getClient().getClientId()); - } - - /** - * Starts up the Receiver's thread. - * @param threadName the thread name. - * @param executorService used to execute the thread - */ - public void start(String threadName, ExecutorService executorService) { - this.threadName = threadName; - final String methodName = "start"; - //@TRACE 855=starting - log.fine(CLASS_NAME,methodName, "855"); - synchronized (lifecycle) { - if (!running) { - running = true; - receiverFuture = executorService.submit(this); - } - } - } - - /** - * Stops the Receiver's thread. This call will block. - */ - public void stop() { - final String methodName = "stop"; - synchronized (lifecycle) { - if (receiverFuture != null) { - receiverFuture.cancel(true); - } - //@TRACE 850=stopping - log.fine(CLASS_NAME,methodName, "850"); - if (running) { - running = false; - receiving = false; - if (!Thread.currentThread().equals(recThread)) { - try { - // Wait for the thread to finish. - runningSemaphore.acquire(); - } - catch (InterruptedException ex) { - } finally { - runningSemaphore.release(); - } - } - } - } - recThread = null; - //@TRACE 851=stopped - log.fine(CLASS_NAME,methodName,"851"); - } - - /** - * Run loop to receive messages from the server. - */ - public void run() { - recThread = Thread.currentThread(); - recThread.setName(threadName); - final String methodName = "run"; - MqttToken token = null; - - try { - runningSemaphore.acquire(); - } catch (InterruptedException e) { - running = false; - return; - } - - while (running && (in != null)) { - try { - //@TRACE 852=network read message - log.fine(CLASS_NAME,methodName,"852"); - receiving = in.available() > 0; - MqttWireMessage message = in.readMqttWireMessage(); - receiving = false; - - // instanceof checks if message is null - if (message instanceof MqttAck) { - token = tokenStore.getToken(message); - if (token!=null) { - synchronized (token) { - // Ensure the notify processing is done under a lock on the token - // This ensures that the send processing can complete before the - // receive processing starts! ( request and ack and ack processing - // can occur before request processing is complete if not! - clientState.notifyReceivedAck((MqttAck)message); - } - } else if(message instanceof MqttPubRec || message instanceof MqttPubComp || message instanceof MqttPubAck) { - //This is an ack for a message we no longer have a ticket for. - //This probably means we already received this message and it's being send again - //because of timeouts, crashes, disconnects, restarts etc. - //It should be safe to ignore these unexpected messages. - log.fine(CLASS_NAME, methodName, "857"); - } else { - // It its an ack and there is no token then something is not right. - // An ack should always have a token assoicated with it. - throw new MqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); - } - } else { - if (message != null) { - // A new message has arrived - clientState.notifyReceivedMsg(message); - } - } - } - catch (MqttException ex) { - //@TRACE 856=Stopping, MQttException - log.fine(CLASS_NAME,methodName,"856",null,ex); - running = false; - // Token maybe null but that is handled in shutdown - clientComms.shutdownConnection(token, ex); - } - catch (IOException ioe) { - //@TRACE 853=Stopping due to IOException - log.fine(CLASS_NAME,methodName,"853"); - - running = false; - // An EOFException could be raised if the broker processes the - // DISCONNECT and ends the socket before we complete. As such, - // only shutdown the connection if we're not already shutting down. - if (!clientComms.isDisconnecting()) { - clientComms.shutdownConnection(token, new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ioe)); - } - } - finally { - receiving = false; - runningSemaphore.release(); - } - } - - //@TRACE 854=< - log.fine(CLASS_NAME,methodName,"854"); - } - - public boolean isRunning() { - return running; - } - - /** - * Returns the receiving state. - * - * @return true if the receiver is receiving data, false otherwise. - */ - public boolean isReceiving() { - return receiving; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsSender.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsSender.java deleted file mode 100644 index 3697a98..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsSender.java +++ /dev/null @@ -1,190 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttOutputStream; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - - -public class CommsSender implements Runnable { - private static final String CLASS_NAME = CommsSender.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - //Sends MQTT packets to the server on its own thread - private boolean running = false; - private Object lifecycle = new Object(); - private ClientState clientState = null; - private MqttOutputStream out; - private ClientComms clientComms = null; - private CommsTokenStore tokenStore = null; - private Thread sendThread = null; - - private String threadName; - private final Semaphore runningSemaphore = new Semaphore(1); - private Future senderFuture; - - public CommsSender(ClientComms clientComms, ClientState clientState, CommsTokenStore tokenStore, OutputStream out) { - this.out = new MqttOutputStream(clientState, out); - this.clientComms = clientComms; - this.clientState = clientState; - this.tokenStore = tokenStore; - log.setResourceName(clientComms.getClient().getClientId()); - } - - /** - * Starts up the Sender thread. - * @param threadName the threadname - * @param executorService used to execute the thread - */ - public void start(String threadName, ExecutorService executorService) { - this.threadName = threadName; - synchronized (lifecycle) { - if (!running) { - running = true; - senderFuture = executorService.submit(this); - } - } - } - - /** - * Stops the Sender's thread. This call will block. - */ - public void stop() { - final String methodName = "stop"; - - synchronized (lifecycle) { - if (senderFuture != null) { - senderFuture.cancel(true); - } - //@TRACE 800=stopping sender - log.fine(CLASS_NAME,methodName,"800"); - if (running) { - running = false; - if (!Thread.currentThread().equals(sendThread)) { - try { - while (running) { - // first notify get routine to finish - clientState.notifyQueueLock(); - // Wait for the thread to finish. - runningSemaphore.tryAcquire(100, TimeUnit.MILLISECONDS); - } - } catch (InterruptedException ex) { - } finally { - runningSemaphore.release(); - } - } - } - sendThread=null; - //@TRACE 801=stopped - log.fine(CLASS_NAME,methodName,"801"); - } - } - - public void run() { - sendThread = Thread.currentThread(); - sendThread.setName(threadName); - final String methodName = "run"; - MqttWireMessage message = null; - - try { - runningSemaphore.acquire(); - } catch (InterruptedException e) { - running = false; - return; - } - - try { - while (running && (out != null)) { - try { - message = clientState.get(); - if (message != null) { - //@TRACE 802=network send key={0} msg={1} - log.fine(CLASS_NAME,methodName,"802", new Object[] {message.getKey(),message}); - - if (message instanceof MqttAck) { - out.write(message); - out.flush(); - } else { - MqttToken token = tokenStore.getToken(message); - // While quiescing the tokenstore can be cleared so need - // to check for null for the case where clear occurs - // while trying to send a message. - if (token != null) { - synchronized (token) { - out.write(message); - try { - out.flush(); - } catch (IOException ex) { - // The flush has been seen to fail on disconnect of a SSL socket - // as disconnect is in progress this should not be treated as an error - if (!(message instanceof MqttDisconnect)) { - throw ex; - } - } - clientState.notifySent(message); - } - } - } - } else { // null message - //@TRACE 803=get message returned null, stopping} - log.fine(CLASS_NAME,methodName,"803"); - - running = false; - } - } catch (MqttException me) { - handleRunException(message, me); - } catch (Exception ex) { - handleRunException(message, ex); - } - } // end while - } finally { - running = false; - runningSemaphore.release(); - } - - //@TRACE 805=< - log.fine(CLASS_NAME, methodName,"805"); - - } - - private void handleRunException(MqttWireMessage message, Exception ex) { - final String methodName = "handleRunException"; - //@TRACE 804=exception - log.fine(CLASS_NAME,methodName,"804",null, ex); - MqttException mex; - if ( !(ex instanceof MqttException)) { - mex = new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ex); - } else { - mex = (MqttException)ex; - } - - running = false; - clientComms.shutdownConnection(null, mex); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java deleted file mode 100644 index dcd5ea4..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java +++ /dev/null @@ -1,254 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Vector; - -import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - - -/** - * Provides a "token" based system for storing and tracking actions across - * multiple threads. - * When a message is sent, a token is associated with the message - * and saved using the {@link CommsTokenStore#saveToken(MqttToken, MqttWireMessage)} method. Anyone interested - * in tacking the state can call one of the wait methods on the token or using - * the asynchronous listener callback method on the operation. - * The {@link CommsReceiver} class, on another thread, reads responses back from - * the network. It uses the response to find the relevant token, which it can then - * notify. - * - * Note: - * Ping, connect and disconnect do not have a unique message id as - * only one outstanding request of each type is allowed to be outstanding - */ -public class CommsTokenStore { - private static final String CLASS_NAME = CommsTokenStore.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - // Maps message-specific data (usually message IDs) to tokens - private Hashtable tokens; - private String logContext; - private MqttException closedResponse = null; - - public CommsTokenStore(String logContext) { - final String methodName = ""; - - log.setResourceName(logContext); - this.tokens = new Hashtable(); - this.logContext = logContext; - //@TRACE 308=<> - log.fine(CLASS_NAME,methodName,"308");//,new Object[]{message}); - - } - - /** - * Based on the message type that has just been received return the associated - * token from the token store or null if one does not exist. - * @param message whose token is to be returned - * @return token for the requested message - */ - public MqttToken getToken(MqttWireMessage message) { - String key = message.getKey(); - return (MqttToken)tokens.get(key); - } - - public MqttToken getToken(String key) { - return (MqttToken)tokens.get(key); - } - - - public MqttToken removeToken(MqttWireMessage message) { - if (message != null) { - return removeToken(message.getKey()); - } - return null; - } - - public MqttToken removeToken(String key) { - final String methodName = "removeToken"; - //@TRACE 306=key={0} - log.fine(CLASS_NAME,methodName,"306",new Object[]{key}); - - if ( null != key ){ - return (MqttToken) tokens.remove(key); - } - - return null; - } - - /** - * Restores a token after a client restart. This method could be called - * for a SEND of CONFIRM, but either way, the original SEND is what's - * needed to re-build the token. - * @param message The {@link MqttPublish} message to restore - * @return {@link MqttDeliveryToken} - */ - protected MqttDeliveryToken restoreToken(MqttPublish message) { - final String methodName = "restoreToken"; - MqttDeliveryToken token; - synchronized(tokens) { - String key = new Integer(message.getMessageId()).toString(); - if (this.tokens.containsKey(key)) { - token = (MqttDeliveryToken)this.tokens.get(key); - //@TRACE 302=existing key={0} message={1} token={2} - log.fine(CLASS_NAME,methodName, "302",new Object[]{key, message,token}); - } else { - token = new MqttDeliveryToken(logContext); - token.internalTok.setKey(key); - this.tokens.put(key, token); - //@TRACE 303=creating new token key={0} message={1} token={2} - log.fine(CLASS_NAME,methodName,"303",new Object[]{key, message, token}); - } - } - return token; - } - - // For outbound messages store the token in the token store - // For pubrel use the existing publish token - protected void saveToken(MqttToken token, MqttWireMessage message) throws MqttException { - final String methodName = "saveToken"; - - synchronized(tokens) { - if (closedResponse == null) { - String key = message.getKey(); - //@TRACE 300=key={0} message={1} - log.fine(CLASS_NAME,methodName,"300",new Object[]{key, message}); - - saveToken(token,key); - } else { - throw closedResponse; - } - } - } - - protected void saveToken(MqttToken token, String key) { - final String methodName = "saveToken"; - - synchronized(tokens) { - //@TRACE 307=key={0} token={1} - log.fine(CLASS_NAME,methodName,"307",new Object[]{key,token.toString()}); - token.internalTok.setKey(key); - this.tokens.put(key, token); - } - } - - protected void quiesce(MqttException quiesceResponse) { - final String methodName = "quiesce"; - - synchronized(tokens) { - //@TRACE 309=resp={0} - log.fine(CLASS_NAME,methodName,"309",new Object[]{quiesceResponse}); - - closedResponse = quiesceResponse; - } - } - - public void open() { - final String methodName = "open"; - - synchronized(tokens) { - //@TRACE 310=> - log.fine(CLASS_NAME,methodName,"310"); - - closedResponse = null; - } - } - - public MqttDeliveryToken[] getOutstandingDelTokens() { - final String methodName = "getOutstandingDelTokens"; - - synchronized(tokens) { - //@TRACE 311=> - log.fine(CLASS_NAME,methodName,"311"); - - Vector list = new Vector(); - Enumeration enumeration = tokens.elements(); - MqttToken token; - while(enumeration.hasMoreElements()) { - token = (MqttToken)enumeration.nextElement(); - if (token != null - && token instanceof MqttDeliveryToken - && !token.internalTok.isNotified()) { - - list.addElement(token); - } - } - - MqttDeliveryToken[] result = new MqttDeliveryToken[list.size()]; - return (MqttDeliveryToken[]) list.toArray(result); - } - } - - public Vector getOutstandingTokens() { - final String methodName = "getOutstandingTokens"; - - synchronized(tokens) { - //@TRACE 312=> - log.fine(CLASS_NAME,methodName,"312"); - - Vector list = new Vector(); - Enumeration enumeration = tokens.elements(); - MqttToken token; - while(enumeration.hasMoreElements()) { - token = (MqttToken)enumeration.nextElement(); - if (token != null) { - list.addElement(token); - } - } - return list; - } - } - - /** - * Empties the token store without notifying any of the tokens. - */ - public void clear() { - final String methodName = "clear"; - //@TRACE 305=> {0} tokens - log.fine(CLASS_NAME, methodName, "305", new Object[] {new Integer(tokens.size())}); - synchronized(tokens) { - tokens.clear(); - } - } - - public int count() { - synchronized(tokens) { - return tokens.size(); - } - } - public String toString() { - String lineSep = System.getProperty("line.separator","\n"); - StringBuffer toks = new StringBuffer(); - synchronized(tokens) { - Enumeration enumeration = tokens.elements(); - MqttToken token; - while(enumeration.hasMoreElements()) { - token = (MqttToken)enumeration.nextElement(); - toks.append("{"+token.internalTok+"}"+lineSep); - } - return toks.toString(); - } - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ConnectActionListener.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ConnectActionListener.java deleted file mode 100644 index aa6bb8d..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ConnectActionListener.java +++ /dev/null @@ -1,200 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - MQTT 3.1.1 support - * Ian Craggs - fix bug 469527 - * James Sutton - Automatic Reconnect & Offline Buffering - */ -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.IMqttToken; -import org.eclipse.paho.client.mqttv3.MqttAsyncClient; -import org.eclipse.paho.client.mqttv3.MqttCallbackExtended; -import org.eclipse.paho.client.mqttv3.MqttClientPersistence; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; -import org.eclipse.paho.client.mqttv3.MqttToken; - -/** - *

This class handles the connection of the AsyncClient to one of the available URLs.

- *

The URLs are supplied as either the singleton when the client is created, or as a list in the connect options.

- *

This class uses its own onSuccess and onFailure callbacks in preference to the user supplied callbacks.

- *

An attempt is made to connect to each URL in the list until either a connection attempt succeeds or all the URLs have been tried

- *

If a connection succeeds then the users token is notified and the users onSuccess callback is called.

- *

If a connection fails then another URL in the list is attempted, otherwise the users token is notified - * and the users onFailure callback is called

- */ -public class ConnectActionListener implements IMqttActionListener { - - private MqttClientPersistence persistence; - private MqttAsyncClient client; - private ClientComms comms; - private MqttConnectOptions options; - private MqttToken userToken; - private Object userContext; - private IMqttActionListener userCallback; - private int originalMqttVersion; - private MqttCallbackExtended mqttCallbackExtended; - private boolean reconnect; - -/** - * @param persistence The {@link MqttClientPersistence} layer - * @param client the {@link MqttAsyncClient} - * @param comms {@link ClientComms} - * @param options the {@link MqttConnectOptions} - * @param userToken the {@link MqttToken} - * @param userContext the User Context Object - * @param userCallback the {@link IMqttActionListener} as the callback for the user - * @param reconnect If true, this is a reconnect attempt - */ - public ConnectActionListener( - MqttAsyncClient client, - MqttClientPersistence persistence, - ClientComms comms, - MqttConnectOptions options, - MqttToken userToken, - Object userContext, - IMqttActionListener userCallback, - boolean reconnect) { - this.persistence = persistence; - this.client = client; - this.comms = comms; - this.options = options; - this.userToken = userToken; - this.userContext = userContext; - this.userCallback = userCallback; - this.originalMqttVersion = options.getMqttVersion(); - this.reconnect = reconnect; - } - - /** - * If the connect succeeded then call the users onSuccess callback - * - * @param token the {@link IMqttToken} from the successful connection - */ - public void onSuccess(IMqttToken token) { - if (originalMqttVersion == MqttConnectOptions.MQTT_VERSION_DEFAULT) { - options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_DEFAULT); - } - userToken.internalTok.markComplete(token.getResponse(), null); - userToken.internalTok.notifyComplete(); - userToken.internalTok.setClient(this.client); // fix bug 469527 - maybe should be set elsewhere? - - comms.notifyConnect(); - - if (userCallback != null) { - userToken.setUserContext(userContext); - userCallback.onSuccess(userToken); - } - - if(mqttCallbackExtended != null){ - String serverURI = comms.getNetworkModules()[comms.getNetworkModuleIndex()].getServerURI(); - mqttCallbackExtended.connectComplete(reconnect, serverURI); - } - - - } - - /** - * The connect failed, so try the next URI on the list. - * If there are no more URIs, then fail the overall connect. - * - * @param token the {@link IMqttToken} from the failed connection attempt - * @param exception the {@link Throwable} exception from the failed connection attempt - */ - public void onFailure(IMqttToken token, Throwable exception) { - - int numberOfURIs = comms.getNetworkModules().length; - int index = comms.getNetworkModuleIndex(); - - if ((index + 1) < numberOfURIs || (originalMqttVersion == MqttConnectOptions.MQTT_VERSION_DEFAULT && options.getMqttVersion() == MqttConnectOptions.MQTT_VERSION_3_1_1)) { - - if (originalMqttVersion == MqttConnectOptions.MQTT_VERSION_DEFAULT) { - if (options.getMqttVersion() == MqttConnectOptions.MQTT_VERSION_3_1_1) { - options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1); - } - else { - options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1); - comms.setNetworkModuleIndex(index + 1); - } - } - else { - comms.setNetworkModuleIndex(index + 1); - } - try { - connect(); - } - catch (MqttPersistenceException e) { - onFailure(token, e); // try the next URI in the list - } - } - else { - if (originalMqttVersion == MqttConnectOptions.MQTT_VERSION_DEFAULT) { - options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_DEFAULT); - } - MqttException ex; - if (exception instanceof MqttException) { - ex = (MqttException) exception; - } - else { - ex = new MqttException(exception); - } - userToken.internalTok.markComplete(null, ex); - userToken.internalTok.notifyComplete(); - userToken.internalTok.setClient(this.client); // fix bug 469527 - maybe should be set elsewhere? - - if (userCallback != null) { - userToken.setUserContext(userContext); - userCallback.onFailure(userToken, exception); - } - } - } - - /** - * Start the connect processing - * @throws MqttPersistenceException if an error is thrown whilst setting up persistence - */ - public void connect() throws MqttPersistenceException { - MqttToken token = new MqttToken(client.getClientId()); - token.setActionCallback(this); - token.setUserContext(this); - - persistence.open(client.getClientId(), client.getServerURI()); - - if (options.isCleanSession()) { - persistence.clear(); - } - - if (options.getMqttVersion() == MqttConnectOptions.MQTT_VERSION_DEFAULT) { - options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1); - } - - try { - comms.connect(options, token); - } - catch (MqttException e) { - onFailure(token, e); - } - } - - /** - * Set the MqttCallbackExtened callback to receive connectComplete callbacks - * @param mqttCallbackExtended the {@link MqttCallbackExtended} to be called when the connection completes - */ - public void setMqttCallbackExtended(MqttCallbackExtended mqttCallbackExtended) { - this.mqttCallbackExtended = mqttCallbackExtended; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java deleted file mode 100644 index 39a9059..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.MqttTopic; - -/** - * This interface exists to act as a common type for - * MqttClient and MqttMIDPClient so they can be passed to - * ClientComms without either client class need to know - * about the other. - * Specifically, this allows the MIDP client to work - * without the non-MIDP MqttClient/MqttConnectOptions - * classes being present. - */ -public interface DestinationProvider { - public MqttTopic getTopic(String topic); -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/DisconnectedMessageBuffer.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/DisconnectedMessageBuffer.java deleted file mode 100644 index 14425e4..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/DisconnectedMessageBuffer.java +++ /dev/null @@ -1,128 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Initial Contribution for Automatic Reconnect & Offline Buffering - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.util.ArrayList; - -import org.eclipse.paho.client.mqttv3.BufferedMessage; -import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -public class DisconnectedMessageBuffer implements Runnable { - - private static final String CLASS_NAME = "DisconnectedMessageBuffer"; - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - private DisconnectedBufferOptions bufferOpts; - private ArrayList buffer; - private Object bufLock = new Object(); // Used to synchronise the buffer - private IDisconnectedBufferCallback callback; - - public DisconnectedMessageBuffer(DisconnectedBufferOptions options){ - this.bufferOpts = options; - buffer = new ArrayList(); - } - - /** - * This will add a new message to the offline buffer, - * if the buffer is full and deleteOldestMessages is enabled - * then the 0th item in the buffer will be deleted and the - * new message will be added. If it is not enabled then an - * MqttException will be thrown. - * @param message the {@link MqttWireMessage} that will be buffered - * @param token the associated {@link MqttToken} - * @throws MqttException if the Buffer is full - */ - public void putMessage(MqttWireMessage message, MqttToken token) throws MqttException{ - BufferedMessage bufferedMessage = new BufferedMessage(message, token); - synchronized (bufLock) { - if(buffer.size() < bufferOpts.getBufferSize()){ - buffer.add(bufferedMessage); - } else if(bufferOpts.isDeleteOldestMessages() == true){ - buffer.remove(0); - buffer.add(bufferedMessage); - }else { - throw new MqttException(MqttException.REASON_CODE_DISCONNECTED_BUFFER_FULL); - } - } - } - - /** - * Retrieves a message from the buffer at the given index. - * @param messageIndex the index of the message to be retrieved in the buffer - * @return the {@link BufferedMessage} - */ - public BufferedMessage getMessage(int messageIndex){ - synchronized (bufLock) { - return((BufferedMessage) buffer.get(messageIndex)); - } - } - - - /** - * Removes a message from the buffer - * @param messageIndex the index of the message to be deleted in the buffer - */ - public void deleteMessage(int messageIndex){ - synchronized (bufLock) { - buffer.remove(messageIndex); - } - } - - /** - * Returns the number of messages currently in the buffer - * @return The count of messages in the buffer - */ - public int getMessageCount() { - synchronized (bufLock) { - return buffer.size(); - } - } - - /** - * Flushes the buffer of messages into an open connection - */ - public void run() { - final String methodName = "run"; - // @TRACE 516=Restoring all buffered messages. - log.fine(CLASS_NAME, methodName, "516"); - while(getMessageCount() > 0){ - try { - BufferedMessage bufferedMessage = getMessage(0); - callback.publishBufferedMessage(bufferedMessage); - // Publish was successful, remove message from buffer. - deleteMessage(0); - } catch (MqttException ex) { - // Error occurred attempting to publish buffered message likely because the client is not connected - // @TRACE 517=Error occured attempting to publish buffered message due to disconnect. - log.warning(CLASS_NAME, methodName, "517"); - break; - } - } - } - - public void setPublishCallback(IDisconnectedBufferCallback callback) { - this.callback = callback; - } - - public boolean isPersistBuffer(){ - return bufferOpts.isPersistBuffer(); - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java deleted file mode 100644 index 02b4c41..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttSecurityException; - -/** - * Utility class to help create exceptions of the correct type. - */ -public class ExceptionHelper { - public static MqttException createMqttException(int reasonCode) { - if ((reasonCode == MqttException.REASON_CODE_FAILED_AUTHENTICATION) || - (reasonCode == MqttException.REASON_CODE_NOT_AUTHORIZED)) { - return new MqttSecurityException(reasonCode); - } - - return new MqttException(reasonCode); - } - - public static MqttException createMqttException(Throwable cause) { - if (cause.getClass().getName().equals("java.security.GeneralSecurityException")) { - return new MqttSecurityException(cause); - } - return new MqttException(cause); - } - - /** - * Returns whether or not the specified class is available to the current - * class loader. This is used to protect the code against using Java SE - * APIs on Java ME. - * @param className The name of the class - * @return If true, the class is available - */ - public static boolean isClassAvailable(String className) { - boolean result = false; - try { - Class.forName(className); - result = true; - } - catch (ClassNotFoundException ex) { - } - return result; - } - - // Utility classes should not have a public or default constructor. - private ExceptionHelper() { - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/FileLock.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/FileLock.java deleted file mode 100644 index ebad8bb..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/FileLock.java +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; -/** - * FileLock - used to obtain a lock that can be used to prevent other MQTT clients - * using the same persistent store. If the lock is already held then an exception - * is thrown. - * - * Some Java runtimes such as JME MIDP do not support file locking or even - * the Java classes that support locking. The class is coded to both compile - * and work on all Java runtimes. In Java runtimes that do not support - * locking it will look as though a lock has been obtained but in reality - * no lock has been obtained. - */ -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.lang.reflect.Method; - -public class FileLock { - private File lockFile; - private RandomAccessFile file; - private Object fileLock; - - /** - * Creates an NIO FileLock on the specified file if on a suitable Java runtime. - * @param clientDir the a File of the directory to contain the lock file. - * @param lockFilename name of the the file to lock - * @throws Exception if the lock could not be obtained for any reason - */ - public FileLock(File clientDir, String lockFilename) throws Exception { - // Create a file to obtain a lock on. - lockFile = new File(clientDir,lockFilename); - if (ExceptionHelper.isClassAvailable("java.nio.channels.FileLock")) { - try { - this.file = new RandomAccessFile(lockFile,"rw"); - Method m = file.getClass().getMethod("getChannel",new Class[]{}); - Object channel = m.invoke(file,new Object[]{}); - m = channel.getClass().getMethod("tryLock",new Class[]{}); - this.fileLock = m.invoke(channel, new Object[]{}); - } catch(NoSuchMethodException nsme) { - this.fileLock = null; - } catch(IllegalArgumentException iae) { - this.fileLock = null; - } catch(IllegalAccessException iae) { - this.fileLock = null; - } - if (fileLock == null) { - // Lock not obtained - release(); - throw new Exception("Problem obtaining file lock"); - } - } - } - - /** - * Releases the lock. - */ - public void release() { - try { - if (fileLock != null) { - Method m = fileLock.getClass().getMethod("release",new Class[]{}); - m.invoke(fileLock, new Object[]{}); - fileLock = null; - } - } catch (Exception e) { - // Ignore exceptions - } - if (file != null) { - try { - file.close(); - } catch (IOException e) { - } - file = null; - } - - if (lockFile != null && lockFile.exists()) { - lockFile.delete(); - } - lockFile = null; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/IDisconnectedBufferCallback.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/IDisconnectedBufferCallback.java deleted file mode 100644 index f38df43..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/IDisconnectedBufferCallback.java +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Initial Contribution for Automatic Reconnect & Offline Buffering - */ -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.BufferedMessage; -import org.eclipse.paho.client.mqttv3.MqttException; - -public interface IDisconnectedBufferCallback { - - public void publishBufferedMessage(BufferedMessage bufferedMessage) throws MqttException; - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java deleted file mode 100644 index a0df46d..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -/** - * Catalog of human readable error messages. - */ -public abstract class MessageCatalog { - private static MessageCatalog INSTANCE = null; - - public static final String getMessage(int id) { - if (INSTANCE == null) { - if (ExceptionHelper.isClassAvailable("java.util.ResourceBundle")) { - try { - // Hide this class reference behind reflection so that the class does not need to - // be present when compiled on midp - INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.ResourceBundleCatalog").newInstance(); - } catch (Exception e) { - return ""; - } - } else if (ExceptionHelper.isClassAvailable("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog")){ - try { - INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog").newInstance(); - } catch (Exception e) { - return ""; - } - } - } - return INSTANCE.getLocalizedMessage(id); - } - - protected abstract String getLocalizedMessage(int id); -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java deleted file mode 100644 index 8ab04c1..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.MqttPersistable; - -public class MqttPersistentData implements MqttPersistable { - // Message key - private String key = null; - - // Message header - private byte[] header = null; - private int hOffset = 0; - private int hLength = 0; - - // Message payload - private byte[] payload = null; - private int pOffset = 0; - private int pLength = 0; - - /** - * Construct a data object to pass across the MQTT client persistence interface. - * - * When this Object is passed to the persistence implementation the key is - * used by the client to identify the persisted data to which further - * update or deletion requests are targeted.
- * When this Object is created for returning to the client when it is - * recovering its state from persistence the key is not required to be set. - * The client can determine the key from the data. - * @param key The key which identifies this data - * @param header The message header - * @param hOffset The start offset of the header bytes in header. - * @param hLength The length of the header in the header bytes array. - * @param payload The message payload - * @param pOffset The start offset of the payload bytes in payload. - * @param pLength The length of the payload in the payload bytes array - * when persisting the message. - */ - public MqttPersistentData( String key, - byte[] header, - int hOffset, - int hLength, - byte[] payload, - int pOffset, - int pLength) { - this.key = key; - this.header = header; - this.hOffset = hOffset; - this.hLength = hLength; - this.payload = payload; - this.pOffset = pOffset; - this.pLength = pLength; - } - - public String getKey() { - return key; - } - - public byte[] getHeaderBytes() { - return header; - } - - public int getHeaderLength() { - return hLength; - } - - public int getHeaderOffset() { - return hOffset; - } - - public byte[] getPayloadBytes() { - return payload; - } - - public int getPayloadLength() { - if ( payload == null ) { - return 0; - } - return pLength; - } - - public int getPayloadOffset() { - return pOffset; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java deleted file mode 100644 index 33ba774..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import org.eclipse.paho.client.mqttv3.MqttException; - - -public interface NetworkModule { - public void start() throws IOException, MqttException; - - public InputStream getInputStream() throws IOException; - - public OutputStream getOutputStream() throws IOException; - - public void stop() throws IOException; - - public String getServerURI(); -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java deleted file mode 100644 index 64160ca..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -public class ResourceBundleCatalog extends MessageCatalog { - - private ResourceBundle bundle; - - public ResourceBundleCatalog() { - //MAY throws MissingResourceException - bundle = ResourceBundle.getBundle("org.eclipse.paho.client.mqttv3.internal.nls.messages"); - } - - protected String getLocalizedMessage(int id) { - try { - return bundle.getString(Integer.toString(id)); - } catch(MissingResourceException mre) { - return "MqttException"; - } - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java deleted file mode 100644 index a20f3c6..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java +++ /dev/null @@ -1,120 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * A network module for connecting over SSL. - */ -public class SSLNetworkModule extends TCPNetworkModule { - private static final String CLASS_NAME = SSLNetworkModule.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME); - - private String[] enabledCiphers; - private int handshakeTimeoutSecs; - private HostnameVerifier hostnameVerifier; - - private String host; - private int port; - /** - * Constructs a new SSLNetworkModule using the specified host and - * port. The supplied SSLSocketFactory is used to supply the network - * socket. - * @param factory the {@link SSLSocketFactory} to be used in this SSLNetworkModule - * @param host the Hostname of the Server - * @param port the Port of the Server - * @param resourceContext Resource Context - */ - public SSLNetworkModule(SSLSocketFactory factory, String host, int port, String resourceContext) { - super(factory, host, port, resourceContext); - this.host = host; - this.port = port; - log.setResourceName(resourceContext); - } - - /** - * Returns the enabled cipher suites. - * @return a string array of enabled Cipher suites - */ - public String[] getEnabledCiphers() { - return enabledCiphers; - } - - /** - * Sets the enabled cipher suites on the underlying network socket. - * @param enabledCiphers a String array of cipher suites to enable - */ - public void setEnabledCiphers(String[] enabledCiphers) { - final String methodName = "setEnabledCiphers"; - this.enabledCiphers = enabledCiphers; - if ((socket != null) && (enabledCiphers != null)) { - if (log.isLoggable(Logger.FINE)) { - String ciphers = ""; - for (int i=0;i0) { - ciphers+=","; - } - ciphers+=enabledCiphers[i]; - } - //@TRACE 260=setEnabledCiphers ciphers={0} - log.fine(CLASS_NAME,methodName,"260",new Object[]{ciphers}); - } - ((SSLSocket) socket).setEnabledCipherSuites(enabledCiphers); - } - } - - public void setSSLhandshakeTimeout(int timeout) { - super.setConnectTimeout(timeout); - this.handshakeTimeoutSecs = timeout; - } - - public HostnameVerifier getSSLHostnameVerifier() { - return hostnameVerifier; - } - - public void setSSLHostnameVerifier(HostnameVerifier hostnameVerifier) { - this.hostnameVerifier = hostnameVerifier; - } - - public void start() throws IOException, MqttException { - super.start(); - setEnabledCiphers(enabledCiphers); - int soTimeout = socket.getSoTimeout(); - // RTC 765: Set a timeout to avoid the SSL handshake being blocked indefinitely - socket.setSoTimeout(this.handshakeTimeoutSecs*1000); - ((SSLSocket)socket).startHandshake(); - if (hostnameVerifier != null) { - SSLSession session = ((SSLSocket)socket).getSession(); - hostnameVerifier.verify(host, session); - } - // reset timeout to default value - socket.setSoTimeout(soTimeout); - } - - public String getServerURI() { - return "ssl://" + host + ":" + port; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java deleted file mode 100644 index 51fb346..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ConnectException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketAddress; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLSocketFactory; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * A network module for connecting over TCP. - */ -public class TCPNetworkModule implements NetworkModule { - private static final String CLASS_NAME = TCPNetworkModule.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME); - - protected Socket socket; - private SocketFactory factory; - private String host; - private int port; - private int conTimeout; - - /** - * Constructs a new TCPNetworkModule using the specified host and - * port. The supplied SocketFactory is used to supply the network - * socket. - * @param factory the {@link SocketFactory} to be used to set up this connection - * @param host The server hostname - * @param port The server port - * @param resourceContext The Resource Context - */ - public TCPNetworkModule(SocketFactory factory, String host, int port, String resourceContext) { - log.setResourceName(resourceContext); - this.factory = factory; - this.host = host; - this.port = port; - - } - - /** - * Starts the module, by creating a TCP socket to the server. - * @throws IOException if there is an error creating the socket - * @throws MqttException if there is an error connecting to the server - */ - public void start() throws IOException, MqttException { - final String methodName = "start"; - try { -// InetAddress localAddr = InetAddress.getLocalHost(); -// socket = factory.createSocket(host, port, localAddr, 0); - // @TRACE 252=connect to host {0} port {1} timeout {2} - log.fine(CLASS_NAME,methodName, "252", new Object[] {host, new Integer(port), new Long(conTimeout*1000)}); - SocketAddress sockaddr = new InetSocketAddress(host, port); - if (factory instanceof SSLSocketFactory) { - // SNI support - Socket tempsocket = new Socket(); - tempsocket.connect(sockaddr, conTimeout*1000); - socket = ((SSLSocketFactory)factory).createSocket(tempsocket, host, port, true); - } else { - socket = factory.createSocket(); - socket.connect(sockaddr, conTimeout*1000); - } - - // SetTcpNoDelay was originally set ot true disabling Nagle's algorithm. - // This should not be required. -// socket.setTcpNoDelay(true); // TCP_NODELAY on, which means we do not use Nagle's algorithm - } - catch (ConnectException ex) { - //@TRACE 250=Failed to create TCP socket - log.fine(CLASS_NAME,methodName,"250",null,ex); - throw new MqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR, ex); - } - } - - public InputStream getInputStream() throws IOException { - return socket.getInputStream(); - } - - public OutputStream getOutputStream() throws IOException { - return socket.getOutputStream(); - } - - /** - * Stops the module, by closing the TCP socket. - * @throws IOException if there is an error closing the socket - */ - public void stop() throws IOException { - if (socket != null) { - // CDA: an attempt is made to stop the receiver cleanly before closing the socket. - // If the socket is forcibly closed too early, the blocking socket read in - // the receiver thread throws a SocketException. - // While this causes the receiver thread to exit, it also invalidates the - // SSL session preventing to perform an accelerated SSL handshake in the - // next connection. - // - // Also note that due to the blocking socket reads in the receiver thread, - // it's not possible to interrupt the thread. Using non blocking reads in - // combination with a socket timeout (see setSoTimeout()) would be a better approach. - // - // Please note that the Javadoc only says that an EOF is returned on - // subsequent reads of the socket stream. - // Anyway, at least with Oracle Java SE 7 on Linux systems, this causes a blocked read - // to return EOF immediately. - // This workaround should not cause any harm in general but you might - // want to move it in SSLNetworkModule. - - socket.shutdownInput(); - socket.close(); - } - } - - /** - * Set the maximum time to wait for a socket to be established - * @param timeout The connection timeout - */ - public void setConnectTimeout(int timeout) { - this.conTimeout = timeout; - } - - public String getServerURI() { - return "tcp://" + host + ":" + port; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/Token.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/Token.java deleted file mode 100644 index b0c7a7a..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/Token.java +++ /dev/null @@ -1,394 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - MQTT 3.1.1 support - */ - -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttSuback; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -public class Token { - private static final String CLASS_NAME = Token.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME); - - private volatile boolean completed = false; - private boolean pendingComplete = false; - private boolean sent = false; - - private Object responseLock = new Object(); - private Object sentLock = new Object(); - - protected MqttMessage message = null; - private MqttWireMessage response = null; - private MqttException exception = null; - private String[] topics = null; - - private String key; - - private IMqttAsyncClient client = null; - private IMqttActionListener callback = null; - - private Object userContext = null; - - private int messageID = 0; - private boolean notified = false; - - public Token(String logContext) { - log.setResourceName(logContext); - } - - public int getMessageID() { - return messageID; - } - - public void setMessageID(int messageID) { - this.messageID = messageID; - } - - public boolean checkResult() throws MqttException { - if ( getException() != null) { - throw getException(); - } - return true; - } - - public MqttException getException() { - return exception; - } - - public boolean isComplete() { - return completed; - } - - protected boolean isCompletePending() { - return pendingComplete; - } - - protected boolean isInUse() { - return (getClient() != null && !isComplete()); - } - - public void setActionCallback(IMqttActionListener listener) { - this.callback = listener; - - } - public IMqttActionListener getActionCallback() { - return callback; - } - - public void waitForCompletion() throws MqttException { - waitForCompletion(-1); - } - - public void waitForCompletion(long timeout) throws MqttException { - final String methodName = "waitForCompletion"; - //@TRACE 407=key={0} wait max={1} token={2} - log.fine(CLASS_NAME,methodName, "407",new Object[]{getKey(), new Long(timeout), this}); - - MqttWireMessage resp = waitForResponse(timeout); - if (resp == null && !completed) { - //@TRACE 406=key={0} timed out token={1} - log.fine(CLASS_NAME,methodName, "406",new Object[]{getKey(), this}); - exception = new MqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT); - throw exception; - } - checkResult(); - } - - /** - * Waits for the message delivery to complete, but doesn't throw an exception - * in the case of a NACK. It does still throw an exception if something else - * goes wrong (e.g. an IOException). This is used for packets like CONNECT, - * which have useful information in the ACK that needs to be accessed. - * @return the {@link MqttWireMessage} - * @throws MqttException if there is an error whilst waiting for the response - */ - protected MqttWireMessage waitForResponse() throws MqttException { - return waitForResponse(-1); - } - - protected MqttWireMessage waitForResponse(long timeout) throws MqttException { - final String methodName = "waitForResponse"; - synchronized (responseLock) { - //@TRACE 400=>key={0} timeout={1} sent={2} completed={3} hasException={4} response={5} token={6} - log.fine(CLASS_NAME, methodName, "400",new Object[]{getKey(), new Long(timeout),new Boolean(sent),new Boolean(completed),(exception==null)?"false":"true",response,this},exception); - - while (!this.completed) { - if (this.exception == null) { - try { - //@TRACE 408=key={0} wait max={1} - log.fine(CLASS_NAME,methodName,"408",new Object[] {getKey(),new Long(timeout)}); - - if (timeout <= 0) { - responseLock.wait(); - } else { - responseLock.wait(timeout); - } - } catch (InterruptedException e) { - exception = new MqttException(e); - } - } - if (!this.completed) { - if (this.exception != null) { - //@TRACE 401=failed with exception - log.fine(CLASS_NAME,methodName,"401",null,exception); - throw exception; - } - - if (timeout > 0) { - // time up and still not completed - break; - } - } - } - } - //@TRACE 402=key={0} response={1} - log.fine(CLASS_NAME,methodName, "402",new Object[]{getKey(), this.response}); - return this.response; - } - - /** - * Mark the token as complete and ready for users to be notified. - * @param msg response message. Optional - there are no response messages for some flows - * @param ex if there was a problem store the exception in the token. - */ - protected void markComplete(MqttWireMessage msg, MqttException ex) { - final String methodName = "markComplete"; - //@TRACE 404=>key={0} response={1} excep={2} - log.fine(CLASS_NAME,methodName,"404",new Object[]{getKey(),msg,ex}); - - synchronized(responseLock) { - // ACK means that everything was OK, so mark the message for garbage collection. - if (msg instanceof MqttAck) { - this.message = null; - } - this.pendingComplete = true; - this.response = msg; - this.exception = ex; - } - } - /** - * Notifies this token that a response message (an ACK or NACK) has been - * received. - */ - protected void notifyComplete() { - final String methodName = "notifyComplete"; - //@TRACE 411=>key={0} response={1} excep={2} - log.fine(CLASS_NAME,methodName,"404",new Object[]{getKey(),this.response, this.exception}); - - synchronized (responseLock) { - // If pending complete is set then normally the token can be marked - // as complete and users notified. An abnormal error may have - // caused the client to shutdown beween pending complete being set - // and notifying the user. In this case - the action must be failed. - if (exception == null && pendingComplete) { - completed = true; - pendingComplete = false; - } else { - pendingComplete = false; - } - - responseLock.notifyAll(); - } - synchronized (sentLock) { - sent=true; - sentLock.notifyAll(); - } - } - -// /** -// * Notifies this token that an exception has occurred. This is only -// * used for things like IOException, and not for MQTT NACKs. -// */ -// protected void notifyException() { -// final String methodName = "notifyException"; -// //@TRACE 405=token={0} excep={1} -// log.fine(CLASS_NAME,methodName, "405",new Object[]{this,this.exception}); -// synchronized (responseLock) { -// responseLock.notifyAll(); -// } -// synchronized (sentLock) { -// sentLock.notifyAll(); -// } -// } - - public void waitUntilSent() throws MqttException { - final String methodName = "waitUntilSent"; - synchronized (sentLock) { - synchronized (responseLock) { - if (this.exception != null) { - throw this.exception; - } - } - while (!sent) { - try { - //@TRACE 409=wait key={0} - log.fine(CLASS_NAME,methodName, "409",new Object[]{getKey()}); - - sentLock.wait(); - } catch (InterruptedException e) { - } - } - - while (!sent) { - if (this.exception == null) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); - } - throw this.exception; - } - } - } - - /** - * Notifies this token that the associated message has been sent - * (i.e. written to the TCP/IP socket). - */ - protected void notifySent() { - final String methodName = "notifySent"; - //@TRACE 403=> key={0} - log.fine(CLASS_NAME, methodName, "403",new Object[]{getKey()}); - synchronized (responseLock) { - this.response = null; - this.completed = false; - } - synchronized (sentLock) { - sent = true; - sentLock.notifyAll(); - } - } - - public IMqttAsyncClient getClient() { - return client; - } - - protected void setClient(IMqttAsyncClient client) { - this.client = client; - } - - public void reset() throws MqttException { - final String methodName = "reset"; - if (isInUse() ) { - // Token is already in use - cannot reset - throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE); - } - //@TRACE 410=> key={0} - log.fine(CLASS_NAME, methodName, "410",new Object[]{getKey()}); - - client = null; - completed = false; - response = null; - sent = false; - exception = null; - userContext = null; - } - - public MqttMessage getMessage() { - return message; - } - - public MqttWireMessage getWireMessage() { - return response; - } - - - public void setMessage(MqttMessage msg) { - this.message = msg; - } - - public String[] getTopics() { - return topics; - } - - public void setTopics(String[] topics) { - this.topics = topics; - } - - public Object getUserContext() { - return userContext; - } - - public void setUserContext(Object userContext) { - this.userContext = userContext; - } - - public void setKey(String key) { - this.key = key; - } - - public String getKey() { - return key; - } - - public void setException(MqttException exception) { - synchronized(responseLock) { - this.exception = exception; - } - } - - public boolean isNotified() { - return notified; - } - - public void setNotified(boolean notified) { - this.notified = notified; - } - - public String toString() { - StringBuffer tok = new StringBuffer(); - tok.append("key=").append(getKey()); - tok.append(" ,topics="); - if (getTopics() != null) { - for (int i=0; i - * The SSLSocketFacotryFactory can be reconfigured at any time. A - * reconfiguration does not affect existing socket factories. - *

- * All properties share the same key space; i.e. the configuration ID is not - * part of the property keys. - *

- * The methods should be called in the following order: - *

    - *
  1. isSupportedOnJVM(): to check whether this class is supported on - * the runtime platform. Not all runtimes support SSL/TLS.
  2. - *
  3. SSLSocketFactoryFactory(): the constructor. Clients - * (in the same JVM) may share an SSLSocketFactoryFactory, or have one each.
  4. - *
  5. initialize(properties, configID): to initialize this object with - * the required SSL properties for a configuration. This may be called multiple - * times, once for each required configuration.It may be called again to change the required SSL - * properties for a particular configuration
  6. - *
  7. getEnabledCipherSuites(configID): to later set the enabled - * cipher suites on the socket [see below].
  8. - *
- *
    - *
  • For an MQTT server: - - *
      - *
    1. getKeyStore(configID): Optionally, to check that if there is no - * keystore, then that all the enabled cipher suits are anonymous.
    2. - *
    3. createServerSocketFactory(configID): to create an - * SSLServerSocketFactory.
    4. - *
    5. getClientAuthentication(configID): to later set on the - * SSLServerSocket (itself created from the SSLServerSocketFactory) whether - * client authentication is needed.
    6. - *
    - *
  • - *
  • For an MQTT client: - *
      - *
    1. createSocketFactory(configID): to create an SSLSocketFactory.
    2. - *
    - *
  • - *
- */ -public class SSLSocketFactoryFactory { - private static final String CLASS_NAME = "org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory"; - /** - * Property keys specific to the client). - */ - public static final String SSLPROTOCOL="com.ibm.ssl.protocol"; - public static final String JSSEPROVIDER="com.ibm.ssl.contextProvider"; - public static final String KEYSTORE="com.ibm.ssl.keyStore"; - public static final String KEYSTOREPWD="com.ibm.ssl.keyStorePassword"; - public static final String KEYSTORETYPE="com.ibm.ssl.keyStoreType"; - public static final String KEYSTOREPROVIDER="com.ibm.ssl.keyStoreProvider"; - public static final String KEYSTOREMGR="com.ibm.ssl.keyManager"; - public static final String TRUSTSTORE="com.ibm.ssl.trustStore"; - public static final String TRUSTSTOREPWD="com.ibm.ssl.trustStorePassword"; - public static final String TRUSTSTORETYPE="com.ibm.ssl.trustStoreType"; - public static final String TRUSTSTOREPROVIDER="com.ibm.ssl.trustStoreProvider"; - public static final String TRUSTSTOREMGR="com.ibm.ssl.trustManager"; - public static final String CIPHERSUITES="com.ibm.ssl.enabledCipherSuites"; - public static final String CLIENTAUTH="com.ibm.ssl.clientAuthentication"; - - /** - * Property keys used for java system properties - */ - public static final String SYSKEYSTORE="javax.net.ssl.keyStore"; - public static final String SYSKEYSTORETYPE="javax.net.ssl.keyStoreType"; - public static final String SYSKEYSTOREPWD="javax.net.ssl.keyStorePassword"; - public static final String SYSTRUSTSTORE="javax.net.ssl.trustStore"; - public static final String SYSTRUSTSTORETYPE="javax.net.ssl.trustStoreType"; - public static final String SYSTRUSTSTOREPWD="javax.net.ssl.trustStorePassword"; - public static final String SYSKEYMGRALGO="ssl.KeyManagerFactory.algorithm"; - public static final String SYSTRUSTMGRALGO="ssl.TrustManagerFactory.algorithm"; - - - public static final String DEFAULT_PROTOCOL = "TLS"; // "SSL_TLS" is not supported by DesktopEE - - private static final String propertyKeys[] = { SSLPROTOCOL, JSSEPROVIDER, - KEYSTORE, KEYSTOREPWD, KEYSTORETYPE, KEYSTOREPROVIDER, KEYSTOREMGR, - TRUSTSTORE, TRUSTSTOREPWD, TRUSTSTORETYPE, TRUSTSTOREPROVIDER, - TRUSTSTOREMGR, CIPHERSUITES, CLIENTAUTH}; - - private Hashtable configs; // a hashtable that maps configIDs to properties. - - private Properties defaultProperties; - - private static final byte[] key = { (byte) 0x9d, (byte) 0xa7, (byte) 0xd9, - (byte) 0x80, (byte) 0x05, (byte) 0xb8, (byte) 0x89, (byte) 0x9c }; - - private static final String xorTag = "{xor}"; - - private Logger logger = null; - - - /** - * Not all of the JVM/Platforms support all of its - * security features. This method determines if is supported. - * - * @return whether dependent classes can be instantiated on the current - * JVM/platform. - * - * @throws Error - * if any unexpected error encountered whilst checking. Note - * this should not be a ClassNotFoundException, which should - * cause the method to return false. - */ - public static boolean isSupportedOnJVM() throws LinkageError, ExceptionInInitializerError { - String requiredClassname = "javax.net.ssl.SSLServerSocketFactory"; - try { - Class.forName(requiredClassname); - } catch (ClassNotFoundException e) { - return false; - } - return true; - } - - - /** - * Create new instance of class. - * Constructor used by clients. - */ - public SSLSocketFactoryFactory() { - configs = new Hashtable(); - } - - /** - * Create new instance of class. - * Constructor used by the broker. - * @param logger the {@link Logger} to be used - */ - public SSLSocketFactoryFactory(Logger logger) { - this(); - this.logger = logger; - } - - /** - * Checks whether a key belongs to the supported IBM SSL property keys. - * - * @param key - * @return whether a key belongs to the supported IBM SSL property keys. - */ - private boolean keyValid(String key) { - int i = 0; - while (i < propertyKeys.length) { - if (propertyKeys[i].equals(key)) { - break; - } - ++i; - } - return i < propertyKeys.length; - } - - /** - * Checks whether the property keys belong to the supported IBM SSL property - * key set. - * - * @param properties - * @throws IllegalArgumentException - * if any of the properties is not a valid IBM SSL property key. - */ - private void checkPropertyKeys(Properties properties) - throws IllegalArgumentException { - Set keys = properties.keySet(); - Iterator i = keys.iterator(); - while (i.hasNext()) { - String k = (String) i.next(); - if (!keyValid(k)) { - throw new IllegalArgumentException(k + " is not a valid IBM SSL property key."); - } - } - } - - /** - * Convert byte array to char array, where each char is constructed from two - * bytes. - * - * @param b - * byte array - * @return char array - */ - public static char[] toChar(byte[] b) { - if(b==null) return null; - char[] c= new char[b.length/2]; - int i=0; int j=0; - while(i> 8)& 0xFF); - } - return b; - } - - /** - * Obfuscates the password using a simple and not very secure XOR mechanism. - * This should not be used for cryptographical purpose, it's a simple - * scrambler to obfuscate clear-text passwords. - * - * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#deObfuscate - * - * @param password - * The password to be encrypted, as a char[] array. - * @return An obfuscated password as a String. - */ - public static String obfuscate(char[] password) { - if (password == null) - return null; - byte[] bytes = toByte(password); - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff); - } - String encryptedValue = xorTag - + new String(SimpleBase64Encoder.encode(bytes)); - return encryptedValue; - } - - /** - * The inverse operation of obfuscate: returns a cleartext password that was - * previously obfuscated using the XOR scrambler. - * - * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate - * - * @param ePassword - * An obfuscated password. - * @return An array of char, containing the clear text password. - */ - public static char[] deObfuscate(String ePassword) { - if (ePassword == null) - return null; - byte[] bytes = null; - try { - bytes = SimpleBase64Encoder.decode(ePassword.substring(xorTag - .length())); - } catch (Exception e) { - return null; - } - - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff); - } - return toChar(bytes); - } - - /** - * Converts an array of ciphers into a single String. - * - * @param ciphers - * The array of cipher names. - * @return A string containing the name of the ciphers, separated by comma. - */ - public static String packCipherSuites(String[] ciphers) { - String cipherSet=null; - if (ciphers != null) { - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < ciphers.length; i++) { - buf.append(ciphers[i]); - if (i < ciphers.length - 1) { - buf.append(','); - } - } - cipherSet = buf.toString(); - } - return cipherSet; - } - - /** - * Inverse operation of packCipherSuites: converts a string of cipher names - * into an array of cipher names - * - * @param ciphers - * A list of ciphers, separated by comma. - * @return An array of string, each string containing a single cipher name. - */ - public static String[] unpackCipherSuites(String ciphers) { - // can't use split as split is not available on all java platforms. - if(ciphers==null) return null; - Vector c=new Vector(); - int i=ciphers.indexOf(','); - int j=0; - // handle all commas. - while(i>-1) { - // add stuff before and up to (but not including) the comma. - c.add(ciphers.substring(j, i)); - j=i+1; // skip the comma. - i=ciphers.indexOf(',',j); - } - // add last element after the comma or only element if no comma is present. - c.add(ciphers.substring(j)); - String[] s = new String[c.size()]; - c.toArray(s); - return s; - } - - /** - * Obfuscate any key & trust store passwords within the given properties. - * - * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate - * - * @param p - * properties - */ - private void convertPassword(Properties p) { - String pw = p.getProperty(KEYSTOREPWD); - if (pw != null && !pw.startsWith(xorTag)) { - String epw = obfuscate(pw.toCharArray()); - p.put(KEYSTOREPWD, epw); - } - pw = p.getProperty(TRUSTSTOREPWD); - if (pw != null && !pw.startsWith(xorTag)) { - String epw = obfuscate(pw.toCharArray()); - p.put(TRUSTSTOREPWD, epw); - } - } - - /** - * Returns the properties object for configuration configID or creates a new - * one if required. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @return the properties object for configuration configID - */ -// private Properties getOrCreate(String configID) { -// Properties res = null; -// if (configID == null) { -// if (this.defaultProperties == null) { -// this.defaultProperties = new Properties(); -// } -// res = this.defaultProperties; -// } else { -// res = (Properties) this.configs.get(configID); -// if (res == null) { -// res = new Properties(); -// this.configs.put(configID, res); -// } -// } -// return res; -// } - - /** - * Initializes the SSLSocketFactoryFactory with the provided properties for - * the provided configuration. - * - * @param props - * A properties object containing IBM SSL properties that are - * qualified by one or more configuration identifiers. - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @throws IllegalArgumentException - * if any of the properties is not a valid IBM SSL property key. - */ - public void initialize(Properties props, String configID) - throws IllegalArgumentException { - checkPropertyKeys(props); - // copy the properties. - Properties p = new Properties(); - p.putAll(props); - convertPassword(p); - if (configID != null) { - this.configs.put(configID, p); - } else { - this.defaultProperties = p; - } - } - - /** - * Merges the given IBM SSL properties into the existing configuration, - * overwriting existing properties. This method is used to selectively - * change properties for a given configuration. The method throws an - * IllegalArgumentException if any of the properties is not a valid IBM SSL - * property key. - * - * @param props - * A properties object containing IBM SSL properties - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @throws IllegalArgumentException - * if any of the properties is not a valid IBM SSL property key. - */ - public void merge(Properties props, String configID) - throws IllegalArgumentException { - checkPropertyKeys(props); - Properties p = this.defaultProperties; - if (configID != null) { - p = (Properties) this.configs.get(configID); - } - if (p == null) { - p = new Properties(); - } - convertPassword(props); - p.putAll(props); - if (configID != null) { - this.configs.put(configID, p); - } else { - this.defaultProperties = p; - } - - } - - /** - * Remove the configuration of a given configuration identifier. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @return true, if the configuation could be removed. - */ - public boolean remove(String configID) { - boolean res = false; - if (configID != null) { - res = this.configs.remove(configID) != null; - } else { - if(null != this.defaultProperties) { - res = true; - this.defaultProperties = null; - } - } - return res; - } - - /** - * Returns the configuration of the SSLSocketFactoryFactory for a given - * configuration. Note that changes in the property are reflected in the - * SSLSocketFactoryFactory. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @return A property object containing the current configuration of the - * SSLSocketFactoryFactory. Note that it could be null. - */ - public Properties getConfiguration(String configID) { - return (Properties) (configID == null ? this.defaultProperties - : this.configs.get(configID)); - } - - /** - * @return Returns the set of configuration IDs that exist in the SSLSocketFactoryFactory. - */ -// public String[] getConfigurationIDs() { -// Set s = this.configs.keySet(); -// String[] configs = new String[s.size()]; -// configs = (String[]) s.toArray(configs); -// return configs; -// } - - /** - * If the value is not null, then put it in the properties object using the - * key. If the value is null, then remove the entry in the properties object - * with the key. - * - * @param p - * @param key - * @param value - */ -// private final void putOrRemove(Properties p, String key, String value) { -// if (value == null) { -// p.remove(key); -// } else { -// p.put(key, value); -// } -// } - - /** - * Sets the SSL protocol variant. If protocol is NULL then an existing value - * will be removed. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @param protocol - * One of SSL, SSLv3, TLS, TLSv1, SSL_TLS - */ -// public void setSSLProtocol(String configID, String protocol) { -// Properties p = getOrCreate(configID); -// putOrRemove(p, SSLPROTOCOL, protocol); -// } - - /** - * Sets the JSSE context provider. If provider is null, then an existing - * value will be removed. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @param provider - * The JSSE provider. For example "IBMJSSE2" or "SunJSSE". - */ -// public void setJSSEProvider(String configID, String provider) { -// Properties p = getOrCreate(configID); -// putOrRemove(p, JSSEPROVIDER, provider); -// } - - /** - * Sets the filename of the keyStore object. A null value is ignored. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @param keyStore - * A filename that points to a valid keystore. - */ -// public void setKeyStore(String configID, String keyStore) { -// if (keyStore == null) -// return; -// Properties p = getOrCreate(configID); -// putOrRemove(p, KEYSTORE, keyStore); -// } - - /** - * Sets the password that is used for the keystore. The password must be - * provided in plain text, but it will be stored internally in a scrambled - * XOR format. - * - * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @param password - * The keystore password - */ -// public void setKeyStorePassword(String configID, char[] password) { -// if (password == null) -// return; -// Properties p = getOrCreate(configID); -// // convert password, using XOR-based scrambling. -// String ePasswd = obfuscate(password); -// for(int i=0;i=3){ - encoded.append(to64((((bytes[i] & 0xff) << 16) - | (int) ((bytes[i+1] & 0xff) << 8) | (int) (bytes[i+2] & 0xff)),4)); - i+=3; - j-=3; - } - // j==2 | j==1 | j==0 - if(j==2) { - // there is a rest of 2 bytes. This encodes into 3 chars. - encoded.append(to64(((bytes[i] &0xff)<<8) | ((bytes[i+1] & 0xff)),3)); - } - if(j==1) { - // there is a rest of 1 byte. This encodes into 1 char. - encoded.append(to64(((bytes[i] & 0xff)),2)); - } - return encoded.toString(); - } - - public static byte[] decode(String string) { - byte[] encoded=string.getBytes(); - int len=encoded.length; - byte[] decoded=new byte[len*3/4]; - int i=0; - int j=len; - int k=0; - while(j>=4) { - long d=from64(encoded, i, 4); - j-=4; - i+=4; - for(int l=2;l>=0;l--) { - decoded[k+l]=(byte) (d & 0xff); - d=d >>8; - } - k+=3; - } - // j==3 | j==2 - if(j==3) { - long d=from64(encoded, i, 3); - for(int l=1;l>=0;l--) { - decoded[k+l]=(byte) (d & 0xff); - d=d >>8; - } - } - if(j==2) { - long d=from64(encoded, i, 2); - decoded[k]=(byte) (d & 0xff); - } - return decoded; - } - - /* the core conding routine. Translates an input integer into - * a string of the given length.*/ - private final static String to64(long input, int size) { - final StringBuffer result = new StringBuffer(size); - while (size > 0) { - size--; - result.append(PWDCHARS_ARRAY[((int) (input & 0x3f))]); - input = input >> 6; - } - return result.toString(); - } - - /* - * The reverse operation of to64 - */ - private final static long from64(byte[] encoded, int idx, int size) { - long res=0; - int f=0; - while(size>0) { - size--; - long r=0; - // convert encoded[idx] back into a 6-bit value. - byte d=encoded[idx++]; - if(d=='/') { - r=1; - } - if(d>='0' && d<='9') { - r=2+d-'0'; - } - if(d>='A' && d<='Z') { - r=12+d-'A'; - } - if(d>='a' && d<='z') { - r=38+d-'a'; - } - res=res+((long)r << f); - f+=6; - } - return res; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/Base64.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/Base64.java deleted file mode 100644 index 6503b81..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/Base64.java +++ /dev/null @@ -1,97 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Bug 459142 - WebSocket support for the Java client. - */ -package org.eclipse.paho.client.mqttv3.internal.websocket; - -import java.util.prefs.AbstractPreferences; -import java.util.prefs.BackingStoreException; - -public class Base64 { - - private static final Base64 instance = new Base64(); - private static final Base64Encoder encoder = instance.new Base64Encoder(); - - public static String encode (String s){ - encoder.putByteArray("akey", s.getBytes()); - String result = encoder.getBase64String(); - return result; - } - - public static String encodeBytes (byte[] b){ - encoder.putByteArray("aKey", b); - String result = encoder.getBase64String(); - return result; - - } - - public class Base64Encoder extends AbstractPreferences { - - private String base64String = null; - - public Base64Encoder() { - super(null, ""); - } - - - protected void putSpi(String key, String value) { - base64String = value; - } - - public String getBase64String() { - return base64String; - } - - - protected String getSpi(String key) { - return null; - } - - - protected void removeSpi(String key) { - } - - - protected void removeNodeSpi() throws BackingStoreException { - - } - - - protected String[] keysSpi() throws BackingStoreException { - return null; - } - - - protected String[] childrenNamesSpi() throws BackingStoreException { - return null; - } - - - protected AbstractPreferences childSpi(String name) { - return null; - } - - - protected void syncSpi() throws BackingStoreException { - - } - - - protected void flushSpi() throws BackingStoreException { - - } - - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java deleted file mode 100644 index 2caee99..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.eclipse.paho.client.mqttv3.internal.websocket; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.ByteBuffer; - -import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule; - -class ExtendedByteArrayOutputStream extends ByteArrayOutputStream { - - final WebSocketNetworkModule webSocketNetworkModule; - final WebSocketSecureNetworkModule webSocketSecureNetworkModule; - - ExtendedByteArrayOutputStream(WebSocketNetworkModule module) { - this.webSocketNetworkModule = module; - this.webSocketSecureNetworkModule = null; - } - - ExtendedByteArrayOutputStream(WebSocketSecureNetworkModule module) { - this.webSocketNetworkModule = null; - this.webSocketSecureNetworkModule = module; - } - - public void flush() throws IOException { - final ByteBuffer byteBuffer; - synchronized (this) { - byteBuffer = ByteBuffer.wrap(toByteArray()); - reset(); - } - WebSocketFrame frame = new WebSocketFrame((byte)0x02, true, byteBuffer.array()); - byte[] rawFrame = frame.encodeFrame(); - getSocketOutputStream().write(rawFrame); - getSocketOutputStream().flush(); - - } - - OutputStream getSocketOutputStream() throws IOException { - - if(webSocketNetworkModule != null ){ - return webSocketNetworkModule.getSocketOutputStream(); - } - if(webSocketSecureNetworkModule != null){ - return webSocketSecureNetworkModule.getSocketOutputStream(); - } - return null; - } - -} \ No newline at end of file diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/HandshakeFailedException.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/HandshakeFailedException.java deleted file mode 100644 index c9f9427..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/HandshakeFailedException.java +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Bug 459142 - WebSocket support for the Java client. - */ -package org.eclipse.paho.client.mqttv3.internal.websocket; - -public class HandshakeFailedException extends Exception { - - private static final long serialVersionUID = 1L; - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketFrame.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketFrame.java deleted file mode 100644 index acc5185..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketFrame.java +++ /dev/null @@ -1,306 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Bug 459142 - WebSocket support for the Java client. - */ -package org.eclipse.paho.client.mqttv3.internal.websocket; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.security.SecureRandom; - -public class WebSocketFrame { - - public static final int frameLengthOverhead = 6; - - private byte opcode; - private boolean fin; - private byte payload[]; - private boolean closeFlag = false; - - public byte getOpcode() { - return opcode; - } - - public boolean isFin() { - return fin; - } - - public byte[] getPayload() { - return payload; - } - - public boolean isCloseFlag() { - return closeFlag; - } - - - /** - * Initialise a new WebSocketFrame - * @param opcode WebSocket Opcode - * @param fin If it's final - * @param payload The payload - */ - public WebSocketFrame(byte opcode, boolean fin, byte[] payload){ - this.opcode = opcode; - this.fin = fin; - this.payload = payload; - } - - - /** - * Initialise WebSocketFrame from raw Data - * @param rawFrame The raw byte buffer - */ - public WebSocketFrame (byte[] rawFrame){ - - ByteBuffer buffer = ByteBuffer.wrap(rawFrame); - - // First Byte: Fin, Reserved, Opcode - byte b = buffer.get(); - setFinAndOpCode(b); - - // Second Byte Masked & Initial Length - b = buffer.get(); - boolean masked = ((b & 0x80) != 0); - int payloadLength = (byte)(0x7F & b); - int byteCount = 0; - if(payloadLength == 0X7F){ - // 8 Byte Extended payload length - byteCount = 8; - } else if (payloadLength == 0X7E){ - // 2 bytes extended payload length - byteCount = 2; - } - - // Decode the extended payload length - while (--byteCount > 0){ - b = buffer.get(); - payloadLength |= (b & 0xFF) << (8 * byteCount); - } - - // Get the Masking key if masked - byte maskingKey[] = null; - if(masked) { - maskingKey = new byte[4]; - buffer.get(maskingKey,0,4); - } - this.payload = new byte[payloadLength]; - buffer.get(this.payload,0,payloadLength); - - // Demask payload if needed - if(masked) - { - for(int i = 0; i < this.payload.length; i++){ - this.payload[i] ^= maskingKey[i % 4]; - } - } - return; - } - - - /** - * Sets the frames Fin flag and opcode. - * @param incomingByte - */ - private void setFinAndOpCode(byte incomingByte){ - this.fin = ((incomingByte & 0x80) !=0); - // Reserved bits, unused right now. - // boolean rsv1 = ((incomingByte & 0x40) != 0); - // boolean rsv2 = ((incomingByte & 0x20) != 0); - // boolean rsv3 = ((incomingByte & 0x10) != 0); - this.opcode = (byte)(incomingByte & 0x0F); - - } - - /** - * Takes an input stream and parses it into a Websocket frame. - * @param input The incoming {@link InputStream} - * @throws IOException if an exception occurs whilst reading the input stream - */ - public WebSocketFrame(InputStream input) throws IOException { - byte firstByte = (byte) input.read(); - setFinAndOpCode(firstByte); - if(this.opcode == 2){ - byte maskLengthByte = (byte) input.read(); - boolean masked = ((maskLengthByte & 0x80) != 0); - int payloadLength = (byte)(0x7F & maskLengthByte); - int byteCount = 0; - if(payloadLength == 0X7F){ - // 8 Byte Extended payload length - byteCount = 8; - } else if (payloadLength == 0X7E){ - // 2 bytes extended payload length - byteCount = 2; - } - - // Decode the payload length - if(byteCount > 0){ - payloadLength = 0; - } - while (--byteCount >= 0){ - maskLengthByte = (byte) input.read(); - payloadLength |= (maskLengthByte & 0xFF) << (8 * byteCount); - } - - // Get the masking key - byte maskingKey[] = null; - if(masked) { - maskingKey = new byte[4]; - input.read(maskingKey,0,4); - } - - this.payload = new byte[payloadLength]; - int offsetIndex = 0; - int tempLength = payloadLength; - int bytesRead = 0; - while (offsetIndex != payloadLength){ - bytesRead = input.read(this.payload,offsetIndex,tempLength); - offsetIndex += bytesRead; - tempLength -= bytesRead; - } - - - // Demask if needed - if(masked) - { - for(int i = 0; i < this.payload.length; i++){ - this.payload[i] ^= maskingKey[i % 4]; - } - } - return; - } else if(this.opcode == 8){ - // Closing connection with server - closeFlag = true; - } else { - throw new IOException("Invalid Frame: Opcode: " +this.opcode); - } - - - } - - - /** - * Encodes the this WebSocketFrame into a byte array. - * @return byte array - */ - public byte[] encodeFrame(){ - int length = this.payload.length + frameLengthOverhead; - // Calculating overhead - if(this.payload.length > 65535){ - length += 8; - } else if(this.payload.length >= 126) { - length += 2; - } - - ByteBuffer buffer = ByteBuffer.allocate(length); - appendFinAndOpCode(buffer, this.opcode, this.fin); - byte mask[] = generateMaskingKey(); - appendLengthAndMask(buffer, this.payload.length, mask); - - for(int i = 0; i < this.payload.length; i ++){ - buffer.put((byte)(this.payload[i] ^=mask[i % 4])); - } - - buffer.flip(); - return buffer.array(); - } - - /** - * Appends the Length and Mask to the buffer - * @param buffer the outgoing {@link ByteBuffer} - * @param length the length of the frame - * @param mask The WebSocket Mask - */ - public static void appendLengthAndMask(ByteBuffer buffer, int length, byte mask[]){ - if(mask != null){ - appendLength(buffer, length, true); - buffer.put(mask); - } else { - appendLength(buffer, length, false); - } - } - - - /** - * Appends the Length of the payload to the buffer - * @param buffer - * @param length - * @param b - */ - private static void appendLength(ByteBuffer buffer, int length, boolean masked) { - - if(length < 0){ - throw new IllegalArgumentException("Length cannot be negative"); - } - - byte b = (masked?(byte)0x80:0x00); - if(length > 0xFFFF){ - buffer.put((byte) (b | 0x7F)); - buffer.put((byte)0x00); - buffer.put((byte)0x00); - buffer.put((byte)0x00); - buffer.put((byte)0x00); - buffer.put((byte)((length >> 24) & 0xFF)); - buffer.put((byte)((length >> 16) & 0xFF)); - buffer.put((byte)((length >> 8) & 0xFF)); - buffer.put((byte)(length & 0xFF)); - } else if(length >= 0x7E){ - buffer.put((byte)(b | 0x7E)); - buffer.put((byte)(length >> 8)); - buffer.put((byte)(length & 0xFF)); - } else { - buffer.put((byte)(b | length)); - } - } - - /** - * Appends the Fin flag and the OpCode - * @param buffer The outgoing buffer - * @param opcode The Websocket OpCode - * @param fin if this is a final frame - */ - public static void appendFinAndOpCode(ByteBuffer buffer, byte opcode, boolean fin){ - byte b = 0x00; - // Add Fin flag - if(fin){ - b |= 0x80; - } - //RSV 1,2,3 aren't important - - // Add opcode - b |= opcode & 0x0F; - buffer.put(b); - } - - /** - * Generates a random masking key - * Nothing super secure, but enough - * for websockets. - * @return ByteArray containing the key; - */ - public static byte[] generateMaskingKey(){ - SecureRandom secureRandomGenerator = new SecureRandom(); - int a = secureRandomGenerator.nextInt(255); - int b = secureRandomGenerator.nextInt(255); - int c = secureRandomGenerator.nextInt(255); - int d = secureRandomGenerator.nextInt(255); - return new byte[] {(byte) a,(byte) b,(byte) c,(byte) d}; - } - - - - - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketHandshake.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketHandshake.java deleted file mode 100644 index 92be901..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketHandshake.java +++ /dev/null @@ -1,215 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Bug 459142 - WebSocket support for the Java client. - */ -package org.eclipse.paho.client.mqttv3.internal.websocket; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -/** - * Helper class to execute a WebSocket Handshake. - */ -public class WebSocketHandshake { - - // Do not change: https://tools.ietf.org/html/rfc6455#section-1.3 - private static final String ACCEPT_SALT = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - private static final String SHA1_PROTOCOL = "SHA1"; - private static final String HTTP_HEADER_SEC_WEBSOCKET_ACCEPT = "sec-websocket-accept"; - private static final String HTTP_HEADER_UPGRADE = "upgrade"; - private static final String HTTP_HEADER_UPGRADE_WEBSOCKET = "websocket"; - private static final String EMPTY = ""; - private static final String LINE_SEPARATOR = "\r\n"; - - private static final String HTTP_HEADER_CONNECTION = "connection"; - private static final String HTTP_HEADER_CONNECTION_VALUE = "upgrade"; - private static final String HTTP_HEADER_SEC_WEBSOCKET_PROTOCOL = "sec-websocket-protocol"; - - InputStream input; - OutputStream output; - String uri; - String host; - int port; - - - public WebSocketHandshake(InputStream input, OutputStream output, String uri, String host, int port){ - this.input = input; - this.output = output; - this.uri = uri; - this.host = host; - this.port = port; - } - - - /** - * Executes a Websocket Handshake. - * Will throw an IOException if the handshake fails - * @throws IOException thrown if an exception occurs during the handshake - */ - public void execute() throws IOException { - byte[] key = new byte[16]; - System.arraycopy(UUID.randomUUID().toString().getBytes(), 0, key, 0, 16); - String b64Key = Base64.encodeBytes(key); - sendHandshakeRequest(b64Key); - receiveHandshakeResponse(b64Key); - } - - /** - * Builds and sends the HTTP Header GET Request - * for the socket. - * @param key Base64 encoded key - * @throws IOException - */ - private void sendHandshakeRequest(String key) throws IOException{ - try { - String path = "/mqtt"; - URI srvUri = new URI(uri); - if (srvUri.getRawPath() != null && !srvUri.getRawPath().isEmpty()) { - path = srvUri.getRawPath(); - if (srvUri.getRawQuery() != null && !srvUri.getRawQuery().isEmpty()) { - path += "?" + srvUri.getRawQuery(); - } - } - - PrintWriter pw = new PrintWriter(output); - pw.print("GET " + path + " HTTP/1.1" + LINE_SEPARATOR); - if (port != 80 && port != 443) { - pw.print("Host: " + host + ":" + port + LINE_SEPARATOR); - } - else { - pw.print("Host: " + host + LINE_SEPARATOR); - } - - pw.print("Upgrade: websocket" + LINE_SEPARATOR); - pw.print("Connection: Upgrade" + LINE_SEPARATOR); - pw.print("Sec-WebSocket-Key: " + key + LINE_SEPARATOR); - pw.print("Sec-WebSocket-Protocol: mqtt" + LINE_SEPARATOR); - pw.print("Sec-WebSocket-Version: 13" + LINE_SEPARATOR); - - String userInfo = srvUri.getUserInfo(); - if(userInfo != null) { - pw.print("Authorization: Basic " + Base64.encode(userInfo) + LINE_SEPARATOR); - } - - pw.print(LINE_SEPARATOR); - pw.flush(); - } catch (URISyntaxException e) { - throw new IllegalStateException(e.getMessage()); - } - } - - /** - * Receives the Handshake response and verifies that it is valid. - * @param key Base64 encoded key - * @throws IOException - */ - private void receiveHandshakeResponse(String key) throws IOException { - BufferedReader in = new BufferedReader(new InputStreamReader(input)); - ArrayList responseLines = new ArrayList(); - String line = in.readLine(); - if(line == null){ - throw new IOException("WebSocket Response header: Invalid response from Server, It may not support WebSockets."); - } - while(!line.equals(EMPTY) ) { - responseLines.add(line); - line = in.readLine(); - } - Map headerMap = getHeaders(responseLines); - - String connectionHeader = (String) headerMap.get(HTTP_HEADER_CONNECTION); - if (connectionHeader == null || connectionHeader.equalsIgnoreCase(HTTP_HEADER_CONNECTION_VALUE)) { - throw new IOException("WebSocket Response header: Incorrect connection header"); - } - - String upgradeHeader = (String) headerMap.get(HTTP_HEADER_UPGRADE); - if(upgradeHeader == null || !upgradeHeader.toLowerCase().contains(HTTP_HEADER_UPGRADE_WEBSOCKET)){ - throw new IOException("WebSocket Response header: Incorrect upgrade."); - } - - String secWebsocketProtocolHeader = (String) headerMap.get(HTTP_HEADER_SEC_WEBSOCKET_PROTOCOL); - if (secWebsocketProtocolHeader == null) { - throw new IOException("WebSocket Response header: empty sec-websocket-protocol"); - } - - if(!headerMap.containsKey(HTTP_HEADER_SEC_WEBSOCKET_ACCEPT)){ - throw new IOException("WebSocket Response header: Missing Sec-WebSocket-Accept"); - } - - try { - verifyWebSocketKey(key, (String)headerMap.get(HTTP_HEADER_SEC_WEBSOCKET_ACCEPT)); - } catch (NoSuchAlgorithmException e) { - throw new IOException(e.getMessage()); - } catch (HandshakeFailedException e) { - throw new IOException("WebSocket Response header: Incorrect Sec-WebSocket-Key"); - } - - } - - /** - * Returns a Hashmap of HTTP headers - * @param ArrayList of headers - * @return A Hashmap of the headers - */ - private Map getHeaders(ArrayList headers){ - Map headerMap = new HashMap(); - for(int i = 1; i < headers.size(); i++){ - String headerPre = (String) headers.get(i); - String[] header = headerPre.split(":"); - headerMap.put(header[0].toLowerCase(), header[1]); - } - return headerMap; - } - - /** - * Verifies that the Accept key provided is correctly built from the - * original key sent. - * @param key - * @param accept - * @throws NoSuchAlgorithmException - * @throws HandshakeFailedException - */ - private void verifyWebSocketKey(String key, String accept) throws NoSuchAlgorithmException, HandshakeFailedException{ - // We build up the accept in the same way the server should - // then we check that the response is the same. - byte[] sha1Bytes = sha1(key + ACCEPT_SALT); - String encodedSha1Bytes = Base64.encodeBytes(sha1Bytes).trim(); - if(!encodedSha1Bytes.equals(accept.trim())){ - throw new HandshakeFailedException(); - } - } - - /** - * Returns the sha1 byte array of the provided string. - * @param input - * @return - * @throws NoSuchAlgorithmException - */ - private byte[] sha1(String input) throws NoSuchAlgorithmException { - MessageDigest mDigest = MessageDigest.getInstance(SHA1_PROTOCOL); - byte[] result = mDigest.digest(input.getBytes()); - return result; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketNetworkModule.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketNetworkModule.java deleted file mode 100644 index 5524a31..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketNetworkModule.java +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Bug 459142 - WebSocket support for the Java client. - */ -package org.eclipse.paho.client.mqttv3.internal.websocket; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.nio.ByteBuffer; - -import javax.net.SocketFactory; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -public class WebSocketNetworkModule extends TCPNetworkModule { - - private static final String CLASS_NAME = WebSocketNetworkModule.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - private String uri; - private String host; - private int port; - private PipedInputStream pipedInputStream; - private WebSocketReceiver webSocketReceiver; - ByteBuffer recievedPayload; - - /** - * Overrides the flush method. - * This allows us to encode the MQTT payload into a WebSocket - * Frame before passing it through to the real socket. - */ - private ByteArrayOutputStream outputStream = new ExtendedByteArrayOutputStream(this); - - public WebSocketNetworkModule(SocketFactory factory, String uri, String host, int port, String resourceContext){ - super(factory, host, port, resourceContext); - this.uri = uri; - this.host = host; - this.port = port; - this.pipedInputStream = new PipedInputStream(); - - log.setResourceName(resourceContext); - } - - public void start() throws IOException, MqttException { - super.start(); - WebSocketHandshake handshake = new WebSocketHandshake(getSocketInputStream(), getSocketOutputStream(), uri, host, port); - handshake.execute(); - this.webSocketReceiver = new WebSocketReceiver(getSocketInputStream(), pipedInputStream); - webSocketReceiver.start("webSocketReceiver"); - } - - OutputStream getSocketOutputStream() throws IOException { - return super.getOutputStream(); - } - - InputStream getSocketInputStream() throws IOException { - return super.getInputStream(); - } - - public InputStream getInputStream() throws IOException { - return pipedInputStream; - } - - public OutputStream getOutputStream() throws IOException { - return outputStream; - } - - /** - * Stops the module, by closing the TCP socket. - */ - public void stop() throws IOException { - // Creating Close Frame - WebSocketFrame frame = new WebSocketFrame((byte)0x08, true, "1000".getBytes()); - byte[] rawFrame = frame.encodeFrame(); - getSocketOutputStream().write(rawFrame); - getSocketOutputStream().flush(); - - if(webSocketReceiver != null){ - webSocketReceiver.stop(); - } - super.stop(); - } - - public String getServerURI() { - return "ws://" + host + ":" + port; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketReceiver.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketReceiver.java deleted file mode 100644 index 9052240..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketReceiver.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Bug 459142 - WebSocket support for the Java client. - */ -package org.eclipse.paho.client.mqttv3.internal.websocket; - -import java.io.IOException; -import java.io.InputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; - -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -public class WebSocketReceiver implements Runnable{ - - private static final String CLASS_NAME = WebSocketReceiver.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - private boolean running = false; - private boolean stopping = false; - private Object lifecycle = new Object(); - private InputStream input; - private Thread receiverThread = null; - private volatile boolean receiving; - private PipedOutputStream pipedOutputStream; - - public WebSocketReceiver(InputStream input, PipedInputStream pipedInputStream) throws IOException{ - this.input = input; - this.pipedOutputStream = new PipedOutputStream(); - pipedInputStream.connect(pipedOutputStream); - } - - /** - * Starts up the WebSocketReceiver's thread - * @param threadName The name of the thread - */ - public void start(String threadName){ - final String methodName = "start"; - //@TRACE 855=starting - log.fine(CLASS_NAME, methodName, "855"); - synchronized (lifecycle) { - if(!running) { - running = true; - receiverThread = new Thread(this, threadName); - receiverThread.start(); - } - } - } - - /** - * Stops this WebSocketReceiver's thread. - * This call will block. - */ - public void stop() { - final String methodName = "stop"; - stopping = true; - boolean closed = false; - synchronized (lifecycle) { - //@TRACE 850=stopping - log.fine(CLASS_NAME,methodName, "850"); - if(running) { - running = false; - receiving = false; - closed = true; - closeOutputStream(); - - } - } - if(closed && !Thread.currentThread().equals(receiverThread)) { - try { - // Wait for the thread to finish - //This must not happen in the synchronized block, otherwise we can deadlock ourselves! - receiverThread.join(); - } catch (InterruptedException ex) { - // Interrupted Exception - } - } - receiverThread = null; - //@TRACE 851=stopped - log.fine(CLASS_NAME, methodName, "851"); - } - - public void run() { - final String methodName = "run"; - - while (running && (input != null)) { - try { - //@TRACE 852=network read message - log.fine(CLASS_NAME, methodName, "852"); - receiving = input.available() > 0; - WebSocketFrame incomingFrame = new WebSocketFrame(input); - if(!incomingFrame.isCloseFlag()){ - for(int i = 0; i < incomingFrame.getPayload().length; i++){ - pipedOutputStream.write(incomingFrame.getPayload()[i]); - } - - pipedOutputStream.flush(); - } else { - if(!stopping){ - throw new IOException("Server sent a WebSocket Frame with the Stop OpCode"); - } - } - - receiving = false; - - } catch (IOException ex) { - // Exception occurred whilst reading the stream. - this.stop(); - } - } - } - - private void closeOutputStream(){ - try { - pipedOutputStream.close(); - } catch (IOException e) { - } - } - - - public boolean isRunning() { - return running; - } - - /** - * Returns the receiving state. - * - * @return true if the receiver is receiving data, false otherwise. - */ - public boolean isReceiving(){ - return receiving; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModule.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModule.java deleted file mode 100644 index 8224103..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModule.java +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * James Sutton - Bug 459142 - WebSocket support for the Java client. - */ -package org.eclipse.paho.client.mqttv3.internal.websocket; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.nio.ByteBuffer; - -import javax.net.ssl.SSLSocketFactory; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -public class WebSocketSecureNetworkModule extends SSLNetworkModule{ - - private static final String CLASS_NAME = WebSocketSecureNetworkModule.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - private PipedInputStream pipedInputStream; - private WebSocketReceiver webSocketReceiver; - private String uri; - private String host; - private int port; - ByteBuffer recievedPayload; - - /** - * Overrides the flush method. - * This allows us to encode the MQTT payload into a WebSocket - * Frame before passing it through to the real socket. - */ - private ByteArrayOutputStream outputStream = new ExtendedByteArrayOutputStream(this); - - public WebSocketSecureNetworkModule(SSLSocketFactory factory, String uri, String host, int port, String clientId) { - super(factory, host, port, clientId); - this.uri = uri; - this.host = host; - this.port = port; - this.pipedInputStream = new PipedInputStream(); - log.setResourceName(clientId); - } - - public void start() throws IOException, MqttException { - super.start(); - WebSocketHandshake handshake = new WebSocketHandshake(super.getInputStream(), super.getOutputStream(), uri, host, port); - handshake.execute(); - this.webSocketReceiver = new WebSocketReceiver(getSocketInputStream(), pipedInputStream); - webSocketReceiver.start("WssSocketReceiver"); - - } - - OutputStream getSocketOutputStream() throws IOException { - return super.getOutputStream(); - } - - InputStream getSocketInputStream() throws IOException { - return super.getInputStream(); - } - - public InputStream getInputStream() throws IOException { - return pipedInputStream; - } - - public OutputStream getOutputStream() throws IOException { - return outputStream; - } - - public void stop() throws IOException { - // Creating Close Frame - WebSocketFrame frame = new WebSocketFrame((byte)0x08, true, "1000".getBytes()); - byte[] rawFrame = frame.encodeFrame(); - getSocketOutputStream().write(rawFrame); - getSocketOutputStream().flush(); - - if(webSocketReceiver != null){ - webSocketReceiver.stop(); - } - super.stop(); - } - - public String getServerURI() { - return "wss://" + host + ":" + port; - } - - - - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java deleted file mode 100644 index 5d4eec7..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.IOException; -import java.io.InputStream; - -/** - * An input stream that counts the bytes read from it. - */ -public class CountingInputStream extends InputStream { - private InputStream in; - private int counter; - - /** - * Constructs a new CountingInputStream wrapping the supplied - * input stream. - * @param in The {@link InputStream} - */ - public CountingInputStream(InputStream in) { - this.in = in; - this.counter = 0; - } - - public int read() throws IOException { - int i = in.read(); - if (i != -1) { - counter++; - } - return i; - } - - /** - * @return the number of bytes read since the last reset. - */ - public int getCounter() { - return counter; - } - - /** - * Resets the counter to zero. - */ - public void resetCounter() { - counter = 0; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java deleted file mode 100644 index ffc7f41..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - - -/** - * Abstract super-class of all acknowledgement messages. - */ -public abstract class MqttAck extends MqttWireMessage { - public MqttAck(byte type) { - super(type); - } - - protected byte getMessageInfo() { - return 0; - } - - /** - * @return String representation of the wire message - */ - public String toString() { - return super.toString() + " msgId " + msgId; - } -} \ No newline at end of file diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java deleted file mode 100644 index 3d21619..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - -/** - * An on-the-wire representation of an MQTT CONNACK. - */ -public class MqttConnack extends MqttAck { - public static final String KEY = "Con"; - - private int returnCode; - private boolean sessionPresent; - - public MqttConnack(byte info, byte[] variableHeader) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_CONNACK); - ByteArrayInputStream bais = new ByteArrayInputStream(variableHeader); - DataInputStream dis = new DataInputStream(bais); - sessionPresent = (dis.readUnsignedByte() & 0x01) == 0x01; - returnCode = dis.readUnsignedByte(); - dis.close(); - } - - public int getReturnCode() { - return returnCode; - } - - protected byte[] getVariableHeader() throws MqttException { - // Not needed, as the client never encodes a CONNACK - return new byte[0]; - } - - /** - * Returns whether or not this message needs to include a message ID. - */ - public boolean isMessageIdRequired() { - return false; - } - - public String getKey() { - return KEY; - } - - public String toString() { - return super.toString() + " session present:" + sessionPresent + " return code: " + returnCode; - } - - public boolean getSessionPresent() { - return sessionPresent; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java deleted file mode 100644 index 6dfadfd..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java +++ /dev/null @@ -1,168 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - -/** - * An on-the-wire representation of an MQTT CONNECT message. - */ -public class MqttConnect extends MqttWireMessage { - - public static final String KEY = "Con"; - - private String clientId; - private boolean cleanSession; - private MqttMessage willMessage; - private String userName; - private char[] password; - private int keepAliveInterval; - private String willDestination; - private int MqttVersion; - - /** - * Constructor for an on the wire MQTT connect message - * - * @param info The info byte - * @param data the data byte array - * @throws IOException thrown if an exception occurs when reading the input streams - * @throws MqttException thrown if an exception occurs when decoding UTF-8 - */ - public MqttConnect(byte info, byte[] data) throws IOException, MqttException { - super(MqttWireMessage.MESSAGE_TYPE_CONNECT); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - - String protocol_name = decodeUTF8(dis); - int protocol_version = dis.readByte(); - byte connect_flags = dis.readByte(); - keepAliveInterval = dis.readUnsignedShort(); - clientId = decodeUTF8(dis); - dis.close(); - } - - public MqttConnect(String clientId, int MqttVersion, boolean cleanSession, int keepAliveInterval, String userName, char[] password, MqttMessage willMessage, String willDestination) { - super(MqttWireMessage.MESSAGE_TYPE_CONNECT); - this.clientId = clientId; - this.cleanSession = cleanSession; - this.keepAliveInterval = keepAliveInterval; - this.userName = userName; - this.password = password; - this.willMessage = willMessage; - this.willDestination = willDestination; - this.MqttVersion = MqttVersion; - } - - public String toString() { - String rc = super.toString(); - rc += " clientId " + clientId + " keepAliveInterval " + keepAliveInterval; - return rc; - } - - protected byte getMessageInfo() { - return (byte) 0; - } - - public boolean isCleanSession() { - return cleanSession; - } - - protected byte[] getVariableHeader() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - - if (MqttVersion == 3) { - encodeUTF8(dos,"MQIsdp"); - } - else if (MqttVersion == 4) { - encodeUTF8(dos,"MQTT"); - } - dos.write(MqttVersion); - - byte connectFlags = 0; - - if (cleanSession) { - connectFlags |= 0x02; - } - - if (willMessage != null ) { - connectFlags |= 0x04; - connectFlags |= (willMessage.getQos()<<3); - if (willMessage.isRetained()) { - connectFlags |= 0x20; - } - } - - if (userName != null) { - connectFlags |= 0x80; - if (password != null) { - connectFlags |= 0x40; - } - } - dos.write(connectFlags); - dos.writeShort(keepAliveInterval); - dos.flush(); - return baos.toByteArray(); - } catch(IOException ioe) { - throw new MqttException(ioe); - } - } - - public byte[] getPayload() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - encodeUTF8(dos,clientId); - - if (willMessage != null) { - encodeUTF8(dos,willDestination); - dos.writeShort(willMessage.getPayload().length); - dos.write(willMessage.getPayload()); - } - - if (userName != null) { - encodeUTF8(dos,userName); - if (password != null) { - encodeUTF8(dos,new String(password)); - } - } - dos.flush(); - return baos.toByteArray(); - } catch (IOException ex) { - throw new MqttException(ex); - } - } - - /** - * Returns whether or not this message needs to include a message ID. - */ - public boolean isMessageIdRequired() { - return false; - } - - public String getKey() { - return KEY; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java deleted file mode 100644 index 9aa618a..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - -/** - * An on-the-wire representation of an MQTT DISCONNECT message. - */ -public class MqttDisconnect extends MqttWireMessage { - public static final String KEY="Disc"; - - public MqttDisconnect() { - super(MqttWireMessage.MESSAGE_TYPE_DISCONNECT); - } - - public MqttDisconnect(byte info, byte[] variableHeader) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_DISCONNECT); - } - - protected byte getMessageInfo() { - return (byte) 0; - } - - protected byte[] getVariableHeader() throws MqttException { - return new byte[0]; - } - - /** - * Returns whether or not this message needs to include a message ID. - */ - public boolean isMessageIdRequired() { - return false; - } - - public String getKey() { - return KEY; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java deleted file mode 100644 index 3a7b049..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.net.SocketTimeoutException; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.internal.ClientState; -import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - - -/** - * An MqttInputStream lets applications read instances of - * MqttWireMessage. - */ -public class MqttInputStream extends InputStream { - private static final String CLASS_NAME = MqttInputStream.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - private ClientState clientState = null; - private DataInputStream in; - private ByteArrayOutputStream bais; - private long remLen; - private long packetLen; - private byte[] packet; - - public MqttInputStream(ClientState clientState, InputStream in) { - this.clientState = clientState; - this.in = new DataInputStream(in); - this.bais = new ByteArrayOutputStream(); - this.remLen = -1; - } - - public int read() throws IOException { - return in.read(); - } - - public int available() throws IOException { - return in.available(); - } - - public void close() throws IOException { - in.close(); - } - - /** - * Reads an MqttWireMessage from the stream. - * If the message cannot be fully read within the socket read timeout, - * a null message is returned and the method can be called again until - * the message is fully read. - * @return The {@link MqttWireMessage} - * @throws IOException if an exception is thrown when reading from the stream - * @throws MqttException if the message is invalid - */ - public MqttWireMessage readMqttWireMessage() throws IOException, MqttException { - final String methodName ="readMqttWireMessage"; - - MqttWireMessage message = null; - try { - // read header - if (remLen < 0) { - // Assume we can read the whole header at once. - // The header is very small so it's likely we - // are able to read it fully or not at all. - // This keeps the parser lean since we don't - // need to cope with a partial header. - // Should we lose synch with the stream, - // the keepalive mechanism would kick in - // closing the connection. - bais.reset(); - - byte first = in.readByte(); - clientState.notifyReceivedBytes(1); - - byte type = (byte) ((first >>> 4) & 0x0F); - if ((type < MqttWireMessage.MESSAGE_TYPE_CONNECT) || - (type > MqttWireMessage.MESSAGE_TYPE_DISCONNECT)) { - // Invalid MQTT message type... - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_INVALID_MESSAGE); - } - remLen = MqttWireMessage.readMBI(in).getValue(); - bais.write(first); - // bit silly, we decode it then encode it - bais.write(MqttWireMessage.encodeMBI(remLen)); - packet = new byte[(int)(bais.size()+remLen)]; - packetLen = 0; - } - - // read remaining packet - if (remLen >= 0) { - // the remaining packet can be read with timeouts - readFully(); - - // reset packet parsing state - remLen = -1; - - byte[] header = bais.toByteArray(); - System.arraycopy(header,0,packet,0, header.length); - message = MqttWireMessage.createWireMessage(packet); - // @TRACE 501= received {0} - log.fine(CLASS_NAME, methodName, "501",new Object[] {message}); - } - } catch (SocketTimeoutException e) { - // ignore socket read timeout - } - - return message; - } - - private void readFully() throws IOException { - int off = bais.size() + (int) packetLen; - int len = (int) (remLen - packetLen); - if (len < 0) - throw new IndexOutOfBoundsException(); - int n = 0; - while (n < len) { - int count = -1; - try { - count = in.read(packet, off + n, len - n); - } catch (SocketTimeoutException e) { - // remember the packet read so far - packetLen += n; - throw e; - } - clientState.notifyReceivedBytes(count); - - if (count < 0) - throw new EOFException(); - n += count; - } - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java deleted file mode 100644 index 95b9cea..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java +++ /dev/null @@ -1,94 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.internal.ClientState; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - - -/** - * An MqttOutputStream lets applications write instances of - * MqttWireMessage. - */ -public class MqttOutputStream extends OutputStream { - private static final String CLASS_NAME = MqttOutputStream.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME); - - private ClientState clientState = null; - private BufferedOutputStream out; - - public MqttOutputStream(ClientState clientState, OutputStream out) { - this.clientState = clientState; - this.out = new BufferedOutputStream(out); - } - - public void close() throws IOException { - out.close(); - } - - public void flush() throws IOException { - out.flush(); - } - - public void write(byte[] b) throws IOException { - out.write(b); - clientState.notifySentBytes(b.length); - } - - public void write(byte[] b, int off, int len) throws IOException { - out.write(b, off, len); - clientState.notifySentBytes(len); - } - - public void write(int b) throws IOException { - out.write(b); - } - - /** - * Writes an MqttWireMessage to the stream. - * @param message The {@link MqttWireMessage} to send - * @throws IOException if an exception is thrown when writing to the output stream. - * @throws MqttException if an exception is thrown when getting the header or payload - */ - public void write(MqttWireMessage message) throws IOException, MqttException { - final String methodName = "write"; - byte[] bytes = message.getHeader(); - byte[] pl = message.getPayload(); -// out.write(message.getHeader()); -// out.write(message.getPayload()); - out.write(bytes,0,bytes.length); - clientState.notifySentBytes(bytes.length); - - int offset = 0; - int chunckSize = 1024; - while (offset < pl.length) { - int length = Math.min(chunckSize, pl.length - offset); - out.write(pl, offset, length); - offset += chunckSize; - clientState.notifySentBytes(length); - } - - // @TRACE 529= sent {0} - log.fine(CLASS_NAME, methodName, "529", new Object[]{message}); - } -} - diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java deleted file mode 100644 index c826d87..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttPersistable; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; - -public abstract class MqttPersistableWireMessage extends MqttWireMessage - implements MqttPersistable { - - public MqttPersistableWireMessage(byte type) { - super(type); - } - - public byte[] getHeaderBytes() throws MqttPersistenceException { - try { - return getHeader(); - } - catch (MqttException ex) { - throw new MqttPersistenceException(ex.getCause()); - } - } - - public int getHeaderLength() throws MqttPersistenceException { - return getHeaderBytes().length; - } - - public int getHeaderOffset() throws MqttPersistenceException{ - return 0; - } - -// public String getKey() throws MqttPersistenceException { -// return new Integer(getMessageId()).toString(); -// } - - public byte[] getPayloadBytes() throws MqttPersistenceException { - try { - return getPayload(); - } - catch (MqttException ex) { - throw new MqttPersistenceException(ex.getCause()); - } - } - - public int getPayloadLength() throws MqttPersistenceException { - return 0; - } - - public int getPayloadOffset() throws MqttPersistenceException { - return 0; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java deleted file mode 100644 index e2472be..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - -/** - * An on-the-wire representation of an MQTT PINGREQ message. - */ -public class MqttPingReq extends MqttWireMessage { - public static final String KEY = "Ping"; - - public MqttPingReq() { - super(MqttWireMessage.MESSAGE_TYPE_PINGREQ); - } - - public MqttPingReq(byte info, byte[] variableHeader) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_PINGREQ); - } - - /** - * Returns false as message IDs are not required for MQTT - * PINGREQ messages. - */ - public boolean isMessageIdRequired() { - return false; - } - - protected byte[] getVariableHeader() throws MqttException { - return new byte[0]; - } - - protected byte getMessageInfo() { - return 0; - } - - public String getKey() { - return KEY; - } -} - diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java deleted file mode 100644 index 4f07fc9..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import org.eclipse.paho.client.mqttv3.MqttException; - - -/** - * An on-the-wire representation of an MQTT PINGRESP. - */ -public class MqttPingResp extends MqttAck { - public static final String KEY = "Ping"; - - public MqttPingResp(byte info, byte[] variableHeader) { - super(MqttWireMessage.MESSAGE_TYPE_PINGRESP); - } - - protected byte[] getVariableHeader() throws MqttException { - // Not needed, as the client never encodes a PINGRESP - return new byte[0]; - } - - /** - * Returns whether or not this message needs to include a message ID. - */ - public boolean isMessageIdRequired() { - return false; - } - - public String getKey() { - return KEY; - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java deleted file mode 100644 index de3ab5f..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2015 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - ack control (bug 472172) - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - - - -/** - * An on-the-wire representation of an MQTT PUBACK message. - */ -public class MqttPubAck extends MqttAck { - public MqttPubAck(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBACK); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - dis.close(); - } - - public MqttPubAck(MqttPublish publish) { - super(MqttWireMessage.MESSAGE_TYPE_PUBACK); - msgId = publish.getMessageId(); - } - - public MqttPubAck(int messageId) { - super(MqttWireMessage.MESSAGE_TYPE_PUBACK); - msgId = messageId; - } - - protected byte[] getVariableHeader() throws MqttException { - return encodeMessageId(); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java deleted file mode 100644 index 59c90b0..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - - - -/** - * An on-the-wire representation of an MQTT PUBCOMP message. - */ -public class MqttPubComp extends MqttAck { - public MqttPubComp(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - dis.close(); - } - - public MqttPubComp(MqttPublish publish) { - super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP); - this.msgId = publish.getMessageId(); - } - - public MqttPubComp(int msgId) { - super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP); - this.msgId = msgId; - } - - protected byte[] getVariableHeader() throws MqttException { - return encodeMessageId(); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java deleted file mode 100644 index f2dac68..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - - - -/** - * An on-the-wire representation of an MQTT PUBREC message. - */ -public class MqttPubRec extends MqttAck { - public MqttPubRec(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBREC); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - dis.close(); - } - - public MqttPubRec(MqttPublish publish) { - super(MqttWireMessage.MESSAGE_TYPE_PUBREC); - msgId = publish.getMessageId(); - } - - protected byte[] getVariableHeader() throws MqttException { - return encodeMessageId(); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java deleted file mode 100644 index 0db14ff..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - -/** - * An on-the-wire representation of an MQTT PUBREL message. - */ -public class MqttPubRel extends MqttPersistableWireMessage { - - /** - * Createa a pubrel message based on a pubrec - * @param pubRec the {@link MqttPubRec} - */ - public MqttPubRel(MqttPubRec pubRec) { - super(MqttWireMessage.MESSAGE_TYPE_PUBREL); - this.setMessageId(pubRec.getMessageId()); - } - - /** - * Creates a pubrel based on a pubrel set of bytes read fro the network - * @param info the info byte - * @param data the byte array - * @throws IOException if an exception occurs whilst reading from the input stream - */ - public MqttPubRel(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBREL); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - dis.close(); - } - - protected byte[] getVariableHeader() throws MqttException { - return encodeMessageId(); - } - - protected byte getMessageInfo() { - return (byte)( 2 | (this.duplicate?8:0)); - } - - public String toString() { - return super.toString() + " msgId " + msgId; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java deleted file mode 100644 index 9c8a278..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java +++ /dev/null @@ -1,181 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - -/** - * An on-the-wire representation of an MQTT SEND message. - */ -public class MqttPublish extends MqttPersistableWireMessage { - - private MqttMessage message; - private String topicName; - - private byte[] encodedPayload = null; - - public MqttPublish(String name, MqttMessage message) { - super(MqttWireMessage.MESSAGE_TYPE_PUBLISH); - topicName = name; - this.message = message; - } - - /** - * Constructs a new MqttPublish object. - * @param info the message info byte - * @param data the variable header and payload bytes - * @throws MqttException if an exception occurs creating the publish - * @throws IOException if an exception occurs creating the publish - */ - public MqttPublish(byte info, byte[] data) throws MqttException, IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBLISH); - message = new MqttReceivedMessage(); - message.setQos((info >> 1) & 0x03); - if ((info & 0x01) == 0x01) { - message.setRetained(true); - } - if ((info & 0x08) == 0x08) { - ((MqttReceivedMessage) message).setDuplicate(true); - } - - ByteArrayInputStream bais = new ByteArrayInputStream(data); - CountingInputStream counter = new CountingInputStream(bais); - DataInputStream dis = new DataInputStream(counter); - topicName = decodeUTF8(dis); - if (message.getQos() > 0) { - msgId = dis.readUnsignedShort(); - } - byte[] payload = new byte[data.length-counter.getCounter()]; - dis.readFully(payload); - dis.close(); - message.setPayload(payload); - } - - public String toString() { - - // Convert the first few bytes of the payload into a hex string - StringBuffer hex = new StringBuffer(); - byte[] payload = message.getPayload(); - int limit = Math.min(payload.length, 20); - for (int i = 0; i < limit; i++) { - byte b = payload[i]; - String ch = Integer.toHexString(b); - if (ch.length() == 1) { - ch = "0" + ch; - } - hex.append(ch); - } - - // It will not always be possible to convert the binary payload into - // characters, but never-the-less we attempt to do this as it is often - // useful - String string = null; - try { - string = new String(payload, 0, limit, "UTF-8"); - } catch (Exception e) { - string = "?"; - } - - StringBuffer sb = new StringBuffer(); - sb.append(super.toString()); - sb.append(" qos:").append(message.getQos()); - if (message.getQos() > 0) { - sb.append(" msgId:").append(msgId); - } - sb.append(" retained:").append(message.isRetained()); - sb.append(" dup:").append(duplicate); - sb.append(" topic:\"").append(topicName).append("\""); - sb.append(" payload:[hex:").append(hex); - sb.append(" utf8:\"").append(string).append("\""); - sb.append(" length:").append(payload.length).append("]"); - - return sb.toString(); - } - - protected byte getMessageInfo() { - byte info = (byte) (message.getQos() << 1); - if (message.isRetained()) { - info |= 0x01; - } - if (message.isDuplicate() || duplicate ) { - info |= 0x08; - } - - return info; - } - - public String getTopicName() { - return topicName; - } - - public MqttMessage getMessage() { - return message; - } - - protected static byte[] encodePayload(MqttMessage message) { - return message.getPayload(); - } - - public byte[] getPayload() throws MqttException { - if (encodedPayload == null) { - encodedPayload = encodePayload(message); - } - return encodedPayload; - } - - public int getPayloadLength() { - int length = 0; - try { - length = getPayload().length; - } catch(MqttException me) { - } - return length; - } - - public void setMessageId(int msgId) { - super.setMessageId(msgId); - if (message instanceof MqttReceivedMessage) { - ((MqttReceivedMessage)message).setMessageId(msgId); - } - } - - protected byte[] getVariableHeader() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - encodeUTF8(dos, topicName); - if (message.getQos() > 0) { - dos.writeShort(msgId); - } - dos.flush(); - return baos.toByteArray(); - } catch (IOException ex) { - throw new MqttException(ex); - } - } - - public boolean isMessageIdRequired() { - // all publishes require a message ID as it's used as the key to the token store - return true; - } -} \ No newline at end of file diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java deleted file mode 100644 index d5290e0..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import org.eclipse.paho.client.mqttv3.MqttMessage; - -public class MqttReceivedMessage extends MqttMessage { - - public void setMessageId(int msgId) { - super.setId(msgId); - } - - public int getMessageId() { - return super.getId(); - } - - // This method exists here to get around the protected visibility of the - // super class method. - public void setDuplicate(boolean value) { - super.setDuplicate(value); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java deleted file mode 100644 index cd74a43..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - * Ian Craggs - MQTT 3.1.1 support - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - - -/** - * An on-the-wire representation of an MQTT SUBACK. - */ -public class MqttSuback extends MqttAck { - private int[] grantedQos; - - public MqttSuback(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_SUBACK); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - int index = 0; - grantedQos = new int[data.length-2]; - int qos = dis.read(); - while (qos != -1) { - grantedQos[index] = qos; - index++; - qos = dis.read(); - } - dis.close(); - } - - protected byte[] getVariableHeader() throws MqttException { - // Not needed, as the client never encodes a SUBACK - return new byte[0]; - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append(super.toString()).append(" granted Qos"); - for (int i = 0; i < grantedQos.length; ++i) { - sb.append(" ").append(grantedQos[i]); - } - return sb.toString(); - } - - public int[] getGrantedQos() { - return grantedQos; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java deleted file mode 100644 index dcca4b8..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - - -/** - * An on-the-wire representation of an MQTT SUBSCRIBE message. - */ -public class MqttSubscribe extends MqttWireMessage { - private String[] names; - private int[] qos; - private int count; - - /** - * Constructor for an on the wire MQTT subscribe message - * - * @param info the info byte - * @param data the data byte array - * @throws IOException if an exception occurs whilst reading the input stream - */ - public MqttSubscribe(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - - count = 0; - names = new String[10]; - qos = new int[10]; - boolean end = false; - while (!end) { - try { - names[count] = decodeUTF8(dis); - qos[count++] = dis.readByte(); - } catch (Exception e) { - end = true; - } - } - dis.close(); - } - - /** - * Constructor for an on the wire MQTT subscribe message - * @param names - one or more topics to subscribe to - * @param qos - the max QoS that each each topic will be subscribed at - */ - public MqttSubscribe(String[] names, int[] qos) { - super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE); - this.names = names; - this.qos = qos; - - if (names.length != qos.length) { - throw new IllegalArgumentException(); - } - this.count = names.length; - - for (int i=0;i 0) { - sb.append(", "); - } - sb.append("\"").append(names[i]).append("\""); - } - sb.append("] qos:["); - for (int i = 0; i < count; i++) { - if (i > 0) { - sb.append(", "); - } - sb.append(qos[i]); - } - sb.append("]"); - - return sb.toString(); - } - - protected byte getMessageInfo() { - return (byte) (2 | (duplicate ? 8 : 0)); - } - - protected byte[] getVariableHeader() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - dos.writeShort(msgId); - dos.flush(); - return baos.toByteArray(); - } catch (IOException ex) { - throw new MqttException(ex); - } - } - - public byte[] getPayload() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - for (int i=0; i 0) { - sb.append(", "); - } - sb.append("\"" + names[i] + "\""); - } - sb.append("]"); - return sb.toString(); - } - - protected byte getMessageInfo() { - return (byte) (2 | (duplicate ? 8 : 0)); - } - - protected byte[] getVariableHeader() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - dos.writeShort(msgId); - dos.flush(); - return baos.toByteArray(); - } catch (IOException ex) { - throw new MqttException(ex); - } - } - - public byte[] getPayload() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - for (int i=0; i> 4); - byte info = (byte) (first &= 0x0f); - long remLen = readMBI(in).getValue(); - long totalToRead = counter.getCounter() + remLen; - - MqttWireMessage result; - long remainder = totalToRead - counter.getCounter(); - byte[] data = new byte[0]; - // The remaining bytes must be the payload... - if (remainder > 0) { - data = new byte[(int) remainder]; - in.readFully(data, 0, data.length); - } - - if (type == MqttWireMessage.MESSAGE_TYPE_CONNECT) { - result = new MqttConnect(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PUBLISH) { - result = new MqttPublish(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PUBACK) { - result = new MqttPubAck(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PUBCOMP) { - result = new MqttPubComp(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_CONNACK) { - result = new MqttConnack(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PINGREQ) { - result = new MqttPingReq(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PINGRESP) { - result = new MqttPingResp(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE) { - result = new MqttSubscribe(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_SUBACK) { - result = new MqttSuback(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_UNSUBSCRIBE) { - result = new MqttUnsubscribe(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_UNSUBACK) { - result = new MqttUnsubAck(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREL) { - result = new MqttPubRel(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREC) { - result = new MqttPubRec(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_DISCONNECT) { - result = new MqttDisconnect(info, data); - } - else { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); - } - return result; - } catch(IOException io) { - throw new MqttException(io); - } - } - - protected static byte[] encodeMBI( long number) { - int numBytes = 0; - long no = number; - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - // Encode the remaining length fields in the four bytes - do { - byte digit = (byte)(no % 128); - no = no / 128; - if (no > 0) { - digit |= 0x80; - } - bos.write(digit); - numBytes++; - } while ( (no > 0) && (numBytes<4) ); - - return bos.toByteArray(); - } - - /** - * Decodes an MQTT Multi-Byte Integer from the given stream. - * @param in the input stream - * @return {@link MultiByteInteger} - * @throws IOException if an exception occurs when reading the input stream - */ - protected static MultiByteInteger readMBI(DataInputStream in) throws IOException { - byte digit; - long msgLength = 0; - int multiplier = 1; - int count = 0; - - do { - digit = in.readByte(); - count++; - msgLength += ((digit & 0x7F) * multiplier); - multiplier *= 128; - } while ((digit & 0x80) != 0); - - return new MultiByteInteger(msgLength, count); - } - - protected byte[] encodeMessageId() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - dos.writeShort(msgId); - dos.flush(); - return baos.toByteArray(); - } - catch (IOException ex) { - throw new MqttException(ex); - } - } - - public boolean isRetryable() { - return false; - } - - public void setDuplicate(boolean duplicate) { - this.duplicate = duplicate; - } - - /** - * Encodes a String given into UTF-8, before writing this to the DataOutputStream the length of the - * encoded string is encoded into two bytes and then written to the DataOutputStream. @link{DataOutputStream#writeUFT(String)} - * should be no longer used. @link{DataOutputStream#writeUFT(String)} does not correctly encode UTF-16 surrogate characters. - * - * @param dos The stream to write the encoded UTF-8 String to. - * @param stringToEncode The String to be encoded - * @throws MqttException Thrown when an error occurs with either the encoding or writing the data to the stream - */ - protected void encodeUTF8(DataOutputStream dos, String stringToEncode) throws MqttException - { - try { - - byte[] encodedString = stringToEncode.getBytes("UTF-8"); - byte byte1 = (byte) ((encodedString.length >>> 8) & 0xFF); - byte byte2 = (byte) ((encodedString.length >>> 0) & 0xFF); - - - dos.write(byte1); - dos.write(byte2); - dos.write(encodedString); - } - catch(UnsupportedEncodingException ex) - { - throw new MqttException(ex); - } catch (IOException ex) { - throw new MqttException(ex); - } - } - - /** - * Decodes a UTF-8 string from the DataInputStream provided. @link(DataInoutStream#readUTF()) should be no longer used, because @link(DataInoutStream#readUTF()) - * does not decode UTF-16 surrogate characters correctly. - * - * @param input The input stream from which to read the encoded string - * @return a decoded String from the DataInputStream - * @throws MqttException thrown when an error occurs with either reading from the stream or - * decoding the encoded string. - */ - protected String decodeUTF8(DataInputStream input) throws MqttException - { - int encodedLength; - try { - encodedLength = input.readUnsignedShort(); - - byte[] encodedString = new byte[encodedLength]; - input.readFully(encodedString); - - return new String(encodedString, "UTF-8"); - } catch (IOException ex) { - throw new MqttException(ex); - } - } - - public String toString() { - return PACKET_NAMES[type]; - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java deleted file mode 100644 index b9b9511..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.IOException; -import java.io.InputStream; - -public class MultiByteArrayInputStream extends InputStream { - - private byte[] bytesA; - private int offsetA; - private int lengthA; - private byte[] bytesB; - private int offsetB; - private int lengthB; - - private int pos = 0; - - public MultiByteArrayInputStream(byte[] bytesA, int offsetA, int lengthA, byte[] bytesB, int offsetB, int lengthB) { - this.bytesA = bytesA; - this.bytesB = bytesB; - this.offsetA = offsetA; - this.offsetB = offsetB; - this.lengthA = lengthA; - this.lengthB = lengthB; - } - public int read() throws IOException { - int result = -1; - if (posA sample java.util.logging properties file - jsr47min.properties is provided that demonstrates - * how to run with a memory based trace facility that runs with minimal performance - * overhead. The memory buffer can be dumped when a log/trace record is written matching - * the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. - * {@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy - * to dump the memory buffer as well as other useful debug info. - */ -public class JSR47Logger implements Logger { - private java.util.logging.Logger julLogger = null; - private ResourceBundle logMessageCatalog = null; - private ResourceBundle traceMessageCatalog = null; - private String catalogID = null; - private String resourceName = null; - private String loggerName = null; - - /** - * - * @param logMsgCatalog The resource bundle associated with this logger - * @param loggerID The suffix for the loggerName (will be appeneded to org.eclipse.paho.client.mqttv3 - * @param resourceContext A context for the logger e.g. clientID or appName... - */ - public void initialise(ResourceBundle logMsgCatalog, String loggerID, String resourceContext ) { - this.traceMessageCatalog = logMessageCatalog; - this.resourceName = resourceContext; -// loggerName = "org.eclipse.paho.client.mqttv3." + ((null == loggerID || 0 == loggerID.length()) ? "internal" : loggerID); - loggerName = loggerID; - this.julLogger = java.util.logging.Logger.getLogger(loggerName); - this.logMessageCatalog = logMsgCatalog; - this.traceMessageCatalog = logMsgCatalog; - this.catalogID = logMessageCatalog.getString("0"); - - } - - public void setResourceName(String logContext) { - this.resourceName = logContext; - } - - public boolean isLoggable(int level) { - return julLogger.isLoggable(mapJULLevel(level)); // || InternalTracer.isLoggable(level); - } - - public void severe(String sourceClass, String sourceMethod, String msg) { - log(SEVERE, sourceClass, sourceMethod, msg, null, null); - } - - public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - log(SEVERE, sourceClass, sourceMethod, msg, inserts, null); - } - - public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { - log(SEVERE, sourceClass, sourceMethod, msg, inserts, thrown); - } - - public void warning(String sourceClass, String sourceMethod, String msg) { - log(WARNING, sourceClass, sourceMethod, msg, null, null); - } - - public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - log(WARNING, sourceClass, sourceMethod, msg, inserts, null); - } - - public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { - log(WARNING, sourceClass, sourceMethod, msg, inserts, thrown); - } - - public void info(String sourceClass, String sourceMethod, String msg) { - log(INFO, sourceClass, sourceMethod, msg, null, null); - } - - public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - log(INFO, sourceClass, sourceMethod, msg, inserts, null); - } - - public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { - log(INFO, sourceClass, sourceMethod, msg, inserts, thrown); - } - - public void config(String sourceClass, String sourceMethod, String msg) { - log(CONFIG, sourceClass, sourceMethod, msg, null, null); - } - - public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - log(CONFIG, sourceClass, sourceMethod, msg, inserts, null); - } - - public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { - log(CONFIG, sourceClass, sourceMethod, msg, inserts, thrown); - } - - public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { -// InternalTracer.log(this.catalogID, level, sourceClass, sourceMethod, msg, inserts, thrown); - java.util.logging.Level julLevel = mapJULLevel(level); - if (julLogger.isLoggable(julLevel)) { - logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.logMessageCatalog, msg, inserts, thrown); - } - } - -// public void setTrace(Trace trace) { -// InternalTracer.setTrace(trace); -// } - - public void fine(String sourceClass, String sourceMethod, String msg) { - trace(FINE, sourceClass, sourceMethod, msg, null, null); - } - - public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - trace(FINE, sourceClass, sourceMethod, msg, inserts, null); - } - - public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { - trace(FINE, sourceClass, sourceMethod, msg, inserts, ex); - } - - public void finer(String sourceClass, String sourceMethod, String msg) { - trace(FINER, sourceClass, sourceMethod, msg, null, null); - } - - public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - trace(FINER, sourceClass, sourceMethod, msg, inserts, null); - } - - public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { - trace(FINER, sourceClass, sourceMethod, msg, inserts, ex); - } - - public void finest(String sourceClass, String sourceMethod, String msg) { - trace(FINEST, sourceClass, sourceMethod, msg, null, null); - } - - public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - trace(FINEST, sourceClass, sourceMethod, msg, inserts, null); - } - - public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { - trace(FINEST, sourceClass, sourceMethod, msg, inserts, ex); - } - - - public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { - java.util.logging.Level julLevel = mapJULLevel(level); - boolean isJULLoggable = julLogger.isLoggable(julLevel); -// if (FINE == level || isJULLoggable || InternalTracer.isLoggable(level)) { -// InternalTracer.traceForced(level, sourceClass, sourceMethod, msg, inserts); -// } - if (isJULLoggable) { - logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.traceMessageCatalog, msg, inserts, ex); - } - } - - - private String getResourceMessage(ResourceBundle messageCatalog, String msg) { - String message; - try { - message = messageCatalog.getString(msg); - } catch (MissingResourceException e) { - // This is acceptable, simply return the given msg string. - message = msg; - } - return message; - } - - private void logToJsr47(java.util.logging.Level julLevel, String sourceClass, String sourceMethod, String catalogName, - ResourceBundle messageCatalog, String msg, Object[] inserts, Throwable thrown) { -// LogRecord logRecord = new LogRecord(julLevel, msg); - String formattedWithArgs = msg; - if (msg.indexOf("=====")== -1) { - formattedWithArgs = MessageFormat.format(getResourceMessage(messageCatalog, msg), inserts); - } - LogRecord logRecord = new LogRecord(julLevel, resourceName + ": " +formattedWithArgs); - - logRecord.setSourceClassName(sourceClass); - logRecord.setSourceMethodName(sourceMethod); - logRecord.setLoggerName(loggerName); -// logRecord.setResourceBundleName(catalogName); -// logRecord.setResourceBundle(messageCatalog); -// if (null != inserts) { -// logRecord.setParameters(inserts); -// } - if (null != thrown) { - logRecord.setThrown(thrown); - } - - julLogger.log(logRecord); - } - - private java.util.logging.Level mapJULLevel(int level) { - java.util.logging.Level julLevel = null; - - switch (level) { - case SEVERE: - julLevel = java.util.logging.Level.SEVERE; - break; - case WARNING: - julLevel = java.util.logging.Level.WARNING; - break; - case INFO: - julLevel = java.util.logging.Level.INFO; - break; - case CONFIG: - julLevel = java.util.logging.Level.CONFIG; - break; - case FINE: - julLevel = java.util.logging.Level.FINE; - break; - case FINER: - julLevel = java.util.logging.Level.FINER; - break; - case FINEST: - julLevel = java.util.logging.Level.FINEST; - break; - - default: - } - - return julLevel; - } - - public String formatMessage(String msg, Object[] inserts) { - String formatString; - try { - formatString = logMessageCatalog.getString(msg); - } catch (MissingResourceException e) { - formatString = msg; - } - return formatString; - } - - public void dumpTrace() { - dumpMemoryTrace47(julLogger); - } - - protected static void dumpMemoryTrace47(java.util.logging.Logger logger) { - MemoryHandler mHand = null; - - if (logger!= null) { - Handler[] handlers = logger.getHandlers(); - - for (int i=0; i - * The int levels define a set of standard logging levels that can be used to - * control logging output. The logging levels are ordered and are specified by - * ordered integers. Enabling logging at a given level also enables logging at - * all higher levels. - *

- * Clients should use the the convenience methods such as severe() and fine() or - * one of the predefined level constants such as Logger.SEVERE and Logger.FINE - * with the appropriate log(int level...) or trace(int level...) methods. - *

- * The levels in descending order are:

- *
    - *
  • SEVERE (log - highest value)
  • - *
  • WARNING (log)
  • - *
  • INFO (log)
  • - *
  • CONFIG (log)
  • - *
  • FINE (trace)
  • - *
  • FINER (trace)
  • - *
  • FINEST (trace - lowest value)
  • - *
- * - */ -public interface Logger { - /** - * SEVERE is a message level indicating a serious failure. - *

- * In general SEVERE messages should describe events that are of - * considerable importance and which will prevent normal program execution. - * They should be reasonably intelligible to end users and to system - * administrators. - */ - public static final int SEVERE = 1; - /** - * WARNING is a message level indicating a potential problem. - *

- * In general WARNING messages should describe events that will be of - * interest to end users or system managers, or which indicate potential - * problems. - */ - public static final int WARNING = 2; - /** - * INFO is a message level for informational messages. - *

- * Typically INFO messages will be written to the console or its equivalent. - * So the INFO level should only be used for reasonably significant messages - * that will make sense to end users and system admins. - */ - public static final int INFO = 3; - /** - * CONFIG is a message level for static configuration messages. - *

- * CONFIG messages are intended to provide a variety of static configuration - * information, to assist in debugging problems that may be associated with - * particular configurations. For example, CONFIG message might include the - * CPU type, the graphics depth, the GUI look-and-feel, etc. - */ - public static final int CONFIG = 4; - /** - * FINE is a message level providing tracing information. - *

- * All of FINE, FINER, and FINEST are intended for relatively detailed - * tracing. The exact meaning of the three levels will vary between - * subsystems, but in general, FINEST should be used for the most voluminous - * detailed output, FINER for somewhat less detailed output, and FINE for - * the lowest volume (and most important) messages. - *

- * In general the FINE level should be used for information that will be - * broadly interesting to developers who do not have a specialized interest - * in the specific subsystem. - *

- * FINE messages might include things like minor (recoverable) failures. - * Issues indicating potential performance problems are also worth logging - * as FINE. - */ - public static final int FINE = 5; - /** - * FINER indicates a fairly detailed tracing message. By default logging - * calls for entering, returning, or throwing an exception are traced at - * this level. - */ - public static final int FINER = 6; - /** - * FINEST indicates a highly detailed tracing message. - */ - public static final int FINEST = 7; - - public void initialise(ResourceBundle messageCatalog, String loggerID, String resourceName); - - /** - * Set a name that can be used to provide context with each log record. - * This overrides the value passed in on initialise - * @param logContext The Log context name - */ - public void setResourceName(String logContext); - - /** - * Check if a message of the given level would actually be logged by this - * logger. This check is based on the Loggers effective level, which may be - * inherited from its parent. - * - * @param level - * a message logging level. - * @return true if the given message level is currently being logged. - */ - public boolean isLoggable(int level); - - /** - * Log a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. - */ - public void severe(String sourceClass, String sourceMethod, String msg); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @param thrown - * Throwable associated with log message. - */ - public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Log a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. - */ - public void warning(String sourceClass, String sourceMethod, String msg); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @param thrown - * Throwable associated with log message. - */ - public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Log a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. - */ - public void info(String sourceClass, String sourceMethod, String msg); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @param thrown - * Throwable associated with log message. - */ - public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Log a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. - */ - public void config(String sourceClass, String sourceMethod, String msg); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @param thrown - * Throwable associated with log message. - */ - public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Trace a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. - */ - public void fine(String sourceClass, String sourceMethod, String msg); - - /** - * Trace a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. The - * formatter uses java.text.MessageFormat style formatting to - * format parameters, so for example a format string "{0} {1}" - * would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); - - /** - * Trace a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. - */ - public void finer(String sourceClass, String sourceMethod, String msg); - - /** - * Trace a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. The - * formatter uses java.text.MessageFormat style formatting to - * format parameters, so for example a format string "{0} {1}" - * would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); - - /** - * Trace a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. - */ - public void finest(String sourceClass, String sourceMethod, String msg); - - /** - * Trace a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. The - * formatter uses java.text.MessageFormat style formatting to - * format parameters, so for example a format string "{0} {1}" - * would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param level - * One of the message level identifiers, e.g. SEVERE. - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message, may be null. - * @param thrown - * Throwable associated with log message. - */ - public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Log a trace message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param level - * One of the message level identifiers, e.g. SEVERE. - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. The - * formatter uses java.text.MessageFormat style formatting to - * format parameters, so for example a format string "{0} {1}" - * would format two inserts into the message. - * @param inserts - * Array of parameters to the message, may be null. - * @param ex - * Throwable associated with log message. - */ - public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); - - /** - * Format a log message without causing it to be written to the log. - * - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @return The formatted message for the current locale. - */ - public String formatMessage(String msg, Object[] inserts); - - public void dumpTrace(); -} \ No newline at end of file diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java deleted file mode 100644 index 08ececb..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java +++ /dev/null @@ -1,155 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.logging; - -import java.lang.reflect.Method; - -/** - * LoggerFactory will create a logger instance ready for use by the caller. - * - * The default is to create a logger that utilises the Java's built in - * logging facility java.util.logging (JSR47). It is possible to override - * this for systems where JSR47 is not available or an alternative logging - * facility is needed by using setLogger and passing the the class name of - * a logger that implements {@link Logger} - */ -import java.util.MissingResourceException; -import java.util.ResourceBundle; -/** - * A factory that returns a logger for use by the MQTT client. - * - * The default log and trace facility uses Java's build in log facility:- - * java.util.logging. For systems where this is not available or where - * an alternative logging framework is required the logging facility can be - * replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)} - * which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger} - * interface. - */ -public class LoggerFactory { - /** - * Default message catalog. - */ - public final static String MQTT_CLIENT_MSG_CAT = "org.eclipse.paho.client.mqttv3.internal.nls.logcat"; - private static final String CLASS_NAME = LoggerFactory.class.getName(); - - private static String overrideloggerClassName = null; - /** - * Default logger that uses java.util.logging. - */ - private static String jsr47LoggerClassName = JSR47Logger.class.getName(); - - /** - * Find or create a logger for a named package/class. - * If a logger has already been created with the given name - * it is returned. Otherwise a new logger is created. By default a logger - * that uses java.util.logging will be returned. - * - * @param messageCatalogName the resource bundle containing the logging messages. - * @param loggerID unique name to identify this logger. - * @return a suitable Logger. - */ - public static Logger getLogger(String messageCatalogName, String loggerID) { - String loggerClassName = overrideloggerClassName; - Logger logger = null; - - if (loggerClassName == null) { - loggerClassName = jsr47LoggerClassName; - } -// logger = getJSR47Logger(ResourceBundle.getBundle(messageCatalogName), loggerID, null) ; - logger = getLogger(loggerClassName, ResourceBundle.getBundle(messageCatalogName), loggerID, null) ; -// } - - if (null == logger) { - throw new MissingResourceException("Error locating the logging class", CLASS_NAME, loggerID); - } - - return logger; - } - - - /** - * Return an instance of a logger - * - * @param the class name of the load to load - * @param messageCatalog the resource bundle containing messages - * @param loggerID an identifier for the logger - * @param resourceName a name or context to associate with this logger instance. - * @return a ready for use logger - */ - private static Logger getLogger(String loggerClassName, ResourceBundle messageCatalog, String loggerID, String resourceName) { //, FFDC ffdc) { - Logger logger = null; - Class logClass = null; - - try { - logClass = Class.forName(loggerClassName); - } catch (NoClassDefFoundError ncdfe) { - return null; - } catch (ClassNotFoundException cnfe) { - return null; - } - if (null != logClass) { - // Now instantiate the log - try { - logger = (Logger)logClass.newInstance(); - } catch (IllegalAccessException e) { - return null; - } catch (InstantiationException e) { - return null; - } catch (ExceptionInInitializerError e) { - return null; - } catch (SecurityException e) { - return null; - } - logger.initialise(messageCatalog, loggerID, resourceName); - } - - return logger; - } - - /** - * When run in JSR47, this allows access to the properties in the logging.properties - * file. - * If not run in JSR47, or the property isn't set, returns null. - * @param name the property to return - * @return the property value, or null if it isn't set or JSR47 isn't being used - */ - public static String getLoggingProperty(String name) { - String result = null; - try { - // Hide behind reflection as java.util.logging is guaranteed to be - // available. - Class logManagerClass = Class.forName("java.util.logging.LogManager"); - Method m1 = logManagerClass.getMethod("getLogManager", new Class[]{}); - Object logManagerInstance = m1.invoke(null, null); - Method m2 = logManagerClass.getMethod("getProperty", new Class[]{String.class}); - result = (String)m2.invoke(logManagerInstance,new Object[]{name}); - } catch(Exception e) { - // Any error, assume JSR47 isn't available and return null - result = null; - } - return result; - } - - /** - * Set the class name of the logger that the LoggerFactory will load - * If not set getLogger will attempt to create a logger - * appropriate for the platform. - * @param loggerClassName - Logger implementation class name to use. - */ - public static void setLogger(String loggerClassName) { - LoggerFactory.overrideloggerClassName = loggerClassName; - } -} \ No newline at end of file diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java deleted file mode 100644 index 63deded..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - */ - -package org.eclipse.paho.client.mqttv3.logging; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.text.MessageFormat; -import java.util.Date; -import java.util.logging.Formatter; -import java.util.logging.LogRecord; - -/** - * SimpleLogFormatter prints a single line - * log record in human readable form. - */ -public class SimpleLogFormatter extends Formatter { - - private static final String LS = System.getProperty("line.separator"); - /** - * Constructs a SimpleFormatter object. - */ - public SimpleLogFormatter() { - super(); - } - - /** - * Format the logrecord as a single line with well defined columns. - */ - public String format(LogRecord r) { - StringBuffer sb = new StringBuffer(); - sb.append(r.getLevel().getName()).append("\t"); - sb.append(MessageFormat.format("{0, date, yy-MM-dd} {0, time, kk:mm:ss.SSSS} ", - new Object[] { new Date(r.getMillis()) })+"\t"); - String cnm = r.getSourceClassName(); - String cn=""; - if (cnm != null) { - int cnl = cnm.length(); - if (cnl>20) { - cn = r.getSourceClassName().substring(cnl-19); - } else { - char sp[] = {' '}; - StringBuffer sb1= new StringBuffer().append(cnm); - cn = sb1.append(sp,0, 1).toString(); - } - } - sb.append(cn).append("\t").append(" "); - sb.append(left(r.getSourceMethodName(),23,' ')).append("\t"); - sb.append(r.getThreadID()).append("\t"); - sb.append(formatMessage(r)).append(LS); - if (null != r.getThrown()) { - sb.append("Throwable occurred: "); - Throwable t = r.getThrown(); - PrintWriter pw = null; - try { - StringWriter sw = new StringWriter(); - pw = new PrintWriter(sw); - t.printStackTrace(pw); - sb.append(sw.toString()); - } finally { - if (pw != null) { - try { - pw.close(); - } catch (Exception e) { - // ignore - } - } - } - } - return sb.toString(); - } - - /** - * Left justify a string. - * - * @param s the string to justify - * @param width the field width to justify within - * @param fillChar the character to fill with - * - * @return the justified string. - */ - public static String left(String s, int width, char fillChar) { - if (s.length() >= width) { - return s; - } - StringBuffer sb = new StringBuffer(width); - sb.append(s); - for (int i = width - s.length(); --i >= 0;) { - sb.append(fillChar); - } - return sb.toString(); - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties b/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties deleted file mode 100644 index 0626551..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties +++ /dev/null @@ -1,83 +0,0 @@ -# Properties file which configures the operation of the JDK logging facility. -# -# The configuration in this file is the suggesgted configuration -# for collecting trace for helping debug problems related to the -# Paho MQTT client. It configures trace to be continuosly collected -# in memory with minimal impact on performance. -# -# When the push trigger (by default a Severe level message) or a -# specific request is made to "push" the in memory trace then it -# is "pushed" to the configured target handler. By default -# this is the standard java.util.logging.FileHandler. The Paho Debug -# class can be used to push the memory trace to its target -# -# To enable trace either: -# - use this properties file as is and set the logging facility up -# to use it by configuring the util logging system property e.g. -# -# >java -Djava.util.logging.config.file=\jsr47min.properties -# -# - This contents of this file can also be merged with another -# java.util.logging config file to ensure provide wider logging -# and trace including Paho trace - -# Global logging properties. -# ------------------------------------------ -# The set of handlers to be loaded upon startup. -# Comma-separated list of class names. -# - Root handlers are not enabled by default - just handlers on the Paho packages. -#handlers=java.util.logging.MemoryHandler,java.util.logging.FileHandler, java.util.logging.ConsoleHandler - -# Default global logging level. -# Loggers and Handlers may override this level -#.level=INFO - -# Loggers -# ------------------------------------------ -# A memoryhandler is attached to the paho packages -# and the level specified to collected all trace related -# to paho packages. This will override any root/global -# level handlers if set. -org.eclipse.paho.client.mqttv3.handlers=java.util.logging.MemoryHandler -org.eclipse.paho.client.mqttv3.level=ALL -# It is possible to set more granular trace on a per class basis e.g. -#org.eclipse.paho.client.mqttv3.internal.ClientComms.level=ALL - -# Handlers -# ----------------------------------------- -# Note: the target handler that is associated with the MemoryHandler is not a root handler -# and hence not returned when getting the handlers from root. It appears accessing -# target handler programatically is not possible as target is a private variable in -# class MemoryHandler -java.util.logging.MemoryHandler.level=FINEST -java.util.logging.MemoryHandler.size=10000 -java.util.logging.MemoryHandler.push=SEVERE -java.util.logging.MemoryHandler.target=java.util.logging.FileHandler -#java.util.logging.MemoryHandler.target=java.util.logging.ConsoleHandler - - -# --- FileHandler --- -# Override of global logging level -java.util.logging.FileHandler.level=ALL - -# Naming style for the output file: -# (The output file is placed in the directory -# defined by the "user.home" System property.) -# See java.util.logging for more options -java.util.logging.FileHandler.pattern=%h/paho%u.log - -# Limiting size of output file in bytes: -java.util.logging.FileHandler.limit=200000 - -# Number of output files to cycle through, by appending an -# integer to the base file name: -java.util.logging.FileHandler.count=3 - -# Style of output (Simple or XML): -java.util.logging.FileHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter - -# --- ConsoleHandler --- -# Override of global logging level -#java.util.logging.ConsoleHandler.level=INFO -#java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter -#java.util.logging.ConsoleHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/package.html b/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/package.html deleted file mode 100644 index a679cf1..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/logging/package.html +++ /dev/null @@ -1,18 +0,0 @@ - -Provides facilities to write and format log and trace to help debug problems. - -

The default log and trace facility uses Java's build in log facility:- -java.util.logging. For systems where this is not available or where -an alternative logging framework is required the logging facility can be -replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)} -which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger} -interface. - -

A sample java.util.logging properties file - jsr47min.properties is provided that demonstrates -how to run with a memory based trace facility that runs with minimal performance -overhead. The memory buffer can be dumped when a log/trace record is written matching -the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. -{@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy -to dump the memory buffer as well as other useful debug info. - - \ No newline at end of file diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/package.html b/EasyModbus/src/org/eclipse/paho/client/mqttv3/package.html deleted file mode 100644 index 00ca45c..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/package.html +++ /dev/null @@ -1,136 +0,0 @@ - -Contains a programming interface enabling applications to communicate with an MQTT server. - -

-The MQ Telemetry Transport (MQTT) is a lightweight broker-based publish/subscribe -messaging protocol designed to be open, simple, lightweight and easy to implement. -These characteristics make it ideal for use in constrained environments, for example, -but not limited to: -

    -
  • Where the network is expensive, has low bandwidth or is unreliable such as mobile and vsat networks -
  • When run on an embedded or mobile device with limited processor, memory or battery -
-

Features of the protocol include: -

    -
  • The publish/subscribe message pattern to provide one-to-many message - distribution and decoupling of applications -
  • A messaging transport that is agnostic to the content of the payload -
  • The use of TCP/IP to provide network connectivity -
  • The use of SSL/TLS to provide network security and trust -
  • Three qualities of service for message delivery which are maintained across - network, client and server breaks. -
      -
    • "At most once", where messages are delivered according to the best efforts - of the underlying TCP/IP network. Message loss or duplication can occur. - This level could be used, for example, with ambient sensor data where it - does not matter if an individual reading is lost as the next one will be published soon after. -
    • "At least once", where messages are assured to arrive but duplicates may occur. -
    • "Exactly once", where message are assured to arrive exactly once. This - level could be used, for example, with billing systems where duplicate or - lost messages could lead to incorrect charges being applied. -
    - The quality of service for message delivery is met even if the network connection - breaks, or the client or the server stop while a message is being delivered -
  • A small transport overhead (the fixed-length header is just 2 bytes), and - protocol exchanges minimised to reduce network traffic -
  • A mechanism to notify interested parties to an abnormal disconnection of - a client using the Last Will and Testament feature -
- -

The basic means of operating the client is:

-
    -
  1. Create an instance of {@link org.eclipse.paho.client.mqttv3.MqttClient} or - {@link org.eclipse.paho.client.mqttv3.MqttAsyncClient}, providing - the address of an MQTT server and a unique client identifier.
  2. -
  3. connect to the server
  4. -
  5. Exchange messages with the server: -
      -
    • publish messages to the server - specifying a topic as the destination on the server
    • -
    • subscribe to one more topics. The server will send any messages - it receives on those topics to the client. The client will be informed when a message - arrives via a callback -
    -
  6. disconnect from the server.
  7. -
- -

The programming model and concepts like the protocol are small and easy to use. Key concepts -to use when creating MQTT application include: -

    -
  • Every client instance that connects to an MQTT server must have a unique client identifier. - If a second instance of a client with the same ID connects to a server the first instance will be - disconnected. -
  • For message delivery to be reliable and withstand normal and abnormal network breaks together with client - and server outages the client must use a persistent store to hold messages while they are being delivered. This is - the default case where a file based persistent store - {@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} is used. -
  • When connecting the {@link org.eclipse.paho.client.mqttv3.MqttConnectOptions#setCleanSession(boolean) cleansession} - option has a big impact on the operation of the client. If set to false: -
      -
    • Message delivery will match the quality of service specified when the message was published even across - failures of the network, client or server -
    • The server will store messages for active subscriptions on behalf of the client when the client is not connected. - The server will deliver these messages to the client the next time it connects. -
    - If set to true: -
      -
    • Any state stored on the client and server related to the client will be cleansed - before the connection is fully started. Subscriptions from earlier sessions will be unsubscribed - and any messages still in-flight from previous sessions will be deleted. -
    • When the client disconnects either as the result of the application requesting a disconnect - or a network failure, state related to the client will be cleansed just as at connect time. -
    • Messages will only be delivered to the quality of service requested at publish time if - the connection is maintained while the message is being delivered -
    -
  • When subscribing for messages the subscription can be for an absolute topic or a wildcarded topic. -
  • When unsubscribing the topic to be unsubscribed must match one specified on an earlier subscribe. -
  • There are two MQTT client libraries to choose from: -
      -
    1. {@link org.eclipse.paho.client.mqttv3.IMqttAsyncClient MqttAsyncClient} which provides a non-blocking interface where - methods return before the requested operation has completed. The completion of the operation - can be monitored by in several ways: -
        -
      • Use the {@link org.eclipse.paho.client.mqttv3.IMqttToken#waitForCompletion waitForCompletion} - call on the token returned from the operation. This will block - until the operation completes. -
      • Pass a {@link org.eclipse.paho.client.mqttv3.IMqttActionListener IMqttActionListener} - to the operation. The listener will then be called back when the operation completes. -
      • Set a {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client. It - will be notified when a message arrives, a message have been delivered to the server and when the - connection to the server is lost. -
      -
    2. {@link org.eclipse.paho.client.mqttv3.IMqttClient MqttClient} where methods block until - the operation has completed. -
    -
  • For both the blocking and non-blocking clients some operations are asynchronous. This includes: -
      -
    • Notification that a new message has arrived: - {@link org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived messageArrived}. -
    • Notification that the connection to the server has broken: - {@link org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost connectionLost}. -
    • Notification that a message has been delivered to the server: - {@link org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete deliveryComplete}. -
    - A client registers interest in these notifications by registering a - {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client -
  • There are a number of programs that demonstrate the different modes of - writing MQTT applications -
      -
    • {@link org.eclipse.paho.sample.mqttv3app.Sample} uses the blocking client interface -
    • {@link org.eclipse.paho.sample.mqttv3app.SampleAsyncCallBack} uses the asynchronous client with - callbacks which are notified when an operation completes -
    • {@link org.eclipse.paho.sample.mqttv3app.SampleAsyncWait} uses the asynchronous client and - shows how to use the token returned from each operation to block until the operation completes. -
    -
  • {@link org.eclipse.paho.client.mqttv3.MqttConnectOptions MqttConnectOptions} can be used to override the - default connection options. This includes: -
      -
    • Setting the cleansession flag -
    • Specifying a list of MQTT servers that the client can attempt to connect to -
    • Set a keepalive interval -
    • Setting the last will and testament -
    • Setting security credentials -
    -
- - \ No newline at end of file diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java deleted file mode 100644 index c684ddf..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.persist; - -import java.util.Enumeration; -import java.util.Hashtable; - -import org.eclipse.paho.client.mqttv3.MqttClientPersistence; -import org.eclipse.paho.client.mqttv3.MqttPersistable; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; - -/** - * Persistence that uses memory - * - * In cases where reliability is not required across client or device - * restarts memory this memory peristence can be used. In cases where - * reliability is required like when clean session is set to false - * then a non-volatile form of persistence should be used. - * - */ -public class MemoryPersistence implements MqttClientPersistence { - - private Hashtable data; - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#close() - */ - public void close() throws MqttPersistenceException { - data.clear(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#keys() - */ - public Enumeration keys() throws MqttPersistenceException { - return data.keys(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#get(java.lang.String) - */ - public MqttPersistable get(String key) throws MqttPersistenceException { - return (MqttPersistable)data.get(key); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#open(java.lang.String, java.lang.String) - */ - public void open(String clientId, String serverURI) throws MqttPersistenceException { - this.data = new Hashtable(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#put(java.lang.String, org.eclipse.paho.client.mqttv3.MqttPersistable) - */ - public void put(String key, MqttPersistable persistable) throws MqttPersistenceException { - data.put(key, persistable); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#remove(java.lang.String) - */ - public void remove(String key) throws MqttPersistenceException { - data.remove(key); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#clear() - */ - public void clear() throws MqttPersistenceException { - data.clear(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#containsKey(java.lang.String) - */ - public boolean containsKey(String key) throws MqttPersistenceException { - return data.containsKey(key); - } -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java deleted file mode 100644 index 19d3d31..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java +++ /dev/null @@ -1,306 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.persist; - -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.util.Enumeration; -import java.util.Vector; - -import org.eclipse.paho.client.mqttv3.MqttClientPersistence; -import org.eclipse.paho.client.mqttv3.MqttPersistable; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; -import org.eclipse.paho.client.mqttv3.internal.FileLock; -import org.eclipse.paho.client.mqttv3.internal.MqttPersistentData; - -/** - * An implementation of the {@link MqttClientPersistence} interface that provides - * file based persistence. - * - * A directory is specified when the Persistence object is created. When the persistence - * is then opened (see {@link #open(String, String)}), a sub-directory is made beneath the base - * for this client ID and connection key. This allows one persistence base directory - * to be shared by multiple clients. - * - * The sub-directory's name is created from a concatenation of the client ID and connection key - * with any instance of '/', '\\', ':' or ' ' removed. - */ -public class MqttDefaultFilePersistence implements MqttClientPersistence { - private static final String MESSAGE_FILE_EXTENSION = ".msg"; - private static final String MESSAGE_BACKUP_FILE_EXTENSION = ".bup"; - private static final String LOCK_FILENAME = ".lck"; - - private File dataDir; - private File clientDir = null; - private FileLock fileLock = null; - - //TODO - private static FilenameFilter FILENAME_FILTER; - - private static FilenameFilter getFilenameFilter(){ - if(FILENAME_FILTER == null){ - FILENAME_FILTER = new PersistanceFileNameFilter(MESSAGE_FILE_EXTENSION); - } - return FILENAME_FILTER; - } - - public MqttDefaultFilePersistence() { //throws MqttPersistenceException { - this(System.getProperty("user.dir")); - } - - /** - * Create an file-based persistent data store within the specified directory. - * @param directory the directory to use. - */ - public MqttDefaultFilePersistence(String directory) { //throws MqttPersistenceException { - dataDir = new File(directory); - } - - public void open(String clientId, String theConnection) throws MqttPersistenceException { - - if (dataDir.exists() && !dataDir.isDirectory()) { - throw new MqttPersistenceException(); - } else if (!dataDir.exists() ) { - if (!dataDir.mkdirs()) { - throw new MqttPersistenceException(); - } - } - if (!dataDir.canWrite()) { - throw new MqttPersistenceException(); - } - - - StringBuffer keyBuffer = new StringBuffer(); - for (int i=0;i -Contains implementations of the MqttClientPersistence interface. - -

-An MQTT client needs a persistence mechanism to store messages while they -are in the process of being delivered. This package contains several -implementations of the interface. If a persistence class is not -specified on the constructor to an MQTT client, -{@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} -is used by default. - - \ No newline at end of file diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/util/Debug.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/util/Debug.java deleted file mode 100644 index 7958182..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/util/Debug.java +++ /dev/null @@ -1,183 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.util; - -import java.util.Enumeration; -import java.util.Properties; - -import org.eclipse.paho.client.mqttv3.internal.ClientComms; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Utility to help debug problems with the Paho MQTT client - * Once initialised a call to dumpClientDebug will force any memory trace - * together with pertinent client and system state to the main log facility. - * - * No client wide lock is taken when the dump is progress. This means the - * set of client state may not be consistent as the client can still be - * processing work while the dump is in progress. - */ -public class Debug { - - private static final String CLASS_NAME = ClientComms.class.getName(); - private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME); - private static final String separator = "=============="; - private static final String lineSep = System.getProperty("line.separator","\n"); - - private String clientID; - private ClientComms comms; - - /** - * Set the debug facility up for a specific client - * @param clientID the ID of the client being debugged - * @param comms the ClientComms object of the client being debugged - */ - public Debug(String clientID, ClientComms comms) { - this.clientID = clientID; - this.comms = comms; - log.setResourceName(clientID); - } - - /** - * Dump maximum debug info. - * This includes state specific to a client as well - * as debug that is JVM wide like trace and system properties. - * All state is written as debug log entries. - */ - public void dumpClientDebug() { - dumpClientComms(); - dumpConOptions(); - dumpClientState(); - dumpBaseDebug(); - } - - /** - * Dump of JVM wide debug info. - * This includes trace and system properties. - * Includes trace and system properties - */ - public void dumpBaseDebug() { - dumpVersion(); - dumpSystemProperties(); - dumpMemoryTrace(); - } - - /** - * If memory trace is being used a request is made to push it - * to the target handler. - */ - protected void dumpMemoryTrace() { - log.dumpTrace(); - } - - /** - * Dump information that show the version of the MQTT client being used. - */ - protected void dumpVersion() { - StringBuffer vInfo = new StringBuffer(); - vInfo.append(lineSep+separator+" Version Info "+ separator+lineSep); - vInfo.append(left("Version",20,' ') + ": "+ ClientComms.VERSION + lineSep); - vInfo.append(left("Build Level",20,' ') + ": "+ ClientComms.BUILD_LEVEL + lineSep); - vInfo.append(separator+separator+separator+lineSep); - log.fine(CLASS_NAME,"dumpVersion", vInfo.toString()); - } - - /** - * Dump the current set of system.properties to a log record - */ - public void dumpSystemProperties() { - - Properties sysProps = System.getProperties(); - log.fine(CLASS_NAME,"dumpSystemProperties", dumpProperties(sysProps, "SystemProperties").toString()); - } - - /** - * Dump interesting variables from ClientState - */ - public void dumpClientState() { - Properties props = null; - if (comms != null && comms.getClientState() != null ) { - props = comms.getClientState().getDebug(); - log.fine(CLASS_NAME,"dumpClientState", dumpProperties(props, clientID + " : ClientState").toString()); - } - } - - /** - * Dump interesting variables from ClientComms - */ - public void dumpClientComms() { - Properties props = null; - if (comms != null) { - props = comms.getDebug(); - log.fine(CLASS_NAME,"dumpClientComms", dumpProperties(props, clientID + " : ClientComms").toString()); - } - } - - /** - * Dump Connection options - */ - public void dumpConOptions() { - Properties props = null; - if (comms != null) { - props = comms.getConOptions().getDebug(); - log.fine(CLASS_NAME,"dumpConOptions", dumpProperties(props, clientID + " : Connect Options").toString()); - } - } - - - /** - * Return a set of properties as a formatted string - * @param props The Dump Properties - * @param name The associated name - * @return a set of properties as a formatted string - */ - public static String dumpProperties(Properties props, String name) { - - StringBuffer propStr = new StringBuffer(); - Enumeration propsE = props.propertyNames(); - propStr.append(lineSep+separator+" "+name+" "+ separator+lineSep); - while (propsE.hasMoreElements()) { - String key = (String)propsE.nextElement(); - propStr.append(left(key,28,' ') + ": "+ props.get(key)+lineSep); - } - propStr.append(separator+separator+separator+lineSep); - - return propStr.toString(); - } - - /** - * Left justify a string. - * - * @param s the string to justify - * @param width the field width to justify within - * @param fillChar the character to fill with - * - * @return the justified string. - */ - public static String left(String s, int width, char fillChar) { - if (s.length() >= width) { - return s; - } - StringBuffer sb = new StringBuffer(width); - sb.append(s); - for (int i = width - s.length(); --i >= 0;) { - sb.append(fillChar); - } - return sb.toString(); - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/util/Strings.java b/EasyModbus/src/org/eclipse/paho/client/mqttv3/util/Strings.java deleted file mode 100644 index 40b31a5..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/util/Strings.java +++ /dev/null @@ -1,173 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Bin Zhang - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.util; - -/** - * String helper - */ -public final class Strings { - // Represents a failed index search. - private static final int INDEX_NOT_FOUND = -1; - - /** - * Checks if the CharSequence equals any character in the given set of characters. - * - * @param cs the CharSequence to check - * @param strs the set of characters to check against - * @return true if equals any - */ - public static boolean equalsAny(CharSequence cs, CharSequence[] strs) { - boolean eq = false; - if (cs == null) { - eq = strs == null; - } - - if (strs != null) { - for (int i = 0; i < strs.length; i++) { - eq = eq || strs[i].equals(cs); - } - } - - return eq; - } - - /** - * Checks if the CharSequence contains any character in the given set of characters. - * - * @param cs the CharSequence to check, may be null - * @param searchChars the chars to search for, may be null - * @return the {@code true} if any of the chars are found, {@code false} if no match or null input - */ - public static boolean containsAny(CharSequence cs, CharSequence searchChars) { - if (searchChars == null) { - return false; - } - return containsAny(cs, toCharArray(searchChars)); - } - - /** - * Checks if the CharSequence contains any character in the given set of characters. - * - * @param cs the CharSequence to check, may be null - * @param searchChars the chars to search for, may be null - * @return the {@code true} if any of the chars are found, {@code false} if no match or null input - */ - public static boolean containsAny(CharSequence cs, char[] searchChars) { - if (isEmpty(cs) || isEmpty(searchChars)) { - return false; - } - int csLength = cs.length(); - int searchLength = searchChars.length; - int csLast = csLength - 1; - int searchLast = searchLength - 1; - for (int i = 0; i < csLength; i++) { - char ch = cs.charAt(i); - for (int j = 0; j < searchLength; j++) { - if (searchChars[j] == ch) { - if (Character.isHighSurrogate(ch)) { - if (j == searchLast) { - // missing low surrogate, fine, like String.indexOf(String) - return true; - } - if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) { - return true; - } - } - else { - // ch is in the Basic Multilingual Plane - return true; - } - } - } - } - return false; - } - - - /** - * Checks if a CharSequence is empty ("") or null. - * - * @param cs the CharSequence to check, may be null - * @return {@code true} if the CharSequence is empty or null - */ - public static boolean isEmpty(CharSequence cs) { - return cs == null || cs.length() == 0; - } - - /** - * @param array - */ - private static boolean isEmpty(char[] array) { - return array == null || array.length == 0; - } - - /** - * Green implementation of toCharArray. - * - * @param cs the {@code CharSequence} to be processed - * @return the resulting char array - */ - private static char[] toCharArray(CharSequence cs) { - if (cs instanceof String) { - return ((String) cs).toCharArray(); - } - else { - int sz = cs.length(); - char[] array = new char[cs.length()]; - for (int i = 0; i < sz; i++) { - array[i] = cs.charAt(i); - } - return array; - } - } - - /** - * Counts how many times the substring appears in the larger string. - * - * @param str the CharSequence to check, may be null - * @param sub the substring to count, may be null - * @return the number of occurrences, 0 if either CharSequence is {@code null} - */ - public static int countMatches(CharSequence str, CharSequence sub) { - if (isEmpty(str) || isEmpty(sub)) { - return 0; - } - int count = 0; - int idx = 0; - while ((idx = indexOf(str, sub, idx)) != INDEX_NOT_FOUND) { - count++; - idx += sub.length(); - } - return count; - } - - /** - * Used by the indexOf(CharSequence methods) as a green implementation of indexOf. - * - * @param cs the {@code CharSequence} to be processed - * @param searchChar the {@code CharSequence} to be searched for - * @param start the start index - * @return the index where the search sequence was found - */ - private static int indexOf(CharSequence cs, CharSequence searchChar, int start) { - return cs.toString().indexOf(searchChar.toString(), start); - } - - private Strings() { - // prevented from constructing objects - } - -} diff --git a/EasyModbus/src/org/eclipse/paho/client/mqttv3/util/package.html b/EasyModbus/src/org/eclipse/paho/client/mqttv3/util/package.html deleted file mode 100644 index 19c41d8..0000000 --- a/EasyModbus/src/org/eclipse/paho/client/mqttv3/util/package.html +++ /dev/null @@ -1,5 +0,0 @@ - -Provides helpers and utilities. - - - \ No newline at end of file