A short story about resource handling

Michał Matłoka
SoftwareMill Tech Blog
4 min readDec 17, 2018

--

Reading a file, handling a stream of data, theoretically those are not ultimately complex operations, but they require some special attention. Every stream, every resource needs to be closed. Closed to free descriptors, memory, or maybe just a TCP port. How to design the code, so that a resource will always be properly closed, even when the programmer forgets?

Going back in time

For start, let’s remind ourselves how resource handling looked like in the C-times. First, you start with opening the file:

#include <stdio.h>
// ...
FILE * file = fopen("test.txt", "r");

Returned value, file, allows to control the stream of data. Usually you would perform some fread ‘s or getc to retrieve the file content, and end the whole operation with:

fclose(file);

Perfect solution? Not really, you have to remember to close the file manually, even when some intermediate operations will return errors.

Java Virtual Machine?

Does it get better in Java? Well, depends on a version. In older ones you still have to manually close resources.

BufferedReader br = new BufferedReader(new FileReader("test.txt"));try {
String line = br.readLine();
// ...
} finally {
br.close();
}

However, you get the try-catch-finally statements guaranteeing that finally block will be automatically executed even when exception is thrown. But, from Java 7 you can leverage the “try-with-resources” construction:

try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
String line = br.readLine();
// ...
}

Better? Definitely! Java 7 introduced an AutoCloseable interface. Every class implementing it can be used with “try-with-resources”. You don’t need to manually close your file, it will get auto closed after the processing block ends.

Side note: From Java 7 you can read files easier e.g. just by using Files.readAllLines(...)

Scala

How does it look in Scala, well…

val source = scala.io.Source.fromFile("test.txt")
val lines = try source.mkString finally source.close()

You can use other classes instead of the BufferedReader, but how about the “try-with-resources” ? Unfortunately there is no built-in, ready to use equivalent. You can implement it on your own, however additional project was created to solve this issue, the scala-arm(Scala Automatic Resource Management).

import resource._val mr = managed(new java.io.FileInputStream("test.txt")
for(r <- mr ) {
// do some reading...
}

scala-arm APIs produce the ManagedResource objects. After the for block ends, the resource will get closed.

Advantages? Resource gets auto closed and what is more, your API can return object, which after processing (foreach or map) will close automatically. API user does not have to worry about that!

FP world

Functional Programming world created the Bracket pattern already many years ago. It’s intended to use with resources which require acquiring and releasing actions. Let’s take a look how it looks in Cats-effect:

IO(new BufferedReader(new FileReader(file))).bracket {
in =>
IO(in.readLine())
} { in =>
IO(in.close())
}

The first bracket block is responsible for resource usage, and the second one for closing. Advantages? It’s pure of course ;) It works with cats IO Monad (in short: type designed to handle operations which may include unpure side effects) and the release function is guaranteed to run even when errors occur. Cats library offers however, one more class for resource handling, named… Resource.

import cats.effect._  val acquire = IO { // it touches file system so let's wrap it in IO  
scala.io.Source.fromString("Hello world")
}
Resource.fromAutoCloseable(acquire)
.use(source => IO(println(source.mkString))) //use ~ map on resource
.unsafeRunSync() // it is lazily evaluated, you need to run

Resource supports the java AutoCloseable which is quite handy. You don’t need to define the release/close methods, only the acquirement and the actual usage. After processing is finished, the underlying resource will get closed.

Summary

Resource handling evolved differently in various languages. For sure the AutoCloseable interface added in Java 7 is a worthy addition. However due to the backward compatibility with Java 6 some libraries are still not using it. On the other hand it’s also good to implement approach, in which developer leveraging your API does not have to worry about resource handling — the approaches like the Bracket pattern looks quite handy. It’s worth to notice that Bracket is not cats-only, and does not originate from cats— you can find it in scalaz, FS2 and of course Haskell. There is no single answer how you should treat your resources, as usually - it depends. But remember about users of your APIs.

Looking for Scala and Java Experts?

Contact us!

We will make technology work for your business. See the projects we have successfully delivered.

--

--

IT Dev (Scala), Consultant, Speaker & Trainer | #ApacheKafka #KafkaStreams #ApacheCassandra #Lightbend Certified | Open Source Contributor | @mmatloka