Tomcat 通过执行程序访问时上下文为空且可运行

Tomcat context is empty when accessed via executor and runnable

您好,我在 apache-tomee-plus-8.0.1 上有一个 Web 应用程序 运行。我的问题是关于从自定义执行程序中的可运行对象获取环境变量。变量定义在 /conf/context.xml:

<?xml version="1.0" encoding="UTF-8"?>

<Context>

    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

    <!-- disable the scan  in order to avoid errors at startup due to ora18n.jar-->
    <JarScanner scanManifest="false"  scanClassPath="false" scanBootstrapClassPath="false"></JarScanner>

    <!-- base64 from user:pass -->  
    <Environment name="myCreds"
    value="toto" type="java.lang.String" />
        
    
    
</Context>

我用来获取变量“myCreds”的函数

 private static String getCredentialsFromContext() throws NamingException {
        Context initialContext = new InitialContext();
        Context environmentContext = (Context) initialContext.lookup("java:comp/env");
        return (String) environmentContext.lookup("myCreds");
    }

此函数通过 JAX-RS 端点调用,该端点用于启动服务器应用程序的长时间后台维护任务。后台任务的进度随后可在另一个端点上获得。 如果我这样做

    @GET
    @Path("/testOK")
    public static String testOK() {
          return getCredentialsFromContext(); // works fine
    }

但是当我使用执行程序时,查找失败并显示 javax.naming.NameNotFoundException:名称 [comp/env] 未在此上下文中绑定。找不到 [comp].

    private static ExecutorService index_executor;
    @GET
    @Path("/testKO")
    public static Response testKO() {
          if (index_executor == null){
               index_executor = Executors.newFixedThreadPool(5);
          }
          index_executor.submit(new Runnable() {

            @Override
            public void run() {
                System.out.println(getCredentialsFromContext()); // FAIL
            }
        });
        return Response.ok.build()  
    }

从可运行对象调用时,InitialContext 似乎不一样。我想避免通过 args 传递“myCreds”的值。我试图在 webapp 的 context.xml 中移动“myCreds”的声明,但它没有帮助。使用 JNDIContext 也会失败。 您了解问题所在以及上下文不同的原因吗?

谢谢:)

JNDI 查找依赖于 运行 线程上的某些上下文信息,通常是上下文 class 加载程序。

在 Java EE/Jakarta EE 服务器上,您不应自己生成新的(非托管)线程,而应使用容器提供的 ManagedExecutorService。此服务自动从调用线程传播某些类型的上下文:

The types of contexts to be propagated from a contextualizing application component include JNDI naming context, classloader, and security information. Containers must support propagation of these context types.

(Jakarta Concurrency Specification,强调我的)

您可以使用 @Resource 注释注入 ManagedExecutorService

    @Resource
    private ManagedExecutorService executorService;

在 Wildfly 上使用 ManagedExecutorService,但在 TomEE 上有一个错误阻止命名上下文的传播:JAX-RS 资源使用 CxfContainerClassLoader作为上下文 classloader,它包装了真正的 classloader,防止它传播到托管线程。

解决方法包括暂时切换到包装的 classloader:

        final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
        if (tccl instanceof org.apache.openejb.util.classloader.Unwrappable) {
            final ClassLoader cl = ((org.apache.openejb.util.classloader.Unwrappable) tccl).unwrap();
            Thread.currentThread().setContextClassLoader(cl);
        }
        executorService.submit(...);
        Thread.currentThread().setContextClassLoader(tccl);

编辑:实际上,将JAX-RS资源标记为@Stateless就足以正确传播JNDI命名上下文。