HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

Webアプリケーションが起動したスレッドからJNDIリソースを取得できるか

Webアプリケーションサーバー上で動作するアプリケーションから、JNDIリソースとして登録されたデータベース接続オブジェクト(データソース)を取得、なんていうのはよくやることだが、今まで知らなかった原因により、データソースを取得できないケースがあったのでメモ。
試してみたのは次のコード。面倒なのでJSPで。

<%@ page contentType="text/html" pageEncoding="UTF-8" %>
<%@ page session="false" %>
<%@ page import="javax.naming.InitialContext" %>
<%@ page import="java.io.PrintWriter" %>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "DTD/xhtml11.dtd">
<%
response.setHeader("pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
%>
<%!
public void testJNDI(String jndiName, PrintWriter _out) {
    try {
        InitialContext initialContext = new InitialContext();
        Object object = initialContext.lookup(jndiName);
        _out.println("### naming.lookup(" + Thread.currentThread().getName() + "): " + object);
    } catch (Throwable e) {
        _out.println("### naming.lookup(" + Thread.currentThread().getName() + "): error");
        e.printStackTrace(_out);
    }
}
%>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
        <title>test JNDI</title>
    </head>
    <body>
        <pre><%
        final String jndiName = "java:comp/env/jdbc/DB2";
        final PrintWriter _out = new PrintWriter(out);
        testJNDI(jndiName, _out);
        Thread t = new Thread("MyThread") {
            public void run() {
                testJNDI(jndiName, _out);
            }
        };
        t.start();
        t.join();
        %></pre>
        <div>
            <a href="index.jsp">to index page</a>
        </div>
    </body>
</html>

やっていることは、アプリケーションサーバーのワーカースレッドからJNDIリソースを取得する、アプリケーションが独自に起動したスレッド上からJNDIリソースを取得する、という2通りの処理の実行。
で、これをApache Tomcat 5.5とWebSphere 6.1にデプロイする。(もちろん、JNDIリソースの設定は適切に行った上で。)
で、このJSPを実行してみる。
Apache Tomcat 5.5の場合は次のような出力が得られる。

### naming.lookup(http-18080-Processor24): org.apache.tomcat.dbcp.dbcp.BasicDataSource@11bed71
### naming.lookup(MyThread): org.apache.tomcat.dbcp.dbcp.BasicDataSource@11bed71

一方、WebSphere 6.1の場合は次のような出力が得られる。

### naming.lookup(WebContainer : 0): com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource@fb1c99cf
### naming.lookup(MyThread): error
javax.naming.ConfigurationException: A JNDI operation on a "java:" name cannot be completed because the serverruntime is not able to associate the operation's thread with any J2EE application component.  This condition can occur when the JNDI client using the "java:" name is not executed on the thread of a server application request.  Make sure that a J2EE application does not execute JNDI operations on "java:" names within static code blocks or in threads created by that J2EE application.  Such code does not necessarily run on the thread of a server application request and therefore is not supported by JNDI operations on "java:" names. [Root exception is javax.naming.NameNotFoundException: Name comp/env/jdbc not found in context "java:".]
    at com.ibm.ws.naming.java.javaURLContextImpl.throwConfigurationExceptionWithDefaultJavaNS(javaURLContextImpl.java:416)
    at com.ibm.ws.naming.java.javaURLContextImpl.lookup(javaURLContextImpl.java:388)
    at com.ibm.ws.naming.java.javaURLContextRoot.lookup(javaURLContextRoot.java:205)
    at com.ibm.ws.naming.java.javaURLContextRoot.lookup(javaURLContextRoot.java:145)
    at javax.naming.InitialContext.lookup(InitialContext.java:363)
    at com.ibm._jsp._action.testJNDI(_action.java:37)
    at com.ibm._jsp._action$1.run(_action.java:92)
Caused by: javax.naming.NameNotFoundException: Name comp/env/jdbc not found in context "java:".
    at com.ibm.ws.naming.ipbase.NameSpace.getParentCtxInternal(NameSpace.java:1767)
    at com.ibm.ws.naming.ipbase.NameSpace.lookupInternal(NameSpace.java:1083)
    at com.ibm.ws.naming.ipbase.NameSpace.lookup(NameSpace.java:991)
    at com.ibm.ws.naming.urlbase.UrlContextImpl.lookup(UrlContextImpl.java:1263)
    at com.ibm.ws.naming.java.javaURLContextImpl.lookup(javaURLContextImpl.java:384)
    ... 5 more

例外のメッセージが長々と書いてあるが‥

A JNDI operation on a "java:" name cannot be completed because the serverruntime is not able to associate the operation's thread with any J2EE application component. This condition can occur when the JNDI client using the "java:" name is not executed on the thread of a server application request. Make sure that a J2EE application does not execute JNDI operations on "java:" names within static code blocks or in threads created by that J2EE application. Such code does not necessarily run on the thread of a server application request and therefore is not supported by JNDI operations on "java:" names.

ざっくりいうと、

アプリケーションサーバーのワーカースレッド(リクエストを受けたときに使用されるスレッド)ではないスレッドからJNDIリソースに対する操作を行うことはできない。

と書いてある。


Webアプリケーションでもタイマー処理みたいなのを実行したい場合があると思うが、そのような処理ではデータベースアクセス等はできないということだろうか。それとも、何らかの手段があるのか。
また余裕があるときに調べてみるとしよう。