How to output that the server has been started - kotlin

I have the following code, that it going to start a http server:
class MainVerticle : CoroutineVerticle() {
override suspend fun start() {
val server = vertx.createHttpServer()
val router = Router.router(vertx)
router.route("/api/genders*")
.subRouter(GenderApi(vertx).create())
server.requestHandler(router)
.listen(8080)
.await()
}
}
Now, I would like to output, if the server has been successfully started or failed(in case the port has been already occupied).
Without the Coroutine, the codes would be:
class MainVerticle : AbstractVerticle() {
override fun start(startPromise: Promise<Void>) {
val server = vertx.createHttpServer()
val router = Router.router(vertx)
server.requestHandler(router).listen(8888) { http ->
if (http.succeeded()) {
startPromise.complete()
println("HTTP server started on port 8888")
} else {
println(http.cause())
startPromise.fail(http.cause());
}
}
}
}
Here I do output, if the server has been started success or not.

If you use coroutines, add a try/catch block:
class MainVerticle : CoroutineVerticle() {
override suspend fun start() {
val server = vertx.createHttpServer()
val router = Router.router(vertx)
router.route("/api/genders*")
.subRouter(GenderApi(vertx).create())
try {
server.requestHandler(router)
.listen(8080)
.await()
println("HTTP server started on port 8888")
} catch (e: Exception) {
println(http.cause())
throw e
}
}
}

Related

Building non-blocking VertX server with CoroutineVerticle

I'm experimenting with VertX+Couroutines and just want to check if this setup is blocking at any point or has potential issues that i need to be aware of.
For example, is runBlocking being used correctly in this instance or should i rather do a deployVerticle? And then inside requestHandler, i'm doing GlobalScope.launch, this seems to be discouraged, what is the correct scope to use here?
I've added VertX 4.0.0-milestone5 to my Gradle build script, i'm not using VertX Web:
val vertxVersion = "4.0.0-milestone5"
implementation("io.vertx:vertx-core:$vertxVersion") {
exclude(group = "com.fasterxml.jackson.core", module = "jackson-core")
exclude(group = "com.fasterxml.jackson.core", module = "jackson-databind")
exclude(group = "log4j", module = "log4j")
exclude(group = "org.apache.logging.log4j", module = "log4j-api")
exclude(group = "org.apache.logging.log4j", module = "log4j-core")
}
implementation("io.vertx:vertx-lang-kotlin:$vertxVersion")
implementation("io.vertx:vertx-lang-kotlin-coroutines:$vertxVersion")
Inside Routing.kt i have the following setup:
class Routing(
private val port: Int
) : CoroutineVerticle() {
override suspend fun start() {
Vertx.vertx().createHttpServer(
HttpServerOptions().setCompressionSupported(true)
).requestHandler { req ->
GlobalScope.launch {
try {
log.info("${req.method()}:${req.path()}")
req.response().setStatusCode(200).end("Hello World")
} catch (e: Exception) {
log.error(e.message ?: "", e)
req.response().setStatusCode(500).end("Something Went Wrong")
}
}
}.listen(port)
log.info("Listening on $port")
}
override suspend fun stop() {
}
companion object {
private val log = LoggerFactory.getLogger(Routing::class.java)
private val root = RoutingTree()
suspend fun setup(port: Int) {
Endpoint.all.forEach {
root.addPath(it.key, it.value)
}
log.info("\n" + root.toString())
Routing(port = port).start()
}
}
}
This Routing.setup is then used inside main()
object Server {
private val log = LoggerFactory.getLogger(this.javaClass)
#JvmStatic
#ExperimentalTime
fun main(args: Array<String>) = runBlocking {
....
// setup routing
Routing.setup(
port = if (ENV.env == LOCAL) {
5555
} else {
80
},
)
The whole point of Kotlin integration with Vert.x is that you don't have to use GlobalScope.launch
Here's a minimal example of how it can be achieved:
fun main() {
val vertx = Vertx.vertx()
vertx.deployVerticle("Server")
}
class Server : CoroutineVerticle() {
override suspend fun start() {
vertx.createHttpServer().requestHandler { req ->
// You already have access to all coroutine generators
launch {
// In this scope you can use suspending functions
delay(1000)
req.response().end("Done!")
}
}.listen(8888)
}
}

Kotlin coroutines, how to async alist of calls and return the result as a map

var responseMap = mutableMapOf<VendorType, ChargeResponse>()
requests.forEach {
val response = when (it.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> {
throw NotImplementedError("${it.vendorType} does not support yet")
}
}
responseMap[it.vendorType] = response
}
responseMap
So I want all the service.charge function run in separate thread. Return the map when all is done
Hope to solve your problem:
Assume your service and request like this:
interface Service {
suspend fun chargeForType1(): ChargeResponse
suspend fun chargeForType2(): ChargeResponse
}
data class Request(val vendorType: VendorType)
suspend fun requestAll(requests: List<Request>): Map<VendorType, ChargeResponse> {
return coroutineScope {
requests
.map { request ->
async {
request.vendorType to when (request.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> throw NotImplementedError("${request.vendorType} does not support yet")
}
}
}
.awaitAll()
.toMap()
}
}

Handling hundreds of routes in Vert.x best practices

Please have a look at the piece of code below. Now suppose i'll have hundreds of entity like "person". How would you code such a thing to get it clean, concise, efficient, well structured ? Tx
class HttpEntryPoint : CoroutineVerticle() {
private suspend fun person(r: RoutingContext) {
val res = vertx.eventBus().requestAwait<String>("/person/:id", "1").body()
r.response().end(res)
}
override suspend fun start() {
val router = Router.router(vertx)
router.get("/person/:id").coroutineHandler { ctx -> person(ctx) }
vertx.createHttpServer()
.requestHandler(router)
.listenAwait(config.getInteger("http.port", 8080))
}
fun Route.coroutineHandler(fn: suspend (RoutingContext) -> Unit) {
handler { ctx ->
launch(ctx.vertx().dispatcher()) {
try {
fn(ctx)
} catch (e: Exception) {
e.printStackTrace()
ctx.fail(e)
}
}
}
}
}
You're looking for subrouter.
https://vertx.io/docs/vertx-web/java/#_sub_routers
From the top of my head:
override suspend fun start() {
router.mountSubrouter("/person", personRouter(vertx))
// x100 if you'd like
}
Then in your PersonRouter.kt:
fun personRouter(vertx: Vertx): Router {
val router = Router.router(vertx)
router.get("/:id").coroutineHandler { ctx -> person(ctx) }
// More endpoints
return router
}

What is a clean way to wait for a response?

I am sending a message(custom protocol, no HTTP) to my server and want to wait for a response. It is working with the following code:
class Connection {
val messages: Observable<Message>
fun sendMessageWithAnswer(message: Message, timeout:Int = 10): Observable<Answer> {
if (!isConnected) {
return Observable.just(Answer.NoConnection)
}
val result = BehaviorSubject.create<Answer>()
val neverDisposed = messages.filter {
it.header.messageId == message.header.messageId
}
.map { Answer.Success(it) as Answer}
.mergeWith(Observable.timer(timeout.toLong(), TimeUnit.SECONDS)
.map { Answer.Timeout })
.take(1).singleOrError()
.subscribe(
{result.onNext(it)},
{
// Should never happen
throw IllegalStateException("Waiting for answer failed: $it")
}
)
sendMessage(message)
return result
}
}
The problem with this solution that "neverDisposed" gets never disposed, is this a memory leak?
My other solutions are not working for this test case:
#Test
fun ImmediateAnswer() {
prepare()
val message = ...
val answerObservable = connection.sendMessageWithAnswer(message, timeout = 1)
connection.receiveMessage(message)
val answer = answerObservable.test()
answer.awaitCount(1)
Thread.sleep(1000)
Assert.assertEquals(1, answer.valueCount())
Assert.assertEquals(Answer.Success(message), answer.values()[0])
}
Do you have a cleaner solution for this problem?

Vertx plus Kotlin coroutines hangs forever

I am rewriting some Java Vertx asynch code using Kotlin coroutines for learning purposes. However, when I try to test a simple HTTP call, the coroutine based test hangs forever and I really don't understand where is the issue. Here a reproducer:
#RunWith(VertxUnitRunner::class)
class HelloWorldTest {
private val vertx: Vertx = Vertx.vertx()
#Before
fun setUp(context: TestContext) {
// HelloWorldVerticle is a simple http server that replies "Hello, World!" to whatever call
vertx.deployVerticle(HelloWorldVerticle::class.java!!.getName(), context.asyncAssertSuccess())
}
// ORIGINAL ASYNC TEST HERE. IT WORKS AS EXPECTED
#Test
fun testAsync(context: TestContext) {
val atc = context.async()
vertx.createHttpClient().getNow(8080, "localhost", "/") { response ->
response.handler { body ->
context.assertTrue(body.toString().equals("Hello, World!"))
atc.complete()
}
}
}
// First attempt, it hangs forever, the response is never called
#Test
fun testSync1(context: TestContext) = runBlocking<Unit> {
val atc = context.async()
val body = await<HttpClientResponse> {
vertx.createHttpClient().getNow(8080, "localhost", "/", { response -> response.handler {it}} )
}
context.assertTrue(body.toString().equals("Hello, World!"))
atc.complete()
}
// Second attempt, it hangs forever, the response is never called
#Test
fun testSync2(context: TestContext) = runBlocking<Unit> {
val atc = context.async()
val response = await<HttpClientResponse> {
vertx.createHttpClient().getNow(8080, "localhost", "/", it )
}
response.handler { body ->
context.assertTrue(body.toString().equals("Hello, World!"))
atc.complete()
}
}
suspend fun <T> await(callback: (Handler<T>) -> Unit) =
suspendCoroutine<T> { cont ->
callback(Handler { result: T ->
cont.resume(result)
})
}
}
Is everyone able to figure out the issue?
It seems to me that your code have several problems:
you may running the test before the http-server got deployed
I believe that since you execute your code inside runBlocking you are blocking the event loop from completing the request.
Finally, I will advise you to use the HttpClienctResponse::bodyHandler method instead of HttpClientResponse::handler as the handler may receive partial data.
Here is an alternative solution that works fine:
import io.vertx.core.AbstractVerticle
import io.vertx.core.Future
import io.vertx.core.Handler
import io.vertx.core.Vertx
import io.vertx.core.buffer.Buffer
import io.vertx.core.http.HttpClientResponse
import kotlin.coroutines.experimental.Continuation
import kotlin.coroutines.experimental.EmptyCoroutineContext
import kotlin.coroutines.experimental.startCoroutine
import kotlin.coroutines.experimental.suspendCoroutine
inline suspend fun <T> await(crossinline callback: (Handler<T>) -> Unit) =
suspendCoroutine<T> { cont ->
callback(Handler { result: T ->
cont.resume(result)
})
}
fun <T : Any> async(code: suspend () -> T) = Future.future<T>().apply {
code.startCoroutine(object : Continuation<T> {
override val context = EmptyCoroutineContext
override fun resume(value: T) = complete()
override fun resumeWithException(exception: Throwable) = fail(exception)
})
}
fun main(args: Array<String>) {
async {
val vertx: Vertx = Vertx.vertx()
//0. take the current context
val ctx = vertx.getOrCreateContext()
//1. deploy the http server
await<Unit> { cont ->
vertx.deployVerticle(object : AbstractVerticle() {
override fun start() {
vertx.createHttpServer()
.requestHandler { it.response().end("Hello World") }
.listen(7777) { ctx.runOnContext { cont.handle(Unit) } }
//note that it is important tp complete the handler in the correct context
}
})
}
//2. send request
val response: HttpClientResponse = await { vertx.createHttpClient().getNow(7777, "localhost", "/", it) }
//3. await response
val body = await<Buffer> { response.bodyHandler(it) }
println("received $body")
}
}