Search code examples
javajerseyhk2

HK2: accessing RunLevel scoped services from child locator


I use Jersey in a Java SE application. HK2 provides dependency injection to the overall application. HK2 RunLevel services are registered in the application service locator, which is the parent to Jerseys service locator.

+ application locator
|\- RunLevel capabilities
| - MyCustomService, @RunLevel(value=1)
 \
  + jersey locator
   \- jersey resource class
     \ @Inject MyCustomService

My problem is that I cannot access runlevel-scoped services from within Jersey. When - in the above example - the jersey resource is opened, injection of MyCustomService fails:

java.lang.IllegalStateException: Could not find an active context for org.glassfish.hk2.runlevel.RunLevel

The reason for this seems to be that the services behind the HK2 RunLevel feature have visibility LOCAL: The jersey locator cannot access them via its parent locator. See here.

Questions:

  • Why are services of the runlevel feature restricted in visibility?
  • What can I do to overcome this?

Update

To give context to the question, I'm using runlevels in a "System-V" style.

  • The Java SE application starts. Default initial runlevel is -1, target runlevel is 3. On its way there, different stages must be passed successfully to continue.
  • At runlevel 1, connections to dependent external applications are established (database, memcache, message broker, etc).
  • At runlevel 2, ExecutorServices for background processing and HTTP services (running jersey) are started. Jersey rejects all incoming requests at this level.
  • At runlevel 3, MessageListeners are attached to the broker, feeding requests to the background executors. Jersey accepts and processes HTTP requests.

This concept allows granular control over availability and long running requests. When shutting down, the application will be at runlevel 2 until previously accepted HTTP requests are fulfilled and enqueued background tasks completed. However, no new tasks/requests are accepted. Then, runlevel 1, 0, -1, exit.


Solution

  • The solution is to respect the DescriptorVisibility#LOCAL and inject services dependent on RunLevelContext only from the service locator that manages them.

    It is a bit cumbersome:

    • from within a Jersey resource, fetch the service locator with runlevel capabilities by name
    • get the runlevel-scoped service injected explicitely

      ServiceLocator applicationLocator = ServiceLocatorFactory.getInstance().find("applicationLocator");
      MyCustomService mcs = applicationLocator.getService(MyCustomService.class);
      mcs.doSomething();
      

    To reduce the danger of forgetting to do it this way, and just injecting MyCustomService into a jersey resource, I've now marked my runlevel-scoped services to be also of DescriptorVisibility#LOCAL. That way they can't be injected by the jersey locator.