HK2:从子定位器访问 RunLevel 范围内的服务
HK2: accessing RunLevel scoped services from child locator
我在 Java SE 应用程序中使用 Jersey。 HK2 为整个应用程序提供依赖注入。 HK2 RunLevel 服务在应用程序服务定位器中注册,它是 Jerseys 服务定位器的父级。
+ application locator
|\- RunLevel capabilities
| - MyCustomService, @RunLevel(value=1)
\
+ jersey locator
\- jersey resource class
\ @Inject MyCustomService
我的问题是我无法从 Jersey 访问运行级别范围内的服务。当 - 在上面的示例中 - 打开球衣资源时,MyCustomService
的注入失败:
java.lang.IllegalStateException: Could not find an active context for org.glassfish.hk2.runlevel.RunLevel
这似乎是因为 HK2 RunLevel 功能背后的服务具有可见性 LOCAL: The jersey locator cannot access them via its parent locator. See here。
问题:
- 为什么运行级别功能的服务在可见性方面受到限制?
- 我该怎么做才能克服这个问题?
更新
为了给问题提供上下文,我使用 "System-V" 风格的运行级别。
- Java SE 应用程序启动。默认初始运行级别为 -1,目标运行级别为 3。在此过程中,必须成功通过不同的阶段才能继续。
- 在运行级别 1,建立与依赖的外部应用程序(数据库、内存缓存、消息代理等)的连接。
- 在运行级别 2,
ExecutorServices
后台处理和 HTTP 服务(运行 球衣)启动。泽西岛拒绝此级别的所有传入请求。
- 在运行级别 3,
MessageListeners
附加到代理,向后台执行程序提供请求。 Jersey 接受并处理 HTTP 请求。
这个概念允许对可用性和长 运行 请求进行精细控制。关闭时,应用程序将处于运行级别 2,直到先前接受的 HTTP 请求得到满足并且排队的后台任务完成。但是,不接受新的 tasks/requests。然后,runlevel 1, 0, -1, exit.
children 和 RunLevelService 的想法是,实际的 RunLevelServices 将是 child 定位器中的精简服务,在 parent 中编排实际服务。一个进程中的多个 "subsystems" 可能有不同的 RunLevelService "compartments," 每个在它自己的 child 中 parent.
在该模型中,您 start/add children 中的 RunLevelService,而不是 parent 中的 RunLevelService。通过这种方式,您可以在同一 parent.
的不同 children 的不同级别拥有多个 RunLevelServices
听起来你有不同的用例,但可能无法完全奏效。值得考虑您的用例。
更新:这不起作用!
出于教育目的,我将把它留在这里,但是不要这样做!一旦从 jersey 的服务定位器中访问 RunLevelController
,它就会丢失在另一个 服务定位器上跟踪它管理的所有服务。
更新结束
我已经用这个 hack 解决了它:我故意覆盖 LOCAL
可见性并将所需的描述符插入到缺少它们的服务定位器中。
public class RunLevelBridge implements Feature {
private final Logger log = LoggerFactory.getLogger(getClass());
private final ServiceLocator sourceLocator;
public RunLevelBridge(ServiceLocator sourceLocator) {
this.sourceLocator = sourceLocator;
}
@Override
public boolean configure(FeatureContext context) {
if (sourceLocator == null) {
log.error("Unable to bridge descriptors, the source service locator cannot be null");
return false;
}
InjectionManager im = InjectionManagerProvider.getInjectionManager(context);
ServiceLocator jerseyLocator = im.getInstance(ServiceLocator.class);
if (jerseyLocator == null) {
log.error("Unable to bridge descriptors, the target service locator cannot be null");
return false;
}
if (!sourceLocator.equals(jerseyLocator.getParent())) {
ExtrasUtilities.bridgeServiceLocator(jerseyLocator, sourceLocator);
log.info("Bridge from {} into {} established", sourceLocator.getName(), jerseyLocator.getName());
}
Filter filter;
filter = BuilderHelper.createContractFilter(RunLevelContext.class.getName());
sourceLocator.getDescriptors(filter).forEach(
(descriptor) -> ServiceLocatorUtilities.addOneDescriptor(
jerseyLocator,
descriptor,
false
)
);
filter = BuilderHelper.createContractFilter(RunLevelController.class.getName());
sourceLocator.getDescriptors(filter).forEach(
(descriptor) -> ServiceLocatorUtilities.addOneDescriptor(
jerseyLocator,
descriptor,
false
)
);
log.info("Added the RunLevel feature to jersey's service locator");
return true;
}
}
网桥注册在ResourceConfig
:
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig(ServiceLocator parentLocator) {
// bridge runlevel into jersey
register(new RunLevelBridge(parentLocator));
// ... other stuff ...
}
}
我鼓励对这种非正统的方法提出反馈意见。
解决方案是尊重 DescriptorVisibility#LOCAL
并注入依赖于 RunLevelContext
的服务,仅来自管理它们的服务定位器。
有点麻烦:
- 从 Jersey 资源中,通过名称
获取具有运行级别功能的服务定位器
明确注入运行级别范围的服务
ServiceLocator applicationLocator = ServiceLocatorFactory.getInstance().find("applicationLocator");
MyCustomService mcs = applicationLocator.getService(MyCustomService.class);
mcs.doSomething();
为了减少忘记这样做的危险,只是将 MyCustomService
注入球衣资源,我现在将我的运行级别范围的服务标记为 also[= DescriptorVisibility#LOCAL
的 30=]。这样他们就不会被球衣定位器注入。
我在 Java SE 应用程序中使用 Jersey。 HK2 为整个应用程序提供依赖注入。 HK2 RunLevel 服务在应用程序服务定位器中注册,它是 Jerseys 服务定位器的父级。
+ application locator
|\- RunLevel capabilities
| - MyCustomService, @RunLevel(value=1)
\
+ jersey locator
\- jersey resource class
\ @Inject MyCustomService
我的问题是我无法从 Jersey 访问运行级别范围内的服务。当 - 在上面的示例中 - 打开球衣资源时,MyCustomService
的注入失败:
java.lang.IllegalStateException: Could not find an active context for org.glassfish.hk2.runlevel.RunLevel
这似乎是因为 HK2 RunLevel 功能背后的服务具有可见性 LOCAL: The jersey locator cannot access them via its parent locator. See here。
问题:
- 为什么运行级别功能的服务在可见性方面受到限制?
- 我该怎么做才能克服这个问题?
更新
为了给问题提供上下文,我使用 "System-V" 风格的运行级别。
- Java SE 应用程序启动。默认初始运行级别为 -1,目标运行级别为 3。在此过程中,必须成功通过不同的阶段才能继续。
- 在运行级别 1,建立与依赖的外部应用程序(数据库、内存缓存、消息代理等)的连接。
- 在运行级别 2,
ExecutorServices
后台处理和 HTTP 服务(运行 球衣)启动。泽西岛拒绝此级别的所有传入请求。 - 在运行级别 3,
MessageListeners
附加到代理,向后台执行程序提供请求。 Jersey 接受并处理 HTTP 请求。
这个概念允许对可用性和长 运行 请求进行精细控制。关闭时,应用程序将处于运行级别 2,直到先前接受的 HTTP 请求得到满足并且排队的后台任务完成。但是,不接受新的 tasks/requests。然后,runlevel 1, 0, -1, exit.
children 和 RunLevelService 的想法是,实际的 RunLevelServices 将是 child 定位器中的精简服务,在 parent 中编排实际服务。一个进程中的多个 "subsystems" 可能有不同的 RunLevelService "compartments," 每个在它自己的 child 中 parent.
在该模型中,您 start/add children 中的 RunLevelService,而不是 parent 中的 RunLevelService。通过这种方式,您可以在同一 parent.
的不同 children 的不同级别拥有多个 RunLevelServices听起来你有不同的用例,但可能无法完全奏效。值得考虑您的用例。
更新:这不起作用!
出于教育目的,我将把它留在这里,但是不要这样做!一旦从 jersey 的服务定位器中访问 RunLevelController
,它就会丢失在另一个 服务定位器上跟踪它管理的所有服务。
更新结束
我已经用这个 hack 解决了它:我故意覆盖 LOCAL
可见性并将所需的描述符插入到缺少它们的服务定位器中。
public class RunLevelBridge implements Feature {
private final Logger log = LoggerFactory.getLogger(getClass());
private final ServiceLocator sourceLocator;
public RunLevelBridge(ServiceLocator sourceLocator) {
this.sourceLocator = sourceLocator;
}
@Override
public boolean configure(FeatureContext context) {
if (sourceLocator == null) {
log.error("Unable to bridge descriptors, the source service locator cannot be null");
return false;
}
InjectionManager im = InjectionManagerProvider.getInjectionManager(context);
ServiceLocator jerseyLocator = im.getInstance(ServiceLocator.class);
if (jerseyLocator == null) {
log.error("Unable to bridge descriptors, the target service locator cannot be null");
return false;
}
if (!sourceLocator.equals(jerseyLocator.getParent())) {
ExtrasUtilities.bridgeServiceLocator(jerseyLocator, sourceLocator);
log.info("Bridge from {} into {} established", sourceLocator.getName(), jerseyLocator.getName());
}
Filter filter;
filter = BuilderHelper.createContractFilter(RunLevelContext.class.getName());
sourceLocator.getDescriptors(filter).forEach(
(descriptor) -> ServiceLocatorUtilities.addOneDescriptor(
jerseyLocator,
descriptor,
false
)
);
filter = BuilderHelper.createContractFilter(RunLevelController.class.getName());
sourceLocator.getDescriptors(filter).forEach(
(descriptor) -> ServiceLocatorUtilities.addOneDescriptor(
jerseyLocator,
descriptor,
false
)
);
log.info("Added the RunLevel feature to jersey's service locator");
return true;
}
}
网桥注册在ResourceConfig
:
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig(ServiceLocator parentLocator) {
// bridge runlevel into jersey
register(new RunLevelBridge(parentLocator));
// ... other stuff ...
}
}
我鼓励对这种非正统的方法提出反馈意见。
解决方案是尊重 DescriptorVisibility#LOCAL
并注入依赖于 RunLevelContext
的服务,仅来自管理它们的服务定位器。
有点麻烦:
- 从 Jersey 资源中,通过名称 获取具有运行级别功能的服务定位器
明确注入运行级别范围的服务
ServiceLocator applicationLocator = ServiceLocatorFactory.getInstance().find("applicationLocator"); MyCustomService mcs = applicationLocator.getService(MyCustomService.class); mcs.doSomething();
为了减少忘记这样做的危险,只是将 MyCustomService
注入球衣资源,我现在将我的运行级别范围的服务标记为 also[= DescriptorVisibility#LOCAL
的 30=]。这样他们就不会被球衣定位器注入。