softshipper
softshipper

Reputation: 34071

Only one sub router per Route object is allowed

I have created a web app with Vert.x as the following:

class MainVerticle : AbstractVerticle() {

  private fun apiResource(r: Router): Unit {
    r.route("/api/*")
      .subRouter(GenderApi(vertx).create())
      .subRouter(InterestApi(vertx).create())
  }

  private fun rootResource(r: Router): Unit {
    r.route("/").handler {
      val res = it.response()
      res.putHeader("content-type", "text/plain");

      // Write to the response and end it
      res.end("I am healthy");
    }
  }

  override fun start(startPromise: Promise<Void>) {
    val server = vertx.createHttpServer()

    val router = Router.router(vertx)

    apiResource(router)
    rootResource(router)
    server.requestHandler(router).listen(8888) { http ->
      if (http.succeeded()) {
        startPromise.complete()
        println("HTTP server started on port 8888")
      } else {
        startPromise.fail(http.cause());
      }
    }
  }
}


class InterestApi(private val vertx: Vertx) {

  private val router = Router.router(vertx)

  init {
     readAll()
  }

  private fun readAll(): Route =
    router
      .get("/interests")
      .handler {
        val res = it.response()
        res.putHeader("content-type", "text/plain");

        // Write to the response and end it
        res.end("I am genders path");
      }


  fun create(): Router = router

}


class GenderApi(private val vertx: Vertx) {

  private val router = Router.router(vertx)

  init {
    readAll()
  }

  private fun readAll() : Route =
    router
      .get("/genders")
      .handler {
        val res = it.response()
        res.putHeader("content-type", "text/plain");

        // Write to the response and end it
        res.end("I am genders path");
      }

  fun create(): Router = router
}

and the compiler complains:

java.lang.IllegalStateException: Only one sub router per Route object is allowed.
        at io.vertx.ext.web.impl.RouteImpl.subRouter(RouteImpl.java:161)
        at io.databaker.MainVerticle.apiResource(MainVerticle.kt:15)
        at io.databaker.MainVerticle.start(MainVerticle.kt:33)
        at io.vertx.core.impl.DeploymentManager.lambda$doDeploy$5(DeploymentManager.java:196)
        at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:96)
        at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:59)
        at io.vertx.core.impl.EventLoopContext.lambda$runOnContext$0(EventLoopContext.java:40)
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:834)

How to solve it?

Upvotes: 0

Views: 188

Answers (2)

Alexey Soshin
Alexey Soshin

Reputation: 17711

There is a better way to achieve that functionality, though. Instead of using subRouter, you can use mountSubRouter:

class MainVerticle : AbstractVerticle() {

    private fun apiResource(r: Router) {
        r.mountSubRouter("/api/", GenderApi(vertx).create())

        r.mountSubRouter("/api/", InterestApi(vertx).create())
    }

    private fun rootResource(r: Router): Unit {
        r.route("/").handler {
            val res = it.response()
            res.putHeader("content-type", "text/plain");

            // Write to the response and end it
            res.end("I am healthy");
        }
    }

    override fun start(startPromise: Promise<Void>) {
        val server = vertx.createHttpServer()

        val router = Router.router(vertx)

        apiResource(router)
        rootResource(router)
        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());
            }
        }
    }
}


class InterestApi(vertx: Vertx) {

    private val router = Router.router(vertx)

    init {
        readAll()
    }

    private fun readAll(): Route =
        router
            .get("/interests")
            .handler {
                val res = it.response()
                res.putHeader("content-type", "text/plain");

                // Write to the response and end it
                res.end("I am interests path");
            }


    fun create(): Router = router
}


class GenderApi(vertx: Vertx) {

    private val router = Router.router(vertx)

    init {
        readAll()
    }

    private fun readAll(): Route =
        router
            .get("/genders")
            .handler {
                val res = it.response()
                res.putHeader("content-type", "text/plain");

                // Write to the response and end it
                res.end("I am genders path");
            }

    fun create(): Router = router
}

Note that when using mountSubRouter, you don't need * after /api/*, and in fact, this will produce an error.

Upvotes: 0

Alexey Soshin
Alexey Soshin

Reputation: 17711

I think I misled you with my previous answer.

The correct syntax for Vertx 4 is:

class MainVerticle : AbstractVerticle() {

    private fun apiResource(r: Router) {
        r.route("/api/genders*")
            .subRouter(GenderApi(vertx).create())
        r.route("/api/interests*")
            .subRouter(InterestApi(vertx).create())
    }

    private fun rootResource(r: Router): Unit {
        r.route("/").handler {
            val res = it.response()
            res.putHeader("content-type", "text/plain");

            // Write to the response and end it
            res.end("I am healthy");
        }
    }

    override fun start(startPromise: Promise<Void>) {
        val server = vertx.createHttpServer()

        val router = Router.router(vertx)

        apiResource(router)
        rootResource(router)
        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());
            }
        }
    }
}


class InterestApi(vertx: Vertx) {

    private val router = Router.router(vertx)

    init {
        readAll()
    }

    private fun readAll(): Route =
        router
            .get("/")
            .handler {
                val res = it.response()
                res.putHeader("content-type", "text/plain");

                // Write to the response and end it
                res.end("I am genders path");
            }


    fun create(): Router = router
}


class GenderApi(vertx: Vertx) {

    private val router = Router.router(vertx)

    init {
        readAll()
    }

    private fun readAll(): Route =
        router
            .get("/")
            .handler {
                val res = it.response()
                res.putHeader("content-type", "text/plain");

                // Write to the response and end it
                res.end("I am genders path");
            }

    fun create(): Router = router
}

Using sub routers in Vert.x makes sense if you plan to use Interests as a resource.

Then you would have something like:

router.get(...)
router.post(...)

Upvotes: 2

Related Questions