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

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

The newest version!
/*******************************************************************************
 * 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;

import static org.slf4j.LoggerFactory.getLogger;

import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

import org.slf4j.Logger;

import com.pushtechnology.diffusion.client.callbacks.Registration;
import com.pushtechnology.diffusion.client.callbacks.Stream;
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.SessionId;
import com.pushtechnology.diffusion.client.types.GlobalPermission;
import com.pushtechnology.diffusion.client.types.PathPermission;

/**
 * This feature provides a client session with request-response messaging
 * capabilities that can be used to implement application services.
 *
 * 

* Request-response messaging allows a session to send requests to other * sessions. Each receiving session provides a corresponding response, which is * returned to the sending session. Each request and response carries an * application provided value. * *

* The method used to send a request determines which sessions will receive it. * Each request is routed using the provided message path – an * application provided string. Two addressing schemes are provided: * unaddressed requests and addressed requests. * *

Unaddressed requests

* *

* A session can provide an application service by implementing a handler and * registering it with the server. This is somewhat similar to implementing a * REST service, except that interactions between the sender and receiver are * asynchronous. * *

* Unaddressed requests sent using * {@link #sendRequest(String, Object, Class, Class) sendRequest} are routed by * the server to a handler that has been pre-registered by another session, and * matches the message path. * *

* Handlers are registered with {@link #addRequestHandler}. Each session may * register at most one handler for a given message path. Optionally, one or * more session property names can be provided (see {@link Session} for a full * description of session properties), in which case the values of the session * properties for each recipient session will be returned along with its * response. To add a request handler, the control client session must have * {@link GlobalPermission#REGISTER_HANDLER REGISTER_HANDLER} permission. If * registering to receive session property values, the session must also have * {@link GlobalPermission#VIEW_SESSION VIEW_SESSION} permission. * *

* Routing works as follows: * *

    *
  1. The session {@link #sendRequest(String, Object, Class, Class) sends} the * request, providing the message path, the request value and data type, and the * expected response type. *
  2. The server uses the message path to apply access control. The sender must * have the {@link PathPermission#SEND_TO_MESSAGE_HANDLER * SEND_TO_MESSAGE_HANDLER} path permission for the message path, or the request * will be rejected. *
  3. The server uses the message path to select a pre-registered handler and * route the request to the appropriate recipient session. The server will * consider all registered handlers and select one registered for the most * specific path. If multiple sessions have registered a handler registered for * a path, one will be chosen arbitrarily. If there is no registered handler * matching the message path, the request will be rejected. *
  4. Otherwise, the server forwards the request to one of the sessions * registered to handle the message path. The message path is also passed to the * recipient session, providing a hierarchical context. *
  5. The recipient session processes the request and returns a response to the * server, which forwards the response to the sending session. *
* *

* Registration works across a cluster of servers. If no matching handler is * registered on the server to which the sending session is connected, the * request will be routed to another server in the cluster that has one. * *

Addressed requests

* *

* Addressed requests provide a way to perform actions on a group of sessions, * or to notify sessions of one-off events (for repeating streams of events, use * a topic instead). * *

* An addressed request can be sent to a set of sessions using * {@link #sendRequestToFilter sendRequestToFilter}. For the details of session * filters, see {@link Session}. Sending a request to a filter will match zero * or more sessions. Each response received will be passed to the provided * {@link Messaging.FilteredRequestCallback callback}. As a convenience, an * addressed request can be sent a specific session using the overloaded variant * of {@link #sendRequest(SessionId, String, Object, Class, Class) sendRequest} * that accepts a session id. * *

* Sending an addressed request requires {@link PathPermission#SEND_TO_SESSION * SEND_TO_SESSION} permission. * *

* If the sending session is connected to a server belonging to a cluster, the * recipient sessions can be connected to other servers in the cluster. The * filter will be evaluated against all sessions hosted by the cluster. * *

* To receive addressed requests, a session must set up a local request stream * to handle the specific message path, using * {@link #setRequestStream(String, Class, Class, Messaging.RequestStream) * setRequestStream}. When a request is received for the message path, the * {@link Messaging.RequestStream#onRequest onRequest} method on the stream is * triggered. The session should respond using the provided * {@link Messaging.RequestStream.Responder responder}. Streams receive an * {@link Stream#onClose onClose} callback when unregistered and an * {@link com.pushtechnology.diffusion.client.callbacks.Callback#onError * onError} callback if the session is closed. * *

* If a request is sent to a session that does not have a matching stream for * the message path, an error will be returned to the sending session. * *

Accessing the feature

* *

* Obtain this feature from a {@link Session session} as follows: * *

 * 
 * Messaging messaging = session.feature(Messaging.class);
 * 
 * 
* * @author DiffusionData Limited * @since 5.0 */ public interface Messaging extends Feature { /** * Send a request. * * A response is returned when the {@link CompletableFuture} has been * completed. * * @param path the path to send a request to * @param request the request to send * @param requestType the request type * @param responseType the response type * * @param request object type * @param response object type * * @return a CompletableFuture that completes when a response has been * received by a handler.If the task completes successfully, the * CompletableFuture result will be the response (to the request) of * type R. *

* 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 UnhandledMessageException} – if there is no * handler registered on the server to receive requests for this * message path; * *
  • {@link IncompatibleDatatypeException} – if the request * is not compatible with the datatype bound to the handler's * message path; * *
  • {@link IllegalArgumentException} – if the response is * not compatible with the specified response type; * *
  • {@link RejectedRequestException} – if the request has * been rejected by the recipient session calling * {@code Responder.reject(message)}; * *
  • {@link SessionClosedException} – if the session is * closed; * *
  • {@link PermissionsException} – if the session does * not have {@code SEND_TO_MESSAGE_HANDLER} permission; * *
  • {@link CancellationException} – if the recipient * session did not respond before the request timed out. *
* * @throws IllegalArgumentException if there is no data type matching the * request type parameter * * @since 6.0 */ CompletableFuture sendRequest( String path, T request, Class requestType, Class responseType); /** * Set a request stream to handle requests to a specified path. * * @param path path to receive requests on * @param requestType the request type. * @param responseType the response type. * @param requestStream request stream to handle requests to this path * * @param request type * @param response type * * @return null if the request stream is the first stream to be set to the * path, otherwise this method will return the previously set * request stream. * * @throws IllegalArgumentException if there is no data type matching the * request type parameter * @since 6.0 */ RequestStream setRequestStream( String path, Class requestType, Class responseType, RequestStream requestStream); /** * Remove the request stream at a particular path. * * @param path path at which to remove the request stream * * @return the request stream that was removed from the path. If the path * does not have a request stream assigned (or the path does not * exist), null will be returned instead * * @since 6.0 */ RequestStream removeRequestStream(String path); /** * Interface which specifies a request stream to receive request * notifications. * * @param request type * @param response type * * @since 6.0 */ interface RequestStream extends Stream { /** * Called to indicate a request has been received. * * @param path path the request was sent on * @param request request that was received * @param responder responder to dispatch a response back to the * requester */ void onRequest(String path, T request, Responder responder); /** * Responder interface to dispatch responses to requests. * * @param response class type */ interface Responder { /** * Dispatch a response to a request. * * @param response the response */ void respond(R response); /** * Reject a request. * * @param message context message to be contained in the rejection. * * @see RejectedRequestException */ void reject(String message); } } /** * Send a request to a session. * * @param sessionId session to send the request to * @param path message path used by the recipient to select an appropriate * handler * @param request request to send * @param requestType the request type * @param responseType the response type * * @param request type * @param response type * * @return A CompletableFuture that completes when a response has been * received by the session. If the task completes successfully, the * CompletableFuture result will be a response (to the request) of * type R. *

* 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 NoSuchSessionException} – if the session does * not exist on the server; * *
  • {@link UnhandledMessageException} – if recipient * session does not have a local request stream registered for this * path; * *
  • {@link IncompatibleDatatypeException} – if the request * is not compatible with the datatype bound to the handler's * message path; * *
  • {@link IllegalArgumentException} – if the response is * not compatible with the specified response type; * *
  • {@link RejectedRequestException} – if the request has * been rejected by the recipient session calling * {@code Responder.reject(message)}; * *
  • {@link SessionClosedException} – if the session is * closed; * *
  • {@link PermissionsException} – if the session does * not have {@code SEND_TO_SESSION} permission; *
* * @since 6.0 */ CompletableFuture sendRequest( SessionId sessionId, String path, T request, Class requestType, Class responseType); /** * Register a request handler to handle requests from other client sessions * for a branch of the message path hierarchy. * *

* Each control session may register a single handler for a branch. When the * handler is no longer required, it may be closed using the * {@link Registration} provided by the CompletableFuture result. To change * the handler for a particular branch the previous handler must first be * closed. * * @param path the request message path * @param requestType the request type. If this class is not supported by * the available datatypes, an {@link IllegalArgumentException} will * be thrown. * @param responseType the response type. If this class is not supported by * the available datatypes, an {@link IllegalArgumentException} will * be thrown. * @param handler request handler to be registered at the server * @param sessionProperties a list of keys of session properties that should * be supplied with each request. See {@link Session} for a full list * of available fixed property keys. To request no properties supply * an empty list. To request all fixed properties include * {@link Session#ALL_FIXED_PROPERTIES} as a key. In this case any * other fixed property keys would be ignored. To request all user * properties include {@link Session#ALL_USER_PROPERTIES} as a key. * In this case any other user properties are ignored. * * @param request type * @param response type * * @return a CompletableFuture that completes when the handler has been * registered, returning 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 SessionClosedException} – if the session is * closed; * *
  • {@link HandlerConflictException} – if the session has * already registered a handler for this message path; * *
  • {@link PermissionsException} – if the session does * not have {@code REGISTER_HANDLER} permission to register a * request handler on the server; * *
  • {@link PermissionsException} – if the session does * not have {@code VIEW_SESSION} permission to access the client's * session properties. *
* * @since 6.0 */ CompletableFuture addRequestHandler( String path, Class requestType, Class responseType, RequestHandler handler, String... sessionProperties); /** * Send a request to all sessions that satisfy a given session filter. * * @param filter the session filter expression. See {@link Session} for a * full description of filter expressions. * @param path message path used by the recipient to select an appropriate * handler * @param request the request object * @param requestType the type of the request to be sent * @param responseType the type of the response to be received * @param callback the callback to receive notification of responses (or * errors) from sessions * * @param request type * @param response type * * @return a CompletableFuture that completes when the server has dispatched * all the requests. * *

* If the server successfully evaluated the filter, the result of * this contains the number of sessions the request was sent to. * Failure to send a request to a particular matching session is * reported to the {@code callback}. * *

* 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 InvalidFilterException} – if the {@code filter} * parameter could not be parsed; * *
  • {@link PermissionsException} – if the calling * session does not have {@code SEND_TO_SESSION} and * {@code VIEW_SESSION} permissions; * *
  • {@link SessionClosedException} – if the calling session * is closed. *
* * @since 6.0 */ CompletableFuture sendRequestToFilter( String filter, String path, T request, Class requestType, Class responseType, FilteredRequestCallback callback); /** * Callback interface for requests dispatched through a filter. * * @param response type * * @since 6.0 */ interface FilteredRequestCallback { /** * Called when a response has been received. * * @param sessionId sessionId of the session that sent the response * @param response response object */ void onResponse(SessionId sessionId, R response); /** * Called when a response from a session results in an error. * * @param sessionId sessionId of the session in error * @param throwable the throwable reason of the response error */ void onResponseError(SessionId sessionId, Throwable throwable); /** * Default implementation of {@link FilteredRequestCallback}. * * This simply logs the calls to each callback at either WARN or DEBUG. * * @param response class type */ class Default extends Stream.Default implements FilteredRequestCallback { private static final Logger LOG = getLogger(FilteredRequestCallback.Default.class); @Override public void onResponse(SessionId sessionId, R response) { LOG.debug("Response received: {} from session {}", response, sessionId); } @Override public void onResponseError(SessionId sessionId, Throwable throwable) { LOG.warn("Error on response from session {}", sessionId, throwable); } } } /** * Interface which specifies a request handler to receive request * notifications. {@link RequestHandler.Responder#respond} must be called to * dispatch a response to the request. * * @param request class type * @param response class type * * @since 6.0 */ interface RequestHandler extends Stream { /** * Context of the request received. */ interface RequestContext { /** * SessionId of the session that sent the request. * * @return the sessionId */ SessionId getSessionId(); /** * Returns the message path of the request. * * @return the path */ String getPath(); /** * Session properties of the session that sent the request. * * @return the session properties */ Map getSessionProperties(); } /** * Responder interface to dispatch responses to requests. * * @param response type */ interface Responder { /** * Dispatch a response to a request. * * @param response the response */ void respond(R response); /** * Reject a request. * * @param message context message to be contained in the rejection. * * @see RejectedRequestException */ void reject(String message); } /** * Called to indicate a request has been received. * * @param request request that was received * @param context context object that provides the session id (session * that sent the request), path and session properties. * @param responder responder to dispatch a response back to the * requester */ void onRequest( T request, RequestContext context, Responder responder); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy