【问题标题】:Why is CDI (with JAXRS) not working in Open Libety Server but works fine on Payara Server?为什么 CDI(使用 JAX RS)不能在 Open Liberty Server 中运行,但在 Payara Server 上运行良好?
【发布时间】:2020-08-02 22:29:48
【问题描述】:

我一直在尝试从 Payara 服务器切换到 Open Liberty,但是我遇到了一个问题:我的代码在 Payara 上运行良好,但在 Open Liberty 上运行不佳。这是说明问题的最简单的代码测试:

ApiEntryPoint.java

@ApplicationPath("/v1")
public class ApiEntryPoint extends Application {

    @Override
    public Map<String, Object> getProperties() {

        Map<String, Object> props = new HashMap<String, Object>();
        props.put("name", "This is my name");
        return props;
    }
}

Test.java

@ApplicationScoped
public class Test {

    @Context
    private Configuration configuration;

    private String name;

    @PostConstruct
    private void init() {
        name = (String) configuration.getProperties().get("name");
    }

    public String getName() {
        return name;
    }
}

TestResources.java

@Path("/test")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@RequestScoped
public class TestResources {

    @Inject
    private Test test;

    @GET
    public String test(@Context HttpHeaders headers, @PathParam("id") int id) {
        return test.getName();
    }
}

您可能已经猜到,此代码在 Payara 服务器上运行,当我尝试在 Open Liberty 服务器上运行它时,我收到以下错误:

[err] org.jboss.weld.exceptions.WeldException: WELD-000049: Unable to invoke private void      com.domain.test.Test.init() on com.domain.test.Test@74caba3d
[err]   at org.jboss.weld.injection.producer.DefaultLifecycleCallbackInvoker.invokeMethods(DefaultLifecycleCallbackInvoker.java:85)
[err]   at [internal classes]
[err]   at com.domain.test.Test$Proxy$_$$_WeldClientProxy.getName(Unknown Source)
[err]   at com.domain.test.TestResources.test(TestResources.java:25)
[err]   at com.domain.test.TestResources$Proxy$_$$_WeldClientProxy.test(Unknown Source)
[err]   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[err]   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[err]   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[err]   at java.lang.reflect.Method.invoke(Method.java:498)
[err]   at com.ibm.ws.jaxrs20.server.LibertyJaxRsServerFactoryBean.performInvocation(LibertyJaxRsServerFactoryBean.java:656)
[err]   at [internal classes]
[err]   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[err]   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[err]   at java.lang.Thread.run(Thread.java:748)
[err] Caused by: java.lang.reflect.InvocationTargetException
[err]   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[err]   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[err]   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[err]   at java.lang.reflect.Method.invoke(Method.java:498)
[err]   at org.jboss.weld.injection.producer.DefaultLifecycleCallbackInvoker.invokeMethods(DefaultLifecycleCallbackInvoker.java:83)
[err]   ... 64 more
[err] Caused by: java.lang.NullPointerException
[err]   at com.domain.test.Test.init(Test.java:18)
[err]   ... 69 more

最后一个异常告诉我 configuration.getProperties() 在 Test.java 后构造函数中返回 null。 为什么在 Open Liberty 中而不是在 Payara 中出现这种行为?另外,如果我不在 TestResources.java 文本方法中调用 test.getName() 并且只返回一个随机字符串,我不会收到任何错误. 这引出了什么时候调用@PostConstructor 方法的问题? 我认为它会在bean 完全初始化时被调用,这应该发生在TestResources.java 中的测试方法执行之前。我在这里有什么遗漏吗?

请注意,我在 maven 中使用的唯一依赖项是完整的 jakartaee-8,其范围为提供。

服务器.xml

<server description="new server">

    <featureManager>
        <feature>jakartaee-8.0</feature>
        <feature>localConnector-1.0</feature>
    </featureManager>

    <keyStore password="mypassword"/>

    <basicRegistry id="basic" realm="BasicRealm"> 
        <user name="username" password="mypassword"/>
    </basicRegistry>

    <httpEndpoint httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>

    <applicationManager autoExpand="true"/>

    <ssl id="defaultSSLConfig" trustDefaultCerts="true"/>

    <applicationMonitor updateTrigger="mbean"/>

    <webApplication id="test" location="test-0.0.1-SNAPSHOT.war" name="test"/>
</server>

【问题讨论】:

  • Test.java:18、configuration.getProperties()configuration 本身中的 究竟 是什么?您是说这是第一个,但是您是否在调试中仔细检查过?我问的原因是我觉得很奇怪 JAX-RS“资源”(Configuration)可用于在纯 CDI bean(即没有 @Path 注释的 bean)中注入。不过我没有在 JAX-RS 中使用过这个功能,所以我可能错了。
  • @NikosParaskevopoulos 是的,它是configuration.getProperties(),我已经仔细检查了这一点。按照@areus 的回答,如果我用Application 替换Configurationconfiguration.getProperties() 不会返回null,而是返回一个空地图。相信我,我对此感到很困惑。我不确定 JAX-RS 资源是否意味着注入纯 CDI bean。假设它不能注入到纯 CDI bean 中,那么为什么这与 Payara 服务器一起工作?
  • 嗯,我发现这两个系统的行为很奇怪:OpenLiberty 让我想知道为什么它会注入 Configuration 却将其留空(或者,无论如何,不​​一致)? Payara 可能正在以自己的方式(诚然方便,但不可移植)解释规范的灰色区域(将 JAX-RS 工件注入“just CDI”bean)。我的意见:当您对规格有疑问时,您会经常遇到这种差异;如果官方规范中没有明确说明,使用它可能会导致可移植性问题,即使在同一服务器的版本之间也是如此!祝你好运!
  • 是的,你是对的!在@areus 更新之后,我已经阅读了规范并决定按照规范格式化我的代码。这是一个很好的教训,谢谢你们!

标签: jakarta-ee jax-rs cdi open-liberty java-ee-8


【解决方案1】:

在我最初的回答中,我认为这是 OpenLiberty 的错,但根据 JAX-RS 2.1 规范,第 10.2.8 节配置,

客户端和服务器运行时配置都可以通过@Context 进行注入。这些配置可用于注入提供程序(客户端或服务器)和资源类(仅限服务器)。

规范不要求 CDI bean 支持特定的 JAX-RS @Context 注释,仅支持资源和提供者。

Payara 似乎超出了规范,并且支持 CDI bean 上的 @Context 注释。

【讨论】:

  • 感谢您建议使用Application 而不是Configuration,这可以解决错误但application.getProperties() 返回一个空地图。请注意,如果我将ConfigurationApplication 注入TestResources.java,则一切正常。是 OpenLiberty 不允许在 CDI 托管 bean 中使用 @Context。也感谢ApiEntryPoint.java 的提示。
  • 你说得对,我没有注意到 Test 不是 JAX-RS 类。 JAX-RS 规范声明配置只能注入到资源或提供者中。看起来 Payara 超越了这一点,并支持 CDI bean 上的 JAX-RS 特定注入。
  • 感谢您的帮助。下次我在两台服务器之间发生矛盾时,我会尝试阅读规范。谢谢!
猜你喜欢
  • 2020-06-04
  • 2021-03-13
  • 2017-11-16
  • 1970-01-01
  • 2014-01-12
  • 1970-01-01
  • 2020-05-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多