Skip to content

Commit 9b031cf

Browse files
authored
Merge pull request #175 from pusher/configurable_reconnections
Configurable reconnections
2 parents 8acdcaf + fe1a020 commit 9b031cf

File tree

8 files changed

+89
-22
lines changed

8 files changed

+89
-22
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ The pusher-java-client is available in Maven Central.
5151
<dependency>
5252
<groupId>com.pusher</groupId>
5353
<artifactId>pusher-java-client</artifactId>
54-
<version>1.7.0</version>
54+
<version>1.8.0</version>
5555
</dependency>
5656
</dependencies>
5757
```
@@ -60,7 +60,7 @@ The pusher-java-client is available in Maven Central.
6060

6161
```groovy
6262
dependencies {
63-
compile 'com.pusher:pusher-java-client:1.7.0'
63+
compile 'com.pusher:pusher-java-client:1.8.0'
6464
}
6565
```
6666

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ apply plugin: 'org.ajoberstar.github-pages'
2121
apply plugin: 'signing'
2222

2323
group = "com.pusher"
24-
version = "1.7.1-SNAPSHOT"
24+
version = "1.8.1-SNAPSHOT"
2525
sourceCompatibility = "1.6"
2626
targetCompatibility = "1.6"
2727

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip
1+
#Wed Feb 21 14:42:32 GMT 2018
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
zipStorePath=wrapper/dists
54
zipStoreBase=GRADLE_USER_HOME
5+
zipStorePath=wrapper/dists
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip

src/main/java/com/pusher/client/PusherOptions.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public class PusherOptions {
2525
private static final long DEFAULT_ACTIVITY_TIMEOUT = 120000;
2626
private static final long DEFAULT_PONG_TIMEOUT = 30000;
2727

28+
private static final int MAX_RECONNECTION_ATTEMPTS = 6; //Taken from the Swift lib
29+
private static final int MAX_RECONNECT_GAP_IN_SECONDS = 30;
30+
2831
// Note that the primary cluster lives on a different domain
2932
// (others are subdomains of pusher.com). This is not an oversight.
3033
// Legacy reasons.
@@ -36,6 +39,8 @@ public class PusherOptions {
3639
private long pongTimeout = DEFAULT_PONG_TIMEOUT;
3740
private Authorizer authorizer;
3841
private Proxy proxy = Proxy.NO_PROXY;
42+
private int maxReconnectionAttempts = MAX_RECONNECTION_ATTEMPTS;
43+
private int maxReconnectGapInSeconds = MAX_RECONNECT_GAP_IN_SECONDS;
3944

4045
/**
4146
* Gets whether an encrypted (SSL) connection should be used when connecting
@@ -182,7 +187,30 @@ public PusherOptions setPongTimeout(final long pongTimeout) {
182187
return this;
183188
}
184189

185-
public long getPongTimeout() {
190+
/**
191+
* Number of reconnect attempts when websocket connection failed
192+
* @param maxReconnectionAttempts
193+
* number of max reconnection attempts, default = {@link #MAX_RECONNECTION_ATTEMPTS} 6
194+
* @return this, for chaining
195+
*/
196+
public PusherOptions setMaxReconnectionAttempts(int maxReconnectionAttempts) {
197+
this.maxReconnectionAttempts = maxReconnectionAttempts;
198+
return this;
199+
}
200+
201+
/**
202+
* The delay in two reconnection extends exponentially (1, 2, 4, .. seconds) This property sets the maximum in between two
203+
* reconnection attempts.
204+
* @param maxReconnectGapInSeconds
205+
* time in seconds of the maximum gab between two reconnection attempts, default = {@link #MAX_RECONNECT_GAP_IN_SECONDS} 30s
206+
* @return this, for chaining
207+
*/
208+
public PusherOptions setMaxReconnectGapInSeconds(int maxReconnectGapInSeconds) {
209+
this.maxReconnectGapInSeconds = maxReconnectGapInSeconds;
210+
return this;
211+
}
212+
213+
public long getPongTimeout() {
186214
return pongTimeout;
187215
}
188216

@@ -221,7 +249,21 @@ public Proxy getProxy() {
221249
return this.proxy;
222250
}
223251

224-
private static String readVersionFromProperties() {
252+
/**
253+
* @return the maximum reconnection attempts
254+
*/
255+
public int getMaxReconnectionAttempts() {
256+
return maxReconnectionAttempts;
257+
}
258+
259+
/**
260+
* @return the maximum reconnection gap in seconds
261+
*/
262+
public int getMaxReconnectGapInSeconds() {
263+
return maxReconnectGapInSeconds;
264+
}
265+
266+
private static String readVersionFromProperties() {
225267
InputStream inStream = null;
226268
try {
227269
final Properties p = new Properties();

src/main/java/com/pusher/client/connection/websocket/WebSocketConnection.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ public class WebSocketConnection implements InternalConnection, WebSocketListene
3030

3131
private static final String INTERNAL_EVENT_PREFIX = "pusher:";
3232
private static final String PING_EVENT_SERIALIZED = "{\"event\": \"pusher:ping\"}";
33-
private static final int MAX_RECONNECTION_ATTEMPTS = 6; //Taken from the Swift lib
34-
private static final int MAX_RECONNECT_GAP_IN_SECONDS = 30;
3533

3634
private final Factory factory;
3735
private final ActivityTimer activityTimer;
3836
private final Map<ConnectionState, Set<ConnectionEventListener>> eventListeners = new ConcurrentHashMap<ConnectionState, Set<ConnectionEventListener>>();
3937
private final URI webSocketUri;
4038
private final Proxy proxy;
39+
private final int maxReconnectionAttempts;
40+
private final int maxReconnectionGap;
4141

4242
private volatile ConnectionState state = ConnectionState.DISCONNECTED;
4343
private WebSocketClientWrapper underlyingConnection;
@@ -49,10 +49,14 @@ public WebSocketConnection(
4949
final String url,
5050
final long activityTimeout,
5151
final long pongTimeout,
52+
int maxReconnectionAttempts,
53+
int maxReconnectionGap,
5254
final Proxy proxy,
5355
final Factory factory) throws URISyntaxException {
5456
webSocketUri = new URI(url);
5557
activityTimer = new ActivityTimer(activityTimeout, pongTimeout);
58+
this.maxReconnectionAttempts = maxReconnectionAttempts;
59+
this.maxReconnectionGap = maxReconnectionGap;
5660
this.proxy = proxy;
5761
this.factory = factory;
5862

@@ -270,7 +274,7 @@ public void onClose(final int code, final String reason, final boolean remote) {
270274
//Reconnection logic
271275
if(state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING){
272276

273-
if(reconnectAttempts < MAX_RECONNECTION_ATTEMPTS){
277+
if(reconnectAttempts < maxReconnectionAttempts){
274278
tryReconnecting();
275279
}
276280
else{
@@ -288,7 +292,7 @@ public void onClose(final int code, final String reason, final boolean remote) {
288292
private void tryReconnecting() {
289293
reconnectAttempts++;
290294
updateState(ConnectionState.RECONNECTING);
291-
long reconnectInterval = Math.min(MAX_RECONNECT_GAP_IN_SECONDS, reconnectAttempts * reconnectAttempts);
295+
long reconnectInterval = Math.min(maxReconnectionGap, reconnectAttempts * reconnectAttempts);
292296

293297
factory.getTimers().schedule(new Runnable() {
294298
@Override

src/main/java/com/pusher/client/util/Factory.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,14 @@ public class Factory {
5050
public synchronized InternalConnection getConnection(final String apiKey, final PusherOptions options) {
5151
if (connection == null) {
5252
try {
53-
connection = new WebSocketConnection(options.buildUrl(apiKey), options.getActivityTimeout(),
54-
options.getPongTimeout(), options.getProxy(), this);
53+
connection = new WebSocketConnection(
54+
options.buildUrl(apiKey),
55+
options.getActivityTimeout(),
56+
options.getPongTimeout(),
57+
options.getMaxReconnectionAttempts(),
58+
options.getMaxReconnectGapInSeconds(),
59+
options.getProxy(),
60+
this);
5561
}
5662
catch (final URISyntaxException e) {
5763
throw new IllegalArgumentException("Failed to initialise connection", e);

src/test/java/com/pusher/client/EndToEndTest.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package com.pusher.client;
22

3-
import static org.mockito.Matchers.*;
4-
import static org.mockito.Mockito.*;
3+
import static org.mockito.Matchers.any;
4+
import static org.mockito.Matchers.anyString;
5+
import static org.mockito.Matchers.eq;
6+
import static org.mockito.Mockito.doAnswer;
7+
import static org.mockito.Mockito.never;
8+
import static org.mockito.Mockito.times;
9+
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
511

612
import java.net.Proxy;
713
import java.net.URI;
814

9-
import com.pusher.java_websocket.handshake.ServerHandshake;
1015
import org.junit.After;
1116
import org.junit.Before;
1217
import org.junit.Test;
@@ -26,6 +31,7 @@
2631
import com.pusher.client.connection.websocket.WebSocketListener;
2732
import com.pusher.client.util.DoNothingExecutor;
2833
import com.pusher.client.util.Factory;
34+
import com.pusher.java_websocket.handshake.ServerHandshake;
2935

3036
@RunWith(MockitoJUnitRunner.class)
3137
public class EndToEndTest {
@@ -38,6 +44,7 @@ public class EndToEndTest {
3844
+ PRIVATE_CHANNEL_NAME + "\",\"auth\":\"" + AUTH_KEY + "\"}}";
3945
private static final long ACTIVITY_TIMEOUT = 120000;
4046
private static final long PONG_TIMEOUT = 120000;
47+
4148
private static final Proxy proxy = Proxy.NO_PROXY;
4249

4350
private @Mock Authorizer mockAuthorizer;
@@ -53,7 +60,8 @@ public class EndToEndTest {
5360
public void setUp() throws Exception {
5461
pusherOptions = new PusherOptions().setAuthorizer(mockAuthorizer).setEncrypted(false);
5562

56-
connection = new WebSocketConnection(pusherOptions.buildUrl(API_KEY), ACTIVITY_TIMEOUT, PONG_TIMEOUT, proxy, factory);
63+
connection = new WebSocketConnection(pusherOptions.buildUrl(API_KEY), ACTIVITY_TIMEOUT, PONG_TIMEOUT, pusherOptions.getMaxReconnectionAttempts(),
64+
pusherOptions.getMaxReconnectGapInSeconds(), proxy, factory);
5765

5866
doAnswer(new Answer() {
5967
@Override

src/test/java/com/pusher/client/connection/websocket/WebSocketConnectionTest.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public class WebSocketConnectionTest {
3232

3333
private static final long ACTIVITY_TIMEOUT = 500;
3434
private static final long PONG_TIMEOUT = 500;
35+
private static final int MAX_RECONNECTIONS = 6;
36+
private static final int MAX_GAP = 30;
3537
private static final String URL = "ws://ws.example.com/";
3638
private static final String EVENT_NAME = "my-event";
3739
private static final String CONN_ESTABLISHED_EVENT = "{\"event\":\"pusher:connection_established\",\"data\":\"{\\\"socket_id\\\":\\\"21112.816204\\\"}\"}";
@@ -65,22 +67,24 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
6567
}).when(factory).queueOnEventThread(any(Runnable.class));
6668
when(factory.getTimers()).thenReturn(new DoNothingExecutor());
6769

68-
connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, PROXY, factory);
70+
connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, MAX_RECONNECTIONS, MAX_GAP, PROXY, factory);
6971
connection.bind(ConnectionState.ALL, mockEventListener);
7072
}
7173

7274
@Test
7375
public void testUnbindingWhenNotAlreadyBoundReturnsFalse() throws URISyntaxException {
7476
final ConnectionEventListener listener = mock(ConnectionEventListener.class);
75-
final WebSocketConnection connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, PROXY, factory);
77+
final WebSocketConnection connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, MAX_RECONNECTIONS, MAX_GAP,
78+
PROXY, factory);
7679
final boolean unbound = connection.unbind(ConnectionState.ALL, listener);
7780
assertEquals(false, unbound);
7881
}
7982

8083
@Test
8184
public void testUnbindingWhenBoundReturnsTrue() throws URISyntaxException {
8285
final ConnectionEventListener listener = mock(ConnectionEventListener.class);
83-
final WebSocketConnection connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, PROXY, factory);
86+
final WebSocketConnection connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, MAX_RECONNECTIONS, MAX_GAP,
87+
PROXY, factory);
8488

8589
connection.bind(ConnectionState.ALL, listener);
8690

@@ -118,7 +122,8 @@ public void testConnectDoesNotCallConnectOnUnderlyingConnectionIfAlreadyInConnec
118122

119123
@Test
120124
public void testListenerDoesNotReceiveConnectingEventIfItIsOnlyBoundToTheConnectedEvent() throws URISyntaxException {
121-
connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, PROXY, factory);
125+
connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, MAX_RECONNECTIONS, MAX_GAP,
126+
PROXY, factory);
122127
connection.bind(ConnectionState.CONNECTED, mockEventListener);
123128
connection.connect();
124129

@@ -219,7 +224,8 @@ public void testOnCloseCallbackUpdatesStateToDisconnectedWhenPreviousStateIsDisc
219224

220225
@Test
221226
public void testOnCloseCallbackDoesNotCallListenerIfItIsNotBoundToDisconnectedEvent() throws URISyntaxException {
222-
connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, PROXY, factory);
227+
connection = new WebSocketConnection(URL, ACTIVITY_TIMEOUT, PONG_TIMEOUT, MAX_RECONNECTIONS, MAX_GAP,
228+
PROXY, factory);
223229
connection.bind(ConnectionState.CONNECTED, mockEventListener);
224230

225231
connection.connect();

0 commit comments

Comments
 (0)