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.
3
Answers
UPDATE: This does not work!
I'll leave this here for educational purposes, but do not do this! Once the
RunLevelController
is accessed from within the service locator of jersey, it looses track of all the services it managed on the other service locator.UPDATE END
I've solved it with this hack: I deliberately override the
LOCAL
visibility and insert the required descriptors to the service locator that is missing them.The bridge is registered in the
ResourceConfig
:I encourage feedback regarding this unorthodox approach.
The idea with children and the RunLevelService was that actual RunLevelServices would be thin service in the child locators that orchestrated the real services in the parent. And that multiple “subsystems” in a process might have different RunLevelService “compartments,” each in it’s own child of a parent.
In that model you start/add the RunLevelService in the children, not in the parent. And in that way you can have multiple RunLevelServices at different levels in different children of the same parent.
Sounds like you have a different use case though, which may not completely work. It’s worth considering your use case.
The solution is to respect the
DescriptorVisibility#LOCAL
and inject services dependent onRunLevelContext
only from the service locator that manages them.It is a bit cumbersome:
get the runlevel-scoped service injected explicitely
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 ofDescriptorVisibility#LOCAL
. That way they can’t be injected by the jersey locator.