プロジェクト

全般

プロフィール

Java SEでJNDI(スタンドアロン)

高橋 徹 さんが4年以上前に追加

JDBC APIを使ってデータベースアクセスするアプリケーションを作っている(スペクトラム表示)。
データベース接続に、DataSourceを使うとよいが、スタンドアロンのJava SEベースのアプリでは自力でJNDIサーバーを用意するのが難儀。
しかし、ファイルに置いた設定を読み込む簡易なJNDIライブラリがあった。

SimpleJNDI

https://github.com/hen/osjava

pomを見ると依存ライブラリは、DHCP機能を使う場合にのみcommons-dhcpとcommons-poolが必要。
2008年9月のVer.0.11.4.1を最後に更新がない。

フォークして更新をしている版があった。
https://github.com/h-thurow/Simple-JNDI
2019-02-22にVer.0.18.1をリリースしている。

> jar --describe-module --file simple-jndi-0.18.1.jar
モジュール・ディスクリプタが見つかりません。自動モジュールが導出されました。

simple.jndi@0.18.1 automatic
requires java.base mandated
contains org.osjava
contains org.osjava.datasource
contains org.osjava.sj
contains org.osjava.sj.jndi
contains org.osjava.sj.loader
contains org.osjava.sj.loader.convert

フォーク版のpom.xmlを見ると、次のライブラリへの依存(実行時)が定義されていた。
commons-logging, slf4j-api, org.jetbrains, commons-lang

サンプルプログラム

簡単なサンプルプログラムを作ってみる。次のディレクトリ構成とファイルを用意。

work
  +-- config
  |     +-- database.properties
  +-- mods
  +-- lib
  |     +-- simple-jndi-0.18.1.jar
  +-- src
        +-- com.torutk.jdbcex
              +-- module-info.java
              +-- com
                   +-- torutk
                         +-- jdbcex
                               +-- Main.java
                               +-- jndi.properties
  • module-info.java
    module com.torutk.jdbcex {
        requires java.naming;
        requires simple.jndi;
    }
    
  • Main.java
    package com.torutk.jdbcex;
    
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    
    public class Main {
    
        public static void main(String... args) {
        InitialContext context = null;
        try {
            context = new InitialContext();
            var type = context.lookup("database.type");
            System.out.println("JNDI type=" + type);
        } catch (NamingException ex) {
            System.err.println("ERROR: " + ex.getMessage());
        } finally {
            try {
            if (context != null) {
                context.close();
            }
            } catch (NamingException ex) {
            // ...
            }
        }
        }
    } 
    
  • database.properties
    type=javax.sql.DataSource
    driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
    url=jdbc:sqlserver://localhost;databaseName=mydatabase
    user=myname
    password=mypassword
    

コンパイルは次

work> javac -d mods ^
 --module-path lib ^
 --module-source-path src ^
 -m com.torutk.jdbcex

実行は次

java --module-path mods;lib ^
 -m com.torutk.jdbcex/com.torutk.jdbcex.Main

実行するとエラーとなる。

ERROR: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial

これは、Java SEのスタンドアロン環境(Java EEコンテナがない)において、new InitialContext() ではコンテキストが生成できないため。
Simple JNDIライブラリは、jndi.propertiesのファイル名でクラス名を指定し、このファイルをクラスパスに置くとなっている。

java.naming.factory.initial=org.osjava.sj.SimpleContextFactory
org.osjava.sj.root=config/

しかし、モジュールパス(mods\com.torutk.jdbcex\com\torutk\jdbcex\jndi.properties)においても認識されない。
jndi.propertiesをカレントディレクトリに移し、実行時にカレントディレクトリをクラスパスに追加したところ、

java --module-path mods;lib -cp . -m com.torutk.jdbcex/com.torutk.jdbcex.Main
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
        at simple.jndi@0.18.1/org.osjava.sj.SimpleJndi.<clinit>(SimpleJndi.java:60)
        at simple.jndi@0.18.1/org.osjava.sj.SimpleJndiContextFactory.getInitialContext(SimpleJndiContextFactory.java:69)
        at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:730)
        at java.naming/javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305)
        at java.naming/javax.naming.InitialContext.init(InitialContext.java:236)
        at java.naming/javax.naming.InitialContext.<init>(InitialContext.java:184)
        at com.torutk.jdbcex/com.torutk.jdbcex.Main.main(Main.java:11)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        ... 7 more

なんと、SLF4J への依存がある。pomを調べると無条件でcommons-loggingへの依存がある。
うーむ、いったん中止。


返答 (1)

オリジナル(simple-jndi-0.11.4.1.jar)に差し替えて実施 - 高橋 徹 さんが4年以上前に追加

他のライブラリへの実行時の依存がないオリジナルのsimple-jndi-0.11.4.1.jarを使用して実行する。

Caused by: java.lang.ClassNotFoundException: javax.sql.DataSource
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        ... 22 more

あ、これはSQLのクラスが含まれるモジュールをmodule-info.javaで要求していないからだなぁ。
module-info.javaに以下を追記

  module com.torutk.jdbcex {
      requires java.naming;
+     requires java.sql;
      requires simple.jndi;
  }

やっとクラスがないといったエラーは解消。
次は、ディレクトリの参照の問題

java --module-path mods;lib -cp .  -m com.torutk.jdbcex/com.torutk.jdbcex.Main
ERROR: Invalid subcontext 'database' in context ''

jndi.propertiesに次を追記すると、エラーが一歩前進した。

  java.naming.factory.initial=org.osjava.sj.SimpleContextFactory
  org.osjava.sj.root=config/
+ org.osjava.sj.delimiter=/

java --module-path mods;lib -cp .  -m com.torutk.jdbcex/com.torutk.jdbcex.Main
JNDI type=null

ここから試行錯誤しつつも進まず

    (1-1/1)