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

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

/*******************************************************************************
 * Copyright (c) 2023 DiffusionData Ltd., All Rights Reserved.
 *
 * Use is subject to license terms.
 *
 * NOTICE: All information contained herein is, and remains the
 * property of Push Technology. The intellectual and technical
 * concepts contained herein are proprietary to Push Technology 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 static org.slf4j.LoggerFactory.getLogger;

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

import org.slf4j.Logger;

import com.pushtechnology.diffusion.client.callbacks.ErrorReason;
import com.pushtechnology.diffusion.client.features.TimeSeries.Event;
import com.pushtechnology.diffusion.client.features.TimeSeries.RangeQuery;
import com.pushtechnology.diffusion.client.features.control.topics.SessionTrees;
import com.pushtechnology.diffusion.client.features.control.topics.SubscriptionControl;
import com.pushtechnology.diffusion.client.features.control.topics.views.TopicViews;
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.SessionFactory;
import com.pushtechnology.diffusion.client.topics.TopicSelector;
import com.pushtechnology.diffusion.client.topics.TopicSelectors;
import com.pushtechnology.diffusion.client.topics.details.TopicSpecification;
import com.pushtechnology.diffusion.client.topics.details.TopicType;
import com.pushtechnology.diffusion.client.types.PathPermission;
import com.pushtechnology.diffusion.datatype.Bytes;
import com.pushtechnology.diffusion.datatype.DataType;
import com.pushtechnology.diffusion.datatype.binary.Binary;
import com.pushtechnology.diffusion.datatype.json.JSON;
import com.pushtechnology.diffusion.datatype.recordv2.RecordV2;

/**
 * This feature allows a client session to subscribe to topics to receive
 * streamed topic updates, fetch the state of topics and/or update topics with
 * new values.
 * 

* Specifically, the feature provides the ability to: *

    *
  • Subscribe to topics and specify streams to receive updates; *
  • Fetch the current state of topics (even if not subscribed); *
  • By extending the {@link TopicUpdate topic update feature}, update topics * with new values; *
  • By extending the {@link TopicViews topic views feature}, manage topic * views. *
*

Subscription and unsubscription

*

* A session can issue requests to subscribe to topics at any time, even if the * topics do not exist at the server. Each subscription request provides a * {@link TopicSelector topic selector} that is evaluated by the server to * select matching topics. The session will be subscribed to any topics that * match the selector unless they are already subscribed, or the session has * insufficient permission. The subscription request is also retained at the * server and the session will be automatically subscribed to newly created * topics that match the selector (unless a subsequent unsubscription cancels * the request). *

* Sessions receive notifications from topics that they are subscribed to via * subscription streams (see below). When a session is subscribed to a * topic, all matching streams will first receive a subscription notification * that provides details about the topic. If the server has a value for the * topic, the value will be delivered to the streams before any other * notifications. *

* A session can unsubscribe from topics at any time. This is also specified * using a topic selector. On unsubscription, matching streams are notified via * the {@code onUnsubscription} notification. This notification will give the * reason for unsubscription (for example, by request of the session, request of * the server, or topic removal). *

* Subscriptions and unsubscriptions can occur for reasons other than requests * from the session. A session can be subscribed to or unsubscribed from a topic * by another session using the {@link SubscriptionControl} feature. The removal * of a topic also automatically causes unsubscription for subscribed sessions. *

* Subscription requests are subject to authorization checks. The session must * have {@link PathPermission#SELECT_TOPIC SELECT_TOPIC} permission for the * topic selector used to subscribe. Matching topics will be further filtered to * those for which the session has {@link PathPermission#READ_TOPIC READ_TOPIC} * permission. * *

Subscription streams

*

* A session can listen to subscription events and updates for a selection of * topics by adding one or more streams. A stream is registered using a topic * selector which specifies the topics that the stream applies to. When an * update is received for a topic then it will be routed to every stream that * matches both the topic selector and the stream's value type. If more than one * stream matches, all will receive the update; the order in which they are * notified is not defined. *

* A stream can be added several times for different selectors. If the same * stream (determined by {@link Object#equals(Object) equals}) is registered for * several selectors that match an event, the stream will only be notified of * the event once. The mapping of topic selectors to streams is maintained * locally in the client process. *

* It is also possible to add one or more fallback streams which will * receive updates that do not match any stream registered with a selector. This * is useful for default processing or simply to catch unprocessed updates. A * fallback stream can be added using {@link #addFallbackStream * addFallbackStream}. Zero, one, or more fallback streams may be assigned. If * no fallback stream is specified, any updates that are not routed to any other * stream will simply be discarded. *

* If the session is already subscribed to a topic when a matching stream is * added, the stream will immediately receive a subscription notification. For * most topic types, the latest value is locally cached and will be provided to * the stream following the subscription notification. *

* A stream will receive an * {@link com.pushtechnology.diffusion.client.callbacks.Stream#onClose() * onClose} callback when unregistered and an * {@link com.pushtechnology.diffusion.client.callbacks.Stream#onError(ErrorReason) * onError(SESSION_CLOSED)} callback if the session is closed. * *

Value streams

*

* A {@link ValueStream ValueStream} receives values for matching topics as and * when updates are received from the server. Delta updates received from the * server are automatically applied to locally cached values so that the stream * always receives full values for each update. * *

* Value streams are typed to a specified value class and only updates for * compatible topics will be routed to the stream. The following table shows how * the value class maps to compatible topic types that will be routed to the * stream: * *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Value ClassCompatible Topic Types
{@link JSON}{@link TopicType#JSON JSON} {@link TopicType#STRING STRING} * {@link TopicType#INT64 INT64} {@link TopicType#DOUBLE DOUBLE} *
{@link String}{@link TopicType#STRING STRING}
{@link Long}{@link TopicType#INT64 INT64}
{@link Double}{@link TopicType#DOUBLE DOUBLE}
{@link Binary}{@link TopicType#BINARY BINARY}
{@link Bytes}{@link TopicType#JSON JSON} {@link TopicType#STRING STRING} * {@link TopicType#INT64 INT64} {@link TopicType#DOUBLE DOUBLE} * {@link TopicType#BINARY BINARY} {@link TopicType#RECORD_V2 RECORD_V2}
{@link RecordV2}{@link TopicType#RECORD_V2 RECORD_V2}
*

* Value stream implementations can be added using * {@link #addStream(String, Class, ValueStream) addStream}. * *

* A value stream can be added to received updates from {@link TimeSeries time * series topics} using {@link #addTimeSeriesStream(String, Class, ValueStream) * addTimeSeriesStream}. The following table shows how the value class specified * when adding the stream maps to the event value class of time series topics * that will be routed to the stream: * *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Event Value ClassTime Series Event Value Class
{@link JSON}{@link TopicType#JSON JSON} {@link TopicType#STRING STRING} * {@link TopicType#INT64 INT64} {@link TopicType#DOUBLE DOUBLE} *
{@link String}{@link TopicType#STRING STRING}
{@link Long}{@link TopicType#INT64 INT64}
{@link Double}{@link TopicType#DOUBLE DOUBLE}
{@link Binary}{@link TopicType#BINARY BINARY}
{@link Bytes}{@link TopicType#JSON JSON} {@link TopicType#STRING STRING} * {@link TopicType#INT64 INT64} {@link TopicType#DOUBLE DOUBLE} * {@link TopicType#BINARY BINARY} {@link TopicType#RECORD_V2 RECORD_V2}
{@link RecordV2}{@link TopicType#RECORD_V2 RECORD_V2}
* *

Fetch

*

* A session can issue a request to fetch details of a topic or topics (subject * to authorization) at any time. The topics required are specified using a * topic selector. *

* The results of a fetch will return the topic path and type of each selected * topic. The results may also optionally return the topic values and/or * properties. *

* A new request can be created using {@link #fetchRequest()} and modified to * specify additional requirements of the fetch operation. The request is issued * to the server using the {@link FetchRequest#fetch(TopicSelector) fetch} * method on the request. This will return the results via a * {@link CompletableFuture}. * * *

Access control

*

* A session must have {@link PathPermission#SELECT_TOPIC SELECT_TOPIC} * permission for the path prefix of the topic selector used to * {@link #subscribe(TopicSelector) subscribe} or {@link #fetchRequest() fetch}. * The topics that result from a subscription or fetch request are further * filtered using the {@link PathPermission#READ_TOPIC READ_TOPIC} permission. * *

* No access control restrictions are applied to * {@link #unsubscribe(TopicSelector) unsubscription}. * *

Accessing the feature

*

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

 * Topics topics = session.feature(Topics.class);
 * 
* * @author DiffusionData Limited * @since 5.0 */ public interface Topics extends TopicUpdate, TopicViews, SessionTrees { /** * Add a value stream to receive topic events for topics that match a given * {@link TopicSelector} and have a value class that matches a specified * type. If the stream matches a time series topic with a compatible time * series event type it will receive onValue events without metadata. *

* See {@link Topics} class documentation for full details of the use of * value streams. * * @param the value class * * @param topics selector of one or more topics * * @param valueClass the class of values that the stream accepts * * @param stream the stream to add * * @since 5.7 */ void addStream( TopicSelector topics, Class valueClass, ValueStream stream); /** * Add a value stream to receive topic events for topics that match a given * {@link TopicSelector} expression and have a value class that matches a * specified type. If the stream matches a time series topic with a * compatible time series event type it will receive onValue events without * metadata. *

* See {@link Topics} class documentation for full details of the use of * value streams. * * @param the value class * * @param topics as a {@link TopicSelector} expression * * @param valueClass the class of values that the stream accepts * * @param stream the stream to add * * @throws IllegalArgumentException if {@code topics} is not a valid * selector expression * * @since 5.7 */ void addStream( String topics, Class valueClass, ValueStream stream) throws IllegalArgumentException; /** * Add a fallback stream. *

* See {@link Topics} class documentation for full details regarding the use * of fallback streams. * * @param valueClass the class of values that the stream accepts * * @param stream the stream to add * * @since 5.7 */ void addFallbackStream( Class valueClass, ValueStream stream); /** * Add a value stream to receive topic events for time series topics that * match a given {@link TopicSelector} and have a compatible time series * event value class. *

* See the {@link Topics} class documentation for details of the use of * value streams, and the {@link TimeSeries} class documentation for details * of time series topics. * *

* This method must be used instead of * {@link #addStream(TopicSelector, Class, ValueStream) addStream} to add a * {@code ValueStream>} because there is no way to * express a class literal of type {@code Class>}. The * stream can be removed with {@link #removeStream}. * * @param the time series value class * * @param topics selector of one or more topics * * @param eventValueClass The type of event values accepted by this stream. * The registration will match time series topics with a compatible * event value class. See the {@link Topics} class documentation for * details. * * @param stream the stream to add * * @since 6.0 */ void addTimeSeriesStream( TopicSelector topics, Class eventValueClass, ValueStream> stream); /** * Add a value stream to receive topic events for time series topics that * match a given {@link TopicSelector} expression and have a compatible time * series value class. *

* See the {@link Topics} class documentation for details of the use of * value streams, and the {@link TimeSeries} class documentation for details * of time series topics. * *

* This method must be used instead of * {@link #addStream(String, Class, ValueStream) addStream} to add a * {@code ValueStream>} because Java has no way to * express a class literal of type {@code Class>}. The * stream can be removed with {@link #removeStream}. * * @param the time series value class * * @param topics as a {@link TopicSelector} expression * * @param eventValueClass The type of event values accepted by this stream. * The registration will match time series topics with a compatible * event value class. See the {@link Topics} class documentation for * details. * * @param stream the stream to add * * @throws IllegalArgumentException if {@code topics} is not a valid * selector expression * * @since 6.0 */ void addTimeSeriesStream( String topics, Class eventValueClass, ValueStream> stream) throws IllegalArgumentException; /** * Remove a stream. *

* More formally, this method removes all streams that compare equal to * {@code stream}, regardless of the topic selector for which they are * registered. It will also remove any fallback stream equal to * {@code stream}. If there are no such streams, no changes are made. * * @param stream the value stream to remove * * @since 5.7 */ void removeStream( com.pushtechnology.diffusion.client.callbacks.Stream stream); /** * Request subscription to topics. *

* The session will become subscribed to each existing topic matching the * selector unless the session is already subscribed to the topic, or the * session does not have {@link PathPermission#READ_TOPIC READ_TOPIC} * permission for the topic path. For each topic to which the session * becomes subscribed, a subscription notification and initial value (if * any) will be delivered to registered value streams before the returned * CompletableFuture completes. * *

* The subscription request is also retained at the server and the session * will be automatically subscribed to newly created topics that match the * selector (unless a subsequent unsubscription cancels the request). * * @param topics specifies the topics to request subscription to * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task completes successfully, the CompletableFuture result * will be null. The result type is any rather than Void to provide * forward compatibility with future iterations of this API that may * provide a non-null result with a more specific result type. * *

* Otherwise, 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 PermissionsException} – if the calling * session does not have {@code SELECT_TOPIC} permission for the * path prefix of the selector expression; * *
  • {@link SessionClosedException} – if the session is * closed. *
* @since 6.0 */ CompletableFuture subscribe(TopicSelector topics); /** * Request subscription to topics. *

* This is equivalent to calling {@link #subscribe(TopicSelector)} with a * selector parsed using {@link TopicSelectors#parse(String)}. * * @param topics specifies the topics to request subscription to * @throws IllegalArgumentException if topics is an invalid topic selector * expression * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task completes successfully, the CompletableFuture result * will be null. The result type is any rather than Void to provide * forward compatibility with future iterations of this API that may * provide a non-null result with a more specific result type. * *

* Otherwise, 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 PermissionsException} – if the calling * session does not have {@code SELECT_TOPIC} permission for the * path prefix of the selector expression; * *
  • {@link SessionClosedException} – if the session is * closed. *
* @since 6.0 */ CompletableFuture subscribe(String topics); /** * Unsubscribe from topics. *

* This can be used at any time whilst connected to reduce the set of topics * to which the session is subscribed or negate earlier subscription * requests. * * @param topics the topics to unsubscribe from * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task completes successfully, the CompletableFuture result * will be null. The result type is any rather than Void to provide * forward compatibility with future iterations of this API that may * provide a non-null result with a more specific result type. * *

* Otherwise, 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 SessionClosedException} – if the session is * closed. *
* @since 6.0 */ CompletableFuture unsubscribe(TopicSelector topics); /** * Unsubscribe from topics. *

* This is equivalent to calling {@link #unsubscribe(TopicSelector)} with a * selector parsed using {@link TopicSelectors#parse(String)}. * * @param topics the topics to unsubscribe from * @throws IllegalArgumentException if topics is an invalid topic selector * expression * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the task completes successfully, the CompletableFuture result * will be null. The result type is any rather than Void to provide * forward compatibility with future iterations of this API that may * provide a non-null result with a more specific result type. * *

* Otherwise, 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 SessionClosedException} – if the session is * closed. *
* @since 6.0 */ CompletableFuture unsubscribe(String topics); /** * Request subscription to topics. * * @param topics specifies the topics to request subscription to * * @param callback the callback object to receive status notifications for * this operation * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated void subscribe( TopicSelector topics, CompletionCallback callback); /** * Request subscription to topics. *

* This is equivalent to calling * {@link #subscribe(TopicSelector, CompletionCallback)} with a selector * parsed using {@link TopicSelectors#parse(String)}. * * @param topics a {@link TopicSelector} expression specifying the topics to * request subscription to * * @param callback the callback object to receive status notifications for * this operation * * @throws IllegalArgumentException if {@code topics} is not a valid * selector expression * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated void subscribe(String topics, CompletionCallback callback) throws IllegalArgumentException; /** * Request subscription to topics. * * @param topics specifies the topics to request subscription to * * @param context an object passed to the callback with the reply to allow * requests and replies to be correlated. The caller can use any * convenient object reference, including {@code null} * * @param callback the callback object to receive status notifications for * this operation * * @param context object type * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated void subscribe( TopicSelector topics, C context, CompletionContextCallback callback); /** * Request subscription to topics. *

* This is equivalent to calling * {@link #subscribe(TopicSelector, Object, CompletionContextCallback)} with * a selector parsed using {@link TopicSelectors#parse(String)}. * * @param topics a {@link TopicSelector} expression specifying the topics to * request subscription to * * @param context an object passed to the callback with the reply to allow * requests and replies to be correlated. The caller can use any * convenient object reference, including {@code null} * * @param callback the callback object to receive status notifications for * this operation * * @param context object type * * @throws IllegalArgumentException if {@code topics} is not a valid * selector expression * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated void subscribe( String topics, C context, CompletionContextCallback callback) throws IllegalArgumentException; /** * Unsubscribe from topics. *

* This can be used at any time whilst connected to reduce the set of topics * to which the session is subscribed or negate earlier subscription * requests. * * @param topics the topics to unsubscribe from * * @param callback the callback handler for status notifications of this * operation * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated void unsubscribe(TopicSelector topics, CompletionCallback callback); /** * Unsubscribe from topics. *

* This is equivalent to calling * {@link #unsubscribe(TopicSelector, CompletionCallback)} with a selector * parsed using {@link TopicSelectors#parse(String)}. * * @param topics the topics to unsubscribe from * * @param callback the callback handler for status notifications of this * operation * * @throws IllegalArgumentException if {@code topics} is not a valid * selector expression * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated void unsubscribe(String topics, CompletionCallback callback) throws IllegalArgumentException; /** * Unsubscribe from topics. *

* This can be used at any time whilst connected to reduce the set of topics * to which the session is subscribed or negate earlier subscription * requests. * * @param topics the topics to unsubscribe from * * @param context an object passed to the callback with the reply to allow * requests and replies to be correlated. The caller can use any * convenient object reference, including {@code null} * * @param callback the callback handler for status notifications of this * operation * * @param context object type * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated void unsubscribe( TopicSelector topics, C context, CompletionContextCallback callback); /** * Unsubscribe from topics. *

* This is equivalent to calling * {@link #unsubscribe(TopicSelector, Object, CompletionContextCallback)} * with a selector parsed using {@link TopicSelectors#parse(String)}. * * @param topics the topics to unsubscribe from * * @param context an object passed to the callback with the reply to allow * requests and replies to be correlated. The caller can use any * convenient object reference, including {@code null} * * @param callback the callback handler for status notifications of this * operation * * @param context object type * * @throws IllegalArgumentException if {@code topics} is not a valid * selector expression * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated void unsubscribe( String topics, C context, CompletionContextCallback callback) throws IllegalArgumentException; /** * Callback interface for success or failure notifications from subscription * and unsubscription operations. * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated interface CompletionCallback extends Callback { /** * Called to indicate that the requested operation has been processed by * the server. */ void onComplete(); /** * Default implementation of {@link CompletionCallback}. *

* This logs onComplete calls at 'debug' level. This method can be * overridden to perform some more specific action. */ class Default extends Callback.Default implements CompletionCallback { private static final Logger LOG = getLogger(CompletionCallback.Default.class); @Override public void onComplete() { LOG.debug("{} - Completed subscribe/unsubscribe", this); } } } /** * Contextual callback interface for success or failure notifications from * subscription operations. *

* Use this alternative to {@link CompletionCallback} to associate some * arbitrary context object with each call. * * @param context object type * * @deprecated since 6.7 *

* Methods that use callbacks are deprecated and will be removed * in a future release. Use CompletableFuture variant instead. */ @Deprecated interface CompletionContextCallback extends ContextCallback { /** * Called to indicate that a requested operation has been processed by * the server. * * @param context the context object supplied when making the call. May * be {@code null} */ void onComplete(C context); /** * Default implementation of {@link CompletionContextCallback}. *

* This logs onComplete calls at 'debug' level. This method can be * overridden to perform some more specific action. * * @param context object type */ class Default extends ContextCallback.Default implements CompletionContextCallback { private static final Logger LOG = getLogger(CompletionContextCallback.Default.class); @Override public void onComplete(C context) { LOG.debug( "{} - Completed subscribe/unsubscribe, context={}", this, context); } } } /** * Base subscriber stream interface. *

* See {@link ValueStream} and {@link Topics} class documentation for * further details of the use of subscriber streams. * * @since 5.7 */ interface SubscriberStream extends com.pushtechnology.diffusion.client.callbacks.Stream { /** * Subscription notification. *

* This method is called when a session is subscribed to a topic that * matches the stream registration. This method is also called when a * stream is added, for all of the session's subscriptions to topics * that match the stream registration. *

* For a given topic, {@code onSubscription} will be the initial * notification, and the first notification following an * {@link #onUnsubscription unsubscription} notification if the session * re-subscribes to the topic. *

* This method is also called for fallback streams that match the topic * type when the session removes the last stream that selected a * subscribed topic. The fallback stream will now receive updates for * the topic, starting with an immediate notification of the currently * cached value (if any). * * @param topicPath the topic path * * @param specification the topic specification */ void onSubscription(String topicPath, TopicSpecification specification); /** * Unsubscription notification. * *

* This method is called if the session is unsubscribed from a topic * that matches the stream registration. The stream will receive no more * updates for the topic unless the session re-subscribes to the topic. * *

* This method is also called for fallback streams that match the topic * type if the session * {@link Topics#addStream(String, Class, ValueStream) adds} the first * stream that selects a subscribed topic. For these notifications, * {@code reason} will be {@link UnsubscribeReason#STREAM_CHANGE}. The * fallback stream will no longer receive updates for the topic. * * @param topicPath the topic path * * @param specification the topic specification * * @param reason the reason for unsubscription */ void onUnsubscription( String topicPath, TopicSpecification specification, UnsubscribeReason reason); /** * Default {@link Topics.SubscriberStream} implementation. *

* This logs calls to onSubscription and onUnsubscription at 'debug' * level. These implementations can be useful during development but are * usually overridden to provide meaningful processing. */ abstract class Default extends com.pushtechnology.diffusion.client.callbacks.Stream.Default implements SubscriberStream { private static final Logger LOG = getLogger(SubscriberStream.Default.class); @Override public void onSubscription( String topicPath, TopicSpecification specification) { LOG.debug( "{} - Topic {} subscribed : {}", this, topicPath, specification); } @Override public void onUnsubscription( String topicPath, TopicSpecification specification, UnsubscribeReason reason) { LOG.debug( "{} - Topic {} unsubscribed : {}", this, topicPath, reason); } } } /** * Stream interface that can be registered to receive subscription and value * events whenever an update is received from the server. *

* A stream implementation can be registered using * {@link Topics#addStream(String, Class, ValueStream) addStream}. The * stream will receive events for the topics that are selected by the topic * selector and have a compatible type. *

* A stream implementation can also be registered as a fallback stream using * {@link Topics#addFallbackStream(Class, ValueStream) addFallbackStream}. * Fallback streams will receive events for all topics that are not selected * by other streams the session has registered using {@link #addStream * addStream}. *

* If the stream is {@link Topics#removeStream removed}, * {@link com.pushtechnology.diffusion.client.callbacks.Stream#onClose() * onClose} will be called. *

* If the session is closed, * {@link com.pushtechnology.diffusion.client.callbacks.Stream#onError(ErrorReason) * onError} will be called with {@link ErrorReason#SESSION_CLOSED}. *

* See {@link Topics} class documentation for further details of the use of * value streams. * * @param the value class * @since 5.7 */ interface ValueStream extends SubscriberStream { /** * Notifies an update to a topic value from the server. *

* This is also called to provide the current value for any matching * topic that the session is already subscribed to when the stream is * added. * * @param topicPath the topic path * * @param specification the topic specification * * @param oldValue the previous value. Will be null for the initial call * to onValue for a topic. It can also be null if the topic's * data type supports null values. * * @param newValue the new value derived from the last update received * from the server. It can be null if the topic's data type * supports null values. */ void onValue( String topicPath, TopicSpecification specification, V oldValue, V newValue); /** * Default {@link Topics.ValueStream} implementation. *

* This logs all calls at 'debug' level. These implementations can be * useful during development but are usually overridden to provide * meaningful processing. * * @param the value class */ class Default extends SubscriberStream.Default implements ValueStream { private static final Logger LOG = getLogger(ValueStream.Default.class); @Override public void onValue( String topicPath, TopicSpecification specification, V oldValue, V newValue) { LOG.debug( "{} - Value received for topic {} : {} {}", this, topicPath, oldValue, newValue); } } } /** * The reason that an unsubscription occurred. */ enum UnsubscribeReason { /** * Unsubscribed by the subscribing session. */ REQUESTED, /** * The unsubscription was requested either by another session or by the * server. */ CONTROL, /** * The unsubscription occurred because the topic was removed. */ REMOVAL, /** * The unsubscription occurred because the session is no longer * authorized to access the topic. * * @since 5.9 */ AUTHORIZATION, /** * The server has re-subscribed this session to the topic. Existing * streams are unsubscribed because the topic type and other attributes * may have changed. * *

* This can happen if a set of servers is configured to use session * replication, and a session connected to one server reconnects ( * "fails over") to a different server. * * @since 5.9 */ SUBSCRIPTION_REFRESH, /** * A fallback stream has been unsubscribed due to the addition of a * stream that selects the topic. * * @since 5.9 */ STREAM_CHANGE, /** * The server has a significant backlog of messages for the session, and * the topic specification has the {@link TopicSpecification#CONFLATION * conflation topic property} set to "unsubscribe". The session can * resubscribe to the topic. The unsubscription is not persisted to the * cluster. If the session fails over to a different server it will be * resubscribed to the topic. */ BACK_PRESSURE, /** * The unsubscription occurred because branch mapping rules changed. * * @since 6.7 * @see SessionTrees */ BRANCH_MAPPINGS, /** * A reason that is unsupported by the session. * * @since 6.1 */ UNKNOWN_UNSUBSCRIBE_REASON, } /** * Creates an unconfigured fetch request. *

* The returned request can be invoked with * {@link FetchRequest#fetch(TopicSelector) fetch}. The server will evaluate * the query and return a fetch result that provides the paths and types of * the matching topics which the session has * {@link PathPermission#READ_TOPIC READ_TOPIC} permission. *

* You will usually want to restrict the query to a subset of the topic * tree, and to retrieve the topic values and/or properties. This is * achieved by applying one or more of the fluent builder methods provided * by {@code FetchRequest} to produce more refined requests. *

* For example: *

* * FetchResult result = * topics.fetchRequest().withValues(String.class).fetch("*A/B//").get(); * * * @see FetchRequest * * @return a new unconfigured fetch request * * @since 6.2 */ FetchRequest fetchRequest(); /** * A parameterised query that can be used to search the topic tree. *

* A new request can be created using the {@link Topics#fetchRequest() * fetchRequest} method and modified to specify a range of topics and/or * various levels of detail. The request can then be issued to the server * using the {@link FetchRequest#fetch(TopicSelector) fetch} method * supplying a topic selector which specifies the selection of topics. The * results are returned via a {@link CompletableFuture}. *

* As a minimum, the path and type of each selected topic will be returned. * It is also possible to request that the topic {@link #withValues values} * and/or {@link #withProperties properties} are returned. *

* If values are selected then the topic types selected are naturally * constrained by the provided {@code valueClass} argument. So if * String.class is specified, only {@link TopicType#STRING * STRING} topics will be selected. However, if JSON.class is * specified, all types compatible with {@link JSON} will be selected * including {@link TopicType#STRING STRING}, {@link TopicType#INT64 INT64} * and {@link TopicType#DOUBLE DOUBLE}. See * {@link DataType#canReadAs(Class)} for the class hierarchy of types. * Selecting a value class of Object.class will return values * for all topic types. *

* If values are requested, the request and results are typed to the value * type, otherwise the type is {@code Void}. To select topic types when * values are not required, or to further constrain the selection when * values are required, it is also possible to specify exactly which * {@link #topicTypes topic types} to select. *

* The values of {@link TopicType#TIME_SERIES time series} topics are only * returned if the specified value class is {@code Object}. In this case the * value returned represents the latest event appended to the topic and is * of type {@link Event Event<Bytes>} The bytes value from the event * can then be read using the {@link DataType} corresponding to the time * series event value type of the topic. A {@link RangeQuery time series * range query} can be used to retrieve all the events of a time series * topic. *

* The topics selected by the topic selector can be further restricted by * range. A range is defined by a start path and an end path, and contains * all paths in-between in path order. Given a topic tree containing the * topics: * *

     * a, a/b, a/c, a/c/x, a/c/y, a/d, a/e, b, b/a/x, b/b/x, c
     * 
* *

* The range from {@code a/c/y} to {@code b/a/x} includes the topics with * paths: * *

     * a/c/x, a/c/y, a/d, a/e, b, b/a/x
     * 
*

* The start point of a range can be specified using {@link #from from} or * {@link #after after} and an end point using {@link #to to} or * {@link #before before}. {@link #from from} and {@link #to to} include any * topic with the specified path in the selection, whereas {@link #after * after} and {@link #before before} are non-inclusive and useful for paging * through a potentially large range of topics. If no start point is * specified, the start point is assumed to be the first topic of the topic * tree, ordered by path name. Similarly, if no end point is specified, the * end point is the last topic of the topic tree. *

* A limit on the number of results returned can be specified using * {@link #first first}. This is advisable if the result set could * potentially be large. The number of results returned is also limited by * the session's maximum message size – see {@link #maximumResultSize}. The * result indicates whether the results have been limited via the * {@link Topics.FetchResult#hasMore() hasMore} method. If {@code hasMore} * returns true, further results can be retrieved by modifying the original * query to request results {@link #after} the last path received. *

* By default, results are returned in path order, earliest path first, * starting from the beginning of any range specified. It is also possible * to request results from the end of the range indicated by specifying a * limit to the number of results using {@link #last last}. This method * complements {@link #first}, returning up to the specified number of * results from the end of the range, but in reverse path order. This is * useful for paging backwards through a range of topics. *

* It can be useful to explore an unknown topic tree in a breadth-first * manner rather than the path order. This can be achieved using * {@link #limitDeepBranches}. *

* {@link TopicType#ROUTING Routing} topics are not supported, and if * encountered will be ignored (i.e. treated as if they did not exist). *

* FetchRequest instances are immutable and can be safely shared and reused. * * @since 6.2 * @param The value type of the request. */ interface FetchRequest { /** * A constant set of all topic types that can be fetched. */ Set ALL_TYPES = Collections.unmodifiableSet( EnumSet.of( TopicType.JSON, TopicType.BINARY, TopicType.RECORD_V2, TopicType.DOUBLE, TopicType.INT64, TopicType.STRING, TopicType.TIME_SERIES)); /** * Specifies a logical start point within the topic tree. *

* If specified, only results for topics with a path that is lexically * equal to or 'after' the specified path will be returned. *

* This is the inclusive equivalent of {@link #after after} and if used * will override any previous {@link #after after} or {@link #from from} * constraint. * * @param topicPath the topic path from which results are to be returned * * @return a new fetch request derived from this fetch request but * selecting only topics from the specified path onwards * (inclusive) */ FetchRequest from(String topicPath); /** * Specifies a logical start point within the topic tree. *

* If specified, only results for topics with a path that is lexically * 'after' the specified path will be returned. *

* This is the non-inclusive equivalent of {@link #from from} and if * used will override any previous {@link #from from} or {@link #after * after} constraint. * * @param topicPath the topic path after which results are to be * returned * * @return a new fetch request derived from this fetch request but * selecting only topics after the specified path (not * inclusive) */ FetchRequest after(String topicPath); /** * Specifies a logical end point within the topic tree. *

* If specified, only results for topics with a path that is lexically * equal to or 'before' the specified path will be returned. *

* This is the inclusive equivalent of {@link #before before} and if * used will override any previous {@link #before before} or {@link #to * to} constraint. * * @param topicPath the topic path to which results are to be returned * * @return a new fetch request derived from this fetch request but * selecting only topics including and before the specified path * (inclusive) */ FetchRequest to(String topicPath); /** * Specifies a logical end point within the topic tree. *

* If specified, only results for topics with a path that is lexically * 'before' the specified path will be returned. *

* This is the non-inclusive equivalent of {@link #to to} and if used * will override any previous {@link #to to } or {@link #before before} * constraint. * * @param topicPath the topic path before which results are to be * returned * * @return a new fetch request derived from this fetch request but * selecting only topics before the specified path (not * inclusive) */ FetchRequest before(String topicPath); /** * Specifies that only topics of the specified topic types should be * returned. *

* If this is not specified, {@link #ALL_TYPES all types} will be * returned (unless constrained by {@link #withValues withValues}). *

* If the specified topic type matches the event type of a time series * topic it will also be returned. The value will be delivered without * the associated metadata. To specify all time series topics use * {@link TopicType#TIME_SERIES} *

* This may be used instead to further constrain the results when using * {@link #withValues withValues}. For example, you can specify * JSON.class to {@link #withValues withValues} then * specify {@link TopicType#JSON JSON} here to ensure that only JSON * topics are returned and not those topics that are logically value * subtypes of JSON (e.g. {@link TopicType#STRING STRING}). *

* If {@link #withValues withValues} has been specified then the types * specified here must be compatible with the value class specified or * the event type for time series topics. *

* {@link TopicType#ROUTING ROUTING} may not be specified. * * @param topicTypes topic types to be selected * * @return a new fetch request derived from this fetch request but * specifying that only topics of the specified topic types * should be returned * * @throws IllegalArgumentException if topicTypes contains * {@link TopicType#ROUTING ROUTING} */ FetchRequest topicTypes(Set topicTypes); /** * Specifies that values should be returned for selected topics, * constraining the selection to only those topics with a data type * compatible with the specified value class. *

* If Object.class is specified, values will be returned * for all topic types (unless constrained by {@link #topicTypes * topicTypes}). *

* The specified value constrains the topic types. So, any topic types * specified in a previous call to {@link #topicTypes topicTypes} that * cannot be read as the specified class will be removed from the list * of topic types. * * @param valueClass the class of values. If {@code null} is specified * this will cancel any previous call (topic types will remain * unchanged). * * @return a new fetch request derived from this fetch request but * specifying that only topics compatible with the specified * class should be returned with values * * @throws IllegalArgumentException if the class is not compatible with * any topic types. */ FetchRequest withValues(Class valueClass); /** * Specifies that all properties associated with each topic's * {@link TopicSpecification specification} should be returned. * * @return a new fetch request derived from this fetch request but * specifying that topic specification properties should be * returned */ FetchRequest withProperties(); /** * Include the details of reference topics that are not yet published. * *

* {@link TopicViews Topic views} that use the {@code delay by} clause * create reference topics in an unpublished state. The topics are * published once the delay time has expired. A topic in the * unpublished state prevents a lower priority topic view from creating * a reference topic with the same path. * *

* A reference topic in the unpublished state which matches the query * will only be included in the fetch results if the session has * {@link PathPermission#READ_TOPIC READ_TOPIC} permission for the * reference's source topic as well as {@code READ_TOPIC} permission for * the reference topic. Requiring {@code READ_TOPIC} permission for the * source topic ensures less privileged sessions cannot derive * information from the existence of the reference topic before the * delay time has expired. * * @return a new fetch request derived from this fetch request, * additionally specifying that unpublished reference topics * should be included in the results * @since 6.5 */ FetchRequest withUnpublishedDelayedTopics(); /** * Specifies a maximum number of topic results to be returned from the * start of the required range. *

* If this is not specified, the number of results returned will only be * limited by other constraints of the request. *

* This should be used to retrieve results in manageable batches and * prevent very large result sets. *

* If there are potentially more results that would satisfy the other * constraints, then the fetch result will indicate so via the * {@link FetchResult#hasMore hasMore} method. *

* Zero can be supplied to return no results. Such a request can be used * together with {@link FetchResult#hasMore} to query whether there are * topics that match the selector provided to * {@link FetchRequest#fetch}, without retrieving the details of any of * the topics. To retrieve unlimited topics use Integer.MAX_VALUE which * is the default value. *

* Either this or {@link #last last} may be specified. This will * therefore override any previous {@link #last last} or {@link #first * first} constraint. * * @param number the maximum number of results to return from the start * of the range * * @return a new fetch request derived from this fetch request but * selecting only the number of topics specified from the start * of the range */ FetchRequest first(int number); /** * Specifies a maximum number of topic results to be returned from the * end of the required range. *

* This is similar to {@link #first first} except that the specified * number of results are returned from the end of the range. This is * useful for paging backwards through a range of topics. Results are * always returned in topic path order (not reverse order). *

* Zero can be supplied to return no results. Such a request can be used * together with {@link FetchResult#hasMore} to query whether there are * topics that match the selector provided to * {@link FetchRequest#fetch}, without retrieving the details of any of * the topics. To retrieve unlimited topics use Integer.MAX_VALUE which * is the default value. *

* Either this or {@link #first first} may be specified. This will * therefore override any previous {@link #first first} or {@link #last * last} constraint. * * @param number the maximum number of results to return from the end of * the range * * @return a new fetch request derived from this fetch request but * selecting only the number of topics specified from the end of * the range */ FetchRequest last(int number); /** * Specifies the maximum data size of the result set. *

* This may be used to constrain the size of the result. If not * specified then by default the maximum message size for the session * (as specified by {@link SessionFactory#maximumMessageSize(int)} is * used. * * @param maximumSize the maximum size of the result set in bytes. If a * value greater than the session's maximum message size is * specified, the maximum message size will be used. * * @return a new fetch request derived from this fetch request but * constraining the size of the result to the specified maximum */ FetchRequest maximumResultSize(int maximumSize); /** * Specifies a limit on the number of results returned for each deep * branch. * *

* A deep branch has a root path that has a number of parts equal to the * {@code deepBranchDepth} parameter. The {@code deepBranchLimit} * specifies the maximum number of results for each deep branch. * *

* This method is particularly useful for incrementally exploring a * topic tree from the root, allowing a breadth-first search strategy. * *

* For example, given a topic tree containing the topics with the * following paths: * *

         * x/0
         * x/x/1
         * x/x/x/2
         * y/y/y/y/3
         * y/y/y/4
         * z/5
         * z/z/6
         * 
* *

* Then * *

         * FetchResult result =
         *     topics.fetchRequest().limitDeepBranches(1, 1).fetch("?.//").get();
         * 
* * will return results with the paths {@code x/0}, {@code y/y/y/y/3}, * and {@code z/5}. The application can then determine the roots of the * tree are {@code x}, {@code y}, and {@code z}. * *

* The {@code deepBranchLimit} parameter can usefully be set to * {@code 0}. For example, given the same example topic tree, * *

         * FetchResult result =
         *     topics.fetchRequest().limitDeepBranches(3, 0).fetch("?.//").get();
         * 
* * will only return results having paths with fewer than three parts; * namely {@code x/0}, and {@code z/5}. * *

* The fetch result does not indicate whether this option caused some * results to be filtered from deep branches. It has no affect on the * {@link FetchResult#hasMore() hasMore()} result. If the result set * contains {@code deepBranchLimit} results for a particular deep * branch, some topics from that branch may have been filtered. * * @param deepBranchDepth the number of parts in the root path of a * branch for it to be considered deep * @param deepBranchLimit the maximum number of results to return for * each deep branch * @return a new fetch request derived from this fetch request but * restricting the number of results for deep branches * @since 6.4 */ FetchRequest limitDeepBranches( int deepBranchDepth, int deepBranchLimit); /** * Sends a fetch request to the server. *

* Results are returned for all topics matching the selector that * satisfy the request constraints within any range defined by * {@link #from from}/{@link #after after} and/or {@link #to * to}/{@link #before before}. * * @param topics specifies a topic selector which selects the topics to * be fetched * * @return a CompletableFuture that completes when a response is * received from the server with the results of the fetch * operation. *

* If the task completes successfully, the CompletableFuture * result will be an object encapsulating all of the results. *

* Otherwise, 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 PermissionsException} – if the calling * session does not have {@code SELECT_TOPIC} permission for the * path prefix of the selector expression; *
  • {@link SessionClosedException} – if the session is * closed. *
*/ CompletableFuture> fetch(TopicSelector topics); /** * Sends a fetch request to the server. *

* This is the equivalent of {@link #fetch(TopicSelector)}, with the * convenience of directly specifying the selector as a String. * * @param topics specifies a topic selector string which selects the * topics to be fetched * * @return a CompletableFuture that completes when a response is * received from the server with the results of the fetch * operation. *

* If the task completes successfully, the CompletableFuture * result will be an object encapsulating all of the results. *

* Otherwise, 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 PermissionsException} – if the calling * session does not have {@code SELECT_TOPIC} permission for the * path prefix of the selector expression; *
  • {@link SessionClosedException} – if the session is * closed. *
*/ CompletableFuture> fetch(String topics); } /** * Encapsulates the results from a fetch operation issued to the server. *

* A fetch operation is issued using a {@link FetchRequest fetch request} * which will return a result of this type via a {@link CompletableFuture}. * * @param The result value type. This would be Void unless the request * indicated that {@link FetchRequest#withValues values} are to be * returned, in which case this would be the value class requested. * * @since 6.2 */ interface FetchResult { /** * Returns the results from the fetch operation. *

* Results are always returned in path order. * * @return a list of topic results, each representing a result single * topic selected by the fetch operation. */ List> results(); /** * Indicates whether the fetch could have returned more results if it * had not been constrained by the {@link FetchRequest#first first}, * {@link FetchRequest#last last} or * {@link FetchRequest#maximumResultSize maximumResultSize} limits. * * A fetch that requests no results can be used together with this * method to query whether there are topics that match the selector * provided to {@link FetchRequest#fetch}, without retrieving the * details of any of the topics. * * For example: *

{@code
         * topics.fetchRequest().first(0).fetch("?x//").thenAccept(fetchResult -> {
         *     if (fetchResult.hasMore()) {
         *         System.out.println("There are topics in branch 'x'.");
         *     }
         * }
         * }
* * @return true if more results could have been returned, otherwise * false */ boolean hasMore(); /** * The number of elements in the fetch result. * * @return the size of the results list * @since 6.3 */ int size(); /** * Returns true if the result contains zero elements. * * @return true if result list is empty * @since 6.3 */ boolean isEmpty(); /** * Encapsulates the result of a {@link FetchRequest#fetch(TopicSelector) * fetch} invocation for a single selected topic. * * @param The result value type. This would be Void unless the * request indicated that {@link FetchRequest#withValues values} * are to be returned, in which case this would be the value * class requested. */ interface TopicResult { /** * Returns the topic path. * * @return the topic path */ String path(); /** * Returns the topic type. *

* This is a convenience method equivalent to calling * {@code specification().getType()}. * * @return the topic type */ TopicType type(); /** * Returns the topic value. *

* This will only return a value if the fetch request specified * {@link FetchRequest#withValues withValues} and the topic actually * had a value. For topics that have no value this will return null. * * @return the topic value or null if none available */ V value(); /** * Returns the topic specification. *

* If the request specified {@link FetchRequest#withProperties() * withProperties}, the result reflect the topic's specification and * can be used to create an identical topic. If the request did not * specify {@link FetchRequest#withProperties() withProperties}, the * specification's property map will be empty. * * @return the topic specification */ TopicSpecification specification(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy