Skip to content

Commit da85ed6

Browse files
authored
Merge pull request #1138 from ably/feature/objects-getroot-refactored
[ECO-5076][LiveObjects] Implement getters for objects, map and counter
2 parents 3e0ca95 + b1c9e47 commit da85ed6

36 files changed

Lines changed: 1996 additions & 120 deletions

lib/src/main/java/io/ably/lib/objects/Adapter.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package io.ably.lib.objects;
22

33
import io.ably.lib.realtime.AblyRealtime;
4+
import io.ably.lib.realtime.ChannelState;
45
import io.ably.lib.realtime.CompletionListener;
56
import io.ably.lib.types.AblyException;
7+
import io.ably.lib.types.ChannelMode;
8+
import io.ably.lib.types.ChannelOptions;
69
import io.ably.lib.types.ProtocolMessage;
710
import io.ably.lib.util.Log;
811
import org.jetbrains.annotations.NotNull;
@@ -34,4 +37,32 @@ public void send(@NotNull ProtocolMessage msg, @NotNull CompletionListener liste
3437
public int maxMessageSizeLimit() {
3538
return ably.connection.connectionManager.maxMessageSize;
3639
}
40+
41+
@Override
42+
public ChannelMode[] getChannelModes(@NotNull String channelName) {
43+
if (ably.channels.containsKey(channelName)) {
44+
// RTO2a - channel.modes is only populated on channel attachment, so use it only if it is set
45+
ChannelMode[] modes = ably.channels.get(channelName).getModes();
46+
if (modes != null) {
47+
return modes;
48+
}
49+
// RTO2b - otherwise as a best effort use user provided channel options
50+
ChannelOptions options = ably.channels.get(channelName).getOptions();
51+
if (options != null && options.hasModes()) {
52+
return options.modes;
53+
}
54+
return null;
55+
}
56+
Log.e(TAG, "getChannelMode(): channel not found: " + channelName);
57+
return null;
58+
}
59+
60+
@Override
61+
public ChannelState getChannelState(@NotNull String channelName) {
62+
if (ably.channels.containsKey(channelName)) {
63+
return ably.channels.get(channelName).state;
64+
}
65+
Log.e(TAG, "getChannelState(): channel not found: " + channelName);
66+
return null;
67+
}
3768
}

lib/src/main/java/io/ably/lib/objects/LiveCounter.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.ably.lib.objects;
22

3-
import io.ably.lib.types.Callback;
43
import org.jetbrains.annotations.Blocking;
54
import org.jetbrains.annotations.NonBlocking;
65
import org.jetbrains.annotations.NotNull;
@@ -33,7 +32,7 @@ public interface LiveCounter {
3332
* @param callback the callback to be invoked upon completion of the operation.
3433
*/
3534
@NonBlocking
36-
void incrementAsync(@NotNull Callback<Void> callback);
35+
void incrementAsync(@NotNull ObjectsCallback<Void> callback);
3736

3837
/**
3938
* Decrements the value of the counter by 1.
@@ -49,7 +48,7 @@ public interface LiveCounter {
4948
* @param callback the callback to be invoked upon completion of the operation.
5049
*/
5150
@NonBlocking
52-
void decrementAsync(@NotNull Callback<Void> callback);
51+
void decrementAsync(@NotNull ObjectsCallback<Void> callback);
5352

5453
/**
5554
* Retrieves the current value of the counter.

lib/src/main/java/io/ably/lib/objects/LiveMap.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.ably.lib.objects;
22

3-
import io.ably.lib.types.Callback;
43
import org.jetbrains.annotations.Blocking;
54
import org.jetbrains.annotations.NonBlocking;
65
import org.jetbrains.annotations.Contract;
@@ -24,6 +23,7 @@ public interface LiveMap {
2423
* If the value associated with the provided key is an objectId string of another LiveObject, a reference to that LiveObject
2524
* is returned, provided it exists in the local pool and is not tombstoned. Otherwise, null is returned.
2625
* If the value is not an objectId, then that value is returned.
26+
* Spec: RTLM5, RTLM5a
2727
*
2828
* @param keyName the key whose associated value is to be returned.
2929
* @return the value associated with the specified key, or null if the key does not exist.
@@ -33,6 +33,7 @@ public interface LiveMap {
3333

3434
/**
3535
* Retrieves all entries (key-value pairs) in the map.
36+
* Spec: RTLM11, RTLM11a
3637
*
3738
* @return an iterable collection of all entries in the map.
3839
*/
@@ -42,6 +43,7 @@ public interface LiveMap {
4243

4344
/**
4445
* Retrieves all keys in the map.
46+
* Spec: RTLM12, RTLM12a
4547
*
4648
* @return an iterable collection of all keys in the map.
4749
*/
@@ -51,6 +53,7 @@ public interface LiveMap {
5153

5254
/**
5355
* Retrieves all values in the map.
56+
* Spec: RTLM13, RTLM13a
5457
*
5558
* @return an iterable collection of all values in the map.
5659
*/
@@ -85,6 +88,7 @@ public interface LiveMap {
8588

8689
/**
8790
* Retrieves the number of entries in the map.
91+
* Spec: RTLM10, RTLM10a
8892
*
8993
* @return the size of the map.
9094
*/
@@ -104,7 +108,7 @@ public interface LiveMap {
104108
* @param callback the callback to handle the result or any errors.
105109
*/
106110
@NonBlocking
107-
void setAsync(@NotNull String keyName, @NotNull Object value, @NotNull Callback<Void> callback);
111+
void setAsync(@NotNull String keyName, @NotNull Object value, @NotNull ObjectsCallback<Void> callback);
108112

109113
/**
110114
* Asynchronously removes the specified key and its associated value from the map.
@@ -117,5 +121,5 @@ public interface LiveMap {
117121
* @param callback the callback to handle the result or any errors.
118122
*/
119123
@NonBlocking
120-
void removeAsync(@NotNull String keyName, @NotNull Callback<Void> callback);
124+
void removeAsync(@NotNull String keyName, @NotNull ObjectsCallback<Void> callback);
121125
}

lib/src/main/java/io/ably/lib/objects/LiveObjects.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.ably.lib.objects;
22

3-
import io.ably.lib.types.Callback;
3+
import io.ably.lib.objects.state.ObjectsStateChange;
44
import org.jetbrains.annotations.Blocking;
55
import org.jetbrains.annotations.NonBlocking;
66
import org.jetbrains.annotations.NotNull;
@@ -16,7 +16,7 @@
1616
* <p>Implementations of this interface must be thread-safe as they may be accessed
1717
* from multiple threads concurrently.
1818
*/
19-
public interface LiveObjects {
19+
public interface LiveObjects extends ObjectsStateChange {
2020

2121
/**
2222
* Retrieves the root LiveMap object.
@@ -95,7 +95,7 @@ public interface LiveObjects {
9595
* @param callback the callback to handle the result or error.
9696
*/
9797
@NonBlocking
98-
void getRootAsync(@NotNull Callback<@NotNull LiveMap> callback);
98+
void getRootAsync(@NotNull ObjectsCallback<@NotNull LiveMap> callback);
9999

100100
/**
101101
* Asynchronously creates a new LiveMap based on an existing LiveMap.
@@ -108,7 +108,7 @@ public interface LiveObjects {
108108
* @param callback the callback to handle the result or error.
109109
*/
110110
@NonBlocking
111-
void createMapAsync(@NotNull LiveMap liveMap, @NotNull Callback<@NotNull LiveMap> callback);
111+
void createMapAsync(@NotNull LiveMap liveMap, @NotNull ObjectsCallback<@NotNull LiveMap> callback);
112112

113113
/**
114114
* Asynchronously creates a new LiveMap based on a LiveCounter.
@@ -121,7 +121,7 @@ public interface LiveObjects {
121121
* @param callback the callback to handle the result or error.
122122
*/
123123
@NonBlocking
124-
void createMapAsync(@NotNull LiveCounter liveCounter, @NotNull Callback<@NotNull LiveMap> callback);
124+
void createMapAsync(@NotNull LiveCounter liveCounter, @NotNull ObjectsCallback<@NotNull LiveMap> callback);
125125

126126
/**
127127
* Asynchronously creates a new LiveMap based on a standard Java Map.
@@ -134,7 +134,7 @@ public interface LiveObjects {
134134
* @param callback the callback to handle the result or error.
135135
*/
136136
@NonBlocking
137-
void createMapAsync(@NotNull Map<String, Object> map, @NotNull Callback<@NotNull LiveMap> callback);
137+
void createMapAsync(@NotNull Map<String, Object> map, @NotNull ObjectsCallback<@NotNull LiveMap> callback);
138138

139139
/**
140140
* Asynchronously creates a new LiveCounter with an initial value.
@@ -147,5 +147,5 @@ public interface LiveObjects {
147147
* @param callback the callback to handle the result or error.
148148
*/
149149
@NonBlocking
150-
void createCounterAsync(@NotNull Long initialValue, @NotNull Callback<@NotNull LiveCounter> callback);
150+
void createCounterAsync(@NotNull Long initialValue, @NotNull ObjectsCallback<@NotNull LiveCounter> callback);
151151
}

lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package io.ably.lib.objects;
22

3+
import io.ably.lib.realtime.ChannelState;
34
import io.ably.lib.realtime.CompletionListener;
45
import io.ably.lib.types.AblyException;
6+
import io.ably.lib.types.ChannelMode;
57
import io.ably.lib.types.ProtocolMessage;
68
import org.jetbrains.annotations.NotNull;
9+
import org.jetbrains.annotations.Nullable;
710

811
public interface LiveObjectsAdapter {
912
/**
@@ -31,5 +34,24 @@ public interface LiveObjectsAdapter {
3134
* @return the maximum message size limit in bytes.
3235
*/
3336
int maxMessageSizeLimit();
37+
38+
/**
39+
* Retrieves the channel modes for a specific channel.
40+
* This method returns the modes that are set for the specified channel.
41+
*
42+
* @param channelName the name of the channel for which to retrieve the modes
43+
* @return the array of channel modes for the specified channel, or null if the channel is not found
44+
* Spec: RTO2a, RTO2b
45+
*/
46+
@Nullable ChannelMode[] getChannelModes(@NotNull String channelName);
47+
48+
/**
49+
* Retrieves the current state of a specific channel.
50+
* This method returns the state of the specified channel, which indicates its connection status.
51+
*
52+
* @param channelName the name of the channel for which to retrieve the state
53+
* @return the current state of the specified channel, or null if the channel is not found
54+
*/
55+
@Nullable ChannelState getChannelState(@NotNull String channelName);
3456
}
3557

lib/src/main/java/io/ably/lib/objects/LiveObjectsPlugin.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ public interface LiveObjectsPlugin {
4646
* Disposes of the LiveObjects instance associated with the specified channel name.
4747
* This method removes the LiveObjects instance for the given channel, releasing any
4848
* resources associated with it.
49+
* This is invoked when ablyRealtimeClient.channels.release(channelName) is called
4950
*
5051
* @param channelName the name of the channel whose LiveObjects instance is to be removed.
5152
*/
5253
void dispose(@NotNull String channelName);
5354

5455
/**
5556
* Disposes of the plugin instance and all underlying resources.
57+
* This is invoked when ablyRealtimeClient.close() is called
5658
*/
5759
void dispose();
5860
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.ably.lib.objects;
2+
3+
import io.ably.lib.types.AblyException;
4+
5+
/**
6+
* Callback interface for handling results of asynchronous LiveObjects operations.
7+
* Used for operations like creating LiveMaps/LiveCounters, modifying entries, and retrieving objects.
8+
* Callbacks are executed on background threads managed by the LiveObjects system.
9+
*
10+
* @param <T> the type of the result returned by the asynchronous operation
11+
*/
12+
public interface ObjectsCallback<T> {
13+
14+
/**
15+
* Called when the asynchronous operation completes successfully.
16+
* For modification operations (set, remove, increment), result is typically Void.
17+
* For creation/retrieval operations, result contains the created/retrieved object.
18+
*
19+
* @param result the result of the operation, may be null for modification operations
20+
*/
21+
void onSuccess(T result);
22+
23+
/**
24+
* Called when the asynchronous operation fails.
25+
* The exception contains detailed error information including error codes and messages.
26+
* Common errors include network issues, authentication failures, and validation errors.
27+
*
28+
* @param exception the exception that occurred during the operation
29+
*/
30+
void onError(AblyException exception);
31+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.ably.lib.objects;
2+
3+
/**
4+
* Represents a objects subscription that can be unsubscribed from.
5+
* This interface provides a way to clean up and remove subscriptions when they are no longer needed.
6+
* Example usage:
7+
* <pre>
8+
* {@code
9+
* ObjectsSubscription s = objects.subscribe(ObjectsStateEvent.SYNCING, new ObjectsStateListener() {});
10+
* // Later when done with the subscription
11+
* s.unsubscribe();
12+
* }
13+
* </pre>
14+
*/
15+
public interface ObjectsSubscription {
16+
/**
17+
* This method should be called when the subscription is no longer needed,
18+
* it will make sure no further events will be sent to the subscriber and
19+
* that references to the subscriber are cleaned up.
20+
*/
21+
void unsubscribe();
22+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package io.ably.lib.objects.state;
2+
3+
import io.ably.lib.objects.ObjectsSubscription;
4+
import org.jetbrains.annotations.NonBlocking;
5+
import org.jetbrains.annotations.NotNull;
6+
7+
public interface ObjectsStateChange {
8+
/**
9+
* Subscribes to a specific Live Objects synchronization state event.
10+
*
11+
* <p>This method registers the provided listener to be notified when the specified
12+
* synchronization state event occurs. The returned subscription can be used to
13+
* unsubscribe later when the notifications are no longer needed.
14+
*
15+
* @param event the synchronization state event to subscribe to (SYNCING or SYNCED)
16+
* @param listener the listener that will be called when the event occurs
17+
* @return a subscription object that can be used to unsubscribe from the event
18+
*/
19+
@NonBlocking
20+
ObjectsSubscription on(@NotNull ObjectsStateEvent event, @NotNull ObjectsStateChange.Listener listener);
21+
22+
/**
23+
* Unsubscribes the specified listener from all synchronization state events.
24+
*
25+
* <p>After calling this method, the provided listener will no longer receive
26+
* any synchronization state event notifications.
27+
*
28+
* @param listener the listener to unregister from all events
29+
*/
30+
@NonBlocking
31+
void off(@NotNull ObjectsStateChange.Listener listener);
32+
33+
/**
34+
* Unsubscribes all listeners from all synchronization state events.
35+
*
36+
* <p>After calling this method, no listeners will receive any synchronization
37+
* state event notifications until new listeners are registered.
38+
*/
39+
@NonBlocking
40+
void offAll();
41+
42+
/**
43+
* Interface for receiving notifications about Live Objects synchronization state changes.
44+
* <p>
45+
* Implement this interface and register it with an ObjectsStateEmitter to be notified
46+
* when synchronization state transitions occur.
47+
*/
48+
interface Listener {
49+
/**
50+
* Called when the synchronization state changes.
51+
*
52+
* @param objectsStateEvent The new state event (SYNCING or SYNCED)
53+
*/
54+
void onStateChanged(ObjectsStateEvent objectsStateEvent);
55+
}
56+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.ably.lib.objects.state;
2+
3+
/**
4+
* Represents the synchronization state of Ably Live Objects.
5+
* <p>
6+
* This enum is used to notify listeners about state changes in the synchronization process.
7+
* Clients can register an {@link ObjectsStateChange.Listener} to receive these events.
8+
*/
9+
public enum ObjectsStateEvent {
10+
/**
11+
* Indicates that synchronization between local and remote objects is in progress.
12+
*/
13+
SYNCING,
14+
15+
/**
16+
* Indicates that synchronization has completed successfully and objects are in sync.
17+
*/
18+
SYNCED
19+
}

0 commit comments

Comments
 (0)