From b29a3cd466d3014fe5d6d64425898b53b07a5270 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Sat, 2 Mar 2024 19:45:22 +0300 Subject: [PATCH 01/23] add auto ping-pong --- src/main/java/com/hosopy/actioncable/Command.java | 4 ++++ src/main/java/com/hosopy/actioncable/Connection.java | 7 +++++++ .../java/com/hosopy/actioncable/ConnectionMonitor.java | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/Command.java b/src/main/java/com/hosopy/actioncable/Command.java index 659ba8f..4c60dde 100644 --- a/src/main/java/com/hosopy/actioncable/Command.java +++ b/src/main/java/com/hosopy/actioncable/Command.java @@ -41,6 +41,10 @@ static Command message(String identifier, JsonObject params) { return new Command("message", identifier, params.toString()); } + static Command pong() { + return new Command("pong", null); + } + /*package*/ String toJson() { return GSON.toJson(this); } diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index de8932a..634bfc0 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -90,6 +90,13 @@ public static class Options { */ public OkHttpClientFactory okHttpClientFactory; + /** + * Whether to pingPong is automatically. + *

+ *

If pingPong is true, the client attempts to response to the server ping by send 'pong' command.

+ */ + public boolean pingPong = false; + /** * The ping interval on how often a ping is sent over the websocket connection *

diff --git a/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java b/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java index 54bf533..afdf254 100644 --- a/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java +++ b/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java @@ -31,6 +31,8 @@ public class ConnectionMonitor { private long stoppedAt = 0; // milliseconds private int reconnectAttempts = 0; + + private boolean pingPong = false; /*package*/ ConnectionMonitor(Connection connection, Connection.Options options) { this.connection = connection; @@ -40,6 +42,7 @@ public class ConnectionMonitor { this.reconnectionMaxAttempts = options.reconnectionMaxAttempts; this.reconnectionDelay = options.reconnectionDelay; this.reconnectionDelayMax = options.reconnectionDelayMax; + this.pingPong = options.pingPong; } /*package*/ void recordConnect() { @@ -54,6 +57,9 @@ public class ConnectionMonitor { /*package*/ void recordPing() { pingedAt = now(); + if(pingPong) { + connection.send(Command.pong().toJson()); + } } /*package*/ void start() { From 0a8bd6671fdca8891bb19f8080818ce0884beb3d Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Sat, 2 Mar 2024 20:27:02 +0300 Subject: [PATCH 02/23] add GeneralListener, make send() public. --- build.gradle | 3 ++- .../java/com/hosopy/actioncable/Command.java | 6 +++--- .../com/hosopy/actioncable/Connection.java | 9 +++++++- .../java/com/hosopy/actioncable/Consumer.java | 14 ++++++++++++- .../hosopy/actioncable/GeneralListener.java | 21 +++++++++++++++++++ 5 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/hosopy/actioncable/GeneralListener.java diff --git a/build.gradle b/build.gradle index e224c79..8d235a7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,8 @@ group 'com.hosopy' version '0.2.0' -apply plugin: 'java' +//apply plugin: 'java' +apply plugin: 'java-library' repositories { mavenCentral() diff --git a/src/main/java/com/hosopy/actioncable/Command.java b/src/main/java/com/hosopy/actioncable/Command.java index 4c60dde..80bf948 100644 --- a/src/main/java/com/hosopy/actioncable/Command.java +++ b/src/main/java/com/hosopy/actioncable/Command.java @@ -3,7 +3,7 @@ import com.google.gson.*; import com.google.gson.annotations.Expose; -class Command { +public class Command { private static final Gson GSON = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); @@ -23,7 +23,7 @@ private Command(String command, String identifier) { this(command, identifier, null); } - private Command(String command, String identifier, String data) { + public Command(String command, String identifier, String data) { this.command = command; this.identifier = identifier; this.data = data; @@ -37,7 +37,7 @@ static Command unsubscribe(String identifier) { return new Command("unsubscribe", identifier); } - static Command message(String identifier, JsonObject params) { + public static Command message(String identifier, JsonObject params) { return new Command("message", identifier, params.toString()); } diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index 634bfc0..ac46260 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -122,6 +122,8 @@ public interface OkHttpClientFactory { private boolean isReopening = false; + private GeneralListener generalListener; + /*package*/ Connection(URI uri, Options options) { this.uri = uri; this.options = options; @@ -241,7 +243,9 @@ private void doOpen() { private void doSend(String data) { if (webSocket != null) { - webSocket.send(data); + boolean isSent = webSocket.send(data); + if (isSent && generalListener != null) + generalListener.onSend(data); } } @@ -260,6 +264,9 @@ private void fireOnFailure(Exception e) { } } + /*package*/ void setGeneralListener(GeneralListener listener) { + generalListener = listener; + } private WebSocketListener webSocketListener = new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) { diff --git a/src/main/java/com/hosopy/actioncable/Consumer.java b/src/main/java/com/hosopy/actioncable/Consumer.java index d720b47..c6ba977 100644 --- a/src/main/java/com/hosopy/actioncable/Consumer.java +++ b/src/main/java/com/hosopy/actioncable/Consumer.java @@ -28,6 +28,8 @@ public static class Options extends Connection.Options { private Subscriptions subscriptions; + private GeneralListener generalListener; + /*package*/ Consumer(URI uri, Options options) { this.subscriptions = new Subscriptions(this); this.connection = new Connection(uri, options); @@ -37,16 +39,19 @@ public static class Options extends Connection.Options { public void onOpen() { connectionMonitor.recordConnect(); subscriptions.reload(); + if (generalListener != null) generalListener.onOpen(); } @Override public void onFailure(Exception e) { subscriptions.notifyFailed(new ActionCableException(e)); + if (generalListener != null) generalListener.onFailure(new ActionCableException(e)); } @Override public void onMessage(String string) { final Message message = Message.fromJson(string); + if (generalListener != null) generalListener.onMessage(string); if (message.isWelcome()) { onOpen(); } else if (message.isPing()) { @@ -64,10 +69,12 @@ public void onMessage(String string) { public void onClosing() { subscriptions.notifyDisconnected(); connectionMonitor.recordDisconnect(); + if (generalListener != null) generalListener.onClosing(); } @Override public void onClosed() { + if (generalListener != null) generalListener.onClosed(); } }); } @@ -107,7 +114,12 @@ public void unsubscribeAndDisconnect() { connectionMonitor.stop(); } - /*package*/ boolean send(Command command) { + public void setGeneralListener(GeneralListener listener) { + generalListener = listener; + connection.setGeneralListener(listener); + } + + public boolean send(Command command) { return connection.send(command.toJson()); } diff --git a/src/main/java/com/hosopy/actioncable/GeneralListener.java b/src/main/java/com/hosopy/actioncable/GeneralListener.java new file mode 100644 index 0000000..816eafe --- /dev/null +++ b/src/main/java/com/hosopy/actioncable/GeneralListener.java @@ -0,0 +1,21 @@ +package com.hosopy.actioncable; + + +/** + * GeneralListener provides a number of callbacks for calling remote procedure calls + * on the corresponding Channel instance on the server side. + * + */ +public interface GeneralListener { + public void onMessage(String string); + /** + * onOpen can be called multi-times + * e.g.[on connection establish, on welcome message received] + * + */ + public void onOpen(); + public void onSend(String data); + public void onClosing(); + public void onClosed(); + public void onFailure(Exception e); +} From 7bcc7f33aa926d0cb1d097f4d3ee88541e008506 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Sun, 3 Mar 2024 22:22:48 +0300 Subject: [PATCH 03/23] update gradle & jitpack.yml file. --- build.gradle | 5 ++--- gradle/wrapper/gradle-wrapper.properties | 2 +- jitpack.yml | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 8d235a7..cfb1da9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,11 @@ group 'com.hosopy' version '0.2.0' -//apply plugin: 'java' -apply plugin: 'java-library' +apply plugin: 'java' repositories { mavenCentral() - jcenter() +// jcenter() google() } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e7ba06c..42c70ae 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip diff --git a/jitpack.yml b/jitpack.yml index 309debd..efde7bf 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,2 +1,2 @@ jdk: - - oraclejdk7 \ No newline at end of file + - openjdk17 From 1565f052dd5e5a048c0a97f928a8512df475b353 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Sun, 15 Sep 2024 01:35:21 +0300 Subject: [PATCH 04/23] update gradle. --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 42c70ae..27182b6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip From 20fd5c6fb4af9ae93ce7d54cc0712dfa43c24623 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Sun, 15 Sep 2024 02:18:53 +0300 Subject: [PATCH 05/23] update libs. --- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index cfb1da9..bba333f 100644 --- a/build.gradle +++ b/build.gradle @@ -18,8 +18,8 @@ dependencies { // Java 7+. These platforms lack support for TLS 1.2 and should not be used. But because // upgrading is difficult we will backport critical fixes to the 3.12.x branch // through December 31, 2020. - implementation 'com.squareup.okhttp3:okhttp:3.12.1' - implementation 'com.google.code.gson:gson:2.3.1' + implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'com.google.code.gson:gson:2.8.9' testImplementation 'junit:junit:4.11' testImplementation 'org.hamcrest:hamcrest-all:1.3' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 27182b6..42c70ae 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip From 6bb4080a4b5f4826adece5f50c07603a9dbb64d2 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 3 Jan 2025 15:43:42 +0300 Subject: [PATCH 06/23] add IDEAL as initialize status. --- src/main/java/com/hosopy/actioncable/Connection.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index ac46260..628204d 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -30,7 +30,8 @@ private enum State { CONNECTING, OPEN, CLOSING, - CLOSED + CLOSED, + IDEAL } /*package*/ interface Listener { @@ -110,7 +111,7 @@ public interface OkHttpClientFactory { } } - private State state = State.CONNECTING; + private State state = State.IDEAL; private URI uri; From b5b4b3f4286bcb1fb012a8c6c6720d28061418c9 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 3 Jan 2025 15:50:52 +0300 Subject: [PATCH 07/23] add WebSocketException --- src/main/java/com/hosopy/actioncable/Connection.java | 3 ++- src/main/java/com/hosopy/actioncable/annotation/Exceptions.kt | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hosopy/actioncable/annotation/Exceptions.kt diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index 628204d..4c3b533 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -1,5 +1,6 @@ package com.hosopy.actioncable; +import com.hosopy.actioncable.annotation.WebSocketException; import com.hosopy.concurrent.EventLoop; import com.hosopy.util.QueryStringUtils; @@ -291,7 +292,7 @@ public void run() { state = State.CLOSED; if (listener != null) { - listener.onFailure((Exception) t); + listener.onFailure(new WebSocketException(t)); } } }); diff --git a/src/main/java/com/hosopy/actioncable/annotation/Exceptions.kt b/src/main/java/com/hosopy/actioncable/annotation/Exceptions.kt new file mode 100644 index 0000000..2d51141 --- /dev/null +++ b/src/main/java/com/hosopy/actioncable/annotation/Exceptions.kt @@ -0,0 +1,3 @@ +package com.hosopy.actioncable.annotation + +class WebSocketException(e: Throwable) : Exception(e) \ No newline at end of file From d0e4f7ef07a2464c525106fb7cf072b0a004cf05 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 3 Jan 2025 16:05:53 +0300 Subject: [PATCH 08/23] add more connection status check functions --- src/main/java/com/hosopy/actioncable/Connection.java | 9 +++++++++ src/main/java/com/hosopy/actioncable/Consumer.java | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index 4c3b533..36daa2e 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -180,6 +180,15 @@ public void run() { /*package*/ boolean isOpen() { return webSocket != null && isState(State.OPEN); } + /*package*/ boolean isConnecting() { + return isState(State.CONNECTING); + } + /*package*/ boolean isClosed() { + return isState(State.CLOSED); + } + /*package*/ boolean isClosing() { + return isState(State.CLOSING); + } /*package*/ boolean send(final String data) { if (isOpen()) { diff --git a/src/main/java/com/hosopy/actioncable/Consumer.java b/src/main/java/com/hosopy/actioncable/Consumer.java index c6ba977..c052610 100644 --- a/src/main/java/com/hosopy/actioncable/Consumer.java +++ b/src/main/java/com/hosopy/actioncable/Consumer.java @@ -127,7 +127,19 @@ public Connection getConnection() { return connection; } + public boolean isConnecting() { + return this.connection != null && this.connection.isConnecting(); + } + public boolean isConnected() { return this.connection != null && this.connection.isOpen(); } + + public boolean isClosing() { + return this.connection != null && this.connection.isClosing(); + } + + public boolean isClosed() { + return this.connection == null || this.connection.isClosed(); + } } From 99bbdff36722d94d2e8bef95ce37632b01447a07 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 3 Jan 2025 16:17:54 +0300 Subject: [PATCH 09/23] add setStaleThresholdInSecond() --- .../com/hosopy/actioncable/ConnectionMonitor.java | 11 +++++++++-- src/main/java/com/hosopy/actioncable/Consumer.java | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java b/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java index afdf254..b74ba5e 100644 --- a/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java +++ b/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java @@ -10,6 +10,8 @@ public class ConnectionMonitor { private static final int STALE_THRESHOLD = 6; // Server::Connections::BEAT_INTERVAL * 2 (missed two pings) + private int staleThresholdInSecond = STALE_THRESHOLD; + private final Connection connection; private final ScheduledExecutorService pollExecutorService; @@ -98,11 +100,11 @@ private void reconnectIfStale() { } private boolean connectionIsStale() { - return secondsSince(pingedAt > 0 ? pingedAt : startedAt) > STALE_THRESHOLD; + return secondsSince(pingedAt > 0 ? pingedAt : startedAt) > staleThresholdInSecond; } private boolean disconnectedRecently() { - return disconnectedAt != 0 && secondsSince(disconnectedAt) < STALE_THRESHOLD; + return disconnectedAt != 0 && secondsSince(disconnectedAt) < staleThresholdInSecond; } private long getInterval() { @@ -121,4 +123,9 @@ private static long now() { private static double clamp(double number, int min, int max) { return Math.max(min, Math.min(max, number)); } + + + /*package*/ void setStaleThresholdInSecond(int staleThresholdInSecond) { + this.staleThresholdInSecond = staleThresholdInSecond; + } } diff --git a/src/main/java/com/hosopy/actioncable/Consumer.java b/src/main/java/com/hosopy/actioncable/Consumer.java index c052610..891813a 100644 --- a/src/main/java/com/hosopy/actioncable/Consumer.java +++ b/src/main/java/com/hosopy/actioncable/Consumer.java @@ -123,6 +123,10 @@ public boolean send(Command command) { return connection.send(command.toJson()); } + public void setStaleThresholdInSecond(int staleThresholdInSecond){ + if(connectionMonitor !=null) connectionMonitor.setStaleThresholdInSecond(staleThresholdInSecond); + } + public Connection getConnection() { return connection; } From be06dd4ecb484e04a0b642d6236131af922c498c Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 3 Jan 2025 16:54:45 +0300 Subject: [PATCH 10/23] add hasSubscription function --- src/main/java/com/hosopy/actioncable/Channel.java | 6 ++++++ src/main/java/com/hosopy/actioncable/Consumer.java | 4 ++++ .../java/com/hosopy/actioncable/SubscriptionProxy.java | 4 ++++ src/main/java/com/hosopy/actioncable/Subscriptions.java | 9 +++++++++ 4 files changed, 23 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/Channel.java b/src/main/java/com/hosopy/actioncable/Channel.java index d4bcaac..c9f5067 100644 --- a/src/main/java/com/hosopy/actioncable/Channel.java +++ b/src/main/java/com/hosopy/actioncable/Channel.java @@ -24,6 +24,7 @@ public class Channel { private final JsonObject params = new JsonObject(); private String identifier; + private String name; /** * Constructor @@ -31,6 +32,7 @@ public class Channel { * @param channel Channel name */ public Channel(String channel) { + this.name = channel; params.addProperty(KEY_CHANNEL, channel); } @@ -92,4 +94,8 @@ private void addParamInternal(String key, JsonElement value) { identifier = null; } } + + public String getName() { + return name; + } } diff --git a/src/main/java/com/hosopy/actioncable/Consumer.java b/src/main/java/com/hosopy/actioncable/Consumer.java index 891813a..01c6f01 100644 --- a/src/main/java/com/hosopy/actioncable/Consumer.java +++ b/src/main/java/com/hosopy/actioncable/Consumer.java @@ -127,6 +127,10 @@ public void setStaleThresholdInSecond(int staleThresholdInSecond){ if(connectionMonitor !=null) connectionMonitor.setStaleThresholdInSecond(staleThresholdInSecond); } + public boolean hasSubscription(String channel) { + return subscriptions != null && subscriptions.hasSubscription(channel); + } + public Connection getConnection() { return connection; } diff --git a/src/main/java/com/hosopy/actioncable/SubscriptionProxy.java b/src/main/java/com/hosopy/actioncable/SubscriptionProxy.java index 3ed2dc6..42bacf7 100644 --- a/src/main/java/com/hosopy/actioncable/SubscriptionProxy.java +++ b/src/main/java/com/hosopy/actioncable/SubscriptionProxy.java @@ -45,6 +45,10 @@ public class SubscriptionProxy { return channel.toIdentifier(); } + /*package*/ String getChannelName() { + return channel.getName(); + } + /*package*/ void onConnected(Subscription.ConnectedCallback callback) { onConnected = callback; } diff --git a/src/main/java/com/hosopy/actioncable/Subscriptions.java b/src/main/java/com/hosopy/actioncable/Subscriptions.java index bf566ad..deab457 100644 --- a/src/main/java/com/hosopy/actioncable/Subscriptions.java +++ b/src/main/java/com/hosopy/actioncable/Subscriptions.java @@ -136,4 +136,13 @@ private void forget(Subscription subscription) { private boolean sendSubscribeCommand(SubscriptionProxy subscriptionProxy) { return consumer.send(Command.subscribe(subscriptionProxy.getIdentifier())); } + + /*package*/ boolean hasSubscription(String channelName) { + for (SubscriptionProxy subscription : subscriptionProxies.values()) { + if (subscription.getChannelName().equals(channelName)) { + return true; + } + } + return false; + } } From 34a3f9c4294e61c498fe88a6fcb017b46c2569ca Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 3 Jan 2025 17:02:11 +0300 Subject: [PATCH 11/23] add WebSocketException.java --- src/main/java/com/hosopy/actioncable/Connection.java | 6 ------ .../java/com/hosopy/actioncable/WebSocketException.java | 8 ++++++++ .../java/com/hosopy/actioncable/annotation/Exceptions.kt | 3 --- 3 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/hosopy/actioncable/WebSocketException.java delete mode 100644 src/main/java/com/hosopy/actioncable/annotation/Exceptions.kt diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index 36daa2e..44449b1 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -1,13 +1,9 @@ package com.hosopy.actioncable; -import com.hosopy.actioncable.annotation.WebSocketException; import com.hosopy.concurrent.EventLoop; import com.hosopy.util.QueryStringUtils; -import java.io.IOException; -import java.net.CookieHandler; import java.net.URI; -import java.time.Duration; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -21,8 +17,6 @@ import okhttp3.Response; import okhttp3.WebSocket; import okhttp3.WebSocketListener; -import okio.Buffer; -import okio.ByteString; public class Connection { diff --git a/src/main/java/com/hosopy/actioncable/WebSocketException.java b/src/main/java/com/hosopy/actioncable/WebSocketException.java new file mode 100644 index 0000000..1842165 --- /dev/null +++ b/src/main/java/com/hosopy/actioncable/WebSocketException.java @@ -0,0 +1,8 @@ +package com.hosopy.actioncable; + +public class WebSocketException extends Exception { + + /*package*/ WebSocketException(Throwable t) { + super(t); + } +} diff --git a/src/main/java/com/hosopy/actioncable/annotation/Exceptions.kt b/src/main/java/com/hosopy/actioncable/annotation/Exceptions.kt deleted file mode 100644 index 2d51141..0000000 --- a/src/main/java/com/hosopy/actioncable/annotation/Exceptions.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.hosopy.actioncable.annotation - -class WebSocketException(e: Throwable) : Exception(e) \ No newline at end of file From c8d17c63af711c5f2d2048d74375dad15e9a7b6c Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 3 Jan 2025 21:07:27 +0300 Subject: [PATCH 12/23] pass exception & skip open connection if it is connecting. --- src/main/java/com/hosopy/actioncable/Connection.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index 44449b1..ffc8a7f 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -136,6 +136,8 @@ public interface OkHttpClientFactory { public void run() { if (isOpen()) { fireOnFailure(new IllegalStateException("Must close existing connection before opening")); + } else if (isConnecting()) { + fireOnFailure(new IllegalStateException("Already connection, must close existing connection before opening")); } else { doOpen(); } From 5e144402116afab37f7fd5b014e444ea2699067b Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 3 Jan 2025 21:11:56 +0300 Subject: [PATCH 13/23] close socket in onClosing. --- src/main/java/com/hosopy/actioncable/Connection.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index ffc8a7f..bf5c384 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -291,10 +291,13 @@ public void run() { @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { + Connection.this.state = State.CLOSED; + EventLoop.execute(new Runnable() { @Override public void run() { state = State.CLOSED; + Connection.this.webSocket = null; if (listener != null) { listener.onFailure(new WebSocketException(t)); @@ -323,6 +326,12 @@ public void onClosing(WebSocket webSocket, int code, String reason) { @Override public void run() { state = State.CLOSING; + try{ + webSocket.close(code, reason); + Connection.this.webSocket = null; + } catch (IllegalStateException e) { + //do nothing + } if (listener != null) { listener.onClosing(); From c5db9c750be57b3333789b9f326351e94abcab01 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Mon, 6 Jan 2025 11:48:57 +0300 Subject: [PATCH 14/23] add remove subscription by channel name. --- .../java/com/hosopy/actioncable/Subscriptions.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/Subscriptions.java b/src/main/java/com/hosopy/actioncable/Subscriptions.java index deab457..e95c1ed 100644 --- a/src/main/java/com/hosopy/actioncable/Subscriptions.java +++ b/src/main/java/com/hosopy/actioncable/Subscriptions.java @@ -64,6 +64,19 @@ public void remove(Subscription subscription) { } } + /** + * Remove subscription from collection. + * + * @param channelName channel name of an exist subscription to remove + */ + public void remove(String channelName) { + for (SubscriptionProxy subscription : subscriptionProxies.values()) { + if (subscription.getChannelName().equals(channelName)) { + remove(subscription.getProxy()); + } + } + } + /** * Remove all subscriptions from collection. */ From dff6b32705695d20fc5c1c76fdb13831add2b191 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Wed, 8 Jan 2025 20:51:39 +0300 Subject: [PATCH 15/23] fix unnecessary reconnecting. --- src/main/java/com/hosopy/actioncable/ConnectionMonitor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java b/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java index b74ba5e..45841db 100644 --- a/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java +++ b/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java @@ -66,8 +66,9 @@ public class ConnectionMonitor { /*package*/ void start() { reset(); - stoppedAt = 0; + pingedAt = 0; startedAt = now(); + stoppedAt = 0; poll(); } From d9dfa7d7efe291115250b8f6764d75e5e4f2b323 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Wed, 29 Jan 2025 16:45:45 +0300 Subject: [PATCH 16/23] rename field member webSocket to mWebSocket. --- .../com/hosopy/actioncable/Connection.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index bf5c384..cb7c35b 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -114,7 +114,7 @@ public interface OkHttpClientFactory { private Listener listener; - private WebSocket webSocket; + private WebSocket mWebSocket; private boolean isReopening = false; @@ -149,11 +149,11 @@ public void run() { EventLoop.execute(new Runnable() { @Override public void run() { - if (webSocket != null) { + if (mWebSocket != null) { try { // http://tools.ietf.org/html/rfc6455#section-7.4.1 if (!isState(State.CLOSING, State.CLOSED)) { - webSocket.close(1000, "connection closed manually"); + mWebSocket.close(1000, "connection closed manually"); state = State.CLOSING; } } catch (IllegalStateException e) { @@ -174,7 +174,7 @@ public void run() { } /*package*/ boolean isOpen() { - return webSocket != null && isState(State.OPEN); + return mWebSocket != null && isState(State.OPEN); } /*package*/ boolean isConnecting() { return isState(State.CONNECTING); @@ -249,8 +249,8 @@ private void doOpen() { } private void doSend(String data) { - if (webSocket != null) { - boolean isSent = webSocket.send(data); + if (mWebSocket != null) { + boolean isSent = mWebSocket.send(data); if (isSent && generalListener != null) generalListener.onSend(data); } @@ -278,7 +278,7 @@ private void fireOnFailure(Exception e) { @Override public void onOpen(WebSocket webSocket, Response response) { Connection.this.state = State.OPEN; - Connection.this.webSocket = webSocket; + Connection.this.mWebSocket = webSocket; EventLoop.execute(new Runnable() { @Override public void run() { @@ -297,7 +297,7 @@ public void onFailure(WebSocket webSocket, Throwable t, Response response) { @Override public void run() { state = State.CLOSED; - Connection.this.webSocket = null; + Connection.this.mWebSocket = null; if (listener != null) { listener.onFailure(new WebSocketException(t)); @@ -328,7 +328,7 @@ public void run() { state = State.CLOSING; try{ webSocket.close(code, reason); - Connection.this.webSocket = null; + Connection.this.mWebSocket = null; } catch (IllegalStateException e) { //do nothing } From 487fca88475a076087b637488027674aa1960440 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Wed, 29 Jan 2025 17:35:33 +0300 Subject: [PATCH 17/23] skip untracked websocket's callbacks. --- src/main/java/com/hosopy/actioncable/Connection.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index cb7c35b..eaff763 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -291,6 +291,7 @@ public void run() { @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { + if(webSocket != mWebSocket) return; Connection.this.state = State.CLOSED; EventLoop.execute(new Runnable() { @@ -320,6 +321,13 @@ public void run() { @Override public void onClosing(WebSocket webSocket, int code, String reason) { + if (mWebSocket != null && webSocket != mWebSocket) { + try { + webSocket.close(code, reason); + } catch (Exception e) { + } + return; + } Connection.this.state = State.CLOSING; EventLoop.execute(new Runnable() { @@ -348,6 +356,7 @@ public void run() { @Override public void onClosed(WebSocket webSocket, int code, String reason) { + if (mWebSocket != null && webSocket != mWebSocket) return; Connection.this.state = State.CLOSED; EventLoop.execute(new Runnable() { From 85510fd46c2a10a0290b64d52c2f23bc9df1eeed Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Tue, 4 Feb 2025 14:24:58 +0300 Subject: [PATCH 18/23] fix skip untracked websocket's callbacks. --- src/main/java/com/hosopy/actioncable/Connection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/hosopy/actioncable/Connection.java b/src/main/java/com/hosopy/actioncable/Connection.java index eaff763..ec9cf5b 100644 --- a/src/main/java/com/hosopy/actioncable/Connection.java +++ b/src/main/java/com/hosopy/actioncable/Connection.java @@ -291,7 +291,7 @@ public void run() { @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { - if(webSocket != mWebSocket) return; + if (mWebSocket != null && webSocket != mWebSocket) return; Connection.this.state = State.CLOSED; EventLoop.execute(new Runnable() { From 921659bf2095beda17db5c9483fb12630ce08827 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 28 Nov 2025 16:56:23 +0300 Subject: [PATCH 19/23] add hasChannel(), hasSubscriptionOf() --- .../com/hosopy/actioncable/Subscriptions.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/Subscriptions.java b/src/main/java/com/hosopy/actioncable/Subscriptions.java index e95c1ed..84d7863 100644 --- a/src/main/java/com/hosopy/actioncable/Subscriptions.java +++ b/src/main/java/com/hosopy/actioncable/Subscriptions.java @@ -150,6 +150,7 @@ private boolean sendSubscribeCommand(SubscriptionProxy subscriptionProxy) { return consumer.send(Command.subscribe(subscriptionProxy.getIdentifier())); } + @Deprecated(since = "use hasChannel()") /*package*/ boolean hasSubscription(String channelName) { for (SubscriptionProxy subscription : subscriptionProxies.values()) { if (subscription.getChannelName().equals(channelName)) { @@ -158,4 +159,23 @@ private boolean sendSubscribeCommand(SubscriptionProxy subscriptionProxy) { } return false; } + + /*package*/ boolean hasChannel(String channelName) { + for (SubscriptionProxy subscription : subscriptionProxies.values()) { + if (subscription.getChannelName().equals(channelName)) { + return true; + } + } + return false; + } + + /*package*/ boolean hasSubscriptionOf(String identifier) { + for (SubscriptionProxy subscriptionProxy : subscriptionProxies.values()) { + if (subscriptionProxy.getIdentifier().equals(identifier)) { + return true; + } + } + return false; + } + } From a74fd15776fc625f2aca87e3dab6534832e68016 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 28 Nov 2025 17:05:49 +0300 Subject: [PATCH 20/23] add hasChannel(), hasSubscriptionOf() --- src/main/java/com/hosopy/actioncable/Channel.java | 2 +- src/main/java/com/hosopy/actioncable/Consumer.java | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hosopy/actioncable/Channel.java b/src/main/java/com/hosopy/actioncable/Channel.java index c9f5067..55acc34 100644 --- a/src/main/java/com/hosopy/actioncable/Channel.java +++ b/src/main/java/com/hosopy/actioncable/Channel.java @@ -76,7 +76,7 @@ public void addParam(String key, JsonElement value) { addParamInternal(key, value); } - /*package*/ String toIdentifier() { + public String toIdentifier() { synchronized (params) { if (identifier == null) { identifier = GSON.toJson(params); diff --git a/src/main/java/com/hosopy/actioncable/Consumer.java b/src/main/java/com/hosopy/actioncable/Consumer.java index 01c6f01..f84a496 100644 --- a/src/main/java/com/hosopy/actioncable/Consumer.java +++ b/src/main/java/com/hosopy/actioncable/Consumer.java @@ -127,10 +127,19 @@ public void setStaleThresholdInSecond(int staleThresholdInSecond){ if(connectionMonitor !=null) connectionMonitor.setStaleThresholdInSecond(staleThresholdInSecond); } + @Deprecated public boolean hasSubscription(String channel) { return subscriptions != null && subscriptions.hasSubscription(channel); } + public boolean hasChannel(String channel) { + return subscriptions != null && subscriptions.hasChannel(channel); + } + + public boolean hasSubscriptionFor(String identifier) { + return subscriptions != null && subscriptions.hasSubscriptionOf(identifier); + } + public Connection getConnection() { return connection; } From 4990cdaca57227119b55ae19c41988f693b42450 Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 28 Nov 2025 18:49:45 +0300 Subject: [PATCH 21/23] . --- src/main/java/com/hosopy/actioncable/Channel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/hosopy/actioncable/Channel.java b/src/main/java/com/hosopy/actioncable/Channel.java index 55acc34..a28c895 100644 --- a/src/main/java/com/hosopy/actioncable/Channel.java +++ b/src/main/java/com/hosopy/actioncable/Channel.java @@ -76,6 +76,7 @@ public void addParam(String key, JsonElement value) { addParamInternal(key, value); } + public String toIdentifier() { synchronized (params) { if (identifier == null) { From 2439e25ee952c1fc2a0002af37e82d12ad0a4bfb Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Fri, 28 Nov 2025 23:04:39 +0300 Subject: [PATCH 22/23] add removeByIdentifier() --- .../java/com/hosopy/actioncable/Subscriptions.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/Subscriptions.java b/src/main/java/com/hosopy/actioncable/Subscriptions.java index 84d7863..0a16863 100644 --- a/src/main/java/com/hosopy/actioncable/Subscriptions.java +++ b/src/main/java/com/hosopy/actioncable/Subscriptions.java @@ -69,6 +69,7 @@ public void remove(Subscription subscription) { * * @param channelName channel name of an exist subscription to remove */ + @Deprecated public void remove(String channelName) { for (SubscriptionProxy subscription : subscriptionProxies.values()) { if (subscription.getChannelName().equals(channelName)) { @@ -76,6 +77,18 @@ public void remove(String channelName) { } } } + /** + * Remove subscription from collection. + * + * @param identifier channel name of an exist subscription to remove + */ + public void removeByIdentifier(String identifier) { + for (SubscriptionProxy subscription : subscriptionProxies.values()) { + if (subscription.getIdentifier().equals(identifier)) { + remove(subscription.getProxy()); + } + } + } /** * Remove all subscriptions from collection. From d3cf3f21cc2648ddfdd4599ecac0d3cfb013839c Mon Sep 17 00:00:00 2001 From: mohammedsalem Date: Mon, 12 Jan 2026 18:55:03 +0300 Subject: [PATCH 23/23] add getReconnectAttempts --- src/main/java/com/hosopy/actioncable/ConnectionMonitor.java | 4 ++++ src/main/java/com/hosopy/actioncable/Consumer.java | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java b/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java index 45841db..0f13d53 100644 --- a/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java +++ b/src/main/java/com/hosopy/actioncable/ConnectionMonitor.java @@ -113,6 +113,10 @@ private long getInterval() { return (long) clamp(interval, reconnectionDelay, reconnectionDelayMax) * 1000; } + public int getReconnectAttempts() { + return reconnectAttempts; + } + private static long secondsSince(long time) { return (now() - time) / 1000; } diff --git a/src/main/java/com/hosopy/actioncable/Consumer.java b/src/main/java/com/hosopy/actioncable/Consumer.java index f84a496..06457f2 100644 --- a/src/main/java/com/hosopy/actioncable/Consumer.java +++ b/src/main/java/com/hosopy/actioncable/Consumer.java @@ -159,4 +159,10 @@ public boolean isClosing() { public boolean isClosed() { return this.connection == null || this.connection.isClosed(); } + + public Integer getReconnectAttempts() { + if (connectionMonitor != null) + return connectionMonitor.getReconnectAttempts(); + else return null; + } }