1

I want a Closeable resource to auto-close itself, but at same time I want to be able to handle any possible exception.

Consider the following piece of code.

val opcPackage: OPCPackage

URI.create(document.fileUrl).toURL().openStream().use {
    opcPackage = OPCPackage.open(it)  // it = InputStream!
}

In case of exception, the InputStream will auto-close itself thanks to the use block. However, the execution flow will be interrupted.

I still want to handle such exception and do something with my program (e.g. log, send a signal ecc...)

The following does not compile:

kotlin.runCatching {
    URI.create(document.fileUrl).toURL().openStream().use {
        opcPackage = OPCPackage.open(it)
    }
}.onFailure { 
    // e.g. log and exit
}

This one kind of defeats the whole purpose of use since I have to manually close the Closeable.

URI.create(document.fileUrl).toURL().openStream().use {
    try {
        opcPackage = OPCPackage.open(it)
    } catch (ex: Exception) {
        // ...
        it.close()
        return internalServerError()
    }
}

At this point, I might as well simply use

val inputStream = URI.create(document.fileUrl).toURL().openStream()

try {
    opcPackage = OPCPackage.open(inputStream)
    inputStream.close()
} catch (ex: Exception) {
    inputStream.close()
    return internalServerError()
}

but I'm trying to use kotlin features properly.

What's the best snippet for this use-case?

9
  • 3
    Who says if you handle the exception in use you’ve to close the resource manually? Commented Mar 15, 2021 at 10:47
  • What compiler error do you get with kotlin.runCatching? Commented Mar 15, 2021 at 10:52
  • Why does the runCatching variant not compile? What error are you getting? It should compile. Commented Mar 15, 2021 at 10:52
  • @AbhijitSarkar yeah fair, it also hit me a few seconds ago. However, even by removing the manual-close, the code doesn't seem quite right. Joffrey's answer below (first snippet) looks more appropriate. Commented Mar 15, 2021 at 10:54
  • Please provide a bit more info about the enclosing function, because it's hard to see what you're doing with this variable mutation and the return statement in the middle of those expressions. A more idiomatic Kotlin approach would be to use expressions to initialize your variable instead Commented Mar 15, 2021 at 10:55

1 Answer 1

2

use() allows you to deal with the resource management in one expression, even in case of exception. You can still try-catch around it:

val opcPackage = try {
    URI.create(document.fileUrl).toURL().openStream().use {
        OPCPackage.open(it)
    }
} catch (e: SomeException) { 
    // e.g. log and exit
    // no need to manually close here
    // you do need to break control flow here or provide a default OPCPackage
}

Your variant with runCatching is actually fine too, you just may need to use it as an expression to initialize your variable:

val opcPackageResult = runCatching {
    URI.create(document.fileUrl).toURL().openStream().use {
       OPCPackage.open(it)
    }
}.onFailure { 
    // e.g. log and exit
}

// you still need to handle the Result type later

But even if you catch the exception inside, the end of the use() block will still close the resource for you:

val opcPackage = URI.create(document.fileUrl).toURL().openStream().use {
    try {
        OPCPackage.open(it)
    } catch (ex: Exception) {
        return internalServerError()
    }
}

Or you can use a plain try-catch-finally as well:

val inputStream = URI.create(document.fileUrl).toURL().openStream()

val opcPackage = try {
    OPCPackage.open(inputStream)
} catch (ex: Exception) {
    return internalServerError()
} finally {
    inputStream.close()
}
Sign up to request clarification or add additional context in comments.

6 Comments

I think your first snippet is what I'm looking for. It combines assignment, resource auto-closing and exception handling all in a couple of lines
I added a variant with runCatching as well, if you really want to use the functional approach. but I don't see a huge improvement over the try-catch here to be honest :)
@MarkoPacak also, whatever your choice, I would personally extract any of these expression snippets into a function in real life. It's usually ugly to leave that low-level handling in the middle of higher-level code, but what you extract depends on how you want to deal with the return.
As @Joffrey said usage of monadic/functional style runCatching could be overkill if code is designed in a classic try/catch style. But it has many advantages. Btw, take note that the runCatching doesn't return OPCPackage type, it returns a Result which contains an OPCPackage.
@LaksithaRanasingha good points. I fixed the runCatching example to avoid the confusion about the return value.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.