Java コード上で環境変数をセット

環境変数の取得は、System.getEnv(String)
でも、setEnv など System には存在しない。
Java コード上から環境変数をセットしなければならないケースなんて、そうそうないのだけれど。。。

参考は、How do I set environment variables from Java? - Stack Overflow
リフレクションで無理やりなのですが。。。

@SuppressWarnings("unchecked")
protected static void setEnv(String key, String value) {
    try{
        Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
        Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
        theEnvironmentField.setAccessible(true);
        Map<String, String> env = (Map<String, String>)theEnvironmentField.get(null);
        env.put(key, value);
        Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
        theCaseInsensitiveEnvironmentField.setAccessible(true);
        Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
        cienv.put(key, value);
    }catch(Exception e){
        try{
            Class<?>[] classes = Collections.class.getDeclaredClasses();
            Map<String, String> env = System.getenv();
            for(Class<?> cl : classes){
                if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
                    Field field = cl.getDeclaredField("m");
                    field.setAccessible(true);
                    Object obj = field.get(env);
                    Map<String, String> map = (Map<String, String>)obj;
                    map.put(key, value);
                }
            }
        }catch(Exception x){
            x.printStackTrace();
        }
    }
}

Mapでまとめてセットする。。。

@SuppressWarnings("unchecked")
protected static void setEnv(Map<String, String> newenv) {
    try{
        Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
        Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
        theEnvironmentField.setAccessible(true);
        Map<String, String> env = (Map<String, String>)theEnvironmentField.get(null);
        env.putAll(newenv);
        Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
        theCaseInsensitiveEnvironmentField.setAccessible(true);
        Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
        cienv.putAll(newenv);
    }catch(Exception e){
        try{
            Class<?>[] classes = Collections.class.getDeclaredClasses();
            Map<String, String> env = System.getenv();
            for(Class<?> cl : classes){
                if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
                    Field field = cl.getDeclaredField("m");
                    field.setAccessible(true);
                    Object obj = field.get(env);
                    Map<String, String> map = (Map<String, String>) obj;
                    map.putAll(newenv);
                }
            }
        }catch(Exception x){
            x.printStackTrace();
        }
    }
}

でも、これらは Java9 以降では java.lang.ProcessEnvironment から取得した theEnvironment フィールドに対して
アクセス権の強制取得、setAccessible(true) を実行したときに
以下のとおり、標準エラー出力(System.err)で、警告(ワーニング)を出力します。

WARNING: Illegal reflective access by xx.xxx.xxx.Xxxx (file:/C:/xxxxxx/target/classes/)
to field java.lang.ProcessEnvironment.theEnvironment
WARNING: Please consider reporting this to the maintainers of xx.xxx.xxx.Xxxx
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

この警告メッセージが出るのを抑制するのに次のようなメソッドを用意して
環境変数をセット実行前に実行しておけば抑制はできます。

public static void ignoreJava9Warning(){
    try{
        Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        sun.misc.Unsafe u = (sun.misc.Unsafe) theUnsafe.get(null);
        Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
        Field logger = cls.getDeclaredField("logger");
        u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
    }catch(Exception e){
    }
}

この抑制は、Java8 で無意味です。catch Exception の中に入ります。