remote.Client.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of yass Show documentation
Show all versions of yass Show documentation
Yet Another Service Solution
package ch.softappeal.yass.remote
import ch.softappeal.yass.Interceptor
import ch.softappeal.yass.args
import ch.softappeal.yass.compositeInterceptor
import ch.softappeal.yass.proxy
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionStage
import java.util.concurrent.CountDownLatch
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)
}
abstract class Client {
@Throws(Exception::class)
protected abstract fun invoke(invocation: ClientInvocation)
@Throws(Exception::class)
protected open fun syncInvoke(
contractId: ContractId<*>, interceptor: Interceptor, method: Method, arguments: List
): Any? = 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
r = reply
ready.countDown()
}
})
ready?.await()
r?.process()
}
@SafeVarargs
fun proxy(contractId: ContractId, vararg interceptors: Interceptor): C {
val interceptor = compositeInterceptor(*interceptors)
return proxy(
contractId.contract,
InvocationHandler { _, method, arguments -> syncInvoke(contractId, interceptor, method, args(arguments)) }
)
}
@JvmOverloads
fun asyncProxy(contractId: ContractId, interceptor: AsyncInterceptor = DirectAsyncInterceptor): C =
proxy(contractId.contract, InvocationHandler { _, 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"
}
invoke(object : ClientInvocation(methodMapping, args(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, args(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 oldPromise = promise_.get()
val promise = CompletableFuture()
@Suppress("UNCHECKED_CAST") promise_.set(promise as CompletableFuture)
try {
execute()
} finally {
promise_.set(oldPromise)
}
return promise
}
private fun handlePrimitiveTypes(type: Class<*>): Any? = when (type) {
Boolean::class.javaPrimitiveType -> java.lang.Boolean.FALSE
Byte::class.javaPrimitiveType -> 0.toByte()
Short::class.javaPrimitiveType -> 0.toShort()
Int::class.javaPrimitiveType -> 0
Long::class.javaPrimitiveType -> 0.toLong()
Char::class.javaPrimitiveType -> 0.toChar()
Float::class.javaPrimitiveType -> 0.toFloat()
Double::class.javaPrimitiveType -> 0.toDouble()
else -> null
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy