public interface ProcessingInterceptor
Interceptors are automatically detected and initialised when they are added to the context registry
(via Context.next(ratpack.registry.Registry)
or Context.insert(ratpack.registry.Registry, Handler...)
).
When they are detected, their init(Context)
method is called with the context they were registered with.
Immediately after, their intercept(ratpack.handling.ProcessingInterceptor.Type, Context, Runnable)
method will be called
(with a type
of FOREGROUND
).
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.
The following example (in Groovy) demonstrates using a processing interceptor to time processing.
import ratpack.launch.LaunchConfig import ratpack.launch.LaunchConfigBuilder import ratpack.handling.Context import ratpack.handling.ProcessingInterceptor import ratpack.test.embed.LaunchConfigEmbeddedApplication import static ratpack.registry.Registries.registry import static ratpack.groovy.Groovy.chain import static ratpack.groovy.test.TestHttpClients.testHttpClient import java.util.concurrent.atomic.AtomicLong class Timer { private final AtomicLong totalForeground = new AtomicLong() private final AtomicLong totalBackground = new AtomicLong() private boolean background private final ThreadLocal<Long> startedAt = new ThreadLocal() { protected Long initialValue() { 0 } } void start(boolean background) { this.background = background startedAt.set(System.currentTimeMillis()) } void stop() { def startedAtTime = startedAt.get() startedAt.remove() def counter = background ? totalBackground : totalForeground counter.addAndGet(startedAtTime > 0 ? System.currentTimeMillis() - startedAtTime : 0) } long getBackgroundTime() { totalBackground.get() } long getForegroundTime() { totalForeground.get() } } class ProcessingTimingInterceptor implements ProcessingInterceptor { void init(Context context) { context.request.register(new Timer()) } void intercept(ProcessingInterceptor.Type type, Context context, Runnable continuation) { context.request.get(Timer).with { start(type == ProcessingInterceptor.Type.BACKGROUND) continuation.run() stop() } } } def interceptor = new ProcessingTimingInterceptor() def application = new LaunchConfigEmbeddedApplication() { protected LaunchConfig createLaunchConfig() { LaunchConfigBuilder. baseDir(new File("some/path")). build { LaunchConfig config -> chain(config) { handler { next(registry(interceptor)) } handler { sleep 100 next() } get { sleep 100 background { sleep 100 } then { def timer = request.get(Timer) timer.stop() render "$timer.foregroundTime:$timer.backgroundTime" } } } } } } def client = testHttpClient(application) def times = client.getText().split(":")*.toInteger() int foregroundTime = times[0] int backgroundTime = times[1] assert foregroundTime >= 200 assert backgroundTime >= 100 application.close()
Modifier and Type | Interface and Description |
---|---|
static class |
ProcessingInterceptor.Type
The processing type (i.e.
|
Modifier and Type | Method and Description |
---|---|
void |
init(Context context)
Called once per request when the interceptor is registered with the context.
|
void |
intercept(ProcessingInterceptor.Type type,
Context context,
Runnable continuation)
Wraps the “rest” of the processing on the current thread.
|
void init(Context context)
A typical thing to do in this method is register some kind of object with the request registry
that is retrieved later in intercept(ratpack.handling.ProcessingInterceptor.Type, Context, Runnable)
.
The intercept(ratpack.handling.ProcessingInterceptor.Type, Context, Runnable)
method will be called immediately
after this method so the rest of the processing can be intercepted.
All exceptions thrown by this method will be forwarded to ReadOnlyContext.error(Exception)
and will effectively halt processing.
context
- The context at the time when the interceptor is registered.void intercept(ProcessingInterceptor.Type type, Context context, Runnable continuation)
The given Runnable
argument represents the rest of the processing to occur on this thread.
This does not necessarily mean the rest of the processing until the rest of the response is determined or sent.
Request processing may involve multiple parallel (but not concurrent) threads of execution because of Background
processing.
Moreover, the continuation includes all of the code that is executed after the response is determined (which ideally is just unravelling the call stack).
As such, when intercepting foreground execution that generates a response, the continuation may be returning after the response has been sent.
All exceptions thrown by this method will be ignored.
type
- indicates whether this is a foreground (i.e. request handling) or background (i.e. blocking operation) threadcontext
- the context at the time of interceptioncontinuation
- the “rest” of the processing