Customize error message using Kotlin's use instead of try catch - kotlin

I'm still learning Kotlin and I just learned about the "use" and how it is a replacement for a try, catch and finally block.
However I am curious if it is possible to customize it's exception handling for example:
var connection: Connection? = null
try {
connection = dataSource.connection
connection.prepareStatement(query).execute()
} catch (e: SQLException) {
logger.log("Specific error for that query")
e.printStackTrace()
} finally {
if (connection != null && !connection.isClosed) {
connection.close()
}
}
That code is my current one, I have a specific error I would like to display on the catch, would that be possible using use?
This is my current use code:
dataSource.connection.use { connection ->
connection.prepareStatement(query).execute()
}

As commented by #Tenfour04, and from the documentation
[use] Executes the given block function on this resource and then closes it down correctly whether an exception is thrown or not.
In particular it is implemented like this:
public inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
this.closeFinally(exception)
}
}
That piece of code should look familiar if you're a Java developer, but basically it executes block passing this (i.e. the receiver object) as an argument to your block of code. At the end it closes the AutoCloseable resource. If at any point an exception is thrown (either inside block or while closing the resource), that exception is thrown back to the caller, i.e. your code.
As an edge case you could have 2 exceptions, one when executing block and one when closing the resource. This is handled by closeFinally (whose source is available in the same file linked above) and the exception thrown while closing the resource is added as a suppressed exception to the one thrown from block – that's because only up to 1 exception can be thrown by a method, so they had to choose which one to throw. The same actually applies to the try-with-resources statement in Java.

Related

single() and first() terminal operators when producer emits many values

I need to collect only the first value from two emitted by flow.
I have a function that returns flow:
fun myFlow = flow {
try {
emit(localDataSource.fetchData())
} catch(e: Exception) {
// just skip this error
}
emit(remoteDataSource.fetchData(1000, 0))
}
In one special case I need only first emitted value, doesn't matter is it from local cache or remote source.
I tried this one:
fun getRandomFavoriteItem() = myFlow.first().filter { it.score > 7 }.randomOrNull()
But first() invocation always throws
java.lang.IllegalStateException: Flow exception transparency is violated:
Previous 'emit' call has thrown exception kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed, but then emission attempt of value.
What I've tried:
single() -
java.lang.IllegalArgumentException: Flow has more than one element
take(1).first() -
java.lang.IllegalStateException: Flow exception transparency is violated:
Previous 'emit' call has thrown exception kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed, but then emission attempt of value
Catch error but it doesn't stop here:
myFlow.catch { e ->
if (e !is IllegalArgumentException) {
throw e
}
}.first().filter { it.score > 7 }.randomOrNull()
My questions are:
What is the point of usage first() if it doesn't work in case of more than 1 emitted values? If I would know that my flow produces only one value I could just use any other terminal operator.
How to avoid those errors and how to collect only first value without adding repeated code?
This isn't an error in first(). It's an error in your flow. You are not permitted to swallow all exceptions in a Flow in the way you have.
Some varying approaches may differ in whether they detect that error, but what you must fix is how you "just skip" all exceptions. Consider catching only the specific exceptions you're concerned about, or at least making sure to catch and rethrow CancellationException or its subclasses.
Lous Wasserman already found the problem, here some more details.
As mentioned in the error message you're also catching the AbortFlowException.
java.lang.IllegalStateException: Flow exception transparency is
violated: Previous 'emit' call has thrown exception
kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted,
no more elements needed, but then emission attempt of value.
You're bascically catching an exception which interferes with the way flows work. The problem is not about the first function.
Since AbortFlowException is internal you cannot access it, but you can access its superclass CancellationException. You need to modify your catch block like this:
try {
emit(localDataSource.fetchData())
} catch (e: Exception) {
if(e is CancellationException) {
throw e
}
}
Now first will work in the way you expect it to.
Edit:
A better solution would be to handle the exception within fetchData (you might return null in case one was thrown). This way you don't get in the way of the flow mechanics.
If that is not possible, you could create a wrapper function which takes care of the exception handling.

Handling checked exception in Mono flow

Not sure how to handle checked exception in the Mono flow.
return Mono.when(monoPubs)
.zipWhen((monos) -> repository.findById(...))
.map((tuple) -> tuple.getT2())
.zipWhen((org) -> createMap(org))
.map((tuple) -> tuple.getT2())
.zipWhen((map) -> emailService.sendEmail(...))
.flatMap(response -> {
return Mono.just(userId);
});
Here, the sendEmail method is declared with throws Exception.
public Mono<Boolean> sendEmail(...)
throws MessagingException, IOException
So, How to handle this checked exception in the zipWhen flow.
Also, How to handle
.zipWhen((map) -> emailService.sendEmail(...))
if the method returns void.
You need to review implementation of the sendEmail. You cannot throw checked exceptions from the publisher and need to wrap any checked exception into an unchecked exception.
The Exceptions class provides a propagate method that could be used to wrap any checked exception into an unchecked exception.
try {
...
}
catch (SomeCheckedException e) {
throw Exceptions.propagate(e);
}
As an alternative, you could use lombok #SneakyThrows to wrap non-reactive method.
Exceptions are thrown from Mono method, you can use onError* methods to handle the exception the way you like
var result = Mono.just("test")
.zipWhen((map) -> sendEmail())
.onErrorMap(SendEmailException.class, e -> new RuntimeException(e.getMessage()))
.flatMap(Mono::just);
Also it is not very clear from your post that if sendEmail takes param or not, sendEmail is not taking any input, I would just use doOnNext as it is a void method.

Kotlin: handling exception when defining a val

I'm working on a kotlin web backend and have something like this:
try {
val uuid = UUID.fromString(someString)
} catch (e: IllegalArgumentException) {
throw BadRequestException("invalid UUID")
}
doSomething(uuid)
The code above doesn't compile since uuid is unresolved outside the try block.
Alternatives I can imagine are:
move doSomething(uuid) inside the try block, but I'd rather avoid that so I don't accidentally catch some other potential IllegalArgumentException thrown by doSomething (if that happens for whatever reason I want things to fail and get a 500 in my logs so I can investigate)
use a nullable var instead and initialize it to null but that seems a bit ugly?
This throw BadRequestException pattern is working well otherwise so I don't want to change the return type of the method or something like that in order to avoid throwing.
Is there a better / more elegant / recommended pattern for this in Kotlin?
In Kotlin, try/catch can be used as an expression. Branches that throw don't affect the resolved type. So you can write:
val uuid = try {
UUID.fromString(someString)
} catch (e: IllegalArgumentException) {
throw BadRequestException("invalid UUID")
}

What's the point of the use function in Kotlin

I'm trying to use the inline function use with a FileInputStream instead of the classic try/catch IOException so that
try {
val is = FileInputStream(file)
// file handling...
}
catch (e: IOException) {
e.printStackTrace()
}
becomes
FileInputStream(file).use { fis ->
// do stuff with file
}
My question is, why use the function use if it stills throws exception? Do I have to wrap use in a try/catch? This seems ridiculous.
From Kotlin documentation:
Executes the given block function on this resource and then closes it
down correctly whether an exception is thrown or not.
When you use an object that implements the Closeable interface, you need to call the close() method when you are done with it, so it releases any system resources associated with the object.
You need to be careful and close it even when an exception is thrown. In this kind of situation that is error prone, cause you might not know or forget to handle it properly, it is better to automate this pattern. That's exactly what the use function does.
Your try-catch does not close the resource so you are comparing apples to oranges. If you close the resource in finally block:
val is = FileInputStream(file)
try {
...
}
catch (e: IOException) {
...
}
finally {
is.close()
}
is definitely more verbose than use which handles closing the resource.

CATCH and throw in custom exception

Should 'CATCH' be called strictly after 'throw'?
Example 1:
say 'Hello World!';
class E is Exception { method message() { "Just stop already!" } }
CATCH {
when E {
.resume;
}
}
E.new.throw;
Error:
Cannot find method 'sink': no method cache and no .^find_method in
block at /tmp/739536251/main.pl6 line 11
Example 2:
say 'Hello World!';
class E is Exception { method message() { "Just stop already!" } }
E.new.throw;
CATCH {
when E {
.resume;
}
}
No error
It's an already filed .resume bug.
The error message isn't the most awesome P6 has ever produced but it isn't technically LTA because it is descriptive and related to the error (caused by the bug).
CATCH and throw in custom exception
I think it's just a .resume bug rather than being about custom exceptions.
Should 'CATCH' be called strictly after 'throw'?
No, that's not the issue. (That said, putting it after the .throw just so happens to avoid this bug; I'll return to that later.)
In the code that goes boom, you throw an exception, then .resume in response to it. Per the doc .resume:
Resumes control flow where .throw left it
Which in this case means where the arrow points:
E.new.throw ;
🡅
Now, consider this program:
42;
If you run that program you'll see:
Useless use of constant integer 42 in sink context (line 1)
That's because Raku applies "sink context" rules when deciding what to do at the end of a statement. Applying sink context entails calling .sink on the value produced by the statement. And for 42 the .sink method generates the "useless" warning.
But what's the value of a .resumed thrown exception?
class E is Exception {}
CATCH { when E { .resume } }
say E.new.throw.^name; # BOOTException
E.new.throw.sink; # Cannot find method 'sink':
# no method cache and no .^find_method
It turns out it's a BOOTException object which isn't a high level Raku object but instead a low level VM object, one that doesn't have a .sink method (and also stymies P6's fallback methods for finding a method, hence the "I tried everything" error message).
So why does putting the CATCH block after the throw make a difference?
It seems the bug only occurs if the throw statement is the last statement. This works fine, just displaying 42:
class E is Exception {}
CATCH { when E { .resume } }
E.new.throw;
say 42;
As you presumably know, Raku treats the last statement of a block specially. Perhaps this bug is related to that.
CATCH must be in same block.
Problem in the first example is that no E but another exceptions is thrown. Try
class E is Exception { method message() { "Just stop already!" } };
CATCH {
when E {
.resume;
}
default { say .perl }
}
E.new.throw;
you could change when block
class E is Exception { method message() { "Just stop already!" } };
CATCH {
when E {
say .message;
}
}
E.new.throw;
or definition of the class E, e.g.
class E is Exception {
has $.resume;
method message() { "Just stop already!" }
};
CATCH {
when E {
say .resume;
}
}
E.new(resume => 'stop here').throw;