Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ android {
dependencies {
api(libs.gson)
implementation(libs.bundles.common)
compileOnly(libs.jetbrains)
testImplementation(libs.bundles.tests)
implementation(project(":network-client-core"))
runtimeOnly(project(":network-client-default"))
Expand Down
6 changes: 4 additions & 2 deletions android/src/main/java/io/ably/lib/realtime/Channel.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.ably.lib.types.AblyException;
import io.ably.lib.types.ChannelOptions;
import io.ably.lib.push.PushChannel;
import io.ably.lib.objects.LiveObjectsPlugin;


public class Channel extends ChannelBase {
/**
Expand All @@ -12,8 +14,8 @@ public class Channel extends ChannelBase {
*/
public final PushChannel push;

Channel(AblyRealtime ably, String name, ChannelOptions options) throws AblyException {
super(ably, name, options);
Channel(AblyRealtime ably, String name, ChannelOptions options, LiveObjectsPlugin liveObjectsPlugin) throws AblyException {
super(ably, name, options, liveObjectsPlugin);
this.push = ((io.ably.lib.rest.AblyRest) ably).channels.get(name, options).push;
}

Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ test-retry = "1.6.0"
kotlin = "2.1.10"
coroutine = "1.9.0"
turbine = "1.2.0"
jetbrains-annoations = "26.0.2"

[libraries]
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
Expand All @@ -47,6 +48,7 @@ okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhtt
coroutine-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutine" }
coroutine-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutine" }
turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" }
jetbrains = { group = "org.jetbrains", name = "annotations", version.ref = "jetbrains-annoations" }

[bundles]
common = ["msgpack", "vcdiff-core"]
Expand Down
1 change: 1 addition & 0 deletions java/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tasks.withType<Jar> {
dependencies {
api(libs.gson)
implementation(libs.bundles.common)
compileOnly(libs.jetbrains)
implementation(project(":network-client-core"))
if (findProperty("okhttp") == null) {
runtimeOnly(project(":network-client-default"))
Expand Down
5 changes: 3 additions & 2 deletions java/src/main/java/io/ably/lib/realtime/Channel.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package io.ably.lib.realtime;

import io.ably.lib.objects.LiveObjectsPlugin;
import io.ably.lib.types.AblyException;
import io.ably.lib.types.ChannelOptions;

public class Channel extends ChannelBase {
Channel(AblyRealtime ably, String name, ChannelOptions options) throws AblyException {
super(ably, name, options);
Channel(AblyRealtime ably, String name, ChannelOptions options, LiveObjectsPlugin liveObjectsPlugin) throws AblyException {
super(ably, name, options, liveObjectsPlugin);
}

public interface MessageListener extends ChannelBase.MessageListener {}
Expand Down
56 changes: 56 additions & 0 deletions lib/src/main/java/io/ably/lib/objects/LiveCounter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.ably.lib.objects;

import io.ably.lib.types.Callback;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Contract;

/**
* The LiveCounter interface provides methods to interact with a live counter.
* It allows incrementing, decrementing, and retrieving the current value of the counter,
* both synchronously and asynchronously.
*/
public interface LiveCounter {

/**
* Increments the value of the counter by 1.
* Send a COUNTER_INC operation to the realtime system to increment a value on this LiveCounter object.
* This does not modify the underlying data of this LiveCounter object. Instead, the change will be applied when
* the published COUNTER_INC operation is echoed back to the client and applied to the object following the regular
* operation application procedure.
*/
void increment();

/**
* Increments the value of the counter by 1 asynchronously.
* Send a COUNTER_INC operation to the realtime system to increment a value on this LiveCounter object.
* This does not modify the underlying data of this LiveCounter object. Instead, the change will be applied when
* the published COUNTER_INC operation is echoed back to the client and applied to the object following the regular
* operation application procedure.
*
* @param callback the callback to be invoked upon completion of the operation.
*/
void incrementAsync(@NotNull Callback<Void> callback);
Comment thread
sacOO7 marked this conversation as resolved.

/**
* Decrements the value of the counter by 1.
* An alias for calling {@link LiveCounter#increment()} with a negative amount.
*/
void decrement();

/**
* Decrements the value of the counter by 1 asynchronously.
* An alias for calling {@link LiveCounter#increment()} with a negative amount.
*
* @param callback the callback to be invoked upon completion of the operation.
*/
void decrementAsync(@NotNull Callback<Void> callback);

/**
* Retrieves the current value of the counter.
*
* @return the current value of the counter as a Long.
*/
@NotNull
@Contract(pure = true) // Indicates this method does not modify the state of the object.
Long value();
}
115 changes: 115 additions & 0 deletions lib/src/main/java/io/ably/lib/objects/LiveMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package io.ably.lib.objects;

import io.ably.lib.types.Callback;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;

import java.util.Map;

/**
* The LiveMap interface provides methods to interact with a live, real-time map structure.
* It supports both synchronous and asynchronous operations for managing key-value pairs.
*/
public interface LiveMap {

/**
* Retrieves the value associated with the specified key.
* If this map object is tombstoned (deleted), `undefined` is returned.
* If no entry is associated with the specified key, `undefined` is returned.
* If map entry is tombstoned (deleted), `undefined` is returned.
* If the value associated with the provided key is an objectId string of another LiveObject, a reference to that LiveObject
* is returned, provided it exists in the local pool and is not tombstoned. Otherwise, `undefined` is returned.
* If the value is not an objectId, then that value is returned.
*
* @param keyName the key whose associated value is to be returned.
* @return the value associated with the specified key, or null if the key does not exist.
*/
@Nullable
Object get(@NotNull String keyName);

Comment thread
coderabbitai[bot] marked this conversation as resolved.
/**
* Retrieves all entries (key-value pairs) in the map.
*
* @return an iterable collection of all entries in the map.
*/
@NotNull
@Unmodifiable
Iterable<Map.Entry<String, Object>> entries();

/**
* Retrieves all keys in the map.
*
* @return an iterable collection of all keys in the map.
*/
@NotNull
@Unmodifiable
Iterable<String> keys();

/**
* Retrieves all values in the map.
*
* @return an iterable collection of all values in the map.
*/
@NotNull
@Unmodifiable
Iterable<Object> values();
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Sets the specified key to the given value in the map.
* Send a MAP_SET operation to the realtime system to set a key on this LiveMap object to a specified value.
* This does not modify the underlying data of this LiveMap object. Instead, the change will be applied when
* the published MAP_SET operation is echoed back to the client and applied to the object following the regular
* operation application procedure.
*
* @param keyName the key to be set.
* @param value the value to be associated with the key.
*/
void set(@NotNull String keyName, @NotNull Object value);

/**
* Removes the specified key and its associated value from the map.
* Send a MAP_REMOVE operation to the realtime system to tombstone a key on this LiveMap object.
* This does not modify the underlying data of this LiveMap object. Instead, the change will be applied when
* the published MAP_REMOVE operation is echoed back to the client and applied to the object following the regular
* operation application procedure.
*
* @param keyName the key to be removed.
*/
void remove(@NotNull String keyName);

/**
* Retrieves the number of entries in the map.
*
* @return the size of the map.
*/
@Contract(pure = true) // Indicates this method does not modify the state of the object.
@NotNull
Long size();
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Asynchronously sets the specified key to the given value in the map.
* Send a MAP_SET operation to the realtime system to set a key on this LiveMap object to a specified value.
* This does not modify the underlying data of this LiveMap object. Instead, the change will be applied when
* the published MAP_SET operation is echoed back to the client and applied to the object following the regular
* operation application procedure.
*
* @param keyName the key to be set.
* @param value the value to be associated with the key.
* @param callback the callback to handle the result or any errors.
*/
void setAsync(@NotNull String keyName, @NotNull Object value, @NotNull Callback<Void> callback);

/**
* Asynchronously removes the specified key and its associated value from the map.
* Send a MAP_REMOVE operation to the realtime system to tombstone a key on this LiveMap object.
* This does not modify the underlying data of this LiveMap object. Instead, the change will be applied when
* the published MAP_REMOVE operation is echoed back to the client and applied to the object following the regular
* operation application procedure.
*
* @param keyName the key to be removed.
* @param callback the callback to handle the result or any errors.
*/
void removeAsync(@NotNull String keyName, @NotNull Callback<Void> callback);
}
159 changes: 159 additions & 0 deletions lib/src/main/java/io/ably/lib/objects/LiveObjects.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package io.ably.lib.objects;

import io.ably.lib.objects.batch.BatchContextBuilder;
import io.ably.lib.types.Callback;
import org.jetbrains.annotations.NotNull;


import java.util.Map;

/**
* The LiveObjects interface provides methods to interact with live data objects,
* such as maps and counters, in a real-time environment. It supports both synchronous
* and asynchronous operations for retrieving and creating live objects.
*
* <p>Implementations of this interface must be thread-safe as they may be accessed
* from multiple threads concurrently.
*/
public interface LiveObjects {
Comment thread
sacOO7 marked this conversation as resolved.

/**
* Retrieves the root LiveMap object.
* When called without a type variable, we return a default root type which is based on globally defined interface for Objects feature.
* A user can provide an explicit type for the getRoot method to explicitly set the type structure on this particular channel.
* This is useful when working with multiple channels with different underlying data structure.
*
* @return the root LiveMap instance.
*/
@NotNull
LiveMap getRoot();

/**
* Initiates a batch operation and provides a BatchContext through a callback.
* Provides access to the synchronous write API for Objects that can be used to batch multiple operations
* together in a single channel message.
*
* @param batchContextCallback the builder to configure the batch operation.
*/
void batch(@NotNull BatchContextBuilder batchContextCallback);

/**
* Creates a new LiveMap based on an existing LiveMap.
* Send a MAP_CREATE operation to the realtime system to create a new map object in the pool.
* Once the ACK message is received, the method returns the object from the local pool if it got created due to
* the echoed MAP_CREATE operation, or if it wasn't received yet, the method creates a new object locally
* using the provided data and returns it.
*
* @param liveMap the existing LiveMap to base the new LiveMap on.
* @return the newly created LiveMap instance.
*/
@NotNull
LiveMap createMap(@NotNull LiveMap liveMap);

/**
* Creates a new LiveMap based on a LiveCounter.
* Send a MAP_CREATE operation to the realtime system to create a new map object in the pool.
* Once the ACK message is received, the method returns the object from the local pool if it got created due to
* the echoed MAP_CREATE operation, or if it wasn't received yet, the method creates a new object locally
* using the provided data and returns it.
*
* @param liveCounter the LiveCounter to base the new LiveMap on.
* @return the newly created LiveMap instance.
*/
@NotNull
LiveMap createMap(@NotNull LiveCounter liveCounter);

/**
* Creates a new LiveMap based on a standard Java Map.
* Send a MAP_CREATE operation to the realtime system to create a new map object in the pool.
* Once the ACK message is received, the method returns the object from the local pool if it got created due to
* the echoed MAP_CREATE operation, or if it wasn't received yet, the method creates a new object locally
* using the provided data and returns it.
*
* @param map the Java Map to base the new LiveMap on.
* @return the newly created LiveMap instance.
*/
@NotNull
LiveMap createMap(@NotNull Map<String, Object> map);

Comment thread
sacOO7 marked this conversation as resolved.
/**
* Creates a new LiveCounter with an initial value.
* Send a COUNTER_CREATE operation to the realtime system to create a new counter object in the pool.
* Once the ACK message is received, the method returns the object from the local pool if it got created due to
* the echoed COUNTER_CREATE operation, or if it wasn't received yet, the method creates a new object locally
* using the provided data and returns it.
*
* @param initialValue the initial value of the LiveCounter.
* @return the newly created LiveCounter instance.
*/
@NotNull
LiveCounter createCounter(@NotNull Long initialValue);

/**
* Asynchronously retrieves the root LiveMap object.
* When called without a type variable, we return a default root type which is based on globally defined interface for Objects feature.
* A user can provide an explicit type for the getRoot method to explicitly set the type structure on this particular channel.
* This is useful when working with multiple channels with different underlying data structure.
*
* @param callback the callback to handle the result or error.
*/
void getRootAsync(@NotNull Callback<@NotNull LiveMap> callback);

/**
* Initiates a batch operation asynchronously.
* Provides access to the synchronous write API for Objects that can be used to batch multiple operations
* together in a single channel message.
*
* @param batchContextCallback the builder to configure the batch operation.
* @param callback the Callback to handle the completion or error of the batch operation.
*/
void batchAsync(@NotNull BatchContextBuilder batchContextCallback, @NotNull Callback<Void> callback);

/**
* Asynchronously creates a new LiveMap based on an existing LiveMap.
* Send a MAP_CREATE operation to the realtime system to create a new map object in the pool.
* Once the ACK message is received, the method returns the object from the local pool if it got created due to
* the echoed MAP_CREATE operation, or if it wasn't received yet, the method creates a new object locally
* using the provided data and returns it.
*
* @param liveMap the existing LiveMap to base the new LiveMap on.
* @param callback the callback to handle the result or error.
*/
void createMapAsync(@NotNull LiveMap liveMap, @NotNull Callback<@NotNull LiveMap> callback);

/**
* Asynchronously creates a new LiveMap based on a LiveCounter.
* Send a MAP_CREATE operation to the realtime system to create a new map object in the pool.
* Once the ACK message is received, the method returns the object from the local pool if it got created due to
* the echoed MAP_CREATE operation, or if it wasn't received yet, the method creates a new object locally
* using the provided data and returns it.
*
* @param liveCounter the LiveCounter to base the new LiveMap on.
* @param callback the callback to handle the result or error.
*/
void createMapAsync(@NotNull LiveCounter liveCounter, @NotNull Callback<@NotNull LiveMap> callback);

/**
* Asynchronously creates a new LiveMap based on a standard Java Map.
* Send a MAP_CREATE operation to the realtime system to create a new map object in the pool.
* Once the ACK message is received, the method returns the object from the local pool if it got created due to
* the echoed MAP_CREATE operation, or if it wasn't received yet, the method creates a new object locally
* using the provided data and returns it.
*
* @param map the Java Map to base the new LiveMap on.
* @param callback the callback to handle the result or error.
*/
void createMapAsync(@NotNull Map<String, Object> map, @NotNull Callback<@NotNull LiveMap> callback);

Comment thread
sacOO7 marked this conversation as resolved.
/**
* Asynchronously creates a new LiveCounter with an initial value.
* Send a COUNTER_CREATE operation to the realtime system to create a new counter object in the pool.
* Once the ACK message is received, the method returns the object from the local pool if it got created due to
* the echoed COUNTER_CREATE operation, or if it wasn't received yet, the method creates a new object locally
* using the provided data and returns it.
*
* @param initialValue the initial value of the LiveCounter.
* @param callback the callback to handle the result or error.
*/
void createCounterAsync(@NotNull Long initialValue, @NotNull Callback<@NotNull LiveCounter> callback);
}
Comment thread
sacOO7 marked this conversation as resolved.
Comment thread
sacOO7 marked this conversation as resolved.
Loading
Loading