All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pushtechnology.diffusion.client.features.TopicUpdate Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2018, 2023 DiffusionData Ltd., All Rights Reserved.
 *
 * Use is subject to licence terms.
 *
 * NOTICE: All information contained herein is, and remains the
 * property of DiffusionData. The intellectual and technical
 * concepts contained herein are proprietary to DiffusionData and
 * may be covered by U.S. and Foreign Patents, patents in process, and
 * are protected by trade secret or copyright law.
 *******************************************************************************/

package com.pushtechnology.diffusion.client.features;

import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

import com.pushtechnology.diffusion.client.features.control.topics.TopicControl.InvalidTopicPathException;
import com.pushtechnology.diffusion.client.features.control.topics.TopicControl.InvalidTopicSpecificationException;
import com.pushtechnology.diffusion.client.features.control.topics.TopicControl.TopicLicenseLimitException;
import com.pushtechnology.diffusion.client.session.Feature;
import com.pushtechnology.diffusion.client.session.PermissionsException;
import com.pushtechnology.diffusion.client.session.Session;
import com.pushtechnology.diffusion.client.session.SessionClosedException;
import com.pushtechnology.diffusion.client.session.SessionException;
import com.pushtechnology.diffusion.client.topics.details.TopicSpecification;
import com.pushtechnology.diffusion.client.topics.details.TopicType;
import com.pushtechnology.diffusion.datatype.Bytes;
import com.pushtechnology.diffusion.datatype.DataTypes;
import com.pushtechnology.diffusion.datatype.binary.Binary;
import com.pushtechnology.diffusion.datatype.json.JSON;

/**
 * This feature provides a client session with the ability to update topics.
 * 

* Topics can be set to new values using stateless * {@link #set(String, Class, Object) set} operations or an {@link UpdateStream * UpdateStream}. Both ensure that new values are applied safely to appropriate * topics. *

* Additionally, JSON topics can be updated with a {@link #applyJsonPatch JSON * Patch}. A patch is a list of operations that modifies a JSON value, removing * the need to supply a complete new value. This is useful if the source of the * updates doesn't provide values. For one-off, small changes to large JSON * values, it can be significantly cheaper to apply a patch than to use * {@code set} to provide the complete value. * *

Update streams

*

* An update stream is created for a specific topic. An UpdateStreamBuilder can * be obtained using {@link UpdateStream.Builder}. The type of the topic must * match the type of values passed to the update stream. An update stream can be * used to send any number of updates. It sends a sequence of updates for a * specific topic to the server. If supported by the data type, updates will be * sent to the server as a stream of binary deltas. *

* Update streams have additional ways of failing compared to stateless set * operations but when used repeatedly have lower overheads. This is because * update streams maintain a small amount of state that reduces the overhead of * operations but can become invalid for example, if the topic is deleted, or * some other session updates the topic value. *

* By default, update streams use a form of optimistic locking. An update stream * can update its topic incrementally as long as nothing else updates the topic. * If the topic is updated independently (for example, by another session, or by * the current session via set or a different update stream), then the next * update performed by the update stream will result in an * {@link InvalidUpdateStreamException}. *

* Applications can chose to use collaborative locking to coordinate exclusive * access to a topic. To follow this pattern acquire a * {@link com.pushtechnology.diffusion.client.session.Session.SessionLock * session lock}, and use it with a * {@link UpdateConstraint.Factory#locked(Session.SessionLock) lock constraint}. * The application is responsible for designing a locking scheme which * determines which lock is required to access a particular topic, and for * ensuring that all parts of the application that update the topic follow this * scheme. Lock constraints and an application locking scheme can also ensure a * sequence of set operations has exclusive access to the topic. * *

Supplying values

*

* When supplying values to an update the value type must be specified * (valueClass parameter). When using update streams, the value type is * specified when creating the stream. The class specified will depend upon the * {@link TopicType} of the topic being updated, according to the supported * types for the corresponding {@link DataTypes DataType}. For example, for a * {@link TopicType#JSON JSON} topic, a type of {@link JSON JSON.class} should * be supplied. *

* Note that for {@link TopicType#BINARY BINARY} topics the value class can be * {@link Binary Binary.class} or {@link Bytes Bytes.class} (or any subtype of * these) but the value must be effectively immutable. This means that any array * backing the supplied value must not be changed otherwise immutability would * be violated and results could be unpredictable. * *

Removing values

*

* When a {@link TopicType#STRING string}, {@link TopicType#INT64 int64}, or * {@link TopicType#DOUBLE double} topic is set to {@code null}, the topic will * be updated to have no value. If a previous value was present subscribers will * receive a notification that the new value is {@code null}. New subscribers * will not receive a value notification. Attempting to set any other type of * topic to {@code null} will cause a {@link NullPointerException} to be thrown. * *

Adding topics

*

* When setting a value using either stateless operations or update streams it * is possible to add a topic if one is not present. This is done using the * {@link #addAndSet(String, TopicSpecification, Class, Object) addAndSet} * methods or providing a topic specification when creating the update stream. * If a topic exists these methods will update the existing topic. * *

Time series topics

*

* All methods provided by this feature are compatible with time series topics * except for {@link #applyJsonPatch(String, String)}. The {@link TimeSeries} * feature can be used to update time series topics with custom metadata and * provides query capabilities. * *

Access control

*

* To update a topic a session needs * {@link com.pushtechnology.diffusion.client.types.PathPermission#UPDATE_TOPIC * UPDATE_TOPIC} permission for the topic path. To create a topic a session * needs * {@link com.pushtechnology.diffusion.client.types.PathPermission#MODIFY_TOPIC * MODIFY_TOPIC} permission for the topic path. Requests that combine adding a * topic and setting the value such as * {@link #addAndSet(String, TopicSpecification, Class, Object) addAndSet} * require both permissions. * *

Accessing the feature

*

* This feature may be obtained from a {@link Session session} as follows: * *

 * TopicUpdate topicUpdate = session.feature(TopicUpdate.class);
 * 
*

* This feature is also extended by the {@link Topics topics feature}. This * means is it possible to use the methods described here through the * {@link Topics topics feature}. * * @author DiffusionData Limited * @since 6.2 */ public interface TopicUpdate extends Feature { /** * Sets the topic to a specified value. *

* The {@code null} value can only be passed to the {@code value} parameter * when updating {@link TopicType#STRING string}, {@link TopicType#INT64 * int64}, or {@link TopicType#DOUBLE double} topics. *

* When a {@link TopicType#STRING string}, {@link TopicType#INT64 int64}, or * {@link TopicType#DOUBLE double} topic is set to {@code null}, the topic * will be updated to have no value. If a previous value was present * subscribers will receive a notification that the new value is * {@code null}. New subscribers will not receive a value notification. * * @param path the path of the topic * @param valueClass the type of the value * @param value the value. {@link TopicType#STRING String}, * {@link TopicType#INT64 int64}, and {@link TopicType#DOUBLE double} * topics accept {@code null}, as described above. Using {@code null} * with other topic types is an error and will throw a * {@link NullPointerException}. * @param the type of the value * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task fails, the CompletableFuture will complete * exceptionally with a {@link CompletionException}. Common reasons * for failure, listed by the exception reported as the * {@link CompletionException#getCause() cause}, include: * *

    *
  • {@link NoSuchTopicException} – if there is no topic * bound to {@code path}; *
  • {@link IncompatibleTopicException} – if updates cannot * be applied to the topic, for example if a topic view has bound a * reference topic to the path; *
  • {@link IncompatibleTopicStateException} – if the topic * is managed by a component (such as fan-out) that prohibits * updates from the caller; *
  • {@link ClusterRoutingException} – if the operation * failed due to a transient cluster error; *
  • {@link PermissionsException} – if the calling session * does not have the * {@link com.pushtechnology.diffusion.client.types.PathPermission#UPDATE_TOPIC * UPDATE_TOPIC} permission for {@code path}; *
  • {@link SessionClosedException} – if the session is * closed. *
*/ CompletableFuture set(String path, Class valueClass, T value); /** * Sets the topic to a specified value. *

* Takes a constraint that must be satisfied for the update to be applied. *

* In other respects this method works in the same way as * {@link #set(String, Class, Object)}. * * @param path the path of the topic * @param valueClass the type of the value * @param value the value. {@link TopicType#STRING String}, * {@link TopicType#INT64 int64}, and {@link TopicType#DOUBLE double} * topics accept {@code null}, as described above. Using {@code null} * with other topic types is an error and will throw a * {@link NullPointerException}. * @param constraint the constraint that must be satisfied for the topic to * be updated * @param the type of the value * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task fails, the CompletableFuture will complete * exceptionally with a {@link CompletionException}. Common reasons * for failure, listed by the exception reported as the * {@link CompletionException#getCause() cause}, include: * *

    *
  • {@link NoSuchTopicException} – if there is no topic * bound to {@code path}; *
  • {@link IncompatibleTopicException} – if updates cannot * be applied to the topic, for example if a topic view has bound a * reference topic to the path; *
  • {@link UnsatisfiedConstraintException} – if the * {@code constraint} is not satisfied by the topic bound to * {@code path}; *
  • {@link IncompatibleTopicStateException} – if the topic * is managed by a component (such as fan-out) that prohibits * updates from the caller; *
  • {@link ClusterRoutingException} – if the operation * failed due to a transient cluster error; *
  • {@link PermissionsException} – if the calling session * does not have the * {@link com.pushtechnology.diffusion.client.types.PathPermission#UPDATE_TOPIC * UPDATE_TOPIC} permission for {@code path}; *
  • {@link SessionClosedException} – if the session is * closed. *
*/ CompletableFuture set( String path, Class valueClass, T value, UpdateConstraint constraint); /** * Ensures a topic exists and sets its value. *

* If a topic does not exist at the {@code path}, one will be created using * the {@code specification}. If a topic does exist, its specification must * match {@code specification}, otherwise the operation will fail with * {@link IncompatibleTopicException IncompatibleTopicException}. *

* In other respects this method works in the same way as * {@link #set(String, Class, Object)}. * * @param path the path of the topic * @param specification the required specification of the topic * @param valueClass the type of the value * @param value the value. {@link TopicType#STRING String}, * {@link TopicType#INT64 int64}, and {@link TopicType#DOUBLE double} * topics accept {@code null}, as described above. Using {@code null} * with other topic types is an error and will throw a * {@link NullPointerException}. * @param the type of the value * @throws IllegalArgumentException if the type of the specification does * not match the type of the update * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task fails, the CompletableFuture will complete * exceptionally with a {@link CompletionException}. Common reasons * for failure, listed by the exception reported as the * {@link CompletionException#getCause() cause}, include those * reported by {@link #set(String, Class, Object) set} except * {@link NoSuchTopicException} as well as: * *

    *
  • {@link InvalidTopicPathException} – {@code path} is not * a valid topic path; *
  • {@link InvalidTopicSpecificationException} – the * specification is invalid, possibly because mandatory properties * not supplied; *
  • {@link TopicLicenseLimitException} – the topic could * not be added as it would breach a licensing limit; *
  • {@link PermissionsException} – if the calling session * does not have the appropriate permissions for {@code path}; in * general the calling session must have the * {@link com.pushtechnology.diffusion.client.types.PathPermission#MODIFY_TOPIC * MODIFY_TOPIC} and * {@link com.pushtechnology.diffusion.client.types.PathPermission#UPDATE_TOPIC * UPDATE_TOPIC} permissions, but if the topic exists and has the * same specification then it's sufficient for the calling session's * principal to be the {@link TopicSpecification#OWNER topic owner}. *
*/ CompletableFuture addAndSet( String path, TopicSpecification specification, Class valueClass, T value); /** * Ensures a topic exists and sets its value. *

* If a topic does not exist at the {@code path}, one will be created using * the {@code specification}. If a topic does exist, its specification must * match {@code specification}, otherwise the operation will fail with * {@link IncompatibleTopicException IncompatibleTopicException}. *

* Takes a constraint that must be satisfied for the topic to be created or * the update to be applied. *

* In other respects this method works in the same way as * {@link #set(String, Class, Object)}. * * @param path the path of the topic * @param specification the required specification of the topic * @param valueClass the type of the value * @param value the value. {@link TopicType#STRING String}, * {@link TopicType#INT64 int64}, and {@link TopicType#DOUBLE double} * topics accept {@code null}, as described above. Using {@code null} * with other topic types is an error and will throw a * {@link NullPointerException}. * @param constraint the constraint that must be satisfied for the topic to * be updated * @param the type of the value * @throws IllegalArgumentException if the type of the specification does * not match the type of the update * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task fails, the CompletableFuture will complete * exceptionally with a {@link CompletionException}. Common reasons * for failure, listed by the exception reported as the * {@link CompletionException#getCause() cause}, include those * reported by {@link #set(String, Class, Object, UpdateConstraint) * set} except {@link NoSuchTopicException} as well as: * *

    *
  • {@link InvalidTopicPathException} – {@code path} is not * a valid topic path; *
  • {@link InvalidTopicSpecificationException} – the * specification is invalid, possibly because mandatory properties * not supplied; *
  • {@link TopicLicenseLimitException} – the topic could * not be added as it would breach a licensing limit; *
  • {@link PermissionsException} – if the calling session * does not have the * {@link com.pushtechnology.diffusion.client.types.PathPermission#MODIFY_TOPIC * MODIFY_TOPIC} and the * {@link com.pushtechnology.diffusion.client.types.PathPermission#UPDATE_TOPIC * UPDATE_TOPIC} permissions for {@code path}; *
*/ CompletableFuture addAndSet( String path, TopicSpecification specification, Class valueClass, T value, UpdateConstraint constraint); /** * Creates an {@link UpdateStream update stream} to use for updating a * specific topic. *

* The type of the topic being updated must match the type derived from the * {@code valueClass} parameter. *

* Update streams send a sequence of updates for a specific topic. The * updates may be delivered to the server as binary deltas. They do not * provide exclusive access to the topic. If exclusive access is required * update streams should be used with {@link Session.SessionLock session * locks} as constraints. *

* Streams are validated lazily when the first {@link UpdateStream#set} or * {@link UpdateStream#validate} operation is completed. Once validated a * stream can be invalidated, after which it rejects future updates. * * @param path the path of the topic * @param valueClass the type of the values expected by the update stream * @param type of the values expected by the update stream * @return an update stream * @deprecated since 6.9 *

* use {@link #newUpdateStreamBuilder} */ @Deprecated UpdateStream createUpdateStream(String path, Class valueClass); /** * Creates an {@link UpdateStream update stream} to use for updating a * specific topic. *

* Takes a constraint that must be satisfied for the update stream to be * validated. *

* In other respects this method works in the same way as * {@link #createUpdateStream(String, Class)}. * * @param path the path of the topic * @param valueClass the type of the values expected by the update stream * @param constraint the constraint that must be satisfied for the update * stream to be validated * @param type of the values expected by the update stream * @return an update stream * @deprecated since 6.9 *

* use {@link #newUpdateStreamBuilder} */ @Deprecated UpdateStream createUpdateStream( String path, Class valueClass, UpdateConstraint constraint); /** * Creates an {@link UpdateStream update stream} to use for creating and * updating a specific topic. *

* If a topic does not exist at the {@code path} one will be created using * the {@code specification} when the update stream is validated. If a topic * does exist, its specification must match {@code specification}, otherwise * the operation will fail with {@link IncompatibleTopicException}. *

* In other respects this method works in the same way as * {@link #createUpdateStream(String, Class)}. * * @param path the path of the topic * @param specification the required specification of the topic * @param valueClass the type of the values expected by the update stream * @param type of the values expected by the update stream * @throws IllegalArgumentException if the topic type of the specification * does not match the type of values * @return an update stream * @deprecated since 6.9 *

* use {@link #newUpdateStreamBuilder} */ @Deprecated UpdateStream createUpdateStream( String path, TopicSpecification specification, Class valueClass); /** * Creates an {@link UpdateStream update stream} to use for creating and * updating a specific topic. *

* If a topic does not exist at the {@code path} one will be created using * the {@code specification} when the update stream is validated. If a topic * does exist, its specification must match {@code specification}, otherwise * the operation will fail with {@link IncompatibleTopicException}. *

* Takes a constraint that must be satisfied for the update stream to be * validated. *

* In other respects this method works in the same way as * {@link #createUpdateStream(String, Class)}. * * @param path the path of the topic * @param specification the required specification of the topic * @param valueClass the type of the values expected by the update stream * @param constraint the constraint that must be satisfied for the update * stream to be validated * @param type of the values expected by the update stream * @throws IllegalArgumentException if the topic type of the specification * does not match the type of values * @return an update stream * @deprecated since 6.9 *

* use {@link #newUpdateStreamBuilder} */ @Deprecated UpdateStream createUpdateStream( String path, TopicSpecification specification, Class valueClass, UpdateConstraint constraint); /** * Creates an update stream builder to use for creating update streams. * * @return an update stream builder * @since 6.9 */ UpdateStream.Builder newUpdateStreamBuilder(); /** * Applies a JSON Patch to a JSON topic. * *

* The {@code patch} argument should be formatted according to the JSON * Patch standard (RFC 6902). * *

* Patches are a sequence of JSON Patch operations contained in an array. * They are applied as an atomic update to the previous value if the * resulting update is successfully calculated. The following patch will * check the value at a specific key and update if the expected value is * correct: *

* [{"op":"test", "path":"/price", "value" : 22}, {"op":"add", * "path":"/price", "value": 23}] * *

* The available operations are: * *

    *
  • Add: {@code {"op": "add", "path": "/a/b/c", "value": * [ "foo", "bar" ]}} *
  • Remove: {@code {"op": "remove", "path": "/a/b/c"}} *
  • Replace: {@code {"op": "replace", "path": "/a/b/c", * "value": 43}} *
  • Move: {@code {"op": "move", "from": "/a/b/c", * "path": "/a/b/d"}} *
  • Copy: {@code {"op": "copy", "from": "/a/b/c", * "path": "/a/b/e"}} *
  • Test: {@code {"op": "test", "path": "/a/b/c", * "value": "foo"}} *
* *

* The test operation checks that the CBOR representation of the value of a * topic is identical to the value provided in the patch after converting it * to CBOR. If the value is represented differently as CBOR, commonly due to * different key ordering, then the patch will return the index of the * failed operation . e.g the values {@code {"foo": "bar", "count": 43}} and * {@code {"count": 43, "foo": "bar"}} are unequal despite semantic equality * due to the differences in a byte for byte comparison. * * @param path the path of the topic to patch * @param patch the JSON Patch * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task fails, the CompletableFuture will complete * exceptionally with a {@link CompletionException}. Common reasons * for failure, listed by the exception reported as the * {@link CompletionException#getCause() cause}, include: * *

    *
  • {@link InvalidPatchException} – if the patch is not a * valid JSON Patch; *
  • {@link FailedPatchException} – if applying the patch * fails, this will occur if the topic's present value is invalid * CBOR (see {@link TopicSpecification#VALIDATE_VALUES}); *
  • {@link NoSuchTopicException} – if there is no topic * bound to {@code path}; *
  • {@link IncompatibleTopicException} – if patch cannot be * applied to the topic, for example if the topic type is not * {@link com.pushtechnology.diffusion.datatype.json.JSON}. *
  • {@link IncompatibleTopicStateException} – if the topic * is managed by a component (such as fan-out) that prohibits * updates from the caller; *
  • {@link ClusterRoutingException} – if the operation * failed due to a transient cluster error; *
  • {@link PermissionsException} – if the calling session * does not have the * {@link com.pushtechnology.diffusion.client.types.PathPermission#UPDATE_TOPIC * UPDATE_TOPIC} permission for {@code path}; *
  • {@link SessionClosedException} – if the session is * closed. *
* * @see RFC 6902: JavaScript * Object Notation (JSON) Patch * * @since 6.4 */ CompletableFuture applyJsonPatch(String path, String patch); /** * Applies a JSON Patch to a JSON topic. * *

* Takes a constraint that must be satisfied for the update to be applied. *

* In other respects this method works in the same way as * {@link #applyJsonPatch(String, String)}. * * @param path the path of the topic to patch * @param patch the JSON Patch * @param constraint the constraint that must be satisfied for the patch to * be applied * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task fails, the CompletableFuture will complete * exceptionally with a {@link CompletionException}. Common reasons * for failure, listed by the exception reported as the * {@link CompletionException#getCause() cause}, include: * *

    *
  • {@link InvalidPatchException} – if the patch is not a * valid JSON patch; *
  • {@link FailedPatchException} – if applying the patch * fails, this will occur if the topic's present value is invalid * CBOR (see {@link TopicSpecification#VALIDATE_VALUES}); *
  • {@link NoSuchTopicException} – if there is no topic * bound to {@code path}; *
  • {@link IncompatibleTopicException} – if patch cannot be * applied to the topic, for example if the topic type is not * {@link com.pushtechnology.diffusion.datatype.json.JSON}. *
  • {@link IncompatibleTopicStateException} – if the topic * is managed by a component (such as fan-out) that prohibits * updates from the caller; *
  • {@link ClusterRoutingException} – if the operation * failed due to a transient cluster error; *
  • {@link PermissionsException} – if the calling session * does not have the * {@link com.pushtechnology.diffusion.client.types.PathPermission#UPDATE_TOPIC * UPDATE_TOPIC} permission for {@code path}; *
  • {@link SessionClosedException} – if the session is * closed. *
* * @see RFC 6902: JavaScript * Object Notation (JSON) Patch * * @since 6.4 */ CompletableFuture applyJsonPatch(String path, String patch, UpdateConstraint constraint); /** * Exception thrown to report that a JSON Patch was invalid. * * @since 6.4 */ final class InvalidPatchException extends SessionException { private static final long serialVersionUID = 2814596011838820640L; /** * Constructor. */ public InvalidPatchException(String message) { this(message, null); } /** * Constructor. */ public InvalidPatchException(String message, Throwable t) { super(message, t); } } /** * Exception thrown to report that applying a JSON Patch failed. * *

* This can happen if the topic's current value is not valid CBOR. See * {@link TopicSpecification#VALIDATE_VALUES}. * * @since 6.4 */ final class FailedPatchException extends SessionException { private static final long serialVersionUID = 971807945449944880L; /** * Constructor. */ public FailedPatchException(String message) { this(message, null); } /** * Constructor. */ public FailedPatchException(String message, Throwable t) { super(message, t); } } /** * Result of {@link #applyJsonPatch}. Check {@link #failedOperation} to * determine whether any of the operations failed. * * @since 6.4 */ interface JsonPatchResult { /** * @return an {@link Optional} which if present, contains the index of * the first operation which failed */ Optional failedOperation(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy