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

ch.softappeal.yass.remote.Client.kt Maven / Gradle / Ivy

There is a newer version: 66.0.0
Show newest version
package ch.softappeal.yass.remote

import ch.softappeal.yass.*
import ch.softappeal.yass.proxy
import java.lang.reflect.*
import java.util.concurrent.*

typealias Tunnel = (request: Request) -> Unit

abstract class ClientInvocation internal constructor(
    methodMapping: MethodMapping, arguments: List
) : AbstractInvocation(methodMapping, arguments) {
    abstract fun invoke(asyncSupported: Boolean, tunnel: Tunnel)
    abstract fun settle(reply: Reply)
}

private fun  proxy(contractId: ContractId, invocation: (method: Method, arguments: List) -> Any?): C =
    proxy(contractId.contract, InvocationHandler { _, method, arguments -> invocation(method, args(arguments)) })

abstract class Client {
    @Throws(Exception::class)
    protected abstract fun invoke(invocation: ClientInvocation)

    /** Must return invocation of [action] ( which calls [invoke]). */
    @Throws(Exception::class)
    protected open fun executeInContext(action: () -> Any?): Any? = action()

    @SafeVarargs
    fun  proxy(contractId: ContractId, vararg interceptors: Interceptor): C {
        val interceptor = compositeInterceptor(*interceptors)
        return proxy(contractId) { method, arguments ->
            executeInContext {
                interceptor(method, arguments) {
                    val methodMapping = contractId.methodMapper.map(method)
                    val ready = if (methodMapping.oneWay) null else CountDownLatch(1)
                    var r: Reply? = null
                    invoke(object : ClientInvocation(methodMapping, arguments) {
                        override fun invoke(asyncSupported: Boolean, tunnel: Tunnel) =
                            tunnel(Request(contractId.id, methodMapping.id, arguments))

                        override fun settle(reply: Reply) {
                            if (ready == null) return // OneWay
                            r = reply
                            ready.countDown()
                        }
                    })
                    ready?.await()
                    r?.process()
                }
            }
        }
    }

    @JvmOverloads
    fun  asyncProxy(contractId: ContractId, interceptor: AsyncInterceptor = DirectAsyncInterceptor): C =
        proxy(contractId) { method, arguments ->
            val methodMapping = contractId.methodMapper.map(method)
            val promise = promise_.get()
            check((promise != null) || methodMapping.oneWay) {
                "asynchronous request/reply proxy call must be enclosed with 'promise' function"
            }
            check((promise == null) || !methodMapping.oneWay) {
                "asynchronous OneWay proxy call must not be enclosed with 'promise' function"
            }
            executeInContext {
                invoke(object : ClientInvocation(methodMapping, arguments) {
                    override fun invoke(asyncSupported: Boolean, tunnel: Tunnel) {
                        check(asyncSupported) { "asynchronous services not supported (service id ${contractId.id})" }
                        interceptor.entry(this)
                        tunnel(Request(contractId.id, methodMapping.id, arguments))
                    }

                    override fun settle(reply: Reply) {
                        if (promise == null) return // OneWay
                        try {
                            val result = reply.process()
                            interceptor.exit(this, result)
                            promise.complete(result)
                        } catch (e: Exception) {
                            interceptor.exception(this, e)
                            promise.completeExceptionally(e)
                        }
                    }
                })
            }
            handlePrimitiveTypes(method.returnType)
        }
}

private val promise_ = ThreadLocal>()

fun  promise(execute: () -> T): CompletionStage {
    val promise = CompletableFuture()
    @Suppress("UNCHECKED_CAST") threadLocal(promise_, promise as CompletableFuture) { execute() }
    return promise
}

private val HandlePrimitiveTypes = mapOf(
    Boolean::class.javaPrimitiveType to false,
    Byte::class.javaPrimitiveType to 0.toByte(),
    Short::class.javaPrimitiveType to 0.toShort(),
    Int::class.javaPrimitiveType to 0,
    Long::class.javaPrimitiveType to 0.toLong(),
    Char::class.javaPrimitiveType to 0.toChar(),
    Float::class.javaPrimitiveType to 0.toFloat(),
    Double::class.javaPrimitiveType to 0.toDouble()
)

private fun handlePrimitiveTypes(type: Class<*>): Any? = HandlePrimitiveTypes[type]




© 2015 - 2025 Weber Informatics LLC | Privacy Policy