Coroutines delegate exceptions - java

Currently, I have some scenario like this where I have java interface callback which looks something like this.
Java Callback
interface Callback<T> {
void onComplete(T result)
void onException(HttpResponse response, Exception ex)
}
Suspending function for the above look like this
suspend inline fun <T> awaitCallback(crossinline block: (Callback<T>) -> Unit) : T =
suspendCancellableCoroutine { cont ->
block(object : Callback<T> {
override fun onComplete(result: T) = cont.resume(result)
override fun onException(e: Exception?) {
e?.let { cont.resumeWithException(it) }
}
})
}
My calling function looks like this
fun getMovies(callback: Callback<Movie>) {
launch(UI) {
awaitCallback<Movie> {
// I want to delegate exceptions here.
fetchMovies(it)
}
}
What I'm currently doing to catch exception is this
fun getMovies(callback: CallbackWrapper<Movie>) {
launch(UI) {
try{
val data = awaitCallback<Movie> {
// I want to delegate exceptions here.
fetchMovies(it)
}
callback.onComplete(data)
}catch(ex: Exception) {
callback.onFailure(ex)
}
}
}
// I have to make a wrapper kotlin callback interface for achieving the above
interface CallbackWrapper<T> {
fun onComplete(result: T)
fun onFailure(ex: Exception)
}
Questions
The above works but is there any better way to do this?? One of the main thing is I'm currently migrating this code from callback so I have ~20 api calls and I don't want to add try/catch everywhere to delegate the result along with the exception.
Also, I'm only able to get exception from my suspending function is there any way to get both HttpResponse as well as the exception. Or is it possible to use existing JAVA interface.
Is there any better way to delegate the result from getMovies without using callback??

Is there any better way to delegate the result from getMovies without using callback?
Let me start with some assumptions:
you're using some async HTTP client library. It has some methods to send requests, for example httpGet and httpPost. They take callbacks.
you have ~20 methods like fetchMovies that send HTTP requests.
I propose to create an extension suspend fun for each HTTP client method that sends a request. For example, this turns an async client.httpGet() into a suspending client.awaitGet():
suspend fun <T> HttpClient.awaitGet(url: String) =
suspendCancellableCoroutine<T> { cont ->
httpGet(url, object : HttpCallback<T> {
override fun onComplete(result: T) = cont.resume(result)
override fun onException(response: HttpResponse?, e: Exception?) {
e?.also {
cont.resumeWithException(it)
} ?: run {
cont.resumeWithException(HttpException(
"${response!!.statusCode()}: ${response.message()}"
))
}
}
})
}
Based on this you can write suspend fun fetchMovies() or any other:
suspend fun fetchMovies(): List<Movie> =
client.awaitGet("http://example.org/movies")
My reduced example is missing the parsing logic that turns the HTTP response into Movie objects, but I don't think this affects the approach.
I'm currently migrating this code from callback so I have ~20 api calls and I don't want to add try/catch everywhere to delegate the result along with the exception.
You don't need a try-catch around each individual call. Organize your code so you just let the exception propagate upwards to the caller and have a central place where you handle exceptions. If you can't do that, it means you've got a specific way to handle each exception; then the try-catch is the best and idiomatic option. It's what you would write if you had a plain blocking API. Especially note how trivial it is to wrap many HTTP calls in a single try-catch, something you can't replicate with callbacks.
I'm only able to get exception from my suspending function is there any way to get both HttpResponse as well as the exception.
This is probably not what you need. What exactly do you plan to do with the response, knowing that it's an error response? In the example above I wrote some standard logic that creates an exception from the response. If you have to, you can catch that exception and provide custom logic at the call site.

I am not so sure whether you really need that awaitCallback or not.
If you really have lots of Callback already in place and that's why you used it then your functions will probably already have everything in place that works correctly with the Callback, e.g. I expect some methods as follows:
fun fetchMovies(callback : Callback<List<Movie>>) {
try {
// get some values from db or from a service...
callback.onComplete(listOf(Movie(1), Movie(2)))
} catch (e : Exception) {
callback.onFailure(e)
}
}
If you do not have something like this in place, you may not even need awaitCallback at all. So if your fetchMovies function rather has a signature as follows:
fun fetchMovies() : List<Movie>
and in getMovies you pass your Callback, then all you need is probably a simple async, e.g.:
fun getMovies(callback: Callback<List<Movie>>) {
GlobalScope.launch { // NOTE: this is now a suspend-block, check the parameters for launch
val job = async { fetchMovies() }
try {
callback.onComplete(job.await())
} catch (e: Exception) {
callback.onException(e)
}
}
}
This sample can of course be changed to many similar variants, e.g. the following will also work:
fun getMovies(callback: Callback<List<Movie>>) {
GlobalScope.launch { // NOTE: this is now a suspend-block, check the parameters for launch
val job = async { fetchMovies() } // you could now also cancel/await, or whatever the job
job.join() // we just join now as a sample
job.getCompletionExceptionOrNull()?.also(callback::onFailure)
?: job.getCompleted().also(callback::onComplete)
}
}
You could also add something like job.invokeOnCompletion. If you just wanted to pass any exception to your callback in your current code, you could just have used callback.onException(RuntimeException()) at the place where you put your comment I want to delegate exceptions here..
(note that I am using Kotlin 1.3 which is a RC now...)

Related

Functional endpoints in micronaut [duplicate]

Simple question. Is it possible to create endpoints without #Endpoint?
I want to create rather dynamic endpoints by a file and depending on the content to its context.
Thanks!
Update about my idea. I would to create something like a plugin system, to make my application more extensible for maintenance and future features.
It is worth to be mentioned I am using Micronaut with Kotlin. Right now I've got fixed defined Endpoints, which matches my command scripts.
My description files will be under /src/main/resources
I've got following example description file how it might look like.
ENDPOINT: GET /myapi/customendpoint/version
COMMAND: """
#!/usr/bin/env bash
# This will be executed via SSH and streamed to stdout for further handling
echo "1.0.0"
"""
# This is a template JSON which will generate a JSON as production on the endpoint
OUTPUT: """
{
"version": "Server version: $RESULT"
}
"""
How I would like to make it work with the application.
import io.micronaut.docs.context.events.SampleEvent
import io.micronaut.context.event.StartupEvent
import io.micronaut.context.event.ShutdownEvent
import io.micronaut.runtime.event.annotation.EventListener
#Singleton
class SampleEventListener {
/*var invocationCounter = 0
#EventListener
internal fun onSampleEvent(event: SampleEvent) {
invocationCounter++
}*/
#EventListener
internal fun onStartupEvent(event: StartupEvent) {
// 1. I read all my description files
// 2. Parse them (for what I created a parser)
// 3. Now the tricky part, how to add those information to Micronaut Runtime
val do = MyDescription() // After I parsed
// Would be awesome if it is that simple! :)
Micronaut.addEndpoint(
do.getEndpoint(), do.getHttpOption(),
MyCustomRequestHandler(do.getCommand()) // Maybe there is a base class for inheritance?
)
}
#EventListener
internal fun onShutdownEvent(event: ShutdownEvent) {
// shutdown logic here
}
}
You can create a custom RouteBuilder that will register your custom endpoints at runtime:
#Singleton
class CustomRouteBuilder extends DefaultRouteBuilder {
#PostConstruct
fun initRoutes() {
val do = MyDescription();
val method = do.getMethod();
val routeUri = do.getEndpoint();
val routeHandle = MethodExecutionHandle<Object, Object>() {
// implement the 'MethodExecutionHandle' in a suitable manner to invoke the 'do.getCommand()'
};
buildRoute(HttpMethod.parse(method), routeUri, routeHandle);
}
}
Note that while this would still feasible, it would be better to consider another extension path as the solution defeats the whole Micronaut philosophy of being an AOT compilation framework.
It was actually pretty easy. The solution for me was to implement a HttpServerFilter.
#Filter("/api/sws/custom/**")
class SwsRouteFilter(
private val swsService: SwsService
): HttpServerFilter {
override fun doFilter(request: HttpRequest<*>?, chain: ServerFilterChain?): Publisher<MutableHttpResponse<*>> {
return Flux.from(Mono.fromCallable {
runBlocking {
swsService.execute(request)
}
}.subscribeOn(Schedulers.boundedElastic()).flux())
}
}
And the service can process with the HttpRequest object:
suspend fun execute(request: HttpRequest<*>?): MutableHttpResponse<Feedback> {
val path = request!!.path.split("/api/sws/custom")[1]
val httpMethod = request.method
val parameters: Map<String, List<String>> = request.parameters.asMap()
// TODO: Handle request body
// and do your stuff ...
}

How to resume a Kotlin Coroutine Continuation in Java

I am adding Kotlin code to a Java project. I created a Kotlin suspend function with suspendCancellableCoroutine
suspend fun someSuspendFunction = suspendCancellableCoroutine<Boolean>{ continuation ->
someJavaObject.someSuspendFunctionContinuation = continuation
}
I need someSuspendFunction to resume by some logic done in someJavaObject so I declare a field in someJavaObject to store the continuation for later use.
CancellableContinuation<Boolean> someSuspendFunctionContinuation;
However, when I want to resume it, I can't find a proper method to call. In Kotlin I can simply call continuation.resume(true). I looked into the definition of resume() and found it called resumeWith(Result.success(value)). So I tried to write this in Java:
someSuspendFunctionContinuation.resumeWith(Result.Companion.success(true))
which gives this error: 'success(java.lang.Boolean)' has private access in 'kotlin.Result.Companion'
So I tried to construct Result directly
someSuspendFunctionContinuation.resumeWith(new Result(true));
This gives me : Expected 0 arguments but found 1
Is it possible to construct a Result with value to reusme a coroutine continuation in Java?
You can create a Result object using reflection:
public void resume() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
var successMethod = Result.Companion.getClass().getDeclaredMethod("success", Object.class);
successMethod.setAccessible(true);
someSuspendFunctionContinuation.resumeWith(successMethod.invoke(Result.Companion, true));
}
But I think it is better to just create yourself a very small util in Kotlin for using it in Java:
fun <T> resumeContinuationWithSuccess(cont: Continuation<T>, value: T) {
cont.resumeWith(Result.success(value))
}
public void resume() {
SuspendUtils.resumeContinuationWithSuccess(someSuspendFunctionContinuation, true);
}

Exceptions thrown by Mono inside flatmap not being handled by Spring webflux reactive stream

I am working on reactive streams application using Spring webflux. I have a usecase where I do a webclient request to get some data and once I get the response data, I validate it and if the validation fails, I want to throw an exception which should be handled by the main reactive pipeline. I'm using webclient call within a flatmap to use the value in the next operator of my pipeline. I have something similar to the following code:
public class Example {
public String getData(String name) {
return Mono.just(name)
.map(name -> name.toLowerCase())
.flatMap(name ->
// Webclient Request that returns a
// Mono<String> for example
.doOnSuccess(Validator::validateData); // The webclient request is chained to this doOnSuccess
)
.doOnError(ex -> log.error("Got an error, {}", er))
.onErrorMap(ex -> new AnotherCustomException(ex.getMessage()));
}
}
public class Validator {
public void validateData(String data) {
if(data.length() < 5) throw new CustomException("Invalid data received."); // public CustomException extends RuntimeException {...}
}
}
Currently this code isn't using the doOnError() & onErrorMap() operators and I'm directly receiving the CustomException stacktrace on my console. I believe the reason being the code inside flatMap itself is a publisher Mono so it should have its own doOnError(), onErrorMap() operators. How do I make this Webclient's response i.e., Mono<String> be able to use the main pipeline that's using the WebClient?
This is what you want
SomeWebclientCall().flatMap(value -> {
Validate.validate(value);
return Mono.just(value);
});
This might look wierd and that is because writing void functions that either return void or an exception is wierd and should be avoided.
Such functions are ineffective (throwing exceptions are expensive and should not be a natural flow of the program) and hard to test.
Thats why validation functions usually returns booleans as in it passed validation yes or no, true or false. Not void or exception.
Functional programming does not like void functions, only pure functions.

java.lang.VerifyError Precise Reference expected while trying to runBlocking Kotlin suspended fun in Android

I'm trying to write a unit test waiting for completion of a kotlin suspended function before checking results like this :
#Test
fun shouldSetupThingsProperly() {
val context = InstrumentationRegistry.getInstrumentation().context
runBlocking { MyObject.enable(context, false) }
Assert.assertTrue( /* whatever usefull */ true)
}
The suspending methods are as follow :
object MyObject {
#JvmStatic
suspend fun enable(context: Context, enable: Boolean) {
withContext(Dispatchers.IO) {
// ... do some work
wakeup(context)
}
}
private suspend fun wakeup(context: Context) {
withContext(Dispatchers.IO) {
try {
// setup things ...
} catch (ignore: Exception) {}
}
}
}
Test run ends with :
java.lang.VerifyError: Verifier rejected class MyObject: java.lang.Object MyObject.enable(android.content.Context, boolean, kotlin.coroutines.Continuation) failed to verify: java.lang.Object MyObject.enable(android.content.Context, boolean, kotlin.coroutines.Continuation): [0x16] register v7 has type Reference: android.content.Context but expected Precise Reference: MyObject (declaration of 'MyObject' appears in /data/app/test-_rphd0tDrOp0KM-Bz09NWA==/base.apk!classes2.dex)
at MyObject.enable(Unknown Source:0)
I'm not familiar with coroutine and I was wondering how to achieve waiting for completion of the enable suspended function inside the test properly or if error was due to some other mistake...
If it happend to coroutines- withContext on Android or Flutter, revert coroutines lib to 1.3.6 solved crash issue for me.
It seems that there is VerifyError bug in android coroutines lib version 1.3.7-1.3.8, and will be fixed after 1.4.0.
Details see links:
https://github.com/Kotlin/kotlinx.coroutines/issues/2049 https://github.com/Kotlin/kotlinx.coroutines/issues/2041
Testing coroutines is a trick, even after some experience.
If you can import, this will be very helpful: https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test
If you have this dependency testing coroutines becomes much more manageable.
First off, if you can have the dispatcher you are running this a variable or parameter that can be set or overridden it will help you increase your testability.
As far as writing the test you can do something like:
#Before
fun before() {
Dispatchers.setMain(mainThreadSurrogate)
}
#Test
fun shouldSetupThingsProperly() = runBlockingTest {
val context = InstrumentationRegistry.getInstrumentation().context
MyObject.enable(context, false, Dispatchers.Main)
Assert.assertTrue( /* whatever useful */ true)
}
Your object itself will have I would say more of the changes
object MyObject {
#JvmStatic
suspend fun enable(context: Context, enable: Boolean, dispatcher: CoroutineDispatcher = Dispatchers.IO) {
// If you need a return feel free to use withContext such as:
// val result = withContext(dispatcher) { /* Return Value */ Any() }
CoroutineScope(dispatcher).run {
// ... do some work
wakeup(context)
}
}
private suspend fun wakeup(context: Context) {
// Another coroutine scope is unnecessary here, it will inherit the parent scope automatically, so you can call
// async functions here
delay(200)
try {
// setup things ...
} catch (exc: Exception) {
// We had an issue
}
}
}

Java Functions, Returns, and Optionals

I am trying to create a client library that reads JSON from an external file online. I already know about the function interfaces and optionals, but I was wondering if there is a way to allow users to supply callback functions such that the parent function exits completely. For JavaScript, such a function is as follows:
file.read('hello', function(err, data) {
// something here
});
Basically, I wish to do the same in Java. How can I do this such that the error callback supersedes the read function? What I mean is that in the event that the error callback is called, then read should not return a value at all. If the callback is not called then the read should return the value.
You could have the user pass in a function and then just not do anything with it if there is no error.
This example assumes that you have a custom class called Error that the caller is aware of and would like to interact with in case of an error.
public void read (String str, Function<Error,Void> errorFunc)
{
//interact w/ libraries, boolean error = true or false
//if there is an error, variable err of type Error contains information
if (error)
{
errorFunc.apply(err);
}
}
In Java upto 1.7 the only way to achieve javascript like callbacks is thru interface. The api user who calls your method read has the liberty of implementing what he feels needs to be done to handle the error by writing an implementation class for the interface at the invocation point.
public String read(String options,IErrorCallBack errorHandler) throws Exception {
try {
// When everything works fine return what you think should be returned.
return "Success";
}
catch(Exception e) {
// On Error call the function on the error handler.
errorHandler.doSomething();
throw e;
}
}
public interface IErrorCallBack {
public void doSomething();
}
// The invocation point.
read("myString", new IErrorCallBack() {
public void doSomething() {
// Your implementation.
}
});

Categories

Resources