/* * Copyright 2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package io.atomix; import io.atomix.catalyst.concurrent.ThreadContext; import io.atomix.catalyst.serializer.Serializer; import io.atomix.catalyst.util.Assert; import io.atomix.collections.DistributedMap; import io.atomix.collections.DistributedMultiMap; import io.atomix.collections.DistributedQueue; import io.atomix.collections.DistributedSet; import io.atomix.concurrent.DistributedLock; import io.atomix.group.DistributedGroup; import io.atomix.manager.ResourceClient; import io.atomix.manager.ResourceManager; import io.atomix.resource.Resource; import io.atomix.resource.ResourceType; import io.atomix.variables.DistributedLong; import io.atomix.variables.DistributedValue; import java.util.Arrays; import java.util.Collection; import java.util.Set; import java.util.concurrent.CompletableFuture; /** * Base type for creating and managing distributed {@link Resource resources} in a Atomix cluster. * <p> * Resources are user provided stateful objects backed by a distributed state machine. This class facilitates the * creation and management of {@link Resource} objects via a filesystem like interface. There is a * one-to-one relationship between keys and resources, so each key can be associated with one and only one resource. * <p> * To create a resource, pass the resource {@link java.lang.Class} to the {@link Atomix#getResource(String, ResourceType)} method. * When a resource is created, the {@link io.atomix.copycat.server.StateMachine} associated with the resource will be created on each Raft server * and future operations submitted for that resource will be applied to the state machine. Internally, resource state * machines are multiplexed across a shared Raft log. * * @author <a href="http://github.com/kuujo">Jordan Halterman</a> */ public abstract class Atomix implements ResourceManager<Atomix> { static final Collection<ResourceType> RESOURCES = Arrays.asList( new ResourceType(DistributedMap.class), new ResourceType(DistributedMultiMap.class), new ResourceType(DistributedSet.class), new ResourceType(DistributedQueue.class), new ResourceType(DistributedValue.class), new ResourceType(DistributedLong.class), new ResourceType(DistributedLock.class), new ResourceType(DistributedGroup.class) ); final ResourceClient client; protected Atomix(ResourceClient client) { this.client = Assert.notNull(client, "client"); } @Override public ThreadContext context() { return client.context(); } /** * Returns the Atomix serializer. * <p> * Serializable types registered on the returned serializer are reflected throughout the system. Types * registered on a client must also be registered on all replicas to be transported across the wire. * * @return The Atomix serializer. */ public Serializer serializer() { return client.client().serializer(); } /** * Gets or creates a distributed map with default configuration and options. * <p> * The returned {@link DistributedMap} replicates and stores map entries in memory in a {@link java.util.HashMap}. * The size of the map is limited by the memory available to the smallest replica in the cluster. The map * requires non-null keys but allows null values. * <p> * Map key and value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the map. * <p> * If no map exists at the given {@code key}, a new map will be created. If a map with the given key * already exists, a reference to the map will be returned in the {@link CompletableFuture}. The map * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the map is guaranteed to be * visible by all clients and replicas in the cluster. * * @param key The resource key. * @param <K> The map key type. * @param <V> The map value type. * @return A completable future to be completed once the map has been created. */ public <K, V> CompletableFuture<DistributedMap<K, V>> getMap(String key) { return getResource(key, DistributedMap.class); } /** * Gets or creates a distributed map with a cluster-wide configuration. * <p> * The returned {@link DistributedMap} replicates and stores map entries in memory in a {@link java.util.HashMap}. * The size of the map is limited by the memory available to the smallest replica in the cluster. The map * requires non-null keys but allows null values. * <p> * Map key and value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the map. * <p> * If no map exists at the given {@code key}, a new map will be created. If a map with the given key * already exists, a reference to the map will be returned in the {@link CompletableFuture}. The map * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the map is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided map {@link DistributedMap.Config Config} will be used to configure the cluster-wide map. * If another process previously configured the map with a different configuration, that configuration * will be overridden for all clients and replicas. * * @param key The resource key. * @param config The cluster-wide map configuration. * @param <K> The map key type. * @param <V> The map value type. * @return A completable future to be completed once the map has been created. */ public <K, V> CompletableFuture<DistributedMap<K, V>> getMap(String key, DistributedMap.Config config) { return getResource(key, DistributedMap.class, config); } /** * Gets or creates a distributed map with local options. * <p> * The returned {@link DistributedMap} replicates and stores map entries in memory in a {@link java.util.HashMap}. * The size of the map is limited by the memory available to the smallest replica in the cluster. The map * requires non-null keys but allows null values. * <p> * Map key and value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the map. * <p> * If no map exists at the given {@code key}, a new map will be created. If a map with the given key * already exists, a reference to the map will be returned in the {@link CompletableFuture}. The map * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the map is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided map {@link DistributedMap.Options options} will be used to configure only the local map * instance. Cluster-wide configurations can be performed by providing a {@link DistributedMap.Config Config}. * * @param key The resource key. * @param options The local map options. * @param <K> The map key type. * @param <V> The map value type. * @return A completable future to be completed once the map has been created. */ public <K, V> CompletableFuture<DistributedMap<K, V>> getMap(String key, DistributedMap.Options options) { return getResource(key, DistributedMap.class, options); } /** * Gets or creates a distributed map with a cluster-wide configuration and local options. * <p> * The returned {@link DistributedMap} replicates and stores map entries in memory in a {@link java.util.HashMap}. * The size of the map is limited by the memory available to the smallest replica in the cluster. The map * requires non-null keys but allows null values. * <p> * Map key and value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the map. * <p> * If no map exists at the given {@code key}, a new map will be created. If a map with the given key * already exists, a reference to the map will be returned in the {@link CompletableFuture}. The map * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the map is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided map {@link DistributedMap.Config Config} will be used to configure the cluster-wide map. * If another process previously configured the map with a different configuration, that configuration * will be overridden for all clients and replicas. * <p> * The provided map {@link DistributedMap.Options options} will be used to configure only the local map * instance. Cluster-wide configurations can be performed by providing a {@link DistributedMap.Config Config}. * * @param key The resource key. * @param config The cluster-wide map configuration. * @param options The local map options. * @param <K> The map key type. * @param <V> The map value type. * @return A completable future to be completed once the map has been created. */ public <K, V> CompletableFuture<DistributedMap<K, V>> getMap(String key, DistributedMap.Config config, DistributedMap.Options options) { return getResource(key, DistributedMap.class, config, options); } /** * Gets or creates a distributed multi map with default configuration and options. * <p> * The multi-map is a map of keys to a collection of values. The order of value collections is dependent * upon the map {@link DistributedMultiMap.Config configuration}. The map requires non-null keys but allows * null values. * <p> * Map key and value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the map. * <p> * If no map exists at the given {@code key}, a new map will be created. If a map with the given key * already exists, a reference to the map will be returned in the {@link CompletableFuture}. The map * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the map is guaranteed to be * visible by all clients and replicas in the cluster. * * @param key The resource key. * @param <K> The multi map key type. * @param <V> The multi map value type. * @return A completable future to be completed once the multi map has been created. */ public <K, V> CompletableFuture<DistributedMultiMap<K, V>> getMultiMap(String key) { return getResource(key, DistributedMultiMap.class); } /** * Gets or creates a distributed multi map with a cluster-wide configuration. * <p> * The multi-map is a map of keys to a collection of values. The order of value collections is dependent * upon the map {@link DistributedMultiMap.Config configuration}. The map requires non-null keys but allows * null values. * <p> * Map key and value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the map. * <p> * If no map exists at the given {@code key}, a new map will be created. If a map with the given key * already exists, a reference to the map will be returned in the {@link CompletableFuture}. The map * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the map is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided map {@link DistributedMultiMap.Config Config} will be used to configure the cluster-wide map. * If another process previously configured the map with a different configuration, that configuration * will be overridden for all clients and replicas. * * @param key The resource key. * @param config The cluster-wide multi map configuration. * @param <K> The multi map key type. * @param <V> The multi map value type. * @return A completable future to be completed once the multi map has been created. */ public <K, V> CompletableFuture<DistributedMultiMap<K, V>> getMultiMap(String key, DistributedMultiMap.Config config) { return getResource(key, DistributedMultiMap.class, config); } /** * Gets or creates a distributed multi map with local options. * <p> * The multi-map is a map of keys to a collection of values. The order of value collections is dependent * upon the map {@link DistributedMultiMap.Config configuration}. The map requires non-null keys but allows * null values. * <p> * Map key and value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the map. * <p> * If no map exists at the given {@code key}, a new map will be created. If a map with the given key * already exists, a reference to the map will be returned in the {@link CompletableFuture}. The map * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the map is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided map {@link DistributedMultiMap.Options options} will be used to configure only the local map * instance. Cluster-wide configurations can be performed by providing a {@link DistributedMultiMap.Config Config}. * * @param key The resource key. * @param options The local multi map options. * @param <K> The multi map key type. * @param <V> The multi map value type. * @return A completable future to be completed once the multi map has been created. */ public <K, V> CompletableFuture<DistributedMultiMap<K, V>> getMultiMap(String key, DistributedMultiMap.Options options) { return getResource(key, DistributedMultiMap.class, options); } /** * Gets or creates a distributed multi map with a cluster-wide configuration and local options. * <p> * The multi-map is a map of keys to a collection of values. The order of value collections is dependent * upon the map {@link DistributedMultiMap.Config configuration}. The map requires non-null keys but allows * null values. * <p> * Map key and value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the map. * <p> * If no map exists at the given {@code key}, a new map will be created. If a map with the given key * already exists, a reference to the map will be returned in the {@link CompletableFuture}. The map * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the map is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided map {@link DistributedMultiMap.Config Config} will be used to configure the cluster-wide map. * If another process previously configured the map with a different configuration, that configuration * will be overridden for all clients and replicas. * <p> * The provided map {@link DistributedMultiMap.Options options} will be used to configure only the local map * instance. Cluster-wide configurations can be performed by providing a {@link DistributedMultiMap.Config Config}. * * @param key The resource key. * @param config The cluster-wide multi map configuration. * @param options The local multi map options. * @param <K> The multi map key type. * @param <V> The multi map value type. * @return A completable future to be completed once the multi map has been created. */ public <K, V> CompletableFuture<DistributedMultiMap<K, V>> getMultiMap(String key, DistributedMultiMap.Config config, DistributedMultiMap.Options options) { return getResource(key, DistributedMultiMap.class, config, options); } /** * Gets or creates a distributed set with default configuration and options. * <p> * The returned set replicates a unique set of values. Values must be non-null and must be serializable * with the local {@code Atomix} instance {@link Serializer} and all {@link AtomixReplica} instances. * By default, all primitives and most collections are serializable. For custom classes, users must * {@link Serializer#register(Class)} serializable types <em>before</em> constructing the set. * <p> * If no set exists at the given {@code key}, a new set will be created. If a set with the given key * already exists, a reference to the set will be returned in the {@link CompletableFuture}. The set * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the set is guaranteed to be * visible by all clients and replicas in the cluster. * * @param key The resource key. * @param <T> The value type. * @return A completable future to be completed once the set has been created. */ public <T> CompletableFuture<DistributedSet<T>> getSet(String key) { return getResource(key, DistributedSet.class); } /** * Gets or creates a distributed set with a cluster-wide configuration. * <p> * The returned set replicates a unique set of values. Values must be non-null and must be serializable * with the local {@code Atomix} instance {@link Serializer} and all {@link AtomixReplica} instances. * By default, all primitives and most collections are serializable. For custom classes, users must * {@link Serializer#register(Class)} serializable types <em>before</em> constructing the set. * <p> * If no set exists at the given {@code key}, a new set will be created. If a set with the given key * already exists, a reference to the set will be returned in the {@link CompletableFuture}. The set * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the set is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided set {@link DistributedSet.Config Config} will be used to configure the cluster-wide set. * If another process previously configured the set with a different configuration, that configuration * will be overridden for all clients and replicas. * * @param key The resource key. * @param config The cluster-wide set configuration. * @param <T> The value type. * @return A completable future to be completed once the set has been created. */ public <T> CompletableFuture<DistributedSet<T>> getSet(String key, DistributedSet.Config config) { return getResource(key, DistributedSet.class, config); } /** * Gets or creates a distributed set with local options. * <p> * The returned set replicates a unique set of values. Values must be non-null and must be serializable * with the local {@code Atomix} instance {@link Serializer} and all {@link AtomixReplica} instances. * By default, all primitives and most collections are serializable. For custom classes, users must * {@link Serializer#register(Class)} serializable types <em>before</em> constructing the set. * <p> * If no set exists at the given {@code key}, a new set will be created. If a set with the given key * already exists, a reference to the set will be returned in the {@link CompletableFuture}. The set * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the set is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided set {@link DistributedSet.Options options} will be used to configure only the local set * instance. Cluster-wide configurations can be performed by providing a {@link DistributedSet.Config Config}. * * @param key The resource key. * @param options The local set options. * @param <T> The value type. * @return A completable future to be completed once the set has been created. */ public <T> CompletableFuture<DistributedSet<T>> getSet(String key, DistributedSet.Options options) { return getResource(key, DistributedSet.class, options); } /** * Gets or creates a distributed set with a cluster-wide configuration and local options. * <p> * The returned set replicates a unique set of values. Values must be non-null and must be serializable * with the local {@code Atomix} instance {@link Serializer} and all {@link AtomixReplica} instances. * By default, all primitives and most collections are serializable. For custom classes, users must * {@link Serializer#register(Class)} serializable types <em>before</em> constructing the set. * <p> * If no set exists at the given {@code key}, a new set will be created. If a set with the given key * already exists, a reference to the set will be returned in the {@link CompletableFuture}. The set * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the set is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided set {@link DistributedSet.Config Config} will be used to configure the cluster-wide set. * If another process previously configured the set with a different configuration, that configuration * will be overridden for all clients and replicas. * <p> * The provided set {@link DistributedSet.Options options} will be used to configure only the local set * instance. Cluster-wide configurations can be performed by providing a {@link DistributedSet.Config Config}. * * @param key The resource key. * @param config The cluster-wide set configuration. * @param options The local set options. * @param <T> The value type. * @return A completable future to be completed once the set has been created. */ public <T> CompletableFuture<DistributedSet<T>> getSet(String key, DistributedSet.Config config, DistributedSet.Options options) { return getResource(key, DistributedSet.class, config, options); } /** * Gets or creates a distributed queue with default configuration and options. * <p> * The returned queue is backed by a replicated {@link java.util.ArrayDeque}. Queue values can be * {@code null}. The size of the queue is limited by the amount of memory available to the smallest * replica in the cluster. * <p> * Queue value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the queue. * <p> * If no queue exists at the given {@code key}, a new queue will be created. If a queue with the given key * already exists, a reference to the queue will be returned in the {@link CompletableFuture}. The queue * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the queue is guaranteed to be * visible by all clients and replicas in the cluster. * * @param key The resource key. * @param <T> The value type. * @return A completable future to be completed once the queue has been created. */ public <T> CompletableFuture<DistributedQueue<T>> getQueue(String key) { return getResource(key, DistributedQueue.class); } /** * Gets or creates a distributed queue with a cluster-wide configuration. * <p> * The returned queue is backed by a replicated {@link java.util.ArrayDeque}. Queue values can be * {@code null}. The size of the queue is limited by the amount of memory available to the smallest * replica in the cluster. * <p> * Queue value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the queue. * <p> * If no queue exists at the given {@code key}, a new queue will be created. If a queue with the given key * already exists, a reference to the queue will be returned in the {@link CompletableFuture}. The queue * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the queue is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided queue {@link DistributedQueue.Config Config} will be used to configure the cluster-wide queue. * If another process previously configured the queue with a different configuration, that configuration * will be overridden for all clients and replicas. * * @param key The resource key. * @param config The cluster-wide queue configuration. * @param <T> The value type. * @return A completable future to be completed once the queue has been created. */ public <T> CompletableFuture<DistributedQueue<T>> getQueue(String key, DistributedQueue.Config config) { return getResource(key, DistributedQueue.class, config); } /** * Gets or creates a distributed queue with local options. * <p> * The returned queue is backed by a replicated {@link java.util.ArrayDeque}. Queue values can be * {@code null}. The size of the queue is limited by the amount of memory available to the smallest * replica in the cluster. * <p> * Queue value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the queue. * <p> * If no queue exists at the given {@code key}, a new queue will be created. If a queue with the given key * already exists, a reference to the queue will be returned in the {@link CompletableFuture}. The queue * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the queue is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided queue {@link DistributedQueue.Options options} will be used to configure only the local queue * instance. Cluster-wide configurations can be performed by providing a {@link DistributedQueue.Config Config}. * * @param key The resource key. * @param options The local queue options. * @param <T> The value type. * @return A completable future to be completed once the queue has been created. */ public <T> CompletableFuture<DistributedQueue<T>> getQueue(String key, DistributedQueue.Options options) { return getResource(key, DistributedQueue.class, options); } /** * Gets or creates a distributed queue with a cluster-wide configuration and local options. * <p> * The returned queue is backed by a replicated {@link java.util.ArrayDeque}. Queue values can be * {@code null}. The size of the queue is limited by the amount of memory available to the smallest * replica in the cluster. * <p> * Queue value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the queue. * <p> * If no queue exists at the given {@code key}, a new queue will be created. If a queue with the given key * already exists, a reference to the queue will be returned in the {@link CompletableFuture}. The queue * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the queue is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided queue {@link DistributedQueue.Config Config} will be used to configure the cluster-wide queue. * If another process previously configured the queue with a different configuration, that configuration * will be overridden for all clients and replicas. * <p> * The provided queue {@link DistributedQueue.Options options} will be used to configure only the local queue * instance. Cluster-wide configurations can be performed by providing a {@link DistributedQueue.Config Config}. * * @param key The resource key. * @param config The cluster-wide queue configuration. * @param options The local queue options. * @param <T> The value type. * @return A completable future to be completed once the queue has been created. */ public <T> CompletableFuture<DistributedQueue<T>> getQueue(String key, DistributedQueue.Config config, DistributedQueue.Options options) { return getResource(key, DistributedQueue.class, config, options); } /** * Gets or creates a distributed value with default configuration and options. * <p> * The returned resource is a replicated representation of an arbitrary value. Values may be {@code null}. * Value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the value. * <p> * If no value exists at the given {@code key}, a new value will be created. If a value with the given key * already exists, a reference to the value will be returned in the {@link CompletableFuture}. The value * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the value is guaranteed to be * visible by all clients and replicas in the cluster. * * @param key The resource key. * @param <T> The value type. * @return A completable future to be completed once the value has been created. */ public <T> CompletableFuture<DistributedValue<T>> getValue(String key) { return getResource(key, DistributedValue.class); } /** * Gets or creates a distributed value with a cluster-wide configuration. * <p> * The returned resource is a replicated representation of an arbitrary value. Values may be {@code null}. * Value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the value. * <p> * If no value exists at the given {@code key}, a new value will be created. If a value with the given key * already exists, a reference to the value will be returned in the {@link CompletableFuture}. The value * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the value is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided value {@link DistributedValue.Config Config} will be used to configure the cluster-wide value. * If another process previously configured the value with a different configuration, that configuration * will be overridden for all clients and replicas. * * @param key The resource key. * @param config The cluster-wide value configuration. * @param <T> The value type. * @return A completable future to be completed once the value has been created. */ public <T> CompletableFuture<DistributedValue<T>> getValue(String key, DistributedValue.Config config) { return getResource(key, DistributedValue.class, config); } /** * Gets or creates a distributed value with local options. * <p> * The returned resource is a replicated representation of an arbitrary value. Values may be {@code null}. * Value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the value. * <p> * If no value exists at the given {@code key}, a new value will be created. If a value with the given key * already exists, a reference to the value will be returned in the {@link CompletableFuture}. The value * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the value is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided value {@link DistributedValue.Options options} will be used to configure only the local value * instance. Cluster-wide configurations can be performed by providing a {@link DistributedValue.Config Config}. * * @param key The resource key. * @param options The local value options. * @param <T> The value type. * @return A completable future to be completed once the value has been created. */ public <T> CompletableFuture<DistributedValue<T>> getValue(String key, DistributedValue.Options options) { return getResource(key, DistributedValue.class, options); } /** * Gets or creates a distributed value with a cluster-wide configuration and local options. * <p> * The returned resource is a replicated representation of an arbitrary value. Values may be {@code null}. * Value types must be serializable with the local {@code Atomix} instance {@link Serializer} * and all {@link AtomixReplica} instances. By default, all primitives and most collections are serializable. * For custom classes, users must {@link Serializer#register(Class)} serializable types <em>before</em> * constructing the value. * <p> * If no value exists at the given {@code key}, a new value will be created. If a value with the given key * already exists, a reference to the value will be returned in the {@link CompletableFuture}. The value * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the value is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided value {@link DistributedValue.Config Config} will be used to configure the cluster-wide value. * If another process previously configured the value with a different configuration, that configuration * will be overridden for all clients and replicas. * <p> * The provided value {@link DistributedValue.Options options} will be used to configure only the local value * instance. Cluster-wide configurations can be performed by providing a {@link DistributedValue.Config Config}. * * @param key The resource key. * @param config The cluster-wide value configuration. * @param options The local value options. * @param <T> The value type. * @return A completable future to be completed once the value has been created. */ public <T> CompletableFuture<DistributedValue<T>> getValue(String key, DistributedValue.Config config, DistributedValue.Options options) { return getResource(key, DistributedValue.class, config, options); } /** * Gets or creates a distributed long with default configuration and options. * <p> * The returned resource is an asynchronous distributed object similar to {@link java.util.concurrent.atomic.AtomicLong}. * Operations that modify the value are guaranteed to be atomic, and changes to the state of the value * are guaranteed to be visible by all clients and replicas when {@link io.atomix.resource.ReadConsistency#ATOMIC} * or {@link io.atomix.resource.ReadConsistency#ATOMIC_LEASE} is enabled. * <p> * If no long exists at the given {@code key}, a new long will be created. If a long with the given key * already exists, a reference to the long will be returned in the {@link CompletableFuture}. The long * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the long is guaranteed to be * visible by all clients and replicas in the cluster. * * @param key The resource key. * @return A completable future to be completed once the long has been created. */ public CompletableFuture<DistributedLong> getLong(String key) { return getResource(key, DistributedLong.class); } /** * Gets or creates a distributed long with a cluster-wide configuration. * <p> * The returned resource is an asynchronous distributed object similar to {@link java.util.concurrent.atomic.AtomicLong}. * Operations that modify the value are guaranteed to be atomic, and changes to the state of the value * are guaranteed to be visible by all clients and replicas when {@link io.atomix.resource.ReadConsistency#ATOMIC} * or {@link io.atomix.resource.ReadConsistency#ATOMIC_LEASE} is enabled. * <p> * If no long exists at the given {@code key}, a new long will be created. If a long with the given key * already exists, a reference to the long will be returned in the {@link CompletableFuture}. The long * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the long is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided long {@link DistributedLong.Config Config} will be used to configure the cluster-wide long. * If another process previously configured the long with a different configuration, that configuration * will be overridden for all clients and replicas. * * @param key The resource key. * @param config The cluster-wide long configuration. * @return A completable future to be completed once the long has been created. */ public CompletableFuture<DistributedLong> getLong(String key, DistributedLong.Config config) { return getResource(key, DistributedLong.class, config); } /** * Gets or creates a distributed long with local options. * <p> * The returned resource is an asynchronous distributed object similar to {@link java.util.concurrent.atomic.AtomicLong}. * Operations that modify the value are guaranteed to be atomic, and changes to the state of the value * are guaranteed to be visible by all clients and replicas when {@link io.atomix.resource.ReadConsistency#ATOMIC} * or {@link io.atomix.resource.ReadConsistency#ATOMIC_LEASE} is enabled. * <p> * If no long exists at the given {@code key}, a new long will be created. If a long with the given key * already exists, a reference to the long will be returned in the {@link CompletableFuture}. The long * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the long is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided long {@link DistributedLong.Options options} will be used to configure only the local long * instance. Cluster-wide configurations can be performed by providing a {@link DistributedLong.Config Config}. * * @param key The resource key. * @param options The local long options. * @return A completable future to be completed once the long has been created. */ public CompletableFuture<DistributedLong> getLong(String key, DistributedLong.Options options) { return getResource(key, DistributedLong.class, options); } /** * Gets or creates a distributed long with a cluster-wide configuration and local options. * <p> * The returned resource is an asynchronous distributed object similar to {@link java.util.concurrent.atomic.AtomicLong}. * Operations that modify the value are guaranteed to be atomic, and changes to the state of the value * are guaranteed to be visible by all clients and replicas when {@link io.atomix.resource.ReadConsistency#ATOMIC} * or {@link io.atomix.resource.ReadConsistency#ATOMIC_LEASE} is enabled. * <p> * If no long exists at the given {@code key}, a new long will be created. If a long with the given key * already exists, a reference to the long will be returned in the {@link CompletableFuture}. The long * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the long is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided long {@link DistributedLong.Config Config} will be used to configure the cluster-wide long. * If another process previously configured the long with a different configuration, that configuration * will be overridden for all clients and replicas. * <p> * The provided long {@link DistributedLong.Options options} will be used to configure only the local long * instance. Cluster-wide configurations can be performed by providing a {@link DistributedLong.Config Config}. * * @param key The resource key. * @param config The cluster-wide long configuration. * @param options The local long options. * @return A completable future to be completed once the long has been created. */ public CompletableFuture<DistributedLong> getLong(String key, DistributedLong.Config config, DistributedLong.Options options) { return getResource(key, DistributedLong.class, config, options); } /** * Gets or creates a distributed lock with default configuration and options. * <p> * The returned resource is a cluster-wide distributed lock. The lock is fair, meaning the lock will be * granted to the longest waiting process. In the event that a lock holder crashes or is partitioned, the * lock will be automatically released once the lock holder's session expires, and the lock will be granted * to the next process waiting in the lock queue. * <p> * If no lock exists at the given {@code key}, a new lock will be created. If a lock with the given key * already exists, a reference to the lock will be returned in the {@link CompletableFuture}. The lock * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the lock is guaranteed to be * visible by all clients and replicas in the cluster. * * @param key The resource key. * @return A completable future to be completed once the lock has been created. */ public CompletableFuture<DistributedLock> getLock(String key) { return getResource(key, DistributedLock.class); } /** * Gets or creates a distributed lock with a cluster-wide configuration. * <p> * The returned resource is a cluster-wide distributed lock. The lock is fair, meaning the lock will be * granted to the longest waiting process. In the event that a lock holder crashes or is partitioned, the * lock will be automatically released once the lock holder's session expires, and the lock will be granted * to the next process waiting in the lock queue. * <p> * If no lock exists at the given {@code key}, a new lock will be created. If a lock with the given key * already exists, a reference to the lock will be returned in the {@link CompletableFuture}. The lock * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the lock is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided lock {@link DistributedLock.Config Config} will be used to configure the cluster-wide lock. * If another process previously configured the lock with a different configuration, that configuration * will be overridden for all clients and replicas. * * @param key The resource key. * @param config The cluster-wide lock configuration. * @return A completable future to be completed once the lock has been created. */ public CompletableFuture<DistributedLock> getLock(String key, DistributedLock.Config config) { return getResource(key, DistributedLock.class, config); } /** * Gets or creates a distributed lock with local options. * <p> * The returned resource is a cluster-wide distributed lock. The lock is fair, meaning the lock will be * granted to the longest waiting process. In the event that a lock holder crashes or is partitioned, the * lock will be automatically released once the lock holder's session expires, and the lock will be granted * to the next process waiting in the lock queue. * <p> * If no lock exists at the given {@code key}, a new lock will be created. If a lock with the given key * already exists, a reference to the lock will be returned in the {@link CompletableFuture}. The lock * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the lock is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided lock {@link DistributedLock.Options options} will be used to configure only the local lock * instance. Cluster-wide configurations can be performed by providing a {@link DistributedLock.Config Config}. * * @param key The resource key. * @param options The local lock options. * @return A completable future to be completed once the lock has been created. */ public CompletableFuture<DistributedLock> getLock(String key, DistributedLock.Options options) { return getResource(key, DistributedLock.class, options); } /** * Gets or creates a distributed lock with a cluster-wide configuration and local options. * <p> * The returned resource is a cluster-wide distributed lock. The lock is fair, meaning the lock will be * granted to the longest waiting process. In the event that a lock holder crashes or is partitioned, the * lock will be automatically released once the lock holder's session expires, and the lock will be granted * to the next process waiting in the lock queue. * <p> * If no lock exists at the given {@code key}, a new lock will be created. If a lock with the given key * already exists, a reference to the lock will be returned in the {@link CompletableFuture}. The lock * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the lock is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided lock {@link DistributedLock.Config Config} will be used to configure the cluster-wide lock. * If another process previously configured the lock with a different configuration, that configuration * will be overridden for all clients and replicas. * <p> * The provided lock {@link DistributedLock.Options options} will be used to configure only the local lock * instance. Cluster-wide configurations can be performed by providing a {@link DistributedLock.Config Config}. * * @param key The resource key. * @param config The cluster-wide lock configuration. * @param options The local lock options. * @return A completable future to be completed once the lock has been created. */ public CompletableFuture<DistributedLock> getLock(String key, DistributedLock.Config config, DistributedLock.Options options) { return getResource(key, DistributedLock.class, config, options); } /** * Gets or creates a distributed group for managing group membership and leader elections. * <p> * The returned resource can be used to perform a variety of common distributed systems tasks, most notable * of which are group membership and leader election. The {@link DistributedGroup} can be used to identify * members of a group or to join a group. Members of the group are guaranteed to be consistent across all * clients and replicas. If a member of the group is partitioned or fails, the member will be removed once * its session expires. * <p> * If no group exists at the given {@code key}, a new group will be created. If a group with the given key * already exists, a reference to the group will be returned in the {@link CompletableFuture}. The group * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the group is guaranteed to be * visible by all clients and replicas in the cluster. * * @param key The resource key. * @return A completable future to be completed once the group has been created. */ public CompletableFuture<DistributedGroup> getGroup(String key) { return getResource(key, DistributedGroup.class); } /** * Gets or creates a distributed group for managing group membership and leader elections. * <p> * The returned resource can be used to perform a variety of common distributed systems tasks, most notable * of which are group membership and leader election. The {@link DistributedGroup} can be used to identify * members of a group or to join a group. Members of the group are guaranteed to be consistent across all * clients and replicas. If a member of the group is partitioned or fails, the member will be removed once * its session expires. * <p> * If no group exists at the given {@code key}, a new group will be created. If a group with the given key * already exists, a reference to the group will be returned in the {@link CompletableFuture}. The group * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the group is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided group {@link DistributedGroup.Config Config} will be used to configure the cluster-wide group. * If another process previously configured the group with a different configuration, that configuration * will be overridden for all clients and replicas. * * @param key The resource key. * @param config The cluster-wide group configuration. * @return A completable future to be completed once the group has been created. */ public CompletableFuture<DistributedGroup> getGroup(String key, DistributedGroup.Config config) { return getResource(key, DistributedGroup.class, config); } /** * Gets or creates a distributed group for managing group membership and leader elections. * <p> * The returned resource can be used to perform a variety of common distributed systems tasks, most notable * of which are group membership and leader election. The {@link DistributedGroup} can be used to identify * members of a group or to join a group. Members of the group are guaranteed to be consistent across all * clients and replicas. If a member of the group is partitioned or fails, the member will be removed once * its session expires. * <p> * If no group exists at the given {@code key}, a new group will be created. If a group with the given key * already exists, a reference to the group will be returned in the {@link CompletableFuture}. The group * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the group is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided group {@link DistributedGroup.Options options} will be used to configure only the local group * instance. Cluster-wide configurations can be performed by providing a {@link DistributedGroup.Config Config}. * * @param key The resource key. * @param options The local group options. * @return A completable future to be completed once the group has been created. */ public CompletableFuture<DistributedGroup> getGroup(String key, DistributedGroup.Options options) { return getResource(key, DistributedGroup.class, options); } /** * Gets or creates a distributed group for managing group membership and leader elections. * <p> * The returned resource can be used to perform a variety of common distributed systems tasks, most notable * of which are group membership and leader election. The {@link DistributedGroup} can be used to identify * members of a group or to join a group. Members of the group are guaranteed to be consistent across all * clients and replicas. If a member of the group is partitioned or fails, the member will be removed once * its session expires. * <p> * If no group exists at the given {@code key}, a new group will be created. If a group with the given key * already exists, a reference to the group will be returned in the {@link CompletableFuture}. The group * can be accessed by any {@link AtomixClient} or {@link AtomixReplica} in the cluster. * <p> * Once the returned {@link CompletableFuture} is completed successfully, the group is guaranteed to be * visible by all clients and replicas in the cluster. * <p> * The provided group {@link DistributedGroup.Config Config} will be used to configure the cluster-wide group. * If another process previously configured the group with a different configuration, that configuration * will be overridden for all clients and replicas. * <p> * The provided group {@link DistributedGroup.Options options} will be used to configure only the local group * instance. Cluster-wide configurations can be performed by providing a {@link DistributedGroup.Config Config}. * * @param key The resource key. * @param config The cluster-wide group configuration. * @param options The local group options. * @return A completable future to be completed once the group has been created. */ public CompletableFuture<DistributedGroup> getGroup(String key, DistributedGroup.Config config, DistributedGroup.Options options) { return getResource(key, DistributedGroup.class, config, options); } @Override public ResourceType type(Class<? extends Resource<?>> type) { return client.type(type); } @Override public CompletableFuture<Boolean> exists(String key) { return client.exists(key); } @Override public CompletableFuture<Set<String>> keys() { return client.keys().thenApply(this::cleanKeys); } @Override public <T extends Resource> CompletableFuture<Set<String>> keys(Class<? super T> type) { return client.keys(type).thenApply(this::cleanKeys); } @Override public CompletableFuture<Set<String>> keys(ResourceType type) { return client.keys(type).thenApply(this::cleanKeys); } /** * Cleans the key set. */ private Set<String> cleanKeys(Set<String> keys) { keys.remove(""); return keys; } @Override public <T extends Resource> CompletableFuture<T> getResource(String key, Class<? super T> type) { Assert.argNot(key.trim().length() == 0, "invalid resource key: key must be of non-zero length"); return client.getResource(key, type, new Resource.Config(), new Resource.Options()); } @Override public <T extends Resource> CompletableFuture<T> getResource(String key, Class<? super T> type, Resource.Config config) { Assert.argNot(key.trim().length() == 0, "invalid resource key: key must be of non-zero length"); return client.getResource(key, type, config, new Resource.Options()); } @Override public <T extends Resource> CompletableFuture<T> getResource(String key, Class<? super T> type, Resource.Options options) { Assert.argNot(key.trim().length() == 0, "invalid resource key: key must be of non-zero length"); return client.getResource(key, type, new Resource.Config(), options); } @Override public <T extends Resource> CompletableFuture<T> getResource(String key, Class<? super T> type, Resource.Config config, Resource.Options options) { Assert.argNot(key.trim().length() == 0, "invalid resource key: key must be of non-zero length"); return client.getResource(key, type, config, options); } @Override public <T extends Resource> CompletableFuture<T> getResource(String key, ResourceType type) { Assert.argNot(key.trim().length() == 0, "invalid resource key: key must be of non-zero length"); return client.getResource(key, type, new Resource.Config(), new Resource.Options()); } @Override public <T extends Resource> CompletableFuture<T> getResource(String key, ResourceType type, Resource.Config config) { Assert.argNot(key.trim().length() == 0, "invalid resource key: key must be of non-zero length"); return client.getResource(key, type, config, new Resource.Options()); } @Override public <T extends Resource> CompletableFuture<T> getResource(String key, ResourceType type, Resource.Options options) { Assert.argNot(key.trim().length() == 0, "invalid resource key: key must be of non-zero length"); return client.getResource(key, type, new Resource.Config(), options); } @Override public <T extends Resource> CompletableFuture<T> getResource(String key, ResourceType type, Resource.Config config, Resource.Options options) { Assert.argNot(key.trim().length() == 0, "invalid resource key: key must be of non-zero length"); return client.getResource(key, type, config, options); } @Override public String toString() { return getClass().getSimpleName(); } }