Reactive Patterns: Try-Catch-Finally

Photo by Egor Myznik on Unsplash

In today’s blog post, we cover a topic that at first seems pretty trivial to programmers outside of the world of Reactive Programming. I’m talking about “try-catch-finally”.

Most programming languages offer keywords for this purpose, so it is very easy to implement and use. In a try block we perform some operation that might fail and throws an exception. To prevent the application from exiting with an unexpected error at this point, we can safe-guard the operation with a try block in order to handle the error appropriately. In theory, we can secure any operation in a try block. However, in practice we often work with some resources that have to be treated at the end of processing, like closing input streams or files we accessed. In a catch block we catch exceptions that were thrown in the try block and handle them appropriately. And lastly, in an optional finally block we do some cleanup work, like closing the resource we accessed in the try block. Pretty simple so far.

However, in the reactive world we cannot just throw an exception in case of a failure, catch it somewhere and maybe clean up afterwards, whether the execution failed or not. Any data and so any exceptions must follow the downstream processing in the reactive chain. Breaking out of the reactive chain means breaking out of any benefit that Reactive Programming delivers. Therefore, it is important to be aware of the corresponding representation of errors and patterns to handle them in the reactive world.

I will focus on the functionality provided by Mono.using() from the framework Project Reactor. Therefore, let’s have a look into the corresponding JavaDoc:

Uses a resource, generated by a supplier for each individual Subscriber, while streaming the value from a Mono derived from the same resource and makes sure the resource is released if the sequence terminates or the Subscriber cancels.

The attentive reader may have realised that these are more or less the individual steps of a try-catch-finally (or actually a try-with-resources), only transferred to the reactive world.

https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#using-java.util.concurrent.Callable-java.util.function.Function-java.util.function.Consumer-

A supplier generates a resource and some processing is performed on this resource by applying a supplier function ( → try). This resource is then cleaned up by a consumer function for the initial resource ( → finally). The only thing we don’t find here right away is catching exceptions.

However, since exceptions are converted into error signals, we can also implement the “catch block” by reacting on those signals using appropriate methods in the reactive chain.

In a recent project, we wanted to load log files from a server and then process them. Later, those log files should be cleaned up, so that no file handles remain open and no temporary files are left on the file system. More precisely, we wanted to download execution logs from an Ansible Tower instance for our executed Ansible jobs. In this context, I had the opportunity to use the reactive “try-catch-finally” pattern. The workflow involves the following steps:

  • First, a temporary file is created. This is our resource.
  • Then, we download the logs from Ansible Tower, write it into the temporary file, and process the result.
  • At the end, we want to delete the temporary file in any case.
  • If anything unexpected happens, we want to handle the exception and rethrow it as an exception of our own.

This can be implemented as follows:

The whole part from line 2 to 23 is the “try-finally”.

Let’s take a closer look at the individual components of the “generateJobLog” method, beginning with the resource supplier:

First, the temporary file is generated by passing a resource supplier function to the method. When this call succeeded, the result will be passed to the source supplier function.

The call to the non-blocking WebClient delivers us a stream of DataBuffers, Spring’s abstraction over byte buffers. The log file is downloaded chunk by chunk, so that the memory pressure is minimized. However, now we end up with a Flux<DataBuffer>, which we need to handle for further processing.

DataBufferUtils provide some really convenient utility methods to handle the data buffers without hassle. We use DataBufferUtils.write here, which takes a Flux of data buffers and chunk-wise writes it to the given file. Neat!

By flat-mapping the result of the DataBufferUtils operation, we can be sure the file exists and contains the job log. Now, we can process the log data.

After handling the data chunks and further processing the populated data, the resource is passed to the resource cleanup consumer, which deletes the file if it exists.

Any occurring exception in the Mono.using part will be properly propagated as error signal down the reactive chain. Therefore, the “catch” part of the “try-catch-finally” is emulated by using onErrorMap.

https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#onErrorMap-java.util.function.Function-

In this variant, onErrorMap takes a function that consumes a thrown exception and maps it to some other exception of choice.

There are plenty of other error handling methods available. Feel free to browse through the JavaDoc for Mono or Flux.

For comparison, I wanted to show you the implementation with the blocking RestTemplate (instead of WebClient) and via “try-catch-finally” as well:

The structure is roughly the same. We will need two try blocks to ensure cleaning up the temporary file at the very end of processing. Instead of Flux or data buffers, we work with the response from the RestTemplate directly by defining a ResponseExtractor in line 11. But beyond that, the differences are relatively small. Except, of course, that the reactive variant is non-blocking and this variant here is blocking.

As you have seen, dealing with resources in a reactive environment is not that difficult. Just try it out for yourself!

Thanks for reading! Feel free to comment or message me, when you have questions or suggestions. You might be interested in the other posts published in the Digital Frontiers blog, announced on our Twitter account.

--

--

--

Dies ist das Blog der Digital Frontiers GmbH & Co. KG (http://www.digitalfrontiers.de). Hier veröffentlichen wir zu Themen, die uns interessieren und bewegen.

Recommended from Medium

Internship Experience Final year at Drishte

[RECAP] LIVE AMA — MONIWAR x UNIX GAMING

Laravel Google ReCaptcha 🚀

How we leveraged a forced data migration into a well-oiled and efficient reports delivery machine

Exquisite Web Development Technologies

Leetcode 2121: Intervals Between Identical Elements

FLUTTER-The Future of Mobile App Development

【iOS APP-swift自學#28】如何重複使用已存在的code_附圖文步驟

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Benedikt Jerat

Benedikt Jerat

More from Medium

Debugging basics in Project Reactor

Fast Reactor Tests With Virtual Time

Converting between java.util.UUID and byte[]

Plain Kafka Consumer in Play Framework