【问题标题】:HK2: accessing RunLevel scoped services from child locatorHK2:从子定位器访问 RunLevel 范围内的服务
【发布时间】:2019-10-18 07:31:08
【问题描述】:

我在 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: 找不到 org.glassfish.hk2.runlevel.RunLevel 的活动上下文

原因似乎是 HK2 RunLevel 功能背后的服务具有可见性LOCAL:球衣定位器无法通过其父定位器访问它们。见here

问题:

  • 为什么运行级别功能的服务的可见性受到限制?
  • 我能做些什么来克服这个问题?

更新

为了给出问题的上下文,我使用的是“System-V”风格的运行级别。

  • Java SE 应用程序启动。默认初始运行级别为 -1,目标运行级别为 3。在此过程中,必须成功通过不同阶段才能继续。
  • 在运行级别 1 中,建立了与相关外部应用程序(数据库、内存缓存、消息代理等)的连接。
  • 在运行级别 2,ExecutorServices 用于后台处理和 HTTP 服务(运行球衣)。 Jersey 拒绝此级别的所有传入请求。
  • 在运行级别 3,MessageListeners 附加到代理,将请求提供给后台执行程序。 Jersey 接受并处理 HTTP 请求。

此概念允许对可用性和长时间运行的请求进行精细控制。关闭时,应用程序将处于运行级别 2,直到满足先前接受的 HTTP 请求并完成排队的后台任务。但是,不接受新的任务/请求。然后,运行级别 1、0、-1,退出。

【问题讨论】:

  • 是的,我的意思是,这正是运行级服务的设计方式/目的。我们在 GlassFish 和 WebLogic 中做的事情几乎完全相同(我们有更多关卡,但想法相同)

标签: java jersey hk2


【解决方案1】:

解决方案是尊重DescriptorVisibility#LOCAL 并注入依赖于RunLevelContext 的服务仅从管理它们的服务定位器

有点麻烦:

  • 从 Jersey 资源中获取具有运行级别功能的服务定位器按名称
  • 明确注入运行级别范围的服务

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

为了减少忘记这样做的危险,并且只是将MyCustomService 注入到球衣资源中,我现在将我的运行级别范围服务标记为DescriptorVisibility#LOCAL .这样他们不能被球衣定位器注入。

【讨论】:

    【解决方案2】:

    子节点和 RunLevelService 的想法是,实际的 RunLevelServices 将是子定位器中的精简服务,它在父节点中编排实际服务。并且一个进程中的多个“子系统”可能有不同的 RunLevelService“隔间”,每个“隔间”都是它自己的父级子系统。

    在该模型中,您在子代而非父代中启动/添加 RunLevelService。通过这种方式,您可以在同一父级的不同子级中拥有多个不同级别的 RunLevelServices。

    听起来你有一个不同的用例,它可能无法完全工作。值得考虑您的用例。

    【讨论】:

    • 谢谢,我希望你能回答。太爱了!我已经更新了这个问题,让上下文重新使用运行级别。如您所见,jersey(及其子定位器)在运行级别概念内运行,而不是相反。有什么可以给我的建议吗?
    • 不幸的是,我对泽西岛的工作了解不多。它正在使用运行级别服务对我来说实际上是个新闻
    • 对不起,我的错误解释。 我的代码从运行级服务启动 Grizzly/Jersey 容器。
    【解决方案3】:

    更新:这不起作用!

    我将把它留在这里用于教育目的,但不要这样做!一旦从 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 ...
    
        }
    }
    

    我鼓励就这种非正统方法提供反馈。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-20
      • 2016-06-23
      • 1970-01-01
      • 2021-06-19
      • 1970-01-01
      • 2014-02-22
      • 1970-01-01
      相关资源
      最近更新 更多