public interface ExecInterceptor
The interception methods wrap the rest of the execution.
They receive a continuation (as a Runnable
) that must be called in order for processing to proceed.
Request handling execution can be intercepted by the ExecControl.addInterceptor(ExecInterceptor, ratpack.func.NoArgAction)
method.
import ratpack.exec.ExecInterceptor;
import ratpack.exec.Execution;
import ratpack.func.NoArgAction;
import ratpack.http.Request;
import ratpack.test.handling.RequestFixture;
import ratpack.test.handling.HandlingResult;
import java.util.concurrent.atomic.AtomicLong;
import static java.lang.Thread.sleep;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class Example {
public static class Timer {
private final AtomicLong totalCompute = new AtomicLong();
private final AtomicLong totalBlocking = new AtomicLong();
private boolean blocking;
private final ThreadLocal<Long> startedAt = ThreadLocal.withInitial(() -> 0l);
public void start(boolean blocking) {
this.blocking = blocking;
startedAt.set(System.currentTimeMillis());
}
public void stop() {
long startedAtTime = startedAt.get();
startedAt.remove();
AtomicLong counter = blocking ? totalBlocking : totalCompute;
counter.addAndGet(startedAtTime > 0 ? System.currentTimeMillis() - startedAtTime : 0);
}
public long getBlockingTime() {
return totalBlocking.get();
}
public long getComputeTime() {
return totalCompute.get();
}
}
public static class ProcessingTimingInterceptor implements ExecInterceptor {
private final Request request;
public ProcessingTimingInterceptor(Request request) {
this.request = request;
request.add(new Timer());
}
public void intercept(Execution execution, ExecInterceptor.ExecType type, NoArgAction continuation) throws Exception {
Timer timer = request.get(Timer.class);
timer.start(type.equals(ExecInterceptor.ExecType.BLOCKING));
try {
continuation.execute();
} finally {
timer.stop();
}
}
}
public static void main(String[] args) throws Exception {
HandlingResult result = RequestFixture.requestFixture().handleChain(chain -> chain
.handler(context ->
context.addInterceptor(new ProcessingTimingInterceptor(context.getRequest()), context::next)
)
.handler(context -> {
sleep(100);
context.blocking(() -> {
sleep(100);
return "foo";
}).then(string -> {
sleep(100);
context.render(string);
});
})
);
assertEquals("foo", result.rendered(String.class));
Timer timer = result.getRequestRegistry().get(Timer.class);
assertTrue(timer.getBlockingTime() >= 100);
assertTrue(timer.getComputeTime() >= 200);
}
}
For other types of executions (e.g. background jobs), the interceptor can be registered via ExecControl.addInterceptor(ExecInterceptor, ratpack.func.NoArgAction)
.Modifier and Type | Interface and Description |
---|---|
static class |
ExecInterceptor.ExecType
The execution type (i.e.
|
Modifier and Type | Method and Description |
---|---|
void |
intercept(Execution execution,
ExecInterceptor.ExecType execType,
NoArgAction continuation)
Intercepts the “rest” of the execution on the current thread.
|
void intercept(Execution execution, ExecInterceptor.ExecType execType, NoArgAction continuation) throws Exception
The given action argument represents the rest of the execution to occur on this thread. This does not necessarily mean the rest of the execution until the work (e.g. responding to a request) is complete. Execution may involve multiple parallel (but not concurrent) threads of execution because of blocking IO or asynchronous APIs.
execution
- the execution who's segment is being interceptedexecType
- indicates whether this is a compute (e.g. request handling) segment or blocking segmentcontinuation
- the “rest” of the executionException
- any