com.pushtechnology.diffusion.client.features.control.topics.TopicControl Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2014, 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.control.topics;
import static com.pushtechnology.diffusion.client.Diffusion.sessionIdFromString;
import static com.pushtechnology.diffusion.client.session.Session.SESSION_ID;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.pushtechnology.diffusion.client.Diffusion;
import com.pushtechnology.diffusion.client.callbacks.Registration;
import com.pushtechnology.diffusion.client.callbacks.Stream;
import com.pushtechnology.diffusion.client.features.Callback;
import com.pushtechnology.diffusion.client.features.ClusterRoutingException;
import com.pushtechnology.diffusion.client.features.ContextCallback;
import com.pushtechnology.diffusion.client.features.HandlerConflictException;
import com.pushtechnology.diffusion.client.features.RegisteredHandler;
import com.pushtechnology.diffusion.client.features.TopicTreeHandler;
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.session.SessionId;
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.GlobalPermission;
import com.pushtechnology.diffusion.client.types.PathPermission;
/**
* This feature allows a session to manage topics. It provides the following
* capabilities:
*
* 1) Adding and removing topics.
* 2) Missing topic notifications — listening for requests to subscribe to
* topics that do not exist thus allowing dynamic topic creation on demand.
* 3) Topic event listeners — listening for topic events, such as the number of
* subscribers to a topic changing from zero to greater than zero or from
* greater than zero to zero.
*
*
Topics
*
* The Diffusion server stores data in topics. Each topic is bound to a topic
* path in the topic tree, and may have a current value. Sessions can subscribe
* to topics. Updates to topic values are broadcast to subscribing sessions.
* There are several types of topic. The {@link TopicType topic type} determines
* the type of the data values a topic publishes to subscribers.
*
*
Adding topics
*
* Creating topics
*
* The simplest way to create a topic is to call
* {@link #addTopic(String, TopicType)}, supplying a topic type. For example, to
* create a {@link TopicType#JSON JSON} topic bound to the topic path
* {@code foo}:
*
*
* CompletableFuture<AddTopicResult%gt; result =
* topicControl.addTopic("foo", TopicType.JSON);
*
*
*
* Success or failure is reported asynchronously through the CompletableFuture
* result.
*
*
* The nature of a topic depends primarily on its topic type, but can be
* customized using topic properties. Some types of topic cannot be created
* without supplying mandatory topic properties. Topic properties can be
* supplied in a {@link TopicSpecification topic specification} using
* {@link #addTopic(String, TopicSpecification, AddCallback)}. Topic
* specifications can be created using {@link #newSpecification(TopicType)} and
* further customized with builder methods. For example, to create a
* {@link TopicType#JSON JSON} topic bound to the topic path {@code foo} with
* the {@link TopicSpecification#VALIDATE_VALUES VALIDATE_VALUES} property set
* to {@code true}:
*
*
* CompletableFuture<AddTopicResult> result =
* topicControl.addTopic(
* "foo",
* topicControl.newSpecification(TopicType.JSON)
* .withProperty(TopicSpecification.VALIDATE_VALUES, "true"));
*
*
*
* See {@link TopicSpecification} for details of the available topic properties
* and their effects on the different types of topic.
*
*
* Topic creation is idempotent. If {@link #addTopic(String, TopicSpecification)
* addTopic(path, specification)} is called and there is already a topic bound
* to {@code path} with a topic specification equal to {@code specification},
* the call will complete normally with an {@link AddTopicResult#EXISTS} result.
* However, if there is a topic bound to {@code path} with a different topic
* specification, the call will complete exceptionally with an
* {@link ExistingTopicException}.
*
*
Removing topics
*
*
* Topics can be removed using {@link #removeTopics(TopicSelector)}. Only those
* selected topics that the caller has {@link PathPermission#MODIFY_TOPIC
* MODIFY_TOPIC} permission to will be removed, any others will remain.
*
*
* Topics can also be automatically removed according to a removal criteria
* specified using the {@link TopicSpecification#REMOVAL REMOVAL} topic
* property.
*
*
Managing topic tree hierarchies
*
*
* A topic can be bound to any path in the topic tree namespace. The only
* restriction is that two topics can not have the same path.
*
* In the following example a topic can be created with the path {@code A/B/foo}
* even though there are no topics with path {@code A} or {@code A/B}:
*
*
* topicControl.addTopic("A/B/foo", TopicType.JSON);
*
*
*
* Topics bound to the paths {@code A} or {@code A/B} can be created later.
*
* Topics can be removed without affecting the topics subordinate to them in the
* topic tree using {@link #remove} providing a path topic selector. By using
* the {@code //} topic selector qualifier it is possible to remove a topic and
* all of its descendant topics, that is to remove whole topic tree branches.
*
*
Access control
*
* To add or remove a topic, a session needs {@link PathPermission#MODIFY_TOPIC
* MODIFY_TOPIC} permission for the topic path. When removing topics with a
* topic selector that matches more than one topic, only topics with paths for
* which the session has {@code MODIFY_TOPIC} permission will be removed.
*
* To register a
* {@link #addMissingTopicHandler(String, MissingTopicNotificationStream)
* missing topic handler} the session needs
* {@link GlobalPermission#REGISTER_HANDLER REGISTER_HANDLER} permission.
*
*
Accessing the feature
*
* This feature may be obtained from a {@link Session session} as follows:
*
*
* TopicControl topicControl = session.feature(TopicControl.class);
*
*
* @author DiffusionData Limited
* @since 5.0
*/
public interface TopicControl extends Feature {
/**
* Create a new {@link TopicSpecification} for a given topic type.
*
* @param topicType the topic type
*
* @return a new immutable specification with no properties set. New
* specifications with different properties can be produced using
* the {@link TopicSpecification#withProperty(String, String)
* withProperty} or
* {@link TopicSpecification#withProperties(java.util.Map)
* withProperties} methods of the provided specification.
*
* @since 5.7
*
* @deprecated since 6.7
*
* Use
* {@link com.pushtechnology.diffusion.client.Diffusion#newTopicSpecification
* Diffusion.newTopicSpecification} which can be imported with a
* static import, instead.
*/
@Deprecated
TopicSpecification newSpecification(TopicType topicType);
/**
* Request creation of a topic.
*
* This is a convenience method that is equivalent to:
*
*
* topicControl.addTopic(
* topicPath,
* topicControl.newSpecification(topicType));
*
*
* @param topicPath the topic path to which the topic will be bound
* @param topicType the type of topic to be created
*
* @return a CompletableFuture that completes when a response is received
* from the server.
*
*
* If the task completes successfully, the CompletableFuture result
* will be indicate whether a new topic was created, or whether a
* topic with an identical topic specification is already bound to
* at {@code topicPath}.
*
*
* 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 ExistingTopicException} – a topic is bound to
* {@code topicPath} with a different topic specification;
*
- {@link IncompatibleExistingTopicException} – an
* incompatible topic already exists at {@code topicPath};
*
- {@link InvalidTopicPathException} – {@code topicPath}
* 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 ClusterRoutingException} – if the operation failed
* due to a transient cluster error;
*
- {@link AddTopicException} – the topic could not be
* created for some reason other than those specified above;
*
- {@link PermissionsException} – the calling session
* does not have {@code MODIFY_TOPIC} permission for
* {@code topicPath};
*
- {@link SessionClosedException} – the session is closed.
*
*
* @since 6.0
*/
CompletableFuture addTopic(
String topicPath,
TopicType topicType);
/**
* Request creation of a topic.
*
* @param topicPath the topic path to which the topic will be bound
* @param specification defines the topic to be created. A specification can
* be created using {@link #newSpecification(TopicType)}.
*
* @return a CompletableFuture that completes when a response is received
* from the server.
*
*
* If the task completes successfully, the CompletableFuture result
* will be indicate whether a new topic was created, or whether a
* topic with an identical topic specification is already bound to
* at {@code topicPath}.
*
*
* 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 ExistingTopicException} – a topic is bound to
* {@code topicPath} with a different topic specification;
*
- {@link IncompatibleExistingTopicException} – an
* incompatible topic already exists at {@code topicPath};
*
- {@link InvalidTopicPathException} – {@code topicPath}
* is not a valid topic path;
*
- {@link InvalidTopicSpecificationException} – the
* specification is invalid;
*
- {@link TopicLicenseLimitException} – the topic could
* not be added as it would breach a licensing limit;
*
- {@link ClusterRoutingException} – if the operation failed
* due to a transient cluster error;
*
- {@link AddTopicException} – the topic could not be
* created for some reason other than those specified above;
*
- {@link PermissionsException} – the calling session
* does not have {@code MODIFY_TOPIC} permission for
* {@code topicPath};
*
- {@link SessionClosedException} – the session is closed.
*
*
* @since 6.0
*/
CompletableFuture addTopic(
String topicPath,
TopicSpecification specification);
/**
* Version of {@link #addTopic(String, TopicSpecification, AddCallback)}
* that allows a user defined context to be provided.
*
* @param topicPath the topic path to which the topic will be bound
*
* @param specification defines the topic to be created. A specification can
* be created using {@link #newSpecification(TopicType)}.
*
* @param context an object passed to the callback with the reply to allow
* requests and replies to be correlated. The caller may use any
* convenient object reference, including {@code null}
*
* @param callback called with the result
*
* @param the context object type
*
* @since 5.7
*
* @deprecated since 6.7
*
* Methods that use callbacks are deprecated and will be removed
* in a future release. Use CompletableFuture variant instead.
*/
@Deprecated
void addTopic(
String topicPath,
TopicSpecification specification,
C context,
AddContextCallback callback);
/**
* Send a request to the server to add a topic.
*
* @param topicPath the topic path to which the topic will be bound
*
* @param specification defines the topic to be created. A specification can
* be created using {@link #newSpecification(TopicType)}.
*
* @param callback called with the result
*
* @since 5.7
*
* @deprecated since 6.7
*
* Methods that use callbacks are deprecated and will be removed
* in a future release. Use CompletableFuture variant instead.
*/
@Deprecated
void addTopic(
String topicPath,
TopicSpecification specification,
AddCallback callback);
/**
* Send a request to remove one or more topics.
*
* All topics that match the provided {@code topicSelector} that the caller
* has permission to remove will be removed.
*
* The selector's {@link TopicSelectors descendant pattern qualifier} (a
* trailing {@code /} or {@code //}), can be used to remove descendant
* topics. If a single {@code /} qualifier is specified, all descendants of
* the matched topic paths will be removed. If {@code //} is specified, the
* matched paths and all descendants of the matched paths (complete
* branches) will be removed.
*
* @param topicSelector specifies the topics to remove
*
* @return a CompletableFuture that completes when a response is received
* from the server.
*
*
* If the task completes successfully, the CompletableFuture result
* will bear a {@link TopicRemovalResult}. This provides the number
* of topics removed by calling
* {@link TopicRemovalResult#getRemovedCount()}.
*
*
* 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} – the session is closed.
*
*
* @since 6.0
*/
CompletableFuture removeTopics(TopicSelector topicSelector);
/**
* Send a request to remove one or more topics.
*
* This is equivalent to calling {@link #removeTopics(TopicSelector)} with a
* selector parsed using {@link TopicSelectors#parse(String)}.
*
* @param topicSelector a {@link TopicSelectors topic selector expression}
* specifying the topics to remove.
*
* @return a CompletableFuture that completes when a response is received
* from the server.
*
*
* If the task completes successfully, the CompletableFuture result
* will bear a {@link TopicRemovalResult}. This provides the number
* of topics removed by calling
* {@link TopicRemovalResult#getRemovedCount()}.
*
*
* 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} – the session is closed.
*
*
* @throws IllegalArgumentException if {@code topicSelector} is an invalid
* topic selector expression
* @since 6.0
*/
CompletableFuture removeTopics(String topicSelector)
throws IllegalArgumentException;
/**
* Send a request to remove one or more topics.
*
* All topics that match the provided {@code topicSelector} that the caller
* has permission to remove will be removed.
*
* The selector's {@link TopicSelectors descendant pattern qualifier} (a
* trailing {@code /} or {@code //}), can be used to remove descendant
* topics. If a single {@code /} qualifier is specified, all descendants of
* the matched topic paths will be removed. If {@code //} is specified, the
* matched paths and all descendants of the matched paths (complete
* branches) will be removed.
*
* @param topicSelector a {@link TopicSelectors topic selector expression}
* specifying the topics to remove
*
* @param callback called to notify request completed
*
* @throws IllegalArgumentException if {@code topicSelector} is an invalid
* topic selector expression
*
* @since 5.9
*
* @deprecated since 6.7
*
* Methods that use callbacks are deprecated and will be removed
* in a future release. Use CompletableFuture variant instead.
*/
@Deprecated
void remove(String topicSelector, RemovalCallback callback)
throws IllegalArgumentException;
/**
* Version of {@link #remove(String, RemovalCallback)} that allows a user
* defined context to be provided.
*
* @param topicSelector a {@link TopicSelectors topic selector expression}
* specifying the topics to remove
*
* @param context an object passed to the callback with the reply to allow
* requests and replies to be correlated. The caller may use any
* convenient object reference, including {@code null}
*
* @param callback called to notify request completed
*
* @param the context object type
*
* @throws IllegalArgumentException if {@code topicSelector} is an invalid
* topic selector expression
*
* @since 5.9
*
* @deprecated since 6.7
*
* Methods that use callbacks are deprecated and will be removed
* in a future release. Use CompletableFuture variant instead.
*/
@Deprecated
void remove(
String topicSelector,
C context,
RemovalContextCallback callback)
throws IllegalArgumentException;
/**
* Register a {@link MissingTopicNotificationStream} to handle requests for
* a branch of the topic tree.
*
*
* The provided handler is called when a session subscribes using a topic
* selector that matches no existing topics. This allows a control client
* session to be notified when another session requests a topic that does
* not exist.
*
*
* A session can register multiple handlers, but may only register a single
* handler for a given topic path. If there is already a handler registered
* for the topic path the operation will fail with a
* {@link HandlerConflictException}. A handler will only be called for topic
* selectors with a {@link TopicSelector#getPathPrefix() path prefix} that
* starts with or is equal to {@code topicPath}. If the path prefix matches
* multiple handlers, the one registered for the most specific (longest)
* topic path will be called.
*
*
* Prefer this method to the callback-based alternative
* {@link #addMissingTopicHandler(String, MissingTopicHandler)} since it
* provides better error reporting.
*
* @param topicPath identifies a branch of the topic tree
*
* @param handler the handler to use for notifying topics at or below the
* {@code topicPath} (unless there is a handler registered for a more
* specific topic path)
*
* @return a CompletableFuture that completes when the handler is registered
* with the server.
*
*
* If registration was successful, the CompletableFuture will
* complete successfully with a {@link Registration} which can be
* used to unregister the handler.
*
*
* 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 HandlerConflictException} – if the session has
* already registered a missing topic handler for {@code topicPath};
*
*
- {@link PermissionsException} – if the session does
* not have {@code REGISTER_HANDLER} permission;
*
*
- {@link SessionClosedException} – if the session is
* closed.
*
*
* @since 6.0
*/
CompletableFuture addMissingTopicHandler(
String topicPath,
MissingTopicNotificationStream handler);
/**
* Register a {@link MissingTopicHandler} to handle requests for a branch of
* the topic tree.
*
* The provided handler is called when a session subscribes using a topic
* selector that matches no existing topics.
*
*
* A session can register multiple handlers, but may only register a single
* handler for a given topic path. See
* {@link MissingTopicHandler#onActive(String, RegisteredHandler) onActive}.
* A handler will only be called for topic selectors with a
* {@link TopicSelector#getPathPrefix() path prefix} that starts with or is
* equal to {@code topicPath}. If the path prefix matches multiple handlers,
* the one registered for the most specific (longest) topic path will be
* called.
*
* @param topicPath identifies a branch of the topic tree
*
* @param handler the handler to use for notifying topics at or below the
* {@code topicPath} (unless there is a handler registered for a more
* specific topic path)
*
* @deprecated since 6.7
*
* Prefer the CompletableFuture-based alternative
* {@link #addMissingTopicHandler(String, MissingTopicNotificationStream)}
* since it provides better error reporting.
*/
@Deprecated
void addMissingTopicHandler(String topicPath, MissingTopicHandler handler);
/**
* Callback interface for adding topics when no context is provided.
*
* @deprecated since 6.7
*
* Methods that use callbacks are deprecated and will be removed
* in a future release. Use CompletableFuture variant instead.
*/
@Deprecated
interface AddCallback extends Callback {
/**
* This will be called when the topic has been successfully added.
*
* @param topicPath the topic path of the topic that was added
*/
void onTopicAdded(String topicPath);
/**
* This will be called if an attempt to add a topic has failed.
*
* @param topicPath the topic path as supplied to the add request
*
* @param reason the reason for failure
*/
void onTopicAddFailed(String topicPath, TopicAddFailReason reason);
/**
* A default implementation of {@link AddCallback}.
*
* Simply logs onTopicAdded callback at 'debug' level and
* onTopicAddFailed callback at 'warn' level. These methods can be
* overridden to perform actions as required.
*/
class Default
extends Callback.Default
implements AddCallback {
private static final Logger LOG =
LoggerFactory.getLogger(AddCallback.Default.class);
@Override
public void onTopicAdded(String topicPath) {
LOG.debug("{} - Topic {} added", this, topicPath);
}
@Override
public void onTopicAddFailed(
String topicPath, TopicAddFailReason reason) {
LOG.warn(
"{} - Failed to add topic {} : {}",
this,
topicPath,
reason);
}
}
}
/**
* Contextual callback interface for adding topics.
*
* Use this alternative to {@link AddCallback} 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 AddContextCallback extends ContextCallback {
/**
* This will be called when the topic has been successfully added.
*
* @param context the context object the application supplied when
* making the call; may be {@code null}
*
* @param topicPath the topic path of the topic that was added
*/
void onTopicAdded(C context, String topicPath);
/**
* This will be called if an attempt to add a topic has failed.
*
* @param context the context object the application supplied when
* making the call; may be {@code null}
*
* @param topicPath the topic path as supplied to the add request
*
* @param reason the reason for failure
*/
void onTopicAddFailed(
C context,
String topicPath,
TopicAddFailReason reason);
/**
* A default implementation of {@link AddContextCallback}.
*
* Simply logs onTopicAdded callback at 'debug' level and
* onTopicAddFailed callback at 'warn' level. These methods can be
* overridden to perform actions as required.
*
* @param context object type
*/
class Default
extends ContextCallback.Default
implements AddContextCallback {
private static final Logger LOG =
LoggerFactory.getLogger(AddContextCallback.Default.class);
@Override
public void onTopicAdded(C context, String topicPath) {
LOG.debug(
"{} - Topic {} added, context={}",
this,
topicPath,
context);
}
@Override
public void onTopicAddFailed(
C context,
String topicPath,
TopicAddFailReason reason) {
LOG.warn(
"{} - Failed to add topic {} : {}, context={}",
this,
topicPath,
reason,
context);
}
}
}
/**
* Callback interface for
* {@link TopicControl#remove(String, RemovalCallback) remove} requests when
* no context is used.
*
* @deprecated since 6.7
*
* Methods that use callbacks are deprecated and will be removed
* in a future release. Use CompletableFuture variant instead.
*/
@Deprecated
interface RemovalCallback
extends com.pushtechnology.diffusion.client.callbacks.Callback {
/**
* Called to indicate that the requested remove operation has been
* actioned at the server. This does not mean that all intended topics
* have actually been removed. For example, if the caller did not have
* sufficient permissions to remove the topic(s).
*/
void onTopicsRemoved();
/**
* A default implementation of {@link RemovalCallback}.
*
* This simply logs onTopicsRemoved calls at 'debug' level. This can be
* overridden to perform some more specific action.
*/
class Default
extends
com.pushtechnology.diffusion.client.callbacks.Callback.Default
implements RemovalCallback {
private static final Logger LOG =
LoggerFactory.getLogger(RemovalCallback.Default.class);
@Override
public void onTopicsRemoved() {
LOG.debug("{} - Removed topic(s)", this);
}
}
}
/**
* Contextual callback interface for
* {@link TopicControl#remove(String, Object, RemovalContextCallback)
* remove} requests.
*
* Use this alternative to {@link RemovalCallback} 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 RemovalContextCallback
extends
com.pushtechnology.diffusion.client.callbacks.ContextCallback {
/**
* Called to indicate that the requested remove operation has been
* actioned at the server. This does not mean that all intended topics
* have actually been removed. For example, if the caller did not have
* sufficient permissions to remove the topic(s).
*
* @param context the context object the application supplied when
* making the call; may be {@code null}
*/
void onTopicsRemoved(C context);
/**
* A default implementation of {@link RemovalContextCallback}.
*
* This simply logs onTopicsRemoved calls at 'debug' level.
*
* @param context object type
*/
class Default
extends
com.pushtechnology.diffusion.client.callbacks.ContextCallback.Default
implements RemovalContextCallback {
private static final Logger LOG =
LoggerFactory.getLogger(RemovalContextCallback.Default.class);
@Override
public void onTopicsRemoved(C context) {
LOG.debug("{} - Removed topic(s), context={}", this, context);
}
}
}
/**
* Stream called when a session subscribes using a topic selector that
* matches no topics.
*
* Instances can be registered using
* {@link TopicControl#addMissingTopicHandler(String, MissingTopicNotificationStream)
* addMissingTopicHandler}.
*
* @since 6.0
*/
interface MissingTopicNotificationStream extends Stream {
/**
* Called when a session requests a topic that does not exist, and the
* topic path belongs to part of the topic tree for which this handler
* was registered.
*
* @param notification the missing topic notification
*/
void onMissingTopic(MissingTopicNotification notification);
/**
* Abstract {@link MissingTopicNotificationStream}.
*
* This can be extended to provide the
* {@link MissingTopicNotificationStream#onMissingTopic(TopicControl.MissingTopicNotification)
* onMissingTopic} method.
*/
abstract class Default extends Stream.Default
implements MissingTopicNotificationStream {
}
}
/**
* Handler called when a session subscribes using a topic selector that
* matches no topics.
*
* Handler instances can be registered using
* {@link TopicControl#addMissingTopicHandler(String, MissingTopicHandler)
* addMissingTopicHandler}.
*
* @deprecated since 6.7
*
* Use {@link MissingTopicNotificationStream} instead.
*/
@Deprecated
interface MissingTopicHandler extends TopicTreeHandler {
/**
* Called when a session subscribes using a topic selector that matches
* no existing topics.
*
* @param notification the missing topic notification
*/
void onMissingTopic(MissingTopicNotification notification);
/**
* Abstract {@link MissingTopicHandler}.
*
* This can be extended to provide the
* {@link MissingTopicHandler#onMissingTopic(TopicControl.MissingTopicNotification)
* onMissingTopic} method.
*/
abstract class Default
extends TopicTreeHandler.Default
implements MissingTopicHandler {
}
}
/**
* Notification that a session has made a subscription request using a
* selector that does not match any topics.
*/
interface MissingTopicNotification {
/**
* Returns the session properties of the session that made the request.
*
* @return the session properties
*
* @since 6.7
*/
Map getSessionProperties();
/**
* Returns the identity of the session that made the request.
*
* This method is equivalent to calling
* {@link Diffusion#sessionIdFromString
* sessionIdFromString(getSessionProperties().get(Session.SESSION_ID)}.
*
* @return the session identifier
*/
default SessionId getSessionId() {
return sessionIdFromString(getSessionProperties().get(SESSION_ID));
}
/**
* Returns the topic path derived from the requested topic selector.
* This method is equivalent to calling
* {@link TopicSelector#getPathPrefix()
* getTopicSelector().getPathPrefix()}.
*
* @return the topic path
*/
default String getTopicPath() {
return getTopicSelector().getPathPrefix();
}
/**
* Returns the {@link TopicSelector} that triggered this notification.
*
* @return the topic selector
* @since 5.1
*/
TopicSelector getTopicSelector();
/**
* Returns a list of the names of the servers through which the
* notification has been routed.
*
* The first name in the list will be the name of the server to which
* the originating session was connected. If the notification was routed
* through remote server connections before reaching the recipient then
* those servers will also be listed in the order that the notification
* passed through them.
*
* @return list of server names, the first being the one which the
* originating session was connected to
*
* @since 6.7
*/
List getServerNames();
/**
* This method has no effect.
*
* @deprecated since 6.6
*
* This method is a no-op. In previous releases this would
* cause the selector to be added to the session's
* selections and the selection to be re-evaluated. Since
* 6.6 the selector is always added to the session's
* selections before this notification is issued.
*/
@Deprecated
void proceed();
/**
* This method has no effect.
*
* @since 5.1
*
* @deprecated since 6.6
*
* This method is a no-op. In previous releases this would
* prevent the selector from being added to the session's
* selections. Since 6.6 the selector is always added to the
* session's selections before this notification is issued
* regardless.
*/
@Deprecated
void cancel();
}
/**
* Used to report the result of adding a topic.
*
* @since 6.0
*/
enum AddTopicResult {
/**
* A new topic was created.
*/
CREATED,
/**
* A topic with the same specification already exists.
*/
EXISTS,
}
/**
* Reports the number of topics removed by a call to {@link #removeTopics}.
*
* @since 6.6
*/
interface TopicRemovalResult {
/**
* The integer returned represents the number of topics removed by the
* operation. This does not include any derived topics created by a
* topic view which were removed as a side effect of this action.
*
* @return the count of topics removed
*/
int getRemovedCount();
}
/**
* Exception thrown to report a failure to add a topic.
*
* @since 6.0
*/
class AddTopicException extends SessionException {
private static final long serialVersionUID = -7842778740912571614L;
/**
* @param message the exception message
*/
public AddTopicException(String message) {
super(message);
}
}
/**
* Exception thrown to report a topic could not be added because the
* specification is invalid.
*
* @since 6.0
*/
final class InvalidTopicSpecificationException extends AddTopicException {
private static final long serialVersionUID = -4737003936657468838L;
/**
* @param message the exception message
*/
public InvalidTopicSpecificationException(String message) {
super(message);
}
}
/**
* Exception thrown to report a topic could not be added because an existing
* topic with a different specification is bound to the topic path.
*
* @since 6.0
*/
final class ExistingTopicException extends AddTopicException {
private static final long serialVersionUID = 177584683079519534L;
/**
* @param message the exception message
*/
public ExistingTopicException(String message) {
super(message);
}
}
/**
* Exception thrown to report a topic could not be added because the topic
* path supplied is invalid.
*
*
* Invalid topic paths include the empty string, and strings adjacent path
* separators such as {@code //}.
*
* @since 6.0
*/
final class InvalidTopicPathException extends AddTopicException {
private static final long serialVersionUID = -132560805100117118L;
/**
* @param message the exception message
*/
public InvalidTopicPathException(String message) {
super(message);
}
}
/**
* Exception thrown to report a topic could not be added because a licence
* limit has been exceeded.
*
* @since 6.0
*/
final class TopicLicenseLimitException extends AddTopicException {
private static final long serialVersionUID = 3459583462376598320L;
/**
* @param message the exception message
*/
public TopicLicenseLimitException(String message) {
super(message);
}
}
/**
* Exception thrown to report a topic could not be added because there is an
* existing topic that is incompatible with the request.
*
* @since 6.0
*/
abstract class IncompatibleTopicException extends AddTopicException {
private static final long serialVersionUID = 8083381286283811102L;
/**
* @param message the exception message
*/
public IncompatibleTopicException(String message) {
super(message);
}
}
/**
* Exception thrown to report that a topic exists at the same path that is
* managed by a component that has exclusive control over the topic.
*
* For example, a topic may already exist at the same path that is under the
* control of fan-out distribution.
*
* @since 6.0
*/
final class IncompatibleExistingTopicException
extends IncompatibleTopicException {
private static final long serialVersionUID = 5075815951750161358L;
/**
* @param message the exception message
*/
public IncompatibleExistingTopicException(String message) {
super(message);
}
}
}