I'm running a simple Jersey/Grizzly HTTP server with a resource to handle server-sent events.
That resource needs to manually take care of a Timer
instance to send some keep-alive data to all connected SSE clients, so I use @PostConstruct
and @PreDestroy
annotations to construct and cancel that timer (and its background thread).
The application is a simple console application and not embedded in any container environment like Tomcat. After starting the web server it waits for a simple System.in.read()
and shuts the server down again by calling shutdownNow() on the Grizzly HTTP server instance.
When connecting to the SSE resource, the resource class in instantiated and the timer is started. After disconnecting from the server, after a short delay the resource is destroyed again and everything is fine.
But due to the nature of the SSE resource a client could possibly be connected endlessly, thus also internally keeping the resource instance around.
If I now try to shutdown the server by calling shutdownNow()
while a client is connected, the server instance properly shuts down, but does not call any @PreDestroy
methods of active resources. This causes my resource with the Timer
instance to stick around and because the timer is still active, this in return keeps the java process running, even though the HTTP server has already been shut down.
Is there any way to retrieve all currently active resource instances, so I could manually call their respective destroy method prior to shutting down the HTTP server? Or could this be a bug in the resource instance management of the Grizzly server?
The versions used are:
- jersey-container-grizzly2-http: 2.27
- grizzly-http-server: 2.4.0 (dependency from above)
This is a boiled down version of the code:
public class ServerSentEventResource {
@Context
private Sse sse;
private volatile SseBroadcaster broadcaster;
private final Timer timer = new Timer();
@PostConstruct
public void init() {
broadcaster = sse.newBroadcaster();
final TimerTask task = new TimerTask() {
// ...
};
timer.scheduleAtFixedRate(task, 0, 1000);
}
@PreDestroy
public void destroy() {
timer.cancel();
broadcaster.close();
}
@GET
@Produces(SseFeature.SERVER_SENT_EVENTS)
public void get(@Context final SseEventSink eventSink) {
broadcaster.register(eventSink);
}
}
public static void main(final String[] args) {
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(ServerSentEventResource.class);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create("0.0.0.0:8888"), resourceConfig);
System.in.read();
server.shutdownNow();
// At this point there is still the resource instance around, keeping the timer running, thus keeping the JVM running :(
}