If you run the following code multiple times you will see the inconsistency: some times there are 3 lines displayed, some times there are only 2 lines displayed (the one missing is "Successfully stopped MyVerticle"). Why the .stop method is not called?
public class Main {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.rxDeployVerticle(new MyVerticle()).subscribe();
Runtime.getRuntime().addShutdownHook(
new Thread(() -> {
//vertx.deploymentIDs().forEach( deploymentId -> vertx.undeploy(deploymentId));
vertx.close(result -> System.out.println("Result" + result));
System.out.println("Successfully stopped Vertx");
})
);
}
}
class MyVerticle extends AbstractVerticle {
#Override
public void start(Future<Void> startFuture) {
System.out.println("Successfully started MyVerticle");
startFuture.complete();
}
#Override
public void stop(Future<Void> stopFuture) {
System.out.println("Successfully stopped MyVerticle");
stopFuture.complete();
}
}
The method stop() is invoked when Vert.x undeploys a verticle.
When terminating your application, Vert.x will attempt to undeploy the verticles as well, but it's a race between event loop still running and your application shutting down.
Related
I'm just beginning to learn Vert.x and how to code Verticles. I wonder if it makes any sense to deploy a Verticle from within an Application server or Web server like Tomcat. For example:
public class HelloVerticle extends AbstractVerticle {
private final Logger logger = LoggerFactory.getLogger(HelloVerticle.class);
private long counter = 1;
#Override
public void start() {
vertx.setPeriodic(5000, id -> {
logger.info("tick");
});
vertx.createHttpServer()
.requestHandler(req -> {
logger.info("Request #{} from {}", counter++, req.remoteAddress().host());
req.response().end("Hello!");
})
.listen(9080);
logger.info("Open http://localhost:9080/");
}
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(new HelloVerticle());
}
}
Obviously the main method needs to be replaced by some ContextListener of any trigger provided by the Application Server. Does it make any sense or it's not supposed to use Vert.x in this Context?
Thanks
Using Vert.x as a Verticle inside a Tomcat app doesn't make much sense from my POV, because it defeats the whole point of componentization.
On the other hand you might want to simply connect to Event Bus to send/publish/receive messages, and is fairly easy to achieve.
I did it for a Grails (SB-based) project and put the Vertx stuff inside a service like:
class VertxService {
Vertx vertx
#PostConstruct
void init() {
def options = [:]
Vertx.clusteredVertx(options){ res ->
if (res.succeeded())
vertx = res.result()
else
System.exit( -1 )
})
}
void publish( addr, msg ){ vertx.publish addr, msg }
//...
}
Well as I understood from the documentation using singel.observeOn(Scheduler) will guarantee that any downstream event will be executed on that scheduler.
Apparently the onError called on the same scheduler that thrown the error as I receiving this error -
> Caused by:java.lang.IllegalStateException: Cannot invoke setValue on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:443)
at androidx.lifecycle.LiveData.setValue(LiveData.java:286)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:33)
at com.bonimoo.womlauncher.presentation.wizard.registration.RegistrationViewModel$1.onError(RegistrationViewModel.java:89)
at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver.onError(SingleFlatMap.java:116)
at io.reactivex.internal.observers.ResumeSingleObserver.onError(ResumeSingleObserver.java:51)
at io.reactivex.internal.disposables.EmptyDisposable.error(EmptyDisposable.java:78)
at io.reactivex.internal.operators.single.SingleError.subscribeActual(SingleError.java:42)
at io.reactivex.Single.subscribe(Single.java:3603)
out of this code -
public static<T> SingleTransformer<T,T> getSingleTransformer(){
return upstream -> upstream
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
GetHotelsList getHotelsList = new GetHotelsList(
AsyncTransformers.getSingleTransformer(), networkRepo);
getHotelsList.getHotels()
.map(hotels->
CollectionsUtil.mapList(hotels,
RegistrationMappers::mapHotelToPresentationHotel)
)
.flatMap((Function<List<HotelPresentation>, SingleSource<List<HotelPresentation>>>) hotelPresentations ->
Completable.timer(5,TimeUnit.SECONDS, Schedulers.io())
.andThen(Single.error(new Throwable()))
)
.subscribe(new SingleObserver<List<HotelPresentation>>() {
#Override
public void onSubscribe(Disposable d) {
addDisposable(d);
RegistrationState currentState = stateLiveData.getValue();
stateLiveData.setValue(currentState.newBuilder().setLoadingHotels(true).build());
}
#Override
public void onSuccess(List<HotelPresentation> hotelPresentations) {
RegistrationState currentState = stateLiveData.getValue();
stateLiveData.setValue(currentState.newBuilder().setHotelsList(hotelPresentations).setLoadingHotels(false).build());
}
#Override
public void onError(Throwable e) {
RegistrationState currentState = stateLiveData.getValue();
stateLiveData.setValue(currentState.newBuilder().setLoadingHotels(false).build());
}
});
and the debugger shown that onError called on RxSchedulerIoThread.
I have the following verticle for testing purposes:
public class UserVerticle extends AbstractVerticle {
private static final Logger log = LoggerFactory.getLogger(UserVerticle.class);
#Override
public void start(Future<Void> sf) {
log.info("start()");
JsonObject cnf = config();
log.info("start.config={}", cnf.toString());
sf.complete();
}
#Override
public void stop(Future<Void> sf) {
log.info("stop()");
sf.complete();
}
private void onMessage(Message<JsonObject> message) { ... }
log.info("onMessage(message={})", message);
}
}
Is is deployed from the main verticle with
vertx.deployVerticle("org.buguigny.cluster.UserVerticle",
new DeploymentOptions()
.setInstances(1)
.setConfig(new JsonObject()
.put(some_key, some_data)
),
ar -> {
if(ar.succeeded()) {
log.info("UserVerticle(uname={}, addr={}) deployed", uname, addr);
// continue when OK
}
else {
log.error("Could not deploy UserVerticle(uname={}). Cause: {}", uname, ar.cause());
// continue when KO
}
});
This code works fine.
I had a look at the Verticle documentation and discovered an init() callback method I didn't see before. As the documentation doesn't say much about what it really does, I defined it to see where in the life cycle of a verticle it gets called.
#Override
public void init(Vertx vertx, Context context) {
log.info("init()");
JsonObject cnf = context.config();
log.info("init.config={}", cnf.toString());
}
However, when init() is defined I get a java.lang.NullPointerException on the line where I call JsonObject cnf = config(); in start():
java.lang.NullPointerException: null
at io.vertx.core.AbstractVerticle.config(AbstractVerticle.java:85)
at org.buguigny.cluster.UserVerticle.start(UserVerticle.java:30)
at io.vertx.core.impl.DeploymentManager.lambda$doDeploy$8(DeploymentManager.java:494)
at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:320)
at io.vertx.core.impl.EventLoopContext.lambda$executeAsync$0(EventLoopContext.java:38)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:462)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
My questions are:
Q1 : any clue why NullPointerException is thrown?
Q2 : what is the purpose of init()? Is it internal to Vertx or can it be be implemented by client code to, for example, define some fields in the verticle objects passed in deployment config ?
The init method is for internal usage and documented as such in the Javadoc. Here's the source code:
/**
* Initialise the verticle.<p>
* This is called by Vert.x when the verticle instance is deployed. Don't call it yourself.
* #param vertx the deploying Vert.x instance
* #param context the context of the verticle
*/
#Override
public void init(Vertx vertx, Context context) {
this.vertx = vertx;
this.context = context;
}
If init is documented in any user documentation it's a mistake, please report it.
Vert.x v3.5.1.
There is my custom start method of Verticle:
#Override
public void start(Future<Void> startFuture) throws Exception {
startFuture.setHandler(event -> {
if (event.succeeded()) {
logger.info("Server started on port: {}", 8080);
} else {
logger.warn("Failed to start: {}", event.cause());
}
});
vertx.createHttpServer()
.requestHandler(router()::accept)
.listen(8080, event -> {
if (event.succeeded()) {
startFuture.complete();
} else {
startFuture.fail(event.cause());
}
});
}
I expect that my custom handler will be invoked when future completes. But it doesn't!
In debug mode I see that FutureImpl::setHandler is called twice: once with my custom handler, and then with DeploymentManager's handler during doDeploy.
So the latest handler is applied.
The question is: is it possible to specify custom callback for Verticle start? If yes how can I do it?
Thank you in advance.
You're not supposed to change the handler of the startFuture, as it is owned by the caller. So simply, don't do that :) The future is used to signal that you're done with your start-code, and not to define what should be done, once you're done.
In your concrete example it'd be better to write the logs once the http server came up, and then indicate to the startFuture, that you're done with your code execution.
#Override
public void start(Future<Void> startFuture) throws Exception {
vertx.createHttpServer()
.requestHandler(router()::accept)
.listen(8080, event -> {
if (event.succeeded()) {
logger.info("Server started on port: {}", 8080);
startFuture.complete();
} else {
logger.warn("Failed to start: {}", event.cause());
startFuture.fail(event.cause());
}
});
}
At first, write your verticle as a AbstractVerticle extension. And do not override start(Future) method. Instead use start() method. It will be called from AbstractVerticle.start(Future), and you can specify that future in Vertx.deployVerticle(verticle, options, future) method.
For example:
You have some verticle. Do in it start() method only initialization tasks:
#Override
public void start() throws Exception {
vertx.eventbus().consume(address, m -> {
// consumer code skipped
});
}
and verticle registration:
final DeploymentOptions opts = new DeploymentOptions().setWorker(true);
vertx.deployVerticle(verticle, opts, event -> {
if (event.succeeded()) {
log.info("Verticle successfully deployed. DeploymentId: " + event.result());
} else {
log.error("Verticle failed to deploy. Cause: " + event.cause().getMessage(), event.cause());
}
});
That's all :)
I am experimenting with Dropwizard (https://github.com/robertkuhar/dropwiz_get_start, https://github.com/robertkuhar/dropwiz_mongo_demo ) and am impressed with how easy it is to integrate with my IDE. To start my dropwizard app, I simply find the class with the main method and "Debug As...Java Application" and I'm on my way. Stopping the application is equally simple, just click the red "Terminate" button from the Debug view. I noticed, however, that I don't make it to the breakpoints in the stop() method of my Managed classes when I stop it in this manner.
How do I get Dropwizard to go through graceful shutdown when its running directly in eclipse's Debugger?
#Override
public void run( BlogConfiguration configuration, Environment environment ) throws Exception {
...
MongoManaged mongoManaged = new MongoManaged( mongo );
environment.manage( mongoManaged );
...
}
Breakpoints in the stop() of MongoManage never get hit.
public class MongoManaged implements Managed {
private final MongoClient mongo;
public MongoManaged( MongoClient mongo ) {
this.mongo = mongo;
}
#Override
public void start() throws Exception {
}
#Override
public void stop() throws Exception {
if ( mongo != null ) {
mongo.close();
}
}
}
Does it help if you use this java feature?
// In case vm shutdown
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run()
{
// what should be closed if forced shudown
// ....
LOG.info(String.format("--- End of ShutDownHook (%s) ---", APPLICATION_NAME));
}
});
Just add this before first breakpoint.