aboutsummaryrefslogtreecommitdiff
path: root/kvision-server
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2018-04-26 15:41:32 +0200
committerRobert Jaros <rjaros@finn.pl>2018-04-26 15:41:32 +0200
commit95c1cdec5b0e65a0f617d5fa88d1d49a7abcfe95 (patch)
treeb3dc67083cad67d44a385605e4c915a0a05f47bb /kvision-server
parent5b9535e9964816eb228d3797f4c8a3e7676d1f53 (diff)
downloadkvision-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')
-rw-r--r--kvision-server/src/main/kotlin/pl/treksoft/kvision/remote/ServiceManager.kt270
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()
}