Skip to content

Commit

Permalink
Merge branch 'develop' into release-1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
paddybyers committed Feb 13, 2019
2 parents f94e639 + a1dc053 commit 91f3daa
Show file tree
Hide file tree
Showing 13 changed files with 629 additions and 155 deletions.
29 changes: 28 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,39 @@

## [v1.1.0](https://github.com/ably/ably-java/tree/v1.1.0)

[Full Changelog](https://github.com/ably/ably-java/compare/v1.0.10...v1.1.0)
[Full Changelog](https://github.com/ably/ably-java/compare/v1.0.12...v1.1.0)

**Implemented enhancements:**

- Android push implementation [\#308](https://github.com/ably/ably-java/issues/308)

## [v1.0.12](https://github.com/ably/ably-java/tree/v1.0.12) (2019-02-13)
[Full Changelog](https://github.com/ably/ably-java/compare/v1.0.11...v1.0.12)

**Merged pull requests:**

- Implemented feature Spec - TP4 [\#451](https://github.com/ably/ably-java/pull/451) ([amsurana](https://github.com/amsurana))
- Implemented Spec: TM3, Message.fromEncoded [\#446](https://github.com/ably/ably-java/pull/446) ([amsurana](https://github.com/amsurana))

## [v1.0.11](https://github.com/ably/ably-java/tree/v1.0.11)

[Full Changelog](https://github.com/ably/ably-java/compare/v1.0.10...v1.0.11)

**Fixed bugs:**

- InternalError when attempting to create a reattach timer [\#452](https://github.com/ably/ably-java/issues/452)
- Realtime Channel: exceptions thrown when attempting attach do not result in the client listener being called [\#448](https://github.com/ably/ably-java/issues/448)

**Closed issues:**

- ConcurrentModificationException in 1.0 [\#321](https://github.com/ably/ably-java/issues/321)

**Merged pull requests:**

- Make the Channels collection a ConcurrentHashMap to permit mutation o… [\#454](https://github.com/ably/ably-java/pull/454) ([paddybyers](https://github.com/paddybyers))
- Wrap construction of Timer instances to handle exceptions … [\#453](https://github.com/ably/ably-java/pull/453) ([paddybyers](https://github.com/paddybyers))
- Attach exception handling [\#449](https://github.com/ably/ably-java/pull/449) ([paddybyers](https://github.com/paddybyers))

## [v1.0.10](https://github.com/ably/ably-java/tree/v1.0.10)

[Full Changelog](https://github.com/ably/ably-java/compare/v1.0.9...v1.0.10)
Expand Down
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@

A Java Realtime and REST client library for [Ably Realtime](https://www.ably.io), the realtime messaging and data delivery service.

## Beta notice

This is a beta release of the Ably 1.1 library specification. A stable 1.1.0 release will be made in due course.

## Supported Platforms

This SDK supports the following platforms:
Expand All @@ -34,13 +30,13 @@ Reference the library by including a compile dependency reference in your gradle
For [Java](https://bintray.com/ably-io/ably/ably-java/_latestVersion):

```
compile 'io.ably:ably-java:1.1.0-RC1'
compile 'io.ably:ably-java:1.1.0'
```

For [Android](https://bintray.com/ably-io/ably/ably-android/_latestVersion):

```
compile 'io.ably:ably-android:1.1.0-RC1'
compile 'io.ably:ably-android:1.1.0'
```

The library is hosted on the [Jcenter repository](https://bintray.com/ably-io/ably), so you need to ensure that the repo is referenced also; IDEs will typically include this by default:
Expand Down Expand Up @@ -529,7 +525,7 @@ This library uses [semantic versioning](http://semver.org/). For each release, t
* Replace all references of the current version number with the new version number (check this file [README.md](./README.md) and [common.gradle](./common.gradle)) and commit the changes
* Run [`github_changelog_generator`](https://github.com/skywinder/Github-Changelog-Generator) to update the [CHANGELOG](./CHANGELOG.md): `github_changelog_generator -u ably -p ably-java --header-label="# Changelog" --release-branch=release-1.1.0 --future-release=v1.0.8`
* Commit [CHANGELOG](./CHANGELOG.md)
* Add a tag and push to origin such as `git tag v1.0.7; git push origin v1.0.7`
* Add a tag and push to origin such as `git tag v1.1.0; git push origin v1.1.0`
* Make a PR against `develop`
* Once the PR is approved, merge it into `develop`
* Fast-forward the master branch: `git checkout master && git merge --ff-only develop && git push origin master`
Expand Down Expand Up @@ -576,4 +572,4 @@ To see what has changed in recent versions of Bundler, see the [CHANGELOG](CHANG

## License

Copyright (c) 2015-2018 Ably Real-time Ltd, Licensed under the Apache License, Version 2.0. Refer to [LICENSE](LICENSE) for the license terms.
Copyright (c) 2015-2019 Ably Real-time Ltd, Licensed under the Apache License, Version 2.0. Refer to [LICENSE](LICENSE) for the license terms.
2 changes: 1 addition & 1 deletion common.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ repositories {
}

group = 'io.ably'
version = '1.1.0-RC1'
version = '1.1.0'
description = """Ably java client library"""

tasks.withType(Javadoc) {
Expand Down
4 changes: 2 additions & 2 deletions lib/src/main/java/io/ably/lib/realtime/AblyRealtime.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.ably.lib.realtime;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import io.ably.lib.rest.AblyRest;
import io.ably.lib.transport.ITransport;
Expand Down Expand Up @@ -91,7 +91,7 @@ protected void onAuthError(ErrorInfo errorInfo) {
*
*/
@SuppressWarnings("serial")
public class Channels extends HashMap<String, Channel> {
public class Channels extends ConcurrentHashMap<String, Channel> {
public Channels() {
/* remove all channels when the connection is closed, to avoid stalled state */
connection.on(ConnectionEvent.closed, new ConnectionStateListener() {
Expand Down
115 changes: 77 additions & 38 deletions lib/src/main/java/io/ably/lib/realtime/ChannelBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.ably.lib.http.BasePaginatedQuery;
import io.ably.lib.http.HttpCore;
import io.ably.lib.http.HttpUtils;
import io.ably.lib.http.PaginatedQuery;
import io.ably.lib.transport.ConnectionManager;
import io.ably.lib.transport.ConnectionManager.QueuedMessage;
import io.ably.lib.transport.Defaults;
Expand Down Expand Up @@ -125,15 +124,14 @@ private void attachImpl(final CompletionListener listener) throws AblyException
}
return;
case attached:
if(listener != null) {
listener.onSuccess();
}
callCompletionListenerSuccess(listener);
return;
default:
}
ConnectionManager connectionManager = ably.connection.connectionManager;
if(!connectionManager.isActive())
if(!connectionManager.isActive()) {
throw AblyException.fromErrorInfo(connectionManager.getStateErrorInfo());
}

/* send attach request and pending state */
Log.v(TAG, "attach(); channel = " + name + "; sending ATTACH request");
Expand Down Expand Up @@ -177,9 +175,7 @@ private void detachImpl(CompletionListener listener) throws AblyException {
switch(state) {
case initialized:
case detached: {
if(listener != null) {
listener.onSuccess();
}
callCompletionListenerSuccess(listener);
return;
}
case detaching:
Expand Down Expand Up @@ -231,6 +227,26 @@ public void sync() throws AblyException {
* internal
*
*/
private static void callCompletionListenerSuccess(CompletionListener listener) {
if(listener != null) {
try {
listener.onSuccess();
} catch(Throwable t) {
Log.e(TAG, "Unexpected exception calling CompletionListener", t);
}
}
}

private static void callCompletionListenerError(CompletionListener listener, ErrorInfo err) {
if(listener != null) {
try {
listener.onError(err);
} catch(Throwable t) {
Log.e(TAG, "Unexpected exception calling CompletionListener", t);
}
}
}

private void setAttached(ProtocolMessage message) {
clearAttachTimers();
boolean resumed = (message.flags & ( 1 << Flag.resumed.ordinal())) != 0;
Expand Down Expand Up @@ -288,42 +304,51 @@ synchronized private void clearAttachTimers() {
* set up timer to reattach it later
*/
synchronized private void attachWithTimeout(final CompletionListener listener) throws AblyException {
final Timer currentAttachTimer = new Timer();
Timer currentAttachTimer;
try {
currentAttachTimer = new Timer();
} catch(Throwable t) {
/* an exception instancing the timer can arise because the runtime is exiting */
callCompletionListenerError(listener, ErrorInfo.fromThrowable(t));
return;
}
attachTimer = currentAttachTimer;

try {
attachImpl(new CompletionListener() {
@Override
public void onSuccess() {
clearAttachTimers();
if (listener != null)
listener.onSuccess();
callCompletionListenerSuccess(listener);
}

@Override
public void onError(ErrorInfo reason) {
clearAttachTimers();
if (listener != null)
listener.onError(reason);
callCompletionListenerError(listener, reason);
}
});
} catch(AblyException e) {
attachTimer = null;
callCompletionListenerError(listener, e.errorInfo);
}

if(attachTimer == null)
if(attachTimer == null) {
/* operation has already succeeded or failed, no need to set the timer */
return;
}

final Timer inProgressTimer = currentAttachTimer;
attachTimer.schedule(
new TimerTask() {
@Override
public void run() {
String errorMessage = String.format("Attach timed out for channel %s", name);
Log.v(TAG, errorMessage);
synchronized (ChannelBase.this) {
if(attachTimer != currentAttachTimer)
if(attachTimer != inProgressTimer) {
return;
}
attachTimer = null;
if(state == ChannelState.attaching) {
setSuspended(new ErrorInfo(errorMessage, 91200), true);
Expand All @@ -339,15 +364,23 @@ public void run() {
* try to attach the channel
*/
synchronized private void reattachAfterTimeout() {
final Timer currentReattachTimer = new Timer();
Timer currentReattachTimer;
try {
currentReattachTimer = new Timer();
} catch(Throwable t) {
/* an exception instancing the timer can arise because the runtime is exiting */
return;
}
reattachTimer = currentReattachTimer;

final Timer inProgressTimer = currentReattachTimer;
reattachTimer.schedule(new TimerTask() {
@Override
public void run() {
synchronized (ChannelBase.this) {
if (currentReattachTimer != reattachTimer)
if (inProgressTimer != reattachTimer) {
return;
}
reattachTimer = null;
if (state == ChannelState.suspended) {
try {
Expand All @@ -365,46 +398,53 @@ public void run() {
* Try to detach the channel. If the server doesn't confirm the detach operation within realtime
* request timeout return channel to previous state
*/
synchronized private void detachWithTimeout(final CompletionListener listener) throws AblyException {
synchronized private void detachWithTimeout(final CompletionListener listener) {
final ChannelState originalState = state;
final Timer currentDetachTimer = new Timer();
Timer currentDetachTimer;
try {
currentDetachTimer = new Timer();
} catch(Throwable t) {
/* an exception instancing the timer can arise because the runtime is exiting */
callCompletionListenerError(listener, ErrorInfo.fromThrowable(t));
return;
}
attachTimer = currentDetachTimer;

try {
detachImpl(new CompletionListener() {
@Override
public void onSuccess() {
clearAttachTimers();
if (listener != null)
listener.onSuccess();
callCompletionListenerSuccess(listener);
}

@Override
public void onError(ErrorInfo reason) {
clearAttachTimers();
if (listener != null)
listener.onError(reason);
callCompletionListenerError(listener, reason);
}
});
} catch (AblyException e) {
attachTimer = null;
}

if(attachTimer == null)
if(attachTimer == null) {
/* operation has already succeeded or failed, no need to set the timer */
return;
}

final Timer inProgressTimer = currentDetachTimer;
attachTimer.schedule(new TimerTask() {
@Override
public void run() {
synchronized (ChannelBase.this) {
if (currentDetachTimer != attachTimer)
if (inProgressTimer != attachTimer) {
return;
}
attachTimer = null;
if (state == ChannelState.detaching) {
ErrorInfo reason = new ErrorInfo("Detach operation timed out", 90007);
if(listener != null)
listener.onError(reason);
callCompletionListenerError(listener, reason);
setState(originalState, reason);
}
}
Expand Down Expand Up @@ -473,7 +513,11 @@ public synchronized void setSuspended(ErrorInfo reason, boolean notifyStateChang

@Override
protected void apply(ChannelStateListener listener, ChannelEvent event, Object... args) {
listener.onChannelStateChanged((ChannelStateListener.ChannelStateChange)args[0]);
try {
listener.onChannelStateChanged((ChannelStateListener.ChannelStateChange)args[0]);
} catch (Throwable t) {
Log.e(TAG, "Unexpected exception calling ChannelStateListener", t);
}
}

static ErrorInfo REASON_NOT_ATTACHED = new ErrorInfo("Channel not attached", 400, 90001);
Expand Down Expand Up @@ -745,9 +789,7 @@ public synchronized void publish(Message[] messages, CompletionListener listener
message.encode(options);
}
} catch(AblyException e) {
if(listener != null) {
listener.onError(e.errorInfo);
}
callCompletionListenerError(listener, e.errorInfo);
return;
}
ProtocolMessage msg = new ProtocolMessage(Action.message, this.name);
Expand Down Expand Up @@ -793,8 +835,9 @@ private void sendQueuedMessages() {
}

/* Call completion callbacks for failed messages without holding the lock */
for (FailedMessage failed: failedMessages)
failed.msg.listener.onError(failed.reason);
for (FailedMessage failed: failedMessages) {
callCompletionListenerError(failed.msg.listener, failed.reason);
}
}

private void failQueuedMessages(ErrorInfo reason) {
Expand All @@ -810,11 +853,7 @@ private void failQueuedMessages(ErrorInfo reason) {
}

for(FailedMessage failed : failedMessages) {
try {
failed.msg.listener.onError(failed.reason);
} catch (Throwable t) {
Log.e(TAG, "failQueuedMessages(): Unexpected exception calling listener", t);
}
callCompletionListenerError(failed.msg.listener, failed.reason);
}
}

Expand Down
Loading

0 comments on commit 91f3daa

Please sign in to comment.