Skip to content

FELIX-6823 - Fix unrecoverable Jetty state after NPE in WhiteboardManager shutdown#481

Merged
paulrutter merged 1 commit intoapache:masterfrom
francescomari:fix/whiteboard-manager-shutdown-npe
Mar 12, 2026
Merged

FELIX-6823 - Fix unrecoverable Jetty state after NPE in WhiteboardManager shutdown#481
paulrutter merged 1 commit intoapache:masterfrom
francescomari:fix/whiteboard-manager-shutdown-npe

Conversation

@francescomari
Copy link
Contributor

Summary

A NullPointerException in WhiteboardManager.deactivate() during a configuration-triggered Jetty restart causes the shutdown sequence to abort. Because neither stopJetty() nor updated() catch the exception, startJetty() is never called — the server keeps running with a corrupted WhiteboardManager, all subsequent servlet registrations fail with IndexOutOfBoundsException, and the system is permanently unusable until the JVM is restarted.

Root cause

Three bugs compound into one unrecoverable failure:

  1. WhiteboardManager.deactivate() calls handler.getRegistry().getEventListenerRegistry() without a null check. Context handlers that were never fully activated have a null registry, causing an NPE.

  2. WhiteboardManager.stop() has no exception handling around its tracker-close loop. The NPE from (1) propagates out before contextMap.clear() is reached, leaving stale, partially-deactivated entries and remaining trackers still open.

  3. JettyService.stopJetty() has no exception handling around controller.unregister(). The NPE propagates through stopJetty() and out of updated(), so server.stop() is never called (the old server keeps running) and startJetty() is never reached (no new WhiteboardManager is created).

Additionally, getMatchingContexts() and getContextHandler() call handlerList.get(0) without checking for empty lists. Empty lists can appear in contextMap via the retry loop in removeContextHelper(), which can drain a list without removing it from the map.

Changes

  • WhiteboardManager.deactivate(): guard handler.getRegistry() against null before calling getEventListenerRegistry().contextDestroyed()
  • WhiteboardManager.stop(): wrap each tracker.close() in a try-catch so the loop always completes and contextMap.clear() is always reached
  • WhiteboardManager.getMatchingContexts() / getContextHandler(): skip empty handler lists instead of calling .get(0) on them
  • WhiteboardManager.removeContextHelper(): remove the map entry when the retry loop drains a handler list to empty
  • JettyService.stopJetty() (both jetty and jetty12): wrap controller.unregister() in a try-catch so server.stop() and the rest of shutdown always execute, and startJetty() is always reached in updated()

During concurrent bundle shutdown, WhiteboardManager.deactivate() can
encounter a null PerContextHandlerRegistry because the handler was
already deactivated by another thread. This causes a NullPointerException
that propagates up through JettyService.stopJetty(), preventing the
Jetty server from stopping and restarting cleanly.

This commit applies five defensive fixes:

- Null-guard handler.getRegistry() in deactivate() before calling
  contextDestroyed()
- Wrap each ServiceTracker.close() in stop() with try-catch so one
  failing tracker does not prevent cleanup of the remaining trackers
  and maps
- Wrap controller.unregister() in JettyService.stopJetty() with
  try-catch so exceptions do not prevent server.stop() from running
  (both jetty and jetty12 variants)
- Guard against empty handler lists in getMatchingContexts() and
  getContextHandler() to prevent IndexOutOfBoundsException
- Remove empty lists from contextMap after the retry loop in
  removeContextHelper() exhausts all fallback candidates
Copy link
Contributor

@paulrutter paulrutter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm, @cziegeler any comments?

@paulrutter paulrutter merged commit 86ac32f into apache:master Mar 12, 2026
3 checks passed
@francescomari francescomari deleted the fix/whiteboard-manager-shutdown-npe branch March 12, 2026 11:06
@francescomari francescomari changed the title Fix unrecoverable Jetty state after NPE in WhiteboardManager shutdown FELIX-6823 - Fix unrecoverable Jetty state after NPE in WhiteboardManager shutdown Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants