I've a micronaut cli application.
Some command are long running process that I want to monitor with micronaut endpoints
Since when you are on a cli app, micronaut detect and don't launch the webserver.
In this long running command, I've added this to start the webserver :
applicationContext
.findBean(EmbeddedServer.class)
.ifPresent(server -> {
// start server
long start = System.currentTimeMillis();
server.start();
log.info(
"Startup completed in {} ms. Server Running: {}",
System.currentTimeMillis() - start,
server.getURL()
);
// force exit
if (server.isForceExit()) {
System.exit(0);
}
});
Most of the code come from Micronaut.java.
The full command source code:
package test;
import io.micronaut.configuration.picocli.PicocliRunner;
import io.micronaut.context.ApplicationContext;
import io.micronaut.runtime.server.EmbeddedServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
import javax.inject.Inject;
import java.util.concurrent.Callable;
@CommandLine.Command(
name = "test",
mixinStandardHelpOptions = true,
subcommands = {
TestApp.TestServer.class,
}
)
public class TestApp implements Callable<Object> {
public static void main(String[] args) throws Exception {
PicocliRunner.call(TestApp.class, args);
}
@Override
public Object call() throws Exception {
return PicocliRunner.call(TestApp.class, "--help");
}
@CommandLine.Command(
name = "server",
description = "Start a webserver"
)
public static class TestServer implements Runnable {
@Inject
ApplicationContext applicationContext;
Logger log = LoggerFactory.getLogger(TestServer.class);
@Override
public void run() {
applicationContext
.findBean(EmbeddedServer.class)
.ifPresent(server -> {
// start server
long start = System.currentTimeMillis();
server.start();
log.info(
"Startup completed in {} ms. Server Running: {}",
System.currentTimeMillis() - start,
server.getURL()
);
// force exit
if (server.isForceExit()) {
System.exit(0);
}
});
}
}
}
The webserver is starting well. I've added 2 endpoints, logger and health. Logger endpoint works well, but the health endpoint (like most every endpoint) failed with with this stacktrace : Full Stacktrace
io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1f5e667f[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@1615aa7e[Wrapped task = io.micronaut.http.context.ServerRequestContext$$Lambda$653/0x00000008406af040@5f7931cf]] rejected from java.util.concurrent.ThreadPoolExecutor@33dd1064[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
...
Caused by: java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1f5e667f[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@1615aa7e[Wrapped task = io.micronaut.http.context.ServerRequestContext$$Lambda$653/0x00000008406af040@5f7931cf]] rejected from java.util.concurrent.ThreadPoolExecutor@33dd1064[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2055)
....
at io.micronaut.scheduling.instrument.InstrumentedExecutorService.submit(InstrumentedExecutorService.java:89)
at io.micronaut.core.async.publisher.AsyncSingleResultPublisher$ExecutorServiceSubscription.request(AsyncSingleResultPublisher.java:98)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onSubscribe(FlowableFlatMap.java:656)
at io.micronaut.core.async.publisher.AsyncSingleResultPublisher.subscribe(AsyncSingleResultPublisher.java:59)
...
at io.reactivex.Flowable.subscribe(Flowable.java:14805)
... 38 more
Exception in thread "nioEventLoopGroup-1-4" io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1f5e667f[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@1615aa7e[Wrapped task = io.micronaut.http.context.ServerRequestContext$$Lambda$653/0x00000008406af040@5f7931cf]] rejected from java.util.concurrent.ThreadPoolExecutor@33dd1064[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
...
at io.micronaut.http.context.ServerRequestTracingPublisher.lambda$subscribe$0(ServerRequestTracingPublisher.java:52)
at io.micronaut.http.context.ServerRequestContext.with(ServerRequestContext.java:52)
at io.micronaut.http.context.ServerRequestTracingPublisher.subscribe(ServerRequestTracingPublisher.java:52)
at io.micronaut.core.async.publisher.Publishers.lambda$map$2(Publishers.java:133)
...
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1f5e667f[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@1615aa7e[Wrapped task = io.micronaut.http.context.ServerRequestContext$$Lambda$653/0x00000008406af040@5f7931cf]] rejected from java.util.concurrent.ThreadPoolExecutor@33dd1064[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
... 38 more
2019-10-11 17:18:50,689 WARN nioEventLoopGroup-1-4 i.n.u.c.AbstractEventExecutor A task raised an exception. Task: io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker@7ebe5c10
java.lang.NullPointerException: Actually not, but can't throw other exceptions due to RS
at io.reactivex.Flowable.subscribe(Flowable.java:14814)
...
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1f5e667f[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@1615aa7e[Wrapped task = io.micronaut.http.context.ServerRequestContext$$Lambda$653/0x00000008406af040@5f7931cf]] rejected from java.util.concurrent.ThreadPoolExecutor@33dd1064[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2055)
at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:825)
at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1355)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118)
at io.micronaut.scheduling.instrument.InstrumentedExecutorService.submit(InstrumentedExecutorService.java:89)
at io.micronaut.core.async.publisher.AsyncSingleResultPublisher$ExecutorServiceSubscription.request(AsyncSingleResultPublisher.java:98)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onSubscribe(FlowableFlatMap.java:656)
at io.micronaut.core.async.publisher.AsyncSingleResultPublisher.subscribe(AsyncSingleResultPublisher.java:59)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.onNext(FlowableFlatMap.java:163)
at io.reactivex.internal.operators.flowable.FlowableFromIterable$IteratorSubscription.slowPath(FlowableFromIterable.java:236)
at io.reactivex.internal.operators.flowable.FlowableFromIterable$BaseRangeSubscription.request(FlowableFromIterable.java:124)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.onSubscribe(FlowableFlatMap.java:117)
at io.reactivex.internal.operators.flowable.FlowableFromIterable.subscribe(FlowableFromIterable.java:69)
at io.reactivex.internal.operators.flowable.FlowableFromIterable.subscribeActual(FlowableFromIterable.java:47)
at io.reactivex.Flowable.subscribe(Flowable.java:14805)
... 38 common frames omitted
Any clue to do this ?
Thanks to Graeme Rocher that give the right direction here : https://github.com/micronaut-projects/micronaut-picocli/issues/9.
Starting the webserver from a cli is the right way:
applicationContext
.findBean(EmbeddedServer.class)
.ifPresent(server -> {
// start server
long start = System.currentTimeMillis();
server.start();
});
But you need to take care that your Command must keep the whole logic on the main thread. In case of multithreaded application, the server will be shutdown if you are not waiting for all the thread in main command