What you don’t want to do is block the underlying Netty event loop within your filter, instead you want the filter to proceed with execution once any I/O is complete.

    As an example, consider the following example that uses RxJava to compose an I/O operation:

    A TraceService Example using RxJava

    A TraceService Example using RxJava

    1. import io.micronaut.http.HttpRequest
    2. import io.reactivex.Flowable
    3. import io.reactivex.schedulers.Schedulers
    4. import org.slf4j.Logger
    5. import org.slf4j.LoggerFactory
    6. import javax.inject.Singleton
    7. @Singleton
    8. class TraceService {
    9. private static final Logger LOG = LoggerFactory.getLogger(TraceService.class)
    10. Flowable<Boolean> trace(HttpRequest<?> request) {
    11. Flowable.fromCallable({ -> (1)
    12. if (LOG.isDebugEnabled()) {
    13. LOG.debug("Tracing request: " + request.getUri())
    14. }
    15. // trace logic here, potentially performing I/O (2)
    16. return true
    17. }).subscribeOn(Schedulers.io()) (3)
    18. }
    19. }
    1. import io.micronaut.http.HttpRequest
    2. import io.reactivex.Flowable
    3. import io.reactivex.schedulers.Schedulers
    4. import org.slf4j.LoggerFactory
    5. import javax.inject.Singleton
    6. @Singleton
    7. private val LOG = LoggerFactory.getLogger(TraceService::class.java)
    8. internal fun trace(request: HttpRequest<*>): Flowable<Boolean> {
    9. return Flowable.fromCallable {
    10. (1)
    11. if (LOG.isDebugEnabled) {
    12. LOG.debug("Tracing request: " + request.uri)
    13. }
    14. // trace logic here, potentially performing I/O (2)
    15. true
    16. }.subscribeOn(Schedulers.io()) (3)
    17. }
    18. }

    You can then inject this implementation into your filter definition:

    An Example HttpServerFilter

    An Example HttpServerFilter

    1. import io.micronaut.http.HttpRequest
    2. import io.micronaut.http.MutableHttpResponse
    3. import io.micronaut.http.annotation.Filter
    4. import io.micronaut.http.filter.HttpServerFilter
    5. import io.micronaut.http.filter.ServerFilterChain
    6. import org.reactivestreams.Publisher
    7. @Filter("/hello/**") (1)
    8. class TraceFilter implements HttpServerFilter { (2)
    9. private final TraceService traceService
    10. TraceFilter(TraceService traceService) { (3)
    11. this.traceService = traceService
    12. }
    13. }

    An Example HttpServerFilter

    1. import io.micronaut.http.HttpRequest
    2. import io.micronaut.http.MutableHttpResponse
    3. import io.micronaut.http.filter.HttpServerFilter
    4. import io.micronaut.http.filter.ServerFilterChain
    5. import org.reactivestreams.Publisher
    6. @Filter("/hello/**") (1)
    7. class TraceFilter((2)
    8. private val traceService: TraceService)(3)
    9. : HttpServerFilter {
    10. }

    The doFilter implementation

    The doFilter implementation

    1. @Override
    2. Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
    3. traceService.trace(request) (1)
    4. .switchMap({ aBoolean -> chain.proceed(request) }) (2)
    5. .doOnNext({ res -> (3)
    6. res.getHeaders().add("X-Trace-Enabled", "true")
    7. })
    8. }

    The doFilter implementation

    1. override fun doFilter(request: HttpRequest<*>, chain: ServerFilterChain): Publisher<MutableHttpResponse<*>> {
    2. return traceService.trace(request) (1)
    3. .switchMap { aBoolean -> chain.proceed(request) } (2)
    4. .doOnNext { res ->
    5. (3)
    6. res.headers.add("X-Trace-Enabled", "true")
    7. }

    The previous example demonstrates some key concepts such as executing logic in a non-blocking matter before proceeding with the request and modifying the outgoing response.