com.pushtechnology.diffusion.client.features.package-info 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.
*******************************************************************************/
/**
* Client API : Standard Features.
*
* This package contains the standard client features.
*
* A {@link com.pushtechnology.diffusion.client.session.Feature feature}
* represents a unit of functionality that is available to use within a client
* {@link com.pushtechnology.diffusion.client.session.Session session}.
*
* Standard client features include:
*
* Topics
* This feature allows a client to subscribe to topics in order to receive
* streaming updates. It also allows a client to fetch the state of topics
* without having to subscribe to them.
*
* Messaging
* This feature allows a client to send messages on a topic (to be delivered to
* the controller of that topic) or receive messages on topics (send by topic
* controllers).
*
* Pings
* This feature allows a client to ping the server to ensure connectivity and
* to obtain round trip timings.
*
* Security
* This feature allows a client to change it's principal.
*
* A feature may be obtained from a session using the
* {@link com.pushtechnology.diffusion.client.session.Session#feature(Class) feature}
* method specifying the feature class. For example:
*
* Topics topics = session.feature(Topics.class);
*
*
*
* CompletableFuture
*
*
* Since version 6.0, the API has been extended to support Java 8's
* CompletableFuture. All methods that have a callback that produces a single
* outcome now have an alternative that returns the outcome using a
* CompletableFuture rather than a callback.
*
*
CompletableFuture<?>
*
*
* Some methods have a return type of {@code CompletableFuture>}. The result
* type is a wildcard rather than {@code Void} to provide forward compatibility
* with future iterations of this API that may provide a non-null result with a
* more specific result type. If such a method is called and the task completes
* successfully, the CompletableFuture result will be null.
*
*
Blocking methods are unsupported when chaining CompletableFutures
*
*
* Calling a blocking CompletableFuture method, such as
* {@link java.util.concurrent.CompletableFuture#get() get} or
* {@link java.util.concurrent.CompletableFuture#join() join}, from a Diffusion
* thread is disallowed since doing so can cause the client to deadlock because
* the same thread is used to deliver responses from the server to the session.
*
*
* This situation typically happens when chaining CompletableFutures. To protect
* against deadlock, Diffusion detects the use of a blocking method from a
* Diffusion thread, and completes the CompletableFuture exceptionally with a
* {@code UnsupportedOperationException}. For example:
*
*
* messaging.sendMessage("x", "m1", String.class, String.class)
* .thenAccept(r1 -> {
* // This will run in the Diffusion input thread, when the
* // result of the first call is received.
* CompletableFuture<String> cf =
* messaging.sendMessage("y", r1, String.class, String.class);
*
* // This will immediately throw an ExecutionException with an
* // UnsupportedOperationException cause.
* String r2 = cf.get();
*
* // Not reached.
* LOG.info("Result {}", r2);
* });
*
*
*
* To avoid the problem, when calling a second Diffusion method from a handler
* attached to a Diffusion-supplied CompletableFuture, only use non-blocking
* methods such as
* {@link java.util.concurrent.CompletableFuture#thenAccept(java.util.function.Consumer)
* thenAccept} on the result returned by the second method. For example:
*
*
* messaging.sendMessage("x", "m1", String.class, String.class)
* .thenAccept(r1 -> {
* // This will run in the Diffusion input thread, when the
* // result of the first call is received.
* CompletableFuture<String> cf =
* messaging.sendMessage("y", r1, String.class, String.class);
*
* cf.thenAccept(r2 -> {
* LOG.info("Result {}", r2);
* });
* });
*
*
*
* The blocking call protection is effective against the use of {@code get} and
* {@code join}, but does not work if a CompletableFuture is exposed indirectly
* as a stage of a plain, unprotected CompletableFuture via
* {@link java.util.concurrent.CompletableFuture#allOf(java.util.concurrent.CompletableFuture...)
* allOf()},
* {@link java.util.concurrent.CompletableFuture#anyOf(java.util.concurrent.CompletableFuture...)
* anyOf},
* {@link java.util.concurrent.CompletableFuture#thenCombine(java.util.concurrent.CompletionStage, java.util.function.BiFunction)
* thenCombine},
* {@link java.util.concurrent.CompletableFuture#acceptEither(java.util.concurrent.CompletionStage, java.util.function.Consumer)
* acceptEither}, etc.
*
* @author DiffusionData Limited
* @since 5.0
*/
package com.pushtechnology.diffusion.client.features;