All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.ch.softappeal.yass2.remote.ServiceReflection.kt Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
package ch.softappeal.yass2.remote

import ch.softappeal.yass2.*
import java.lang.reflect.*
import java.util.concurrent.*
import kotlin.coroutines.*
import kotlin.reflect.*
import kotlin.reflect.full.*

internal class FunctionMapper(private val service: KClass<*>) {
    private val functions = service.serviceFunctions().toTypedArray()
    private val name2id = functions.withIndex().map { (index, function) -> function.name to index }.toMap()
    fun toFunction(id: Int) = if (id >= 0 && id < functions.size) functions[id] else error("'$service' has no function $id")
    fun toId(name: String) = name2id[name] ?: error("'$service' has no function '$name'")
}

private val FunctionMappers = ConcurrentHashMap, FunctionMapper>(16)
private fun functionMapper(service: KClass<*>) = FunctionMappers.computeIfAbsent(service) { FunctionMapper(service) }

fun reflectionRemoteProxyFactory(tunnel: Tunnel) = object : RemoteProxyFactory {
    override fun  create(serviceId: ServiceId): S {
        serviceId.service.checkService()
        val functionMapper = functionMapper(serviceId.service)
        val javaClass = serviceId.service.java
        @Suppress("UNCHECKED_CAST")
        return Proxy.newProxyInstance(javaClass.classLoader, arrayOf(javaClass)) { _, method, args ->
            invokeSuspendFunction(args.last() as Continuation<*>) {
                tunnel(
                    Request(serviceId.id, functionMapper.toId(method.name), listOf(*args.copyOf(args.size - 1)))
                ).process()
            }
        } as S
    }
}

suspend fun reflectionInvoker(request: Request, service: Service): Any? = try {
    functionMapper(service.serviceId.service)
        .toFunction(request.functionId)
        .callSuspend(service.implementation, *request.parameters.toTypedArray())
} catch (e: InvocationTargetException) {
    throw e.cause!!
}