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

ratpack.exec.ExecControl Maven / Gradle / Ivy

There is a newer version: 2.0.0-rc-1
Show newest version
/*
 * Copyright 2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ratpack.exec;

import org.reactivestreams.Publisher;
import ratpack.exec.internal.DefaultOperation;
import ratpack.exec.internal.JustInTimeExecControl;
import ratpack.func.Action;
import ratpack.func.Block;
import ratpack.func.Factory;
import ratpack.stream.TransformablePublisher;

import java.util.concurrent.Callable;

/**
 * Provides methods for controlling execution(i.e. blocking, forking and calling async API), independent of the current execution.
 *
 * 
{@code
 * import ratpack.exec.ExecControl;
 * import ratpack.exec.ExecController;
 * import ratpack.exec.Promise;
 *
 * import ratpack.test.handling.RequestFixture;
 * import ratpack.test.handling.HandlingResult;
 *
 * import static org.junit.Assert.assertEquals;
 *
 * public class Example {
 *
 *   public static class AsyncUpperCaseService {
 *     private final ExecControl control;
 *
 *     public AsyncUpperCaseService(ExecControl control) {
 *       this.control = control;
 *     }
 *
 *     public Promise toUpper(final String lower) {
 *       return control.promise(f -> f.success(lower.toUpperCase()));
 *     }
 *   }
 *
 *   public static void main(String[] args) throws Exception {
 *     HandlingResult result = RequestFixture.requestFixture().handleChain(chain -> {
 *       ExecControl control = chain.getRegistry().get(ExecController.class).getControl();
 *       AsyncUpperCaseService service = new AsyncUpperCaseService(control);
 *       chain.get(ctx -> service.toUpper("foo").then(ctx::render));
 *     });
 *
 *     assertEquals("FOO", result.rendered(String.class));
 *   }
 * }
 * }
*

* Note: when using the Guice integration, the exec control is made available for injection. */ public interface ExecControl { /** * Provides the execution control bound to the current thread. *

* This method will fail when called outside of a Ratpack compute thread as it relies on {@link ExecController#require()}. * * @return the execution control bound to the current thread */ static ExecControl current() throws UnmanagedThreadException { return ExecController.require().getControl(); } /** * An exec control that binds to the thread's execution on demand. *

* Unlike the {@link #current()} method, this method can be called outside of a Ratpack managed thread. * However, the methods of the returned exec control can only be called on managed threads. * If a method is called while not on a managed thread, a {@link UnmanagedThreadException} will be thrown. * *

{@code
   * import ratpack.exec.ExecControl;
   * import ratpack.test.exec.ExecHarness;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   // Get an exec control on a non managed thread (i.e. the JVM main thread)
   *   public static ExecControl control = ExecControl.execControl();
   *
   *   public static void main(String... args) throws Exception {
   *     String value = ExecHarness.yieldSingle(e ->
   *       control.blocking(() -> "foo")
   *     ).getValue();
   *     assertEquals("foo", value);
   *   }
   * }
   * }
* * @return an exec control */ static ExecControl execControl() { return JustInTimeExecControl.INSTANCE; } Execution getExecution() throws UnmanagedThreadException; ExecController getController(); /** * Adds an interceptor that wraps the rest of the current execution segment and all future segments of this execution. *

* 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 {@link ExecInterceptor} for example use of an interceptor. * * @param execInterceptor the execution interceptor to add * @param continuation the rest of the code to be executed * @throws Exception any thrown by {@code continuation} * @see ExecInterceptor */ void addInterceptor(ExecInterceptor execInterceptor, Block continuation) throws Exception; /** * Performs a blocking operation on a separate thread, returning a promise for its value. *

* This method should be used to perform blocking IO, or to perform any operation that synchronously waits for something to happen. * The given {@code 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 {@link #promise}, and shares all of the same semantics with regard to * execution binding and execution-on-promise-subscription. * * @param blockingOperation the operation that blocks * @param the type of value created by the operation * @return a promise for the return value of the given blocking operation */ Promise blocking(Callable blockingOperation); default Operation blockingOperation(Block block) { return blocking(() -> { block.execute(); return null; }).operation(); } /** * Creates a promise for an asynchronously created value. *

* 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 {@link Fulfiller} that the action is given. * * @param action an action that invokes an asynchronous API, forwarding the result to the given fulfiller * @param the type of promised value * @return a promise for the asynchronously created value * @see Fulfiller */ Promise promise(Action> action); /** * Creates a promise for the given value. *

* This method can be used when a promise is called for, but the value is immediately available. * * @param item the promised value * @param the type of promised value * @return a promise for the given item */ default Promise promiseOf(T item) { return promise(f -> f.success(item)); } default Promise promiseFrom(Factory factory) { return promise(f -> f.success(factory.create())); } /** * Creates a failed promise with the given error. *

* This method can be used when a promise is called for, but the failure is immediately available. * * @param error the promise failure * @param the type of promised value * @return a failed promise */ default Promise failedPromise(Throwable error) { return promise(f -> f.error(error)); } /** * Executes the given promise producing factory, converting any thrown exception into a failed promise. *

* Can be used to wrap execution of promise returning functions that may themselves throw errors. *

{@code
   * import ratpack.exec.Promise;
   * import ratpack.exec.ExecControl;
   * import ratpack.exec.ExecResult;
   * import ratpack.test.exec.ExecHarness;
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static Promise someMethod() throws Exception {
   *     throw new Exception("bang!");
   *   }
   *
   *   public static void main(String... args) throws Exception {
   *     ExecResult result = ExecHarness.yieldSingle(e ->
   *       e.wrap(() -> someMethod())
   *     );
   *
   *     assertEquals("bang!", result.getThrowable().getMessage());
   *   }
   * }
   * }
* * @param factory the promise factory * @param the type of promised value * @return the promise returned by the factory, or a promise for the exception it threw */ default Promise wrap(Factory> factory) { try { return factory.create(); } catch (Exception e) { return failedPromise(e); } } default Operation operation(Block operation) { return new DefaultOperation(this.promise(f -> { operation.execute(); f.success(null); })); } default void nest(Block nested, Block then) { operation(nested).then(then); } default void nest(Block nested, Block then, Action onError) { operation(nested).onError(onError).then(then); } /** * Initiates a new {@link Execution execution}. *

* * * @return a new execution builder */ ExecBuilder fork(); /** * Process streams of data asynchronously with non-blocking back pressure. *

* 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. * * @param publisher the provider of a potentially unbounded number of sequenced elements, publishing them according to the demand * received from its Subscriber(s) * @return effectively the given publisher, emitting each “event” as an execution segment of the current execution * @param the type of streamed elements */ TransformablePublisher stream(Publisher publisher); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy