Last active
October 7, 2017 19:48
-
-
Save ntoskrnl/01570793df6f829d80c3cdd157a6c9ce to your computer and use it in GitHub Desktop.
Creating a retrofit-like service based on annotations.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun <T> createJsonRpcService(service: Class<T>, client: JsonRpcClient): T { | |
val classLoader = service.classLoader | |
val interfaces = arrayOf<Class<*>>(service) | |
val invocationHandler = createInvocationHandler(service, client) | |
@Suppress("UNCHECKED_CAST") | |
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler) as T | |
} | |
private fun <T, B> createInvocationHandler(service: Class<T>, client: JsonRpcClient<B>): InvocationHandler { | |
return object : InvocationHandler { | |
val requestId = AtomicLong(0) | |
override fun invoke(proxy: Any, method: Method, args: Array<Any?>?): Any { | |
val methodAnnotation = | |
method.getAnnotation(JsonRpc::class.java) ?: return method.invoke(this, args) | |
if (!method.returnsSingle) { | |
error("Only io.reactivex.Single<T> is supported as return type") | |
} | |
val id = requestId.incrementAndGet() | |
val methodName = methodAnnotation.value | |
val parameters = method.jsonRpcParameters(args, service) | |
val request = JsonRpcRequest(id, methodName, parameters) | |
val returnType = method.resultGenericTypeArgument | |
return client.call<Any>(request, returnType) | |
} | |
} | |
} | |
private fun Method.jsonRpcParameters(args: Array<Any?>?, service: Class<*>): Map<String, Any?> { | |
return parameterAnnotations | |
.map { it?.firstOrNull { JsonRpcParam::class.java.isInstance(it) } } | |
.mapIndexed { i, a -> | |
when (a) { | |
is JsonRpc -> a.value | |
else -> error("Argument #$i of ${service.name}#$name()" + | |
" must be annotated with @${JsonRpc::class.java.simpleName}") | |
} | |
} | |
.mapIndexed { i, name -> name to args?.get(i) } | |
.associate { it } | |
} | |
private val Method.returnsSingle: Boolean | |
get() = returnType.canonicalName == Single::class.java.canonicalName | |
private val Method.resultGenericTypeArgument: Type | |
@Suppress("CAST_NEVER_SUCCEEDS") | |
get() = (this.genericReturnType as ParameterizedType).actualTypeArguments.first() | |
private class NullJsonRpcCallResultException : Exception() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment