public interface ExecHarness extends ExecControl, AutoCloseable
An execution harness is backed by a thread pool.
It is important to call close()
when the object is no longer needed to shutdown this thread pool.
Alternatively, if you are performing a single operation you can use one of the *single
static methods.
yield(Function)
,
yieldSingle(Function)
,
run(Action)
,
runSingle(Action)
Modifier and Type | Method and Description |
---|---|
default void |
addInterceptor(ExecInterceptor execInterceptor,
Block continuation)
Adds an interceptor that wraps the rest of the current execution segment and all future segments of this execution.
|
default <T> Promise<T> |
blocking(Callable<T> blockingOperation)
Performs a blocking operation on a separate thread, returning a promise for its value.
|
void |
close()
Shuts down the thread pool backing this harness.
|
default ExecStarter |
exec()
Creates a new execution starter that can be used to initiate a new execution.
|
ExecControl |
getControl()
The execution control for the harness.
|
default ExecController |
getController() |
default Execution |
getExecution() |
static ExecHarness |
harness()
Creates a new execution harness.
|
static ExecHarness |
harness(int numThreads) |
default <T> Promise<T> |
promise(Action<? super Fulfiller<T>> action)
Creates a promise for an asynchronously created value.
|
default void |
run(Action<? super ExecControl> action)
Initiates an execution and blocks until it completes.
|
void |
run(Action<? super RegistrySpec> registry,
Action<? super ExecControl> action)
Initiates an execution and blocks until it completes.
|
static void |
runSingle(Action<? super ExecControl> action)
Convenient form of
run(Action) that creates and closes a harness for the run. |
static void |
runSingle(Action<? super RegistrySpec> registry,
Action<? super ExecControl> action)
Convenient form of
run(Action, Action) that creates and closes a harness for the run. |
default <T> TransformablePublisher<T> |
stream(org.reactivestreams.Publisher<T> publisher)
Process streams of data asynchronously with non-blocking back pressure.
|
<T> ExecResult<T> |
yield(Action<? super RegistrySpec> registry,
Function<ExecControl,Promise<T>> func)
Synchronously returns a promised value.
|
default <T> ExecResult<T> |
yield(Function<ExecControl,Promise<T>> func)
Synchronously returns a promised value.
|
static <T> ExecResult<T> |
yieldSingle(Action<? super RegistrySpec> registry,
Function<ExecControl,Promise<T>> func)
Creates an exec harness,
executes the given function with it before closing it, then returning execution result. |
static <T> ExecResult<T> |
yieldSingle(Function<ExecControl,Promise<T>> func)
Creates an exec harness,
executes the given function with it before closing it, then returning execution result. |
current, execControl, failedPromise, nest, nest, promiseOf, wrap
static ExecHarness harness()
import ratpack.exec.ExecControl;
import ratpack.exec.Promise;
import ratpack.test.exec.ExecHarness;
import ratpack.exec.ExecResult;
import static org.junit.Assert.assertEquals;
public class Example {
// An async callback based API
static class AsyncApi {
static interface Callback<T> {
void receive(T value);
}
public <T> void returnAsync(T value, Callback<? super T> callback) {
new Thread(() -> callback.receive(value)).run();
}
}
// Our service class that wraps the raw async API
// In the real app this is created by the DI container (e.g. Guice)
static class AsyncService {
private final ExecControl execControl;
private final AsyncApi asyncApi = new AsyncApi();
public AsyncService(ExecControl execControl) {
this.execControl = execControl;
}
// Our method under test
public <T> Promise<T> promise(final T value) {
return execControl.promise(fulfiller -> asyncApi.returnAsync(value, fulfiller::success));
}
}
public static void main(String[] args) throws Throwable {
// the harness must be close()'d when finished with to free resources
try (ExecHarness harness = ExecHarness.harness()) {
// set up the code under test using the exec control from the harness
final AsyncService service = new AsyncService(harness.getControl());
// exercise the async code using the harness, blocking until the promised value is available
ExecResult<String> result = harness.yield(execution -> service.promise("foo"));
assertEquals("foo", result.getValue());
}
}
}
When using Ratpack's RxJava integration, ExecHarness can be used to test rx.Observable
instances by first converting them to a promise.
See the ratpack.rx.RxRatpack.asPromise(Observable)
documentation for an example of testing observables.static ExecHarness harness(int numThreads)
default <T> ExecResult<T> yield(Function<ExecControl,Promise<T>> func) throws Exception
The given function will execute in a separate thread. The calling thread will block, waiting for the promised value to be provided.
T
- the type of promised valuefunc
- a function that exercises some code that returns a promiseException
- any thrown by the function<T> ExecResult<T> yield(Action<? super RegistrySpec> registry, Function<ExecControl,Promise<T>> func) throws Exception
The given function will execute in a separate thread. The calling thread will block, waiting for the promised value to be provided.
T
- the type of promised valueregistry
- the intial contents of the execution registryfunc
- a function that exercises some code that returns a promiseException
- any thrown by the functionstatic <T> ExecResult<T> yieldSingle(Function<ExecControl,Promise<T>> func) throws Exception
executes
the given function with it before closing it, then returning execution result.T
- the type of promised valuefunc
- a function that exercises some code that returns a promiseException
- any thrown by the function, or the promise failure exceptionstatic <T> ExecResult<T> yieldSingle(Action<? super RegistrySpec> registry, Function<ExecControl,Promise<T>> func) throws Exception
executes
the given function with it before closing it, then returning execution result.T
- the type of promised valueregistry
- the intial contents of the execution registryfunc
- a function that exercises some code that returns a promiseException
- any thrown by the function, or the promise failure exceptiondefault void run(Action<? super ExecControl> action) throws Exception
This method is useful for testing an execution that has some detectable side effect, as this method does not return the “result” of the execution.
action
- the start of the executionException
- any thrown during the execution that is not explicitly caughtrunSingle(Action)
,
yield(Function)
void run(Action<? super RegistrySpec> registry, Action<? super ExecControl> action) throws Exception
This method is useful for testing an execution that has some detectable side effect, as this method does not return the “result” of the execution.
registry
- the intial contents of the execution registryaction
- the start of the executionException
- any thrown during the execution that is not explicitly caughtrunSingle(Action)
,
yield(Function)
static void runSingle(Action<? super ExecControl> action) throws Exception
run(Action)
that creates and closes a harness for the run.action
- the start of the executionException
- any thrown during the execution that is not explicitly caughtrun(Action)
,
yield(Function)
static void runSingle(Action<? super RegistrySpec> registry, Action<? super ExecControl> action) throws Exception
run(Action, Action)
that creates and closes a harness for the run.registry
- the intial contents of the execution registryaction
- the start of the executionException
- any thrown during the execution that is not explicitly caughtrun(Action)
,
yield(Function)
ExecControl getControl()
Note that the execution harness implements ExecControl
itself, simply delegating calls this the return of this method.
void close()
close
in interface AutoCloseable
default ExecStarter exec()
exec
in interface ExecControl
default Execution getExecution()
getExecution
in interface ExecControl
default ExecController getController()
getController
in interface ExecControl
default void addInterceptor(ExecInterceptor execInterceptor, Block continuation) throws Exception
The given action is executed immediately (i.e. as opposed to being queued to be executed as the next execution segment). Any code executed after a call to this method in the same execution segment WILL NOT be intercepted. Therefore, it is advisable to not execute any code after calling this method in a given execution segment.
See ExecInterceptor
for example use of an interceptor.
addInterceptor
in interface ExecControl
execInterceptor
- the execution interceptor to addcontinuation
- the rest of the code to be executedException
- any thrown by continuation
ExecInterceptor
default <T> Promise<T> blocking(Callable<T> blockingOperation)
This method should be used to perform blocking IO, or to perform any operation that synchronously waits for something to happen.
The given blockingOperation
will be performed on a thread from a special thread pool for such operations
(i.e. not a thread from the main compute event loop).
The operation should do as little computation as possible. It should just perform the blocking operation and immediately return the result. Performing computation during the operation will degrade performance.
This method is just a specialization of ExecControl.promise(ratpack.func.Action<? super ratpack.exec.Fulfiller<T>>)
, and shares all of the same semantics with regard to
execution binding and execution-on-promise-subscription.
blocking
in interface ExecControl
T
- the type of value created by the operationblockingOperation
- the operation that blocksdefault <T> Promise<T> promise(Action<? super Fulfiller<T>> action)
This method can be used to integrate with APIs that produce values asynchronously.
The asynchronous API should be invoked during the execute method of the action given to this method.
The result of the asynchronous call is then given to the Fulfiller
that the action is given.
promise
in interface ExecControl
T
- the type of promised valueaction
- an action that invokes an asynchronous API, forwarding the result to the given fulfillerFulfiller
,
Fulfillment
default <T> TransformablePublisher<T> stream(org.reactivestreams.Publisher<T> publisher)
This method allows the processing of elements (onNext) or termination signals (onError, onComplete) to happen outside of the execution stack of the Publisher. In other words these "events" are executed asynchronously, on a Ratpack managed thread, without blocking the Publisher.
stream
in interface ExecControl
T
- the type of streamed elementspublisher
- the provider of a potentially unbounded number of sequenced elements, publishing them according to the demand
received from its Subscriber(s)