How to write parametric/generic functions in kotlin - kotlin

I'm trying to find a workaround to make a Spring Reactive Webclient work with JDBC.
Here is were I got the idea from: https://gitorko.github.io/2019/04/02/Spring-Webflux-Reactive-JDBC/.
I'm writing a service that calls the jdbc repository interface and instead of returning the type of my domain object MyClass returns a Mono<MyClass> like this:
//other specific imports here
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.core.scheduler.Scheduler
import reactor.core.scheduler.Schedulers
import java.util.concurrent.Callable
#Service
class MyClassService(val repo: MyClassRepository, val jdbcScheduler: Scheduler){
fun save(obj: MyClass?): Mono<MyClass?>? {
return asyncCallable { repo.save(obj) }
}
protected fun <S> asyncCallable(callable: Callable<S>?): Mono<S>? {
return Mono.fromCallable(callable).subscribeOn(Schedulers.parallel()).publishOn(jdbcScheduler)
}
}
//this is a jdbc repository
interface MyClassRepository : CrudRepository<MyClass, UUID> {}
Now the problem is that calling asyncCallable { repo.save(obj) } returns the compile error inferred type is MyClass? but TypeVariable(S) was expected and Mono.fromCallable(callable).subscribeOn(Schedulers.parallel()).publishOn(jdbcScheduler) returns the compile error inferred type is Callable<S>? but Callable<out TypeVariable(T)!> was expected.
I understand by reading about kotlin generics that this has to do with the variance. If I'm not wrong the function asyncCallableis invariant on the generic type Sand in this case covariance is required?

I think the syntax you need is asyncCallable(Callable { repo.save(obj) }).
Complete example:
#Service
class MyClassService(val repo: MyClassRepository, val jdbcScheduler: Scheduler){
fun save(obj: MyClass): Mono<MyClass?>? {
return asyncCallable(Callable { repo.save(obj) })
}
protected fun <S> asyncCallable(callable: Callable<S>): Mono<S>? {
return Mono.fromCallable(callable).subscribeOn(Schedulers.parallel()).publishOn(jdbcScheduler)
}
}
I'd also remove the ?s, but I left them to keep it as close to your code as possible.

Related

Kotlin: invoke function in companion object (via reflection)

I am trying to notify a central class on startup about the existence of some classes that will be used during runtime. My idea is to use reflection: scan for annotation (or interface implementation) and call a method from the companion's object of those classes.
As suggested in [1] I am using classgraph but I'm totally open to alternatives.
package com.test
import io.github.classgraph.ClassGraph
import io.github.classgraph.ClassInfo
import io.github.classgraph.ScanResult
import kotlin.reflect.KFunction
import kotlin.reflect.jvm.kotlinFunction
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
annotation class OnStartup
// a class that will be instantiated a lot of times during runtime
data class SomeClass(val name: String) {
companion object {
#OnStartup
fun onStartup() {
// notify someone at startup about our existence
}
}
}
fun main() {
val scanResult: ScanResult = ClassGraph().enableAllInfo().acceptPackages("com.test").scan()
scanResult
.getClassesWithMethodAnnotation(OnStartup::class.java.name)
.filter { it.isStatic }
.flatMap { findStartupMethods(it) }
.forEach { it.call() }
}
private fun findStartupMethods(classInfo: ClassInfo): List<KFunction<*>> {
return classInfo.methodInfo.filter { function ->
function.hasAnnotation(OnStartup::class.java)
}.mapNotNull { method ->
method.loadClassAndGetMethod().kotlinFunction
}
}
The problem is, that the code exits with
Exception in thread "main" java.lang.IllegalArgumentException: Callable expects 1 arguments, but 0 were provided.
From reading the Kotlin Docs and [2] my guess is that I should hand over the companionObjectInstance as a parameter. But I have absolutely no idea how to get it...
Any help is really appreciated.
[1] Getting a list of annotated functions in Kotlin using reflection
[2] Kotlin invoke companion function with reflection
Maybe it looks ugly, but it works...
it.call((it.parameters[0].type.classifier as KClass<*>).objectInstance)

Serialize generic class using kotlinix.serialization in Kotlin/JS fails

Serializing a generic class with kotlinx.serialization succeeds in JVM but fails in JavaScript with message TypeError: tmp$.serializer is not a function. Please, see the following Unit Test.
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString
import kotlin.test.Test
#Serializable
data class SerializationTest<T>(val myValue: T)
#Serializable
data class A(val st : SerializationTest<Int>)
class SerializationTests {
#Test
fun serializeWithKotlinx() {
// Succeeds in JVM but throws in JavaScript with "TypeError: tmp$.serializer is not a function"
Json.encodeToString(SerializationTest(3))
}
#Test
fun serializeWithKotlinxWithBox() {
// Succeeds always
Json.encodeToString(A(SerializationTest(3)))
}
}
How can I serialize a generic class in JavaScript?
See the docs here
Please note that this example works only on JVM because of serializer function restrictions. For JS and Native, explicit serializer should be used: format.encodeToString(PolymorphicSerializer(Project::class), data) You can keep track of this issue here.
In your case, this code:
Json.encodeToString(SerializationTest(3))`
uses generics, which is only available on JVM.
You'll have to manually pass the serializer to encodeToString(...)
#Test
fun serializeWithKotlinx() {
val encoded =
Json.encodeToString(
SerializationTest.serializer(Int.serializer()),
SerializationTest(3),
)
println(encoded)
}
Or use a SerializersModule (documented here):
#Test
fun serializeWithKotlinxSerializersModule() {
val module = SerializersModule {
contextual(SerializationTest::class) { args ->
SerializationTest.serializer(args[0])
}
}
val mapper = Json { serializersModule = module }
val encoded = mapper.encodeToString(
SerializationTest(3),
)
println(encoded)
}
As per this GitHub issue:
Unfortunately, this is a known problem and we do not support JS legacy anymore. Please use IR backend if possible
With JS IR it works well, giving the same results as JVM.

Serializer for sealed interface (kotlinx.serialization)

I am trying to serialize my base class that is implementing two sealed interfaces. I have tried multiple approaches, yet i always get the error :
caused by: kotlinx.serialization.SerializationException: Class 'PayloadFromBuilder' is not registered for polymorphic serialization in the scope of 'Payload'.
Mark the base class as 'sealed' or register the serializer explicitly.
I was following mostly this guide Kotlinx/polymorphism and checked some similar questions here.
My code:
sealed inteface MyClass {
dataetc
}
#Serializable
private class DefaultMyClass(dataetc): MyClass
fun MyClass(dataetc): MyClass = DefaultMyClass
Sealed interface MyClassBuilder {
fun dataetc(value: ByteArray)
fun dataetc(value: ByteArray)
fun dataetc(value: ByteArray?)
}
#PublishedApi
#Serializable
#SerialName("payload")
internal class MyClassFromBuilder: MyClassBuilder, MyClass {
}
//Serialization
val module = SerializersModule {
polymorphic(MyClass::class) {
subclass(MyClassFromBuilder::class, MyClassFromBuilder.serializer())
default { MyClassFromBuilder.serializer() }
}
polymorphic(MyClassBuilder::class) {
subclass(MyClassFromBuilder::class, MyClassFromBuilder.serializer())
default { MyClassFromBuilder.serializer() }
}
}
val ConfiguredProtoBuf = ProtoBuf { serializersModule = module }
#ExperimentalSerializationApi
internal inline fun <reified T> ProtoBuf.encodeToMessage(value: T): Message =
Message(encodeToByteArray(value))
From what i have seen i think i am very close to the solution yet i am missing something, since my example is very generic if you need more info let me know, thank you in advance.
Note: In my several tries i have tried to annotate both sealed intefaces with #Polymorphic but i am not sure if it changed anything.
Note 2: My code breaks when i am calling the encodeToMessage fun
So i messed big time, turns out i was not using my ConfiguredProtoBuf when i was calling my encodeToMessage

Getting a list of annotated functions in Kotlin using reflection

I am new to Kotlin and I want to do the following:
Annotate some functions with an annotation e.g "Executable"
At runtime, get all the functions with this annotation
Inspect a property on the annotation and if it matches a condition, invoke the function
I have the following code
annotation class Executable(val name : String)
#Executable("doSomething")
fun stepDoSomething (param1 : String) {
println("I am a step that does something! I print $param1")
}
However, I am unclear on how to retrieve all functions with the Executable annotation at runtime and inspect them.
Thank you for your help!
To accomplish this, you will need to use a classpath scanner, such as ClassGraph. Classpath scanners offer APIs to find classes based on various criteria, such as what package they’re in, what interface they implement, or what annotations they have. In the case of ClassGraph, the ScanResult has a getClassesWithMethodAnnotation(String name) method. Once you have all of those classes, you can use ordinary reflection to find which method(s) in those classes have the specific annotation you’re looking for and inspect the properties of the annotations. Here is a good overview of how to create an annotation and inspect it using reflection.
Here is my implementation based on (very helpful) Matthew Pope's answer:
import io.github.classgraph.ClassGraph
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.jvm.kotlinFunction
#Image(filename = "image-1.svg")
fun foo() {
println("in foo")
}
#Image(filename = "image-2.svg")
fun bar() {
println("in bar")
}
#Throws(Exception::class)
fun getAllAnnotatedWith(annotation: KClass<out Annotation>): List<KFunction<*>> {
val `package` = annotation.java.`package`.name
val annotationName = annotation.java.canonicalName
return ClassGraph()
.enableAllInfo()
.acceptPackages(`package`)
.scan().use { scanResult ->
scanResult.getClassesWithMethodAnnotation(annotationName).flatMap { routeClassInfo ->
routeClassInfo.methodInfo.filter{ function ->
function.hasAnnotation(annotation.java) }.mapNotNull { method ->
method.loadClassAndGetMethod().kotlinFunction
// if parameter needed:
// method.getAnnotationInfo(routeAnnotation).parameterValues.map { it.value }
}
}
}
}
fun main(args: Array<String>) {
getAllAnnotatedWith(Image::class)
.forEach { function ->
function.call()
}
}

Junit 5 Assertions.fail() can not infer type in Kotlin

When I attempt to use JUnit 5 Assertions.fail in my Kotlin tests I get a compilation failure because parameter V can not be inferred:
import org.junit.jupiter.api.Assertions.fail
internal class MyTests {
#Test
fun simpleTest() {
fail("Does not compile")
}
}
Of course a simple solution to this problem is:
import org.junit.jupiter.api.Assertions.fail
internal class MyTests {
#Test
fun simpleTest() {
val result: Any = fail("Compiles")
}
}
However I do not wish to have to create an unused value in my code. Is there a way to define the type without having to declare a val? Also why does this happen in Kotlin? Java has no such problem with generics:
import org.junit.jupiter.api.Assertions.fail;
class MyJavaTests {
#Test
public void simpleTest() {
fail("Compiles);
}
}
EDIT: I discovered immediately after posting the question that the solution is to parameterize the call:
import org.junit.jupiter.api.Assertions.fail
internal class MyTests {
#Test
fun simpleTest() {
fail<Any>("Does not compile")
}
}
However still willing to accept an answer that can explain why I need to do this in kotlin.
Please see this issue: https://github.com/junit-team/junit5/issues/1209
It seems that this is already fixed in the junit-jupiter-api Assertions.kt file as a top-level function in org.junit.jupiter.api package.
Import the org.junit.jupiter.api.fail and not the org.junit.jupiter.api.Assertions.fail