diff options
author | Robert Jaros <rjaros@finn.pl> | 2018-04-26 15:41:32 +0200 |
---|---|---|
committer | Robert Jaros <rjaros@finn.pl> | 2018-04-26 15:41:32 +0200 |
commit | 95c1cdec5b0e65a0f617d5fa88d1d49a7abcfe95 (patch) | |
tree | b3dc67083cad67d44a385605e4c915a0a05f47bb /kvision-server/src/main/kotlin/pl/treksoft | |
parent | 5b9535e9964816eb228d3797f4c8a3e7676d1f53 (diff) | |
download | kvision-95c1cdec5b0e65a0f617d5fa88d1d49a7abcfe95.tar.gz kvision-95c1cdec5b0e65a0f617d5fa88d1d49a7abcfe95.tar.bz2 kvision-95c1cdec5b0e65a0f617d5fa88d1d49a7abcfe95.zip |
Automatic JSON-RPC conectivity.
CallAgent component for remote JSON-RPC and AJAX.
Diffstat (limited to 'kvision-server/src/main/kotlin/pl/treksoft')
-rw-r--r-- | kvision-server/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt | 270 |
1 files changed, 185 insertions, 85 deletions
diff --git a/kvision-server/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt b/kvision-server/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt index 45a995ac..7b339621 100644 --- a/kvision-server/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt +++ b/kvision-server/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt @@ -24,7 +24,9 @@ package pl.treksoft.kvision.remote import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import kotlinx.coroutines.experimental.Deferred import kotlinx.coroutines.experimental.runBlocking +import org.jooby.Response import org.jooby.Status +import org.slf4j.LoggerFactory /** * Multiplatform service manager. @@ -32,6 +34,10 @@ import org.jooby.Status @Suppress("EXPERIMENTAL_FEATURE_WARNING") actual open class ServiceManager<out T> actual constructor(val service: T?) { + companion object { + val LOG = LoggerFactory.getLogger(ServiceManager::class.java.name) + } + protected val routes: MutableList<JoobyServer.() -> Unit> = mutableListOf() val mapper = jacksonObjectMapper() @@ -39,24 +45,33 @@ actual open class ServiceManager<out T> actual constructor(val service: T?) { * Binds a given route with a function of the receiver. * @param route a route * @param function a function of the receiver + * @param method a HTTP method + * @param prefix an URL address prefix */ protected actual inline fun <reified RET> bind( route: String, - noinline function: T.(Request?) -> Deferred<RET> + noinline function: T.(Request?) -> Deferred<RET>, method: RpcHttpMethod, prefix: String ) { routes.add({ - post("$SERVICE_PREFIX/$route") { req, res -> + call(method, "$prefix$route") { req, res -> if (service != null) { + val jsonRpcRequest = req.body(JsonRpcRequest::class.java) try { - res.send(runBlocking { function.invoke(service, req).await() }) + val result = runBlocking { function.invoke(service, req).await() } + res.send( + JsonRpcResponse( + id = jsonRpcRequest.id, + result = mapper.writeValueAsString(result) + ) + ) } catch (e: Exception) { - e.printStackTrace() - res.status(500).send(e.message ?: "Error") + LOG.error(e.message, e) + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = e.message ?: "Error")) } } else { - res.status(Status.BAD_REQUEST) + res.status(Status.SERVER_ERROR) } - } + }.invoke(this) }) } @@ -64,29 +79,38 @@ actual open class ServiceManager<out T> actual constructor(val service: T?) { * Binds a given route with a function of the receiver. * @param route a route * @param function a function of the receiver + * @param method a HTTP method + * @param prefix an URL address prefix */ protected actual inline fun <reified PAR, reified RET> bind( route: String, - noinline function: T.(PAR, Request?) -> Deferred<RET> + noinline function: T.(PAR, Request?) -> Deferred<RET>, method: RpcHttpMethod, prefix: String ) { routes.add({ - post("$SERVICE_PREFIX/$route") { req, res -> - val param = try { - req.body(PAR::class.java) - } catch (e: Exception) { - null as PAR - } + call(method, "$prefix$route") { req, res -> if (service != null) { - try { - res.send(runBlocking { function.invoke(service, param, req).await() }) - } catch (e: Exception) { - e.printStackTrace() - res.status(500).send(e.message ?: "Error") + val jsonRpcRequest = req.body(JsonRpcRequest::class.java) + if (jsonRpcRequest.params.size == 1) { + val param = getParameter<PAR>(jsonRpcRequest.params[0]) + try { + val result = runBlocking { function.invoke(service, param, req).await() } + res.send( + JsonRpcResponse( + id = jsonRpcRequest.id, + result = mapper.writeValueAsString(result) + ) + ) + } catch (e: Exception) { + LOG.error(e.message, e) + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = e.message ?: "Error")) + } + } else { + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = "Invalid parameters")) } } else { - res.status(Status.BAD_REQUEST) + res.status(Status.SERVER_ERROR) } - } + }.invoke(this) }) } @@ -94,28 +118,39 @@ actual open class ServiceManager<out T> actual constructor(val service: T?) { * Binds a given route with a function of the receiver. * @param route a route * @param function a function of the receiver + * @param method a HTTP method + * @param prefix an URL address prefix */ protected actual inline fun <reified PAR1, reified PAR2, reified RET> bind( route: String, - noinline function: T.(PAR1, PAR2, Request?) -> Deferred<RET> + noinline function: T.(PAR1, PAR2, Request?) -> Deferred<RET>, method: RpcHttpMethod, prefix: String ) { routes.add({ - post("$SERVICE_PREFIX/$route") { req, res -> - val str = req.body(String::class.java) - val tree = mapper.readTree(str) - if (tree.size() == 2 && service != null) { - val p1 = mapper.treeToValue(tree[0], PAR1::class.java) - val p2 = mapper.treeToValue(tree[1], PAR2::class.java) - try { - res.send(runBlocking { function.invoke(service, p1, p2, req).await() }) - } catch (e: Exception) { - e.printStackTrace() - res.status(500).send(e.message ?: "Error") + call(method, "$prefix$route") { req, res -> + if (service != null) { + val jsonRpcRequest = req.body(JsonRpcRequest::class.java) + if (jsonRpcRequest.params.size == 2) { + val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) + val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) + try { + val result = runBlocking { function.invoke(service, param1, param2, req).await() } + res.send( + JsonRpcResponse( + id = jsonRpcRequest.id, + result = mapper.writeValueAsString(result) + ) + ) + } catch (e: Exception) { + LOG.error(e.message, e) + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = e.message ?: "Error")) + } + } else { + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = "Invalid parameters")) } } else { - res.status(Status.BAD_REQUEST) + res.status(Status.SERVER_ERROR) } - } + }.invoke(this) }) } @@ -123,29 +158,40 @@ actual open class ServiceManager<out T> actual constructor(val service: T?) { * Binds a given route with a function of the receiver. * @param route a route * @param function a function of the receiver + * @param method a HTTP method + * @param prefix an URL address prefix */ protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, reified RET> bind( route: String, - noinline function: T.(PAR1, PAR2, PAR3, Request?) -> Deferred<RET> + noinline function: T.(PAR1, PAR2, PAR3, Request?) -> Deferred<RET>, method: RpcHttpMethod, prefix: String ) { routes.add({ - post("$SERVICE_PREFIX/$route") { req, res -> - val str = req.body(String::class.java) - val tree = mapper.readTree(str) - if (tree.size() == 3 && service != null) { - val p1 = mapper.treeToValue(tree[0], PAR1::class.java) - val p2 = mapper.treeToValue(tree[1], PAR2::class.java) - val p3 = mapper.treeToValue(tree[2], PAR3::class.java) - try { - res.send(runBlocking { function.invoke(service, p1, p2, p3, req).await() }) - } catch (e: Exception) { - e.printStackTrace() - res.status(500).send(e.message ?: "Error") + call(method, "$prefix$route") { req, res -> + if (service != null) { + val jsonRpcRequest = req.body(JsonRpcRequest::class.java) + if (jsonRpcRequest.params.size == 3) { + val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) + val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) + val param3 = getParameter<PAR3>(jsonRpcRequest.params[2]) + try { + val result = runBlocking { function.invoke(service, param1, param2, param3, req).await() } + res.send( + JsonRpcResponse( + id = jsonRpcRequest.id, + result = mapper.writeValueAsString(result) + ) + ) + } catch (e: Exception) { + LOG.error(e.message, e) + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = e.message ?: "Error")) + } + } else { + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = "Invalid parameters")) } } else { - res.status(Status.BAD_REQUEST) + res.status(Status.SERVER_ERROR) } - } + }.invoke(this) }) } @@ -153,30 +199,42 @@ actual open class ServiceManager<out T> actual constructor(val service: T?) { * Binds a given route with a function of the receiver. * @param route a route * @param function a function of the receiver + * @param method a HTTP method + * @param prefix an URL address prefix */ protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, reified PAR4, reified RET> bind( route: String, - noinline function: T.(PAR1, PAR2, PAR3, PAR4, Request?) -> Deferred<RET> + noinline function: T.(PAR1, PAR2, PAR3, PAR4, Request?) -> Deferred<RET>, method: RpcHttpMethod, prefix: String ) { routes.add({ - post("$SERVICE_PREFIX/$route") { req, res -> - val str = req.body(String::class.java) - val tree = mapper.readTree(str) - if (tree.size() == 4 && service != null) { - val p1 = mapper.treeToValue(tree[0], PAR1::class.java) - val p2 = mapper.treeToValue(tree[1], PAR2::class.java) - val p3 = mapper.treeToValue(tree[2], PAR3::class.java) - val p4 = mapper.treeToValue(tree[3], PAR4::class.java) - try { - res.send(runBlocking { function.invoke(service, p1, p2, p3, p4, req).await() }) - } catch (e: Exception) { - e.printStackTrace() - res.status(500).send(e.message ?: "Error") + call(method, "$prefix$route") { req, res -> + if (service != null) { + val jsonRpcRequest = req.body(JsonRpcRequest::class.java) + if (jsonRpcRequest.params.size == 4) { + val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) + val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) + val param3 = getParameter<PAR3>(jsonRpcRequest.params[2]) + val param4 = getParameter<PAR4>(jsonRpcRequest.params[3]) + try { + val result = + runBlocking { function.invoke(service, param1, param2, param3, param4, req).await() } + res.send( + JsonRpcResponse( + id = jsonRpcRequest.id, + result = mapper.writeValueAsString(result) + ) + ) + } catch (e: Exception) { + LOG.error(e.message, e) + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = e.message ?: "Error")) + } + } else { + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = "Invalid parameters")) } } else { - res.status(Status.BAD_REQUEST) + res.status(Status.SERVER_ERROR) } - } + }.invoke(this) }) } @@ -184,34 +242,76 @@ actual open class ServiceManager<out T> actual constructor(val service: T?) { * Binds a given route with a function of the receiver. * @param route a route * @param function a function of the receiver + * @param method a HTTP method + * @param prefix an URL address prefix */ - protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, reified PAR4, reified PAR5, reified RET> bind( + protected actual inline fun <reified PAR1, reified PAR2, reified PAR3, + reified PAR4, reified PAR5, reified RET> bind( route: String, - noinline function: T.(PAR1, PAR2, PAR3, PAR4, PAR5, Request?) -> Deferred<RET> + noinline function: T.(PAR1, PAR2, PAR3, PAR4, PAR5, Request?) -> Deferred<RET>, + method: RpcHttpMethod, + prefix: String ) { routes.add({ - post("$SERVICE_PREFIX/$route") { req, res -> - val str = req.body(String::class.java) - val tree = mapper.readTree(str) - if (tree.size() == 5 && service != null) { - val p1 = mapper.treeToValue(tree[0], PAR1::class.java) - val p2 = mapper.treeToValue(tree[1], PAR2::class.java) - val p3 = mapper.treeToValue(tree[2], PAR3::class.java) - val p4 = mapper.treeToValue(tree[3], PAR4::class.java) - val p5 = mapper.treeToValue(tree[4], PAR5::class.java) - try { - res.send(runBlocking { function.invoke(service, p1, p2, p3, p4, p5, req).await() }) - } catch (e: Exception) { - e.printStackTrace() - res.status(500).send(e.message ?: "Error") + call(method, "$prefix$route") { req, res -> + if (service != null) { + val jsonRpcRequest = req.body(JsonRpcRequest::class.java) + if (jsonRpcRequest.params.size == 5) { + val param1 = getParameter<PAR1>(jsonRpcRequest.params[0]) + val param2 = getParameter<PAR2>(jsonRpcRequest.params[1]) + val param3 = getParameter<PAR3>(jsonRpcRequest.params[2]) + val param4 = getParameter<PAR4>(jsonRpcRequest.params[3]) + val param5 = getParameter<PAR5>(jsonRpcRequest.params[4]) + try { + val result = + runBlocking { + function.invoke(service, param1, param2, param3, param4, param5, req).await() + } + res.send( + JsonRpcResponse( + id = jsonRpcRequest.id, + result = mapper.writeValueAsString(result) + ) + ) + } catch (e: Exception) { + LOG.error(e.message, e) + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = e.message ?: "Error")) + } + } else { + res.send(JsonRpcResponse(id = jsonRpcRequest.id, error = "Invalid parameters")) } } else { - res.status(Status.BAD_REQUEST) + res.status(Status.SERVER_ERROR) } - } + }.invoke(this) }) } + fun call( + method: RpcHttpMethod, + path: String, + handler: (Request, Response) -> Unit + ): JoobyServer.() -> Unit { + return { + when (method) { + RpcHttpMethod.POST -> post(path, handler) + RpcHttpMethod.PUT -> put(path, handler) + RpcHttpMethod.DELETE -> delete(path, handler) + RpcHttpMethod.OPTIONS -> options(path, handler) + } + } + } + + protected inline fun <reified T> getParameter(str: String?): T { + return str?.let { + if (T::class == String::class) { + str as T + } else { + mapper.readValue(str, T::class.java) + } + } ?: null as T + } + /** * Applies all defined routes to the given server. * @param k a Jooby server @@ -226,5 +326,5 @@ actual open class ServiceManager<out T> actual constructor(val service: T?) { * Returns the list of defined bindings. * Not used on the jvm platform. */ - actual fun getCalls(): Map<String, String> = mapOf() + actual fun getCalls(): Map<String, Pair<String, RpcHttpMethod>> = mapOf() } |