Is there a proper way in Kotlin-js to globally catch unhandled exception?
I have made a general attempt with window.onerror (see below).
The only reported error is on non_existing_function2, but nothing is reported for the other 2 cases.
Any guidance?
Thanks
window.onerror = { msg, url, line, col, error ->
window.alert(“Error: $msg \nurl: $url\nline: $line\ncol: $col\nerror: $error”)
true
}
async {
throw Exception(“generated”)
}
async {
js(“non_existing_function1();”)
}
js("non_existing_function2();")
The async in Kotlin you're using is defined as
fun <T> async(block: suspend CoroutineScope.() -> T): Deferred<T>
Where Deferred is stateful and doesn't propagate the exception anywhere. You could use async { ... }.asPromise() to bridge to JavaScript and propagate the rejection to handle it properly.
Related
i'm using Quarkus, Vert.x and Kotlin and trying to call my code through a unit test while using #QuarkusTest and #TestTransaction.
I send out an Event and my Consumer throws an exception when i talk to the database:
2022-05-26 18:38:25,322 ERROR [io.qua.ver.cor.run.VertxCoreRecorder] (vert.x-eventloop-thread-3) Uncaught exception received by Vert.x: java.lang.IllegalArgumentException: Unable to find an EntityManagerFactory for persistence unit '<default>'
at io.quarkus.hibernate.orm.runtime.JPAConfig.getEntityManagerFactory(JPAConfig.java:85)
at io.quarkus.hibernate.orm.runtime.TransactionSessions.lambda$getSession$0(TransactionSessions.java:40)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
...
My consumer looks like this:
#ApplicationScoped
class Consumer {
#Blocking
#ConsumeEvent(EventSet.UPDATE)
#Transactional
fun update(foo: Foo) {
val newFoo = NewFoo.findById(foo.id) ?: let { NewFoo.create(foo.id) }
}
}
I assumed Quarkus whould just make sure that the database works. What am i missing? I looked into hibernate-reactive because it popped up but nothing states that i would need to configure that as well.
Problem
I am using kotlinx.coroutines.future.FutureKt#await to await for an async code. But when any exception is thrown from this async code, the Exception doesn't contain full stack call. E.g.:
fun main() {
try {
myFun1Blocking()
} catch (e: Throwable) {
e.printStackTrace(System.out)
}
}
fun myFun1Blocking() {
runBlocking {
myFun2Suspend()
}
}
suspend fun myFun2Suspend() {
runAsync().await()
}
fun runAsync(): CompletableFuture<Void> {
return CompletableFuture.runAsync {
Thread.sleep(2000)
throw Exception()
}
}
This results in the following output:
java.lang.Exception
at TestKotlinKt.runAsync$lambda-0(TestKotlin.kt:34)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
The stack trace only has the runAsync method part.
Solution #1
While trying to workaround this problem, I first thought of catching it just outside of await:
suspend fun <T> CompletionStage<T>.awaitWithException(): T {
try {
return await()
} catch (e: Exception) {
throw Exception(e)
}
}
java.lang.Exception: java.lang.Exception
at TestKotlinKt.awaitWithException(TestKotlin.kt:36)
at TestKotlinKt$awaitWithException$1.invokeSuspend(TestKotlin.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at TestKotlinKt.myFun1Blocking(TestKotlin.kt:23)
at TestKotlinKt.main(TestKotlin.kt:16)
at TestKotlinKt.main(TestKotlin.kt)
Caused by: java.lang.Exception
at TestKotlinKt.runAsync$lambda-0(TestKotlin.kt:43)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Better, but I still lost the myFun2Suspend call in the stack.
Solution #2
Then I tried saving the stacktrace just before await():
suspend fun <T> CompletionStage<T>.awaitWithException(printStream: PrintStream): T {
val throwable = Throwable("Await Exception")
try {
return await()
} catch (e: Exception) {
throwable.printStackTrace(printStream)
throw e
}
}
java.lang.Throwable: Await Exception
at TestKotlinKt.awaitWithException(TestKotlin.kt:43)
at TestKotlinKt.myFun2Suspend(TestKotlin.kt:31)
at TestKotlinKt$myFun1Blocking$1.invokeSuspend(TestKotlin.kt:26)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at TestKotlinKt.myFun1Blocking(TestKotlin.kt:25)
at TestKotlinKt.main(TestKotlin.kt:18)
at TestKotlinKt.main(TestKotlin.kt)
AsyncException
at TestKotlinKt.runAsync$lambda-0(TestKotlin.kt:55)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Now I see everything.
All in all, my solution seems to be very hacky. Is there something better?
Referring to Kotlin's documentation about debugging both of your solutions have officially supported counterparts:
Set the system property kotlinx.coroutines.debug to on to enable debug mode. This enables stacktrace recovery which is a more comprehensive version of solution #1.
Use the Kotlin debug agent to enable creation stacktraces which is the official version of solution #2. Do be aware that this is a very expensive feature because it will need to dump stack traces each time a coroutine is created.
In theory kotlin debug mode should be enough since the exception has to unwind through the 'stack' of coroutines. It just isn't the prettiest solution.
I faced the same problem. The Kotlin coroutine debug library didn't help me in any way.
Therefore, after studying the implementation of coroutines, I wrote my own solution based on bytecode generation and MethodHandle API. It supports JVM 1.8 and Android API 25 or higher.
I called it Stacktrace-decoroutinator.
The reason why the stacktrace is lost is that when the coroutine wakes up, only the last method of its call stack is called.
My library replaces the coroutine awakening implementation. It generates classes at runtime with names that match the entire coroutine call stack.
These classes don't do anything except call each other in the coroutine call stack sequence.
Thus, if the coroutine throws an exception, they mimic the real call stack of the coroutine during the creation of the exception stacktrace.
I am using OkHttp to make a synchronous http request. To avoid blocking the main thread, I wrapped the blocking network call in a suspend function and withContext(Dispatchers.IO)
suspend fun run(): String {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build()
return withContext(Dispatchers.IO) {
val response = client.newCall(request).execute()
return#withContext "Request completed successfully"
}
}
Android Studio gives me a warning that execute() is an "Inappropriate blocking method call". My understanding is that execute() will block during the http request taking up a thread in Dispatchers.IO for the duration of the request, which is not ideal. To avoid this issue, I can use the asynchronous version of the request wrapped in suspendCoroutine
suspend fun runAsync(): String = suspendCoroutine { continuation ->
val client = OkHttpClient()
val request = Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
continuation.resumeWithException(e)
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
continuation.resume("Request completed successfully")
}
}
})
}
This avoids the warning, but I do not understand how it is functionally different than the synchronous version above. I am assuming that the async version of the http call uses a thread to wait on the request. Is this assumption correct? If not, how does the async function wait for the callback to return?
I do not understand how it is functionally different than the synchronous version above. I am assuming that the async version of the http call uses a thread to wait on the request. Is this assumption correct? If not, how does the async function wait for the callback to return?
It's not correct, the point of the async approach is that there are no threads being blocked while the call takes place. Your second approach achieves just that -- when the coroutine suspends, it doesn't live on any thread at all. Depending on the implementation details of OkHttp, it may or may not be the case that some internal thread is blocked because it relies on blocking IO. Ideally it should be implemented in terms of Java NIO (more typically, through the Netty library) and rely on the low-level non-blocking primitive called the Selector.
The correct approach is the way you did it with implementing suspension, and kotlin-coroutines-okhttp already provides exactly that implementation in its Call.await() function.
As per Customizing Error Handling "Throwing the exception in the catch block will forward the message to the error queue. If that's not desired, remove the throw from the catch block to indicate that the message has been successfully processed." That's not true for me even if I simply swallow any kind of exception in a behavior:
public override async Task Invoke(IInvokeHandlerContext context, Func<Task> next)
{
try
{
await next().ConfigureAwait(false);
}
catch (Exception ex)
{
}
}
I put a breakpoint there and made sure execution hit the catch block. Nevertheless after intimidate and delayed retries messages inevitably ends up in error queue. And I have no more Behaviours in the pipeline besides this one.
Only if I run context.DoNotContinueDispatchingCurrentMessageToHandlers(); inside the catch block it prevents sending error to the error queue, but it also prevents any further immediate and delayed retries.
Any idea on why it works in contravention of Particular NserviceBus documentation is very appreciated
NserviceBus ver. used: 6.4.3
UPDATE:
I want only certain type of exceptions not being sent to an error queue in NServiceBus 6, however to make test case more clear and narrow down the root cause of an issue I use just type Exception. After throwing exception, execution certainly hits the empty catch block. Here is more code to that:
public class EndpointConfig : IConfigureThisEndpoint
{
public void Customize(EndpointConfiguration endpointConfiguration)
{
endpointConfiguration.DefineEndpointName("testEndpoint");
endpointConfiguration.UseSerialization<XmlSerializer>();
endpointConfiguration.DisableFeature<AutoSubscribe>();
configure
.Conventions()
.DefiningCommandsAs(t => t.IsMatched("Command"))
.DefiningEventsAs(t => t.IsMatched("Event"))
.DefiningMessagesAs(t => t.IsMatched("Message"));
var transport = endpointConfiguration.UseTransport<MsmqTransport>();
var routing = transport.Routing();
var rountingConfigurator = container.GetInstance<IRountingConfiguration>();
rountingConfigurator.ApplyRountingConfig(routing);
var instanceMappingFile = routing.InstanceMappingFile();
instanceMappingFile.FilePath("routing.xml");
transport.Transactions(TransportTransactionMode.TransactionScope);
endpointConfiguration.Pipeline.Register(
new CustomFaultMechanismBehavior(),
"Behavior to add custom handling logic for certain type of exceptions");
endpointConfiguration.UseContainer<StructureMapBuilder>(c => c.ExistingContainer(container));
var recoverability = endpointConfiguration.Recoverability();
recoverability.Immediate(immediate =>
{
immediate.NumberOfRetries(2);
});
endpointConfiguration.LimitMessageProcessingConcurrencyTo(16);
recoverability.Delayed(delayed =>
{
delayed.NumberOfRetries(2);
});
endpointConfiguration.SendFailedMessagesTo("errorQueue");
...
}
}
public class CustomFaultMechanismBehavior : Behavior<IInvokeHandlerContext>
{
public override async Task Invoke(IInvokeHandlerContext context, Func<Task> next)
{
try
{
await next().ConfigureAwait(false);
}
catch (Exception ex)
{
}
}
}
UPDATE 2
I think I know what's going on: message is handled by first handler that throws an exception which is caught by the Behavior catch block, but then NServiceBus runtime tries to instantiate second handler class which is also supposed to handle the message (it handles class the message is derived from). That's where another exception is thrown in a constructor of one of dependent class. StructureMap tries to instantiate the handler and all its dependent services declared in the constructor and in the process runs into the exception. And this exception is not caught by CustomFaultMechanismBehavior.
So my I rephrase my question now: Is there any way to suppress errors (ignore error queue) occurring inside constructor or simply during StructureMap classes initialization? Seems like the described way does not cover this kind of situations
Your behavior is activated on Handler invocation. This means you are catching exceptions happening inside the Handle method so any other exception, e.g. in the Constructor of the handler would not be caught.
To change the way you 'capture' the exceptions, you can change the way the behavior is activated, e.g. change it from Behavior<IInvokeHandlerContext> to Behavior<ITransportReceiveContext> which is activated when the transport receives a message. You can investigate on different stages and behaviors to see which one suits your purpose best.
I'm using the Android Studio 3.0 Beta 2 Canary Preview, and am calling a Kotlin extension function from Java:
The Kotlin code:
fun Metadata.validate(context: Context) {
validateInnerList(context)
// other validation functions
}
private fun Metadata.validateInnerList(context: Context) {
taskGroupList.removeIf { !it.isDataValid() } // error goes here
}
the java code:
metadataDecoratorKt.validate(metadata, context);
The code compiles fine, but during runtime calling it gives me a - java.lang.NoClassDefFoundError
Am I doing something wrong? is there an error on Intellij's side?
If more information is needed please say so.