Search code examples
javaspringspring-webfluxreactor

Execute some action when client disconnects from hot stream


I made a simple spring boot application. I have a REST endpoint that returns a hot stream of the current time.

@RestController
public class NowResource {

    @GetMapping(value = "/now", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> now() {
        return Flux.interval(Duration.ofSeconds(1))
            .flatMap(t -> Flux.just(Instant.now().toString()));
    }

}

When I call http://localhost:8080/now I get a stream of data that looks like:

data:2018-04-03T13:20:38.313222100Z

data:2018-04-03T13:20:39.311493500Z

data:2018-04-03T13:20:40.310878800Z

...

When I disconnect from the stream (close the browser tab) an IOException is thrown, caught and the stacktrace printed.

java.io.IOException: An established connection was aborted by the software in your host machine

...

I have tried catching it but it is already caught and does not make it back to my method.

I tried adding doOnTerminate(), doOnError() etc. to the Flux but does not seem to have any effect, I'm guessing the actual event is of a different type.

Can I somehow get access to this exception to handle it differently than just printing it? (I would like to avoid have the 200+ lines of output in the log and just print "DONE" instead.)

EDIT: My solution based on the answer from Tomas Pinos

I ended up taking that approach, the difference being that I moved it to a new class and this way it handles all exceptions of this type from all controllers.

@Slf4j
@ControllerAdvice
class IOExceptionHandler implements WebExceptionHandler {

    @ExceptionHandler(IOException.class)
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        return Mono.just(ex.getMessage())
            .doOnNext(
                msg -> log.warn("IOException occurred: {}.", msg)
            )
            .then();
    }

}

Solution

  • The exception is related to HTTP connection handling between the browser ant the controller (simply put).

    It can be handled in controller's @ExceptionHandler method (or in a @ControllerAdvice class, if you want to apply the same exception handling across more controllers).

    For example:

    @RestController
    public class NowResource {
        ...
    
        @ExceptionHandler(IOException.class)
        public void handleException(IOException e) {
            log.warn("IOException occurred: {}", e.getMessage());
        }
    }