argius note

プログラミング関連

JUnitテストで使う即席のJNDIデータソースを用意する

Cayenneの接続設定でJNDIDataSourceFactoryを使っている場合、JNDIサーバが参照できない環境で設定を切り替えずに単体テストをするにはどうしたら良いのか...と考えたらこうなりました。

サーバ側の準備

Javaのツールに、"rmiregistry"というものがあります。これを起動しておきます。Windowsなら$JDK_HOME/bin/rmiregistry.exeをダブルクリックで実行してもOKです。
ここに、即席のJNDI参照可能なDataSourceを登録します。


まず、RMIレジストリに名前サーバとしてアクセスするために、jndi.propertiesの設定が必要です。
登録するDataSourceは、下記のDataSourceReferenceクラスのように、javax.naming.Referenceのサブクラスか、java.rmi.Remote,javax.naming.Referenceableの実装クラスのインスタンスである必要があります。
このクラスとjndi.propertiesはコネクションを使うリモートクライアント側でもクラスパスに設定する必要があります。

  • jndi.properties
java.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory
java.naming.provider.url=rmi://localhost:1099/
  • DataSourceReference.java(Java1.6バージョン)
package local;

import java.io.*;
import java.sql.*;

import javax.naming.*;
import javax.sql.*;

public final class DataSourceReference extends Reference implements DataSource {
    private static final String JNDI_NAME = "jdbc/ds1";
    private static final String URL = "jdbc:...";
    private static final String USER = "...";
    private static final String PASSWD = "...";
    public DataSourceReference() {
        super(DataSourceReference.class.getName());
    }
    @Override
    public Connection getConnection() throws SQLException {
        return getConnection(USER, PASSWD);
    }
    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return DriverManager.getConnection(URL, username, password);
    }
    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null; // ignore
    }
    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false; // ignore
    }
    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }
    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // ignore
    }
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null; // ignore
    }
    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        // ignore
    }

    public static void main(String[] args) {
        try {
            Context ctx = new InitialContext();
            try {
                ctx.rebind(JNDI_NAME, new DataSourceReference());
                System.out.printf("lookup: [%s]%n", ctx.lookup(JNDI_NAME));
            } finally {
                ctx.close();
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

これをjavaで実行すると、DataSourceReferenceがRMIレジストリに登録されます。プロセスが終了しませんが、これはRMI経由で呼び出されるのをサーバとして待つためです。Eclipseデバッガで見ると、"RMI Runtime"という名前のスレッドグループが残っているのが分かります。

クライアントの準備

サーバ側で設定したDataSourceReferenceクラスとjndi.propertiesをクラスパスに設定するだけでOKです。