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.
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.http.Request
import ratpack.exec.ExecInterceptor
import ratpack.test.embed.LaunchConfigEmbeddedApplication
import static ratpack.groovy.Groovy.chain
import static ratpack.groovy.test.TestHttpClients.testHttpClient
import java.util.concurrent.atomic.AtomicLong
class Timer {
private final AtomicLong totalCompute = new AtomicLong()
private final AtomicLong totalBlocking = new AtomicLong()
private boolean blocking
private final ThreadLocal<Long> startedAt = new ThreadLocal() {
protected Long initialValue() { 0 }
}
void start(boolean blocking) {
this.blocking = blocking
startedAt.set(System.currentTimeMillis())
}
void stop() {
def startedAtTime = startedAt.get()
startedAt.remove()
def counter = blocking ? totalBlocking : totalCompute
counter.addAndGet(startedAtTime > 0 ? System.currentTimeMillis() - startedAtTime : 0)
}
long getBlockingTime() {
totalBlocking.get()
}
long getComputeTime() {
totalCompute.get()
}
}
class ProcessingTimingInterceptor implements ExecInterceptor {
final Request request
ProcessingTimingInterceptor(Request request) {
this.request = request
request.register(new Timer())
}
void intercept(ExecInterceptor.ExecType type, Runnable continuation) {
request.get(Timer).with {
start(type == ExecInterceptor.ExecType.BLOCKING)
continuation.run()
stop()
}
}
}
import static ratpack.groovy.test.embed.EmbeddedApplications.embeddedApp
def app = embeddedApp {
handlers {
handler {
addExecInterceptor(new ProcessingTimingInterceptor(request)) {
next()
}
}
handler {
sleep 100
next()
}
get {
sleep 100
blocking {
sleep 100
} then {
def timer = request.get(Timer)
timer.stop()
render "$timer.computeTime:$timer.blockingTime"
}
}
}
}
def client = testHttpClient(app)
try {
def times = client.getText().split(":")*.toInteger()
int computeTime = times[0]
int blockingTime = times[1]
assert computeTime >= 200
assert blockingTime >= 100
} finally {
app.close()
}
| Modifier and Type | Interface and Description |
|---|---|
static class |
ExecInterceptor.ExecType
The execution type (i.e.
|
| Modifier and Type | Method and Description |
|---|---|
void |
intercept(ExecInterceptor.ExecType execType,
Runnable continuation)
Intercepts the “rest” of the execution on the current thread.
|
void intercept(ExecInterceptor.ExecType execType, Runnable continuation)
The given Runnable 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.
All exceptions thrown by this method will be ignored.
execType - indicates whether this is a compute (e.g. request handling) or blocking IO threadcontinuation - the “rest” of the execution