通常はアプリ/プログラムの実行時や、
ミドルウェアの定義等で環境変数を設定することになります。 ただ、単体試験時にJunit経由で実行する時は、
ミドルウェアを経由しないといった理由等から、
それらの設定を埋め込むことが困難・面倒なこともあるので、
プログラムから環境変数を設定したい!というタイミングもあるかと思います。
そこで、Javaのプログラム内から設定(setenv)できるような仕組みの一例を
解説したいと思います。(自分メモ的にも)
注意
Javaのアプリ/プログラム内から環境変数を設定するのは、通常では禁止行為となります。
それはスコープでアクセスできないようにしていることから、
想定できます。
そのため試験実施時など、
商用運用以外の用途でのみ使用することをお勧めします。
処理方法
リフレクション機能を利用して、強引に環境変数を格納しているMAPを持ってきて、
そのMapの任意の値をputするようにしています。 例として、Junit試験で、
環境変数の設定を最初に行う処理を記述しました。
package hoge;
import static org.junit.Assert.*;
import java.lang.reflect.Field;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
public class SetEnvTest {
@SuppressWarnings("unchecked")
@Before
public void before() throws Exception {
/*
* 環境変数を設定する。
*/
// 環境変数が設定されているマップをリフレクションを使用して持ってくる。
Class<?> clazz = Class.forName("java.lang.ProcessEnvironment");
Field f = clazz.getDeclaredField("theCaseInsensitiveEnvironment");
f.setAccessible(true);
Map<String,String> fi = (Map<String,String>) f.get(null);
// 後はそのマップにputするだけ。
fi.put("USER", "hogehoge");
}
@Test
public void testSomeLibraryMethod() {
String val = System.getenv("USER");
System.out.println(val);
assertEquals("hogehoge", val);
}
}
上記の例で更に環境変数の箇所のみを抜粋します。下記の箇所が環境変数の設定箇所になります。
// 環境変数が設定されているマップをリフレクションを使用して持ってくる。
Class<?> clazz = Class.forName("java.lang.ProcessEnvironment");
Field f = clazz.getDeclaredField("theCaseInsensitiveEnvironment");
f.setAccessible(true);
Map<String,String> fi = (Map<String,String>) f.get(null);
// 後はそのマップにputするだけ。
fi.put("USER", "hogehoge");
処理の解説
まず、環境変数の取得処理System.getenv(String)のソースの中身を追いかけてみたところ、最終的には
java.lang.ProcessEnvironmen
のクラスのtheCaseInsensitiveEnvironment
というフィールドから値を取得していました。
theCaseInsensitiveEnvironment
がstatic設定だったので、どこからでもアクセスできることから、
リフレクションのフィールド取得でインスタンス指定なしに取得し※1、
欲しい箇所のMapが取得できる。というのが環境変数を設定できる仕組みになります。 ※1:普通、リフレクションではインスタンス生成し、それをパラメータに渡すと、
対象のインスタンスのフィールドにアクセスするという動きですが、
ProcessEnvironmen
がpublicクラスではなかったため、インスタンスを生成できなかったので、このような処理にしました。
目標フィールドがstaticだったのが幸いしました。
まとめ
私が使用したのは、資産を引き継いだ際、
Junitの試験を実施しようとしたときに、
環境変数の設定手順が非常に面倒だったので、
それの回避方法として使用するためでした。 リフレクションで強引に変更禁止領域にアクセスしていることから、
全然推奨方法ではないのですが、
どうしてもやりたい場合はこのような方法もあるという感じで、
参考になれば幸いです。
コメント