|
| 1 | +package io.ably.lib.object; |
| 2 | + |
| 3 | +import io.ably.lib.object.path.types.LiveMapPathObject; |
| 4 | +import io.ably.lib.object.state.ObjectStateChange; |
| 5 | +import io.ably.lib.object.state.ObjectStateEvent; |
| 6 | +import io.ably.lib.types.AblyException; |
| 7 | +import io.ably.lib.types.ErrorInfo; |
| 8 | +import org.jetbrains.annotations.NotNull; |
| 9 | + |
| 10 | +import java.util.concurrent.CompletableFuture; |
| 11 | + |
| 12 | +/** |
| 13 | + * The RealtimeObject interface is the entry point to the strongly-typed, path-based |
| 14 | + * LiveObjects API on a channel. It exposes the root of the objects graph as a |
| 15 | + * {@link LiveMapPathObject} and, via {@link ObjectStateChange}, lets callers observe |
| 16 | + * synchronization state transitions for the channel's objects. |
| 17 | + * |
| 18 | + * <p>Implementations of this interface must be thread-safe as they may be accessed |
| 19 | + * from multiple threads concurrently. |
| 20 | + * |
| 21 | + * <p>Spec: RTO23 |
| 22 | + */ |
| 23 | +public interface RealtimeObject extends ObjectStateChange { |
| 24 | + |
| 25 | + /** |
| 26 | + * Retrieves a {@link LiveMapPathObject} rooted at the channel's root {@code LiveMap}. |
| 27 | + * The returned object has an empty path and resolves to the root {@code LiveMap}; use |
| 28 | + * its navigation methods to address nested values within the objects graph. |
| 29 | + * |
| 30 | + * <p>When called without a type variable, we return a default root type which is based |
| 31 | + * on the globally defined interface for the Objects feature. A user can provide an |
| 32 | + * explicit type to set the type structure on this particular channel. This is useful |
| 33 | + * when working with multiple channels with different underlying data structures. |
| 34 | + * |
| 35 | + * <p>This operation requires the {@code OBJECT_SUBSCRIBE} channel mode. It implicitly |
| 36 | + * attaches the channel if it is not already attached; the returned future completes once |
| 37 | + * the objects synchronization state has transitioned to {@code SYNCED}, and completes |
| 38 | + * exceptionally with an {@code AblyException} if synchronization fails. |
| 39 | + * |
| 40 | + * <p>Spec: RTO23, RTO23f (typed SDKs return a {@link LiveMapPathObject}) |
| 41 | + * |
| 42 | + * @return a future that completes with the root {@link LiveMapPathObject} for this |
| 43 | + * channel's objects graph. |
| 44 | + */ |
| 45 | + @NotNull |
| 46 | + CompletableFuture<LiveMapPathObject> get(); |
| 47 | + |
| 48 | + /** |
| 49 | + * Null-Object guard for {@link RealtimeObject}, used as the value of {@code channel.object} |
| 50 | + * when the LiveObjects plugin is not installed. |
| 51 | + * |
| 52 | + * <p>Because {@code channel.object} is a field, dereferencing it can never throw; instead |
| 53 | + * every method here fails fast with the plugin-missing error, so {@code get()}, {@code on()}, |
| 54 | + * {@code off()} and {@code offAll()} surface a clear, consistent error rather than a |
| 55 | + * {@link NullPointerException}. |
| 56 | + * |
| 57 | + * <p>A stateless singleton ({@link #INSTANCE}) shared across all channels that lack the |
| 58 | + * plugin. Adding a method to {@link RealtimeObject} will fail compilation here until it is |
| 59 | + * guarded, which is the intended safety net. |
| 60 | + */ |
| 61 | + final class Unavailable implements RealtimeObject { |
| 62 | + |
| 63 | + public static final Unavailable INSTANCE = new Unavailable(); |
| 64 | + |
| 65 | + private Unavailable() {} |
| 66 | + |
| 67 | + @Override |
| 68 | + public @NotNull CompletableFuture<LiveMapPathObject> get() { |
| 69 | + throw missing(); |
| 70 | + } |
| 71 | + |
| 72 | + @Override |
| 73 | + public Subscription on(@NotNull ObjectStateEvent event, ObjectStateChange.@NotNull Listener listener) { |
| 74 | + throw missing(); |
| 75 | + } |
| 76 | + |
| 77 | + @Override |
| 78 | + public void off(ObjectStateChange.@NotNull Listener listener) { |
| 79 | + throw missing(); |
| 80 | + } |
| 81 | + |
| 82 | + @Override |
| 83 | + public void offAll() { |
| 84 | + throw missing(); |
| 85 | + } |
| 86 | + |
| 87 | + private static RuntimeException missing() { |
| 88 | + return new IllegalStateException("LiveObjects plugin hasn't been installed", AblyException.fromErrorInfo( |
| 89 | + new ErrorInfo("add runtimeOnly('io.ably:liveobjects:<ably-version>') to your dependency tree", 400, 40019) |
| 90 | + )); |
| 91 | + } |
| 92 | + } |
| 93 | +} |
0 commit comments