Vertx - This Route is exclusive for already mounted sub router issue - vert.x

I am trying to create 2 Verticles one for the http server and another for my sockjs eventbus bridge. Then I am trying to deplowy the two Verticles in the Main app. For for some reason I get this error in my Sockjs Verticle.
java.lang.IllegalStateException: This Route is exclusive for already mounted sub router.
Router router = Router.router(vertx);
vertx.deployVerticle(new RestApiVerticle(router))
.onFailure(startPromise::fail)
.onSuccess(id -> {
LOG.info("Deployed {} with id {}", RestApiVerticle.class.getSimpleName(), id);
startPromise.complete();
});
vertx.deployVerticle(new com.ali.websockjs.WebSocketVerticle(router))
.onFailure(startPromise::fail)
.onSuccess(id -> {
LOG.info("Deployed {} with id {}", com.ali.websockjs.WebSocketVerticle.class.getSimpleName(), id);
startPromise.complete();
});
In my RestApiVerticle
private Router router;
public RestApiVerticle(Router router) {
this.router = router;
}
router.route().handler(bodyHandler).failureHandler(FailureResponse.handleFailure());
router.route("/").handler(req -> req.response().end("Hello there!"))
.failureHandler(error -> LOG.error("Something is wrong with server!!"));
and in my WebSocketVerticle, where I get error
private Router router;
public WebSocketVerticle(Router router) {
this.router = router;
}
Below code I get error
// mount the bridge on the router
router.mountSubRouter("/eventbus", subRouter).failureHandler(error -> LOG.error("error {}", error));
Please help!!!!!!
UPDATE
Doing your approach is giving my CORS error in the browser
Access to XMLHttpRequest at 'http://localhost:1234/eventbus/info?t=1653982669356' from origin 'http://localhost:4200' has been blocked by CORS policy:
vertx.deployVerticle(new RestApiVerticle(router))
.compose(var ->
vertx.deployVerticle(new WebSocketVerticle(router)))
.onFailure(startPromise::fail)
.onSuccess(server -> {
LOG.debug("Server and Sockjs started !!");
startPromise.complete();
});
any suggestion??

The error is telling you that there is a configuration problem with your setup. In a way this is what is happening:
You have a router and in this router you have a route /eventbus that transfers the route evaluation from the 1st router to the 2nd (the sockjs router).
It seems that your code is trying to call that setup twice. This means that there's something wrong. The problem is that on that given route, if you transfer the execution to the 2nd router, the 3rd one (causing the problem) will never be called because the processing is now happening on the 2nd router.
I'd say that probably you need to debug your application startup and see why is the setup being called more than once.
My assumption here is that the problem you're seeing is coming from a race condition on your deployment. You're calling:
vertx.deployVerticle(new RestApiVerticle(router))
vertx.deployVerticle(new WebSocketVerticle(router))
These are asynchronous operations, and they will race to add their handlers to the shared router, now it could be that the WebSocketVerticle runs first and adds the eventbus handler, and then the RestApiVerticle runs and tries to add more handlers and this is problematic (like described above).
I believe the solution is to use composition to ensure your handlers are added in the right order:
vertx.deployVerticle(new RestApiVerticle(router))
.compose(1stDeploymentId ->
vertx.deployVerticle(new WebSocketVerticle(router)))
.onSuccess(2ndDeploymentId -> startPromise.complete();
.onFailure(startPromise::fail);
You may want to re-arrange the composition if you need to log the deployment ids.

Related

Response not submitted when rxEnd is used in HTTP server

I have a two verticle server written in vert.x + reactive extensions. HTTP server verticle uses event bus to send requests to the DB verticle. After receiving the response from the DB verticle (through event bus) I send the response to the http client using rxEnd. However clients does not seem to receive this response and times out eventually. If I were to use end() instead, things work fine. I use postman to test this REST API. Please see below for the code which forward results from the DB verticle to client.
routerFactory.addHandlerByOperationId("createChargePoints", routingContext -> {
RequestParameters params = routingContext.get("parsedParameters");
RequestParameter body = params.body();
JsonObject jsonBody = body.getJsonObject();
vertx.eventBus().rxRequest("dbin", jsonBody)
.map(message -> {
System.out.println(message.body());
return routingContext.response().setStatusCode(200).rxEnd(message.body().toString());
})
.subscribe(res -> {
System.out.println(res);
}, res -> {
System.out.println(res);
});
});
The rxEnd method is a variant of end that returns a Completable. The former is lazy, the latter is not.
In other words, if you invoke rxEnd you have to subscribe to the Completable otherwise nothing happens.
Looking at the code of your snippet, I don't believe using rxEnd is necessary. Indeed, it doesn't seem like you need to know if the request was sent succesfully.

RxJava disposing Observable/Flowable in a microservice Server project

I am using RxJava for a server microservice project, where using Jetty as HTTP Servlet server.
I am handling requests either from client or main server with Observable for different flows.
When a request hitting the api below, I will return a Response after Observable finishes the job.
#GET
#Path("{uuid}")
#Produces(MediaType.APPLICATION_JSON)
public Response doThingsForClient(#PathParam("uuid") String uuid) {
Worker worker = new Worker(uuid);
worker.run();
return Response.ok("Awesome").build();
}
class Worker {
String uuid = null;
public Worker(String uuid) {
this.uuid = uuid;
}
public void run() {
Observable.concat(Observable1,Observable2,Observable3);
}
}
I am wondering if I need to dispose these Observables or Flowables.
According to this: Does RxJava2 auto dispose observable when they call completed or error?
and the RxJava3 sourcecode, i don't think Flowable at least is disposed automatically?
If I need to manually dispose the resources,
Is it better to create a CompositeDisposable, then add disposable to the CompositeDisposable at each Observer(Observable1...Observable3)'s onSubscribe() being called, call compositeDisposable.dispose() after the concat finishes.
Should I also monitor the Jetty AbstractLifeCycle to dispose these Observables(It sounds similar as Android)? I am not sure how other people are using RxJava at the server side, open to any suggestions to these questions and general Rx approach at server projects.
Thanks!

Spring Cloud Gateway Routing Based On Content of the Request Body

I need to create a reverse proxy that takes incoming request and based on the content of the request body, route the request to specific URI.
This is for a routing micro service that acts like a reverse proxy and does routing based on some information from each request body. This means for each request I need to parse the request body and get the "username" field and then make a JDBC connection to fetch additional information from the database. Based on that information in database, it would finally redirect the request to the correct URI.
From what I have now, I have 2 blocking methods. The first one is the parsing for the request body, the other one is the JDBC connection to the database. I understand that I should not put any blocking calls inside the gateway filter. I just don't know what I should do in this case. I could have both operations running async but in the end I still need the information from database to do routing.
#Bean
public RouteLocator apiLocator(RouteLocatorBuilder builder, XmlMapper xmlMapper) {
return builder.routes()
.route(r -> r
.path("/test")
.and()
.readBody(String.class, s -> true) // Read the request body, data will be cached as cachedRequestBodyObject
.filters(f -> f.filter(new GatewayFilter() {
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
// The following method is blocking and should not be put here
xmlMapper.readValue((String) exchange.getAttribute("cachedRequestBodyObject"), Map.class);
} catch (Exception e) {
//TODO
}
return chain.filter(exchange);
}
}))
.uri("http://localhost:8080"))
.build();
}
The above example only includes the blocking parsing as my request body is XML based. My IDE is warning me of having a blocking call there which I really appreciate.
Any help is greatly appreciated. Thank you everyone!
After some research, Mono.fromCallable seems to be a good fit. I then asked the same question directly under the github repo, it turns out that using a servlet app may be better. For anyone who is interested to see what I came up with, please take a look here https://github.com/spring-cloud/spring-cloud-gateway/issues/1229

vertx timeout if async result is failed

I am seeing a timeout in the browser when the server-side service ends in a failed result. Everything works fine if the service call succeeds but it seems as though the browser never receives a response if the call fails.
My service passes a result handler to a DAO containing the following code:
final SQLConnection conn = ar.result();
conn.updateWithParams(INSERT_SQL, params, insertAsyncResult -> {
if (insertAsyncResult.failed()) {
conn.close();
resultHandler.handle(ServiceException.fail(1, "TODO"));
} else {
resultHandler.handle(Future.succeededFuture());
}
});
I'm not sure where to go from here. How do I debug what the framework is sending back to the client?
The problem was that I needed to register a ServiceExceptionMessageCodec in an intermediate Verticle, one that was sitting between the browser and the Verticle that was performing the database operation.

How to get all established connections to vertx based http server?

I have got https server run by vertx http stack. Is there a way to get client IP addresses for all established connections (i.e. for audit, security, monitoring, QoS and other purposes). Ideally, I would like to have event (callback) driven API notifying about established and closed connections. How can I achieve it?
Current workaround is to poll a tool similar to netstat, but it is very inconvenient and not really real-time (i.e. short connections can be missed).
Github community responded with the answer: https://github.com/vert-x3/vertx-web/issues/685
You can use the HttpServer connection handler and manage it yourself easily:
server.connectionHandler(conn -> {
// Track conn.remoteAddress()
conn.closeHandler(v -> {
// Cleanup track of conn.remoteAddress()
});
});
In my example I'll write all IP addresses to a console. But of course you could write it to a log file, DB, or some other place of your choice.
Vertx vertx = Vertx.vertx();
// This your regular router
Router router = Router.router(vertx);
// We'll have two routes, just for fun
router.route("/a").handler((ctx) -> {
ctx.response().end("A");
});
router.route("/b").handler((ctx) -> {
ctx.response().end("B");
});
Router filterRouter = Router.router(vertx);
filterRouter.get().handler((ctx)->{
System.out.println("IP is " + ctx.request().remoteAddress());
// Forward to your actual router
ctx.next();
});
filterRouter.mountSubRouter("/", router);
vertx.createHttpServer().requestHandler(filterRouter::accept).listen(8080);