1. 序章

Java Naming and Directory Interface(JNDI)は、Java APIとしてのネーミングおよび/またはディレクトリサービスの一貫した使用を提供します。このインターフェイスは、オブジェクトのバインド、オブジェクトの検索またはクエリ、および変更の検出に使用できます。同じオブジェクト上。

JNDIの使用法には、サポートされているネーミングおよびディレクトリサービスのさまざまなリストが含まれていますが、このチュートリアルでは、JNDIのAPIを調べながらJDBCに焦点を当てます。

2. JNDIの説明

JNDIを使用する場合は、基盤となるサービスの理解と、アクセス可能な実装が必要です。たとえば、データベース接続サービスでは、特定のプロパティと例外処理が必要です。

ただし、JNDIの抽象化により、接続構成がアプリケーションから切り離されます。

JNDIのコア機能を含むNameContextを調べてみましょう。

2.1. 名前インターフェース

Name objectName = new CompositeName("java:comp/env/jdbc");

Name インターフェースは、JNDI名のコンポーネント名と構文を管理する機能を提供します。 文字列の最初のトークンはグローバルコンテキストを表し、その後、追加された各文字列は次のサブコンテキストを表します。

Enumeration<String> elements = objectName.getAll();
while(elements.hasMoreElements()) {
  System.out.println(elements.nextElement());
}

出力は次のようになります。

java:comp
env
jdbc

ご覧のとおり、/名前サブコンテキストの区切り文字です。 次に、サブコンテキストを追加しましょう。

objectName.add("example");

次に、追加をテストします。

assertEquals("example", objectName.get(objectName.size() - 1));

2.2. コンテキストインターフェース

環境ネーミングおよびディレクトリサービスのプロパティが含まれています 。 ここでは、Springのヘルパーコードを使用して、 環境

SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder(); 
builder.activate();

SpringのSimpleNamingContextBuilderはJNDIプロバイダーを作成し、NamingManagerを使用してビルダーをアクティブ化します。

JndiTemplate jndiTemplate = new JndiTemplate();
ctx = (InitialContext) jndiTemplate.getContext();

最後に、 JndiTemplate は、InitialContextへのアクセスに役立ちます。

3. JNDIオブジェクトのバインドとルックアップ

NameContextの使用方法を確認したので、JNDIを使用してJDBC DataSourceを格納しましょう。

ds = new DriverManagerDataSource("jdbc:h2:mem:mydb");

3.1. JNDIオブジェクトのバインド

コンテキストがあるので、オブジェクトをそれにバインドしましょう。

ctx.bind("java:comp/env/jdbc/datasource", ds);

一般に、サービスは、オブジェクト参照、シリアル化されたデータ、または属性をディレクトリコンテキストに格納する必要があります。 それはすべて、アプリケーションのニーズによって異なります。

この方法でJNDIを使用することはあまり一般的ではないことに注意してください。 通常、JNDIは、アプリケーションランタイムの外部で管理されるデータとインターフェースします。

ただし、アプリケーションがすでに DataSource を作成または検索できる場合は、Springを使用してそれを配線する方が簡単な場合があります。 対照的に、アプリケーションの外部にあるものがJNDIのオブジェクトにバインドされている場合、アプリケーションはそれらを消費する可能性があります。

3.2. JNDIオブジェクトの検索

DataSource を調べてみましょう:

DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");

次に、DataSourceが期待どおりであることを確認するためにテストしてみましょう。

assertNotNull(ds.getConnection());

4. 一般的なJNDI例外

JNDIを使用すると、実行時例外が発生する場合があります。 ここにいくつかの一般的なものがあります。

4.1. NameNotFoundException

ctx.lookup("badJndiName");

この名前はこのコンテキストではバインドされていないため、次のスタックトレースが表示されます。

javax.naming.NameNotFoundException: Name [badJndiName] not bound; 0 bindings: []
  at org.springframework.mock.jndi.SimpleNamingContext.lookup(SimpleNamingContext.java:140)
  at java.naming/javax.naming.InitialContext.lookup(InitialContext.java:409)

スタックトレースにはバインドされたすべてのオブジェクトが含まれていることに注意してください。これは、例外が発生した理由を追跡するのに役立ちます。

4.2. NoInitialContextException

InitialContext との相互作用は、NoInitialContextExceptionをスローする可能性があります。

assertThrows(NoInitialContextException.class, () -> {
  JndiTemplate jndiTemplate = new JndiTemplate();
  InitialContext ctx = (InitialContext) jndiTemplate.getContext();
  ctx.lookup("java:comp/env/jdbc/datasource");
}).printStackTrace();

以前に使用したように、このJNDIの使用は有効であることに注意してください。 ただし、今回はJNDIコンテキストプロバイダーがないため、例外がスローされます。

javax.naming.NoInitialContextException: Need to specify class name in environment or system property, 
  or in an application resource file: java.naming.factory.initial
    at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:685)

5. 最新のアプリケーションアーキテクチャにおけるJNDIの役割

JNDIは、Spring Bootなどの軽量のコンテナ化されたJavaアプリケーションではあまり役割を果たしませんが、他の用途もあります。 まだJNDIを使用している3つのJavaテクノロジは、 JDBC EJB 、およびJMSです。 これらはすべて、Javaエンタープライズアプリケーション全体で幅広い用途があります。

たとえば、別のDevOpsチームが、すべての環境で機密性の高いデータベース接続のユーザー名やパスワードなどの環境変数を管理する場合があります。 JNDIリソースは、Webアプリケーションコンテナで作成できます。JNDIは、すべての環境で機能する一貫した抽象化のレイヤーとして使用されます。

この設定により、開発者は、同じJNDI名を使用して実稼働環境の機密リソースに接続しながら、開発目的でローカル定義を作成および制御できます。

6. 結論

このチュートリアルでは、Java Naming and Directory Interfaceを使用したオブジェクトの接続、バインド、および検索について説明しました。JNDIによってスローされる一般的な例外についても確認しました。

最後に、JNDIが最新のアプリケーションアーキテクチャにどのように適合するかを確認しました。

いつものように、コードはGitHub利用できます。