Java HK2:从子定位器访问运行级别作用域服务

Java HK2:从子定位器访问运行级别作用域服务,java,jersey,hk2,Java,Jersey,Hk2,我在JavaSE应用程序中使用Jersey。HK2向整个应用程序提供依赖注入。服务在应用程序服务定位器中注册,该定位器是Jersey服务定位器的父级 + application locator |\- RunLevel capabilities | - MyCustomService, @RunLevel(value=1) \ + jersey locator \- jersey resource class \ @Inject MyCustomService 我的问题是

我在JavaSE应用程序中使用Jersey。HK2向整个应用程序提供依赖注入。服务在应用程序服务定位器中注册,该定位器是Jersey服务定位器的父级

+ application locator
|\- RunLevel capabilities
| - MyCustomService, @RunLevel(value=1)
 \
  + jersey locator
   \- jersey resource class
     \ @Inject MyCustomService
我的问题是我无法从Jersey内部访问运行级别范围的服务。在上面的示例中,当jersey资源打开时,
MyCustomService
的注入失败:

java.lang.IllegalStateException:找不到org.glassfish.hk2.runlevel.runlevel的活动上下文

原因似乎是HK2运行级功能背后的服务具有可见性:jersey定位器无法通过其父定位器访问它们。看

问题:

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

为了给这个问题提供上下文,我使用了“System-V”风格的运行级别

  • JavaSE应用程序启动。默认初始运行级别为-1,目标运行级别为3。在通往那里的路上,必须成功地通过不同的阶段才能继续
  • 在运行级别1,建立到相关外部应用程序(数据库、memcache、MessageBroker等)的连接
  • 在运行级别2,启动用于后台处理的执行器服务和HTTP服务(运行jersey)。Jersey拒绝此级别的所有传入请求
  • 在运行级别3,
    MessageListeners
    附加到代理,将请求提供给后台执行器。Jersey接受并处理HTTP请求

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

关于子级和运行级别服务的想法是,实际的运行级别服务将是子定位器中的精简服务,该定位器协调父级中的实际服务。一个流程中的多个“子系统”可能有不同的RunLevelService“隔间”,每个隔间都位于它自己的父级子级中

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


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

更新:这不起作用

出于教育目的,我将把它留在这里,但不要这样做
一旦从jersey的服务定位器中访问
RunLevelController
,它将失去对它在另一个服务定位器上管理的所有服务的跟踪

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

我已经解决了这个问题:我故意覆盖
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 ...

    }
}

我鼓励对这种非正统方法进行反馈。

解决方案是尊重
描述符可视性#LOCAL
并仅从管理它们的服务定位器注入依赖于
运行级别上下文的服务

这有点麻烦:

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

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

为了减少忘记这样做的危险,只需将
MyCustomService
注入jersey资源,我现在将运行级别范围内的服务也标记为
DescriptorVisibility\LOCAL
。这样他们就不能被球衣定位器注射。

谢谢,我希望你能回答。我爱死你了!我已经更新了这个问题,以提供运行级别的上下文重用。如您所见,jersey(及其子定位器)在运行级概念中运行,而不是相反。你能给我什么建议吗?不幸的是,我对泽西的职业了解不多。对我来说,它正在使用运行级服务是个新闻对不起,我的解释不好。我的代码从运行级服务启动Grizzly/Jersey容器。是的,我的意思是,这正是运行级服务的设计目的。我们在GlassFish和WebLogic中做的事情几乎完全相同(我们有更多的级别,但想法相同)