nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

JNDIをさらっと振り返る

JBoss Advent Calendar 2011の12日目のエントリです。

JNDIはJava EEサーバのEJBJDBCデータソースやJMSのConnectionFactoryなど、各種サービスにアクセスするためのエントリポイントとなる部分なのですが、あまりエキサイティングな技術ではないので注目されることはないですし、どのようなものなのかを調べたりする機会もなかなかないでしょう。というわけでさらっと振り返ります。

まず基本的な機能はリモートアクセスもできるサーバ上のグローバルHashMapみたいなものです。メソッド名がちょっと違い、get()ではなくlookup()、put()ではなくrebind()となっています。

データソースを取得するようなコードはこのようになります。

InitialContext context = new InitialContext();
DataSource ds = (DataSource)context.lookup("java:DefaultDS");

lookup()の引数ですが、以下で始まるものは少し特殊な意味を持ちます。

  • java:
    • Java VMローカル空間。Java VMの外からリモートアクセスすることはできません。
  • java:comp
    • コンポーネントローカル空間。コンポーネントごと(たとえば一つのEJB)に作られる隔離空間です。この下に"env"サブツリーとUserTransactionがバインドされることになっています。
  • java:comp/env
    • 環境ネーミングコンテキストと呼ばれる、コンポーネントで利用するJNDI空間。Java EEではここに"jdbc", "ejb", "jms", "mail"などのサブツリーをバインドして、その中にJDBCデータソースやEJBなどをバインドすることを推奨しています。が、必須ではないのでコンテナによってenv以下のサブツリーの構成が異なることもあるでしょう。

上記以外はグローバル空間ということになります。現在はjava:compを使うようなコンポーネントローカル空間を使って各コンポーネントを隔離する、という厳格な設計はあまり流行っておらず、必要性が無ければ単にグローバル空間を自由に使う、というスタイルのほうが主流だと思います。

JNDIにリモートアクセスするにはjndi.peopertiesをクラスパス上に配置するか、InitialContextをnewするときに接続先情報を渡します。古いAPIのためパラメータの型がHashtableだったりしますが、Propertiesクラスを利用するほうがふさわしいと思うので個人的にはHashtableではなくPropertiesを利用しています。

Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY,
          "org.jnp.interfaces.NamingContextFactory");
props.put(Context.URL_PKG_PREFIXES,
          "org.jboss.naming:org.jnp.interfaces");
props.put(Context.PROVIDER_URL, "localhost:1099");
InitialContext context = new InitialContext(props);

jndi.peopertiesの場合は以下のように記述します。jndi.peopertiesをクラスパスに含める場合はInitialContextをnewするときにパラメータ指定は必要ありません。Java EEコンテナ上では元々コンテナ側でjndi.peopertiesが設定されているはずなので、Java EEコンテナ上にデプロイするアプリケーションなどにjndi.propertiesを含めてはいけません。JNDIアクセスが誤動作します。

jndi.peopertiesは上記のようないろいろな制約を生みやすいので、単純なケース以外では基本的には使わないほうが無難です。

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099

他にJNDIはRMIレジストリとして機能したり、LDAPへのアクセスインタフェースとなったりもしますが、あまり使うことはないので省略します。