Is it possible to set an environment variable at runtime from Java?

JavaEnvironment Variables

Java Problem Overview


Is it possible to set an environment variable at runtime from a Java application? In Java 1.5 java.lang.System class there is the getenv() method, I would only need a setenv() method...

Is it possible to modify the environment variables in the java process itself; not in the child process.

Is it possible to achieve it through JNI? And how would that work?

Thanks.

EDIT: Ok let me put it this way - Can we do the following with Java. Please answer.

  1. Can we modify the environment of the current process?
  2. Can we modify the environment of the parent process?
  3. Can we modify the environment of the child process?

Hemal Pandya has answered that "You can modify the environment of current and child processes but not of the parent process that spawned this process." Do you agree with this ?

Java Solutions


Solution 1 - Java

If my intuition is correct, and you actually want to modify the environment for the benefit of a spawned (forked) sub-process (Runtime.getRuntime().exec()), then use ProcessBuilder instead of exec(). You can build a custom environment via your ProcessBuilder instance's environment() method.

If this is not what you are trying to achieve then kindly disregard this answer.


UPDATE

The answer to your three updated, specific questions is as follows:

  1. Can we modify the environment of the current process?
  • Not easily. Depends whether you want to change the process' environment, to change the value(s) returned by System.getenv() in the same JVM, or both.
  • As Greg Hewgill pointed out, to change the current process' environment you can call setenv or its platform-specific equivalent via JNI. You may also employ the extremely convoluted method from point 2 below, which works for any process (provided you have the permissions.) However, be aware that in most JVMs this change might never be reflected in the values returned by System.getenv(), as the environment is more often than not cached at virtual machine startup in a java.util.Map (or equivalent.)
  • To change the JVM's cached copy of the environment, when a cache is used (see the source code in System.java in whichever JVM distribution you will be using to deploy), you may try hacking the implementation (via class loading order, reflection, or instrumentation.) In the case of SUN's v1.6 JVM, for example, the environment cache is managed by the undocumented ProcessEnvironment class (which you can patch.)
  1. Can we modify the environment of the parent process?
  2. Can we modify the environment of the child process?
    • Yes, through ProcessBuilder when spawning the process.
    • If the process has already been spawned when the environment alteration is required, you need method 2 above (or some equally convoluted method, such as code-injection at spawn time, ulteriorly controlled through e.g. socket by the parent process.)

Note that all methods above, except for the one involving ProcessBuilder, are brittle, error prone, non-portable to various degrees, and prone to race conditions in multi-threaded environments.

Solution 2 - Java

You can get a handle on the underlying map that java.lang.ProcessEnvironment is holding on to, and then put new stuff and remove stuff all you want.

This works on java 1.8.0_144. Can't guarantee it works on any other version of java, but it's probably similar if you really need to change the environment at run time.

private static Map<String,String> getModifiableEnvironment() throws Exception{
    Class pe = Class.forName("java.lang.ProcessEnvironment");
    Method getenv = pe.getDeclaredMethod("getenv");
    getenv.setAccessible(true);
    Object unmodifiableEnvironment = getenv.invoke(null);
    Class map = Class.forName("java.util.Collections$UnmodifiableMap");
    Field m = map.getDeclaredField("m");
    m.setAccessible(true);
    return (Map) m.get(unmodifiableEnvironment);
}

After you get the reference to the map, just add whatever you want and you can now retrieve it using the regular old System.getenv("") call.

I tried this its working in MAC not working in Windows in both os java version 1.8_161

Solution 3 - Java

In response to your updated question:

  1. Can we modify the environment of the current process?
    Yes, if you use JNI to call setenv() or something. You probably don't need to do this though, and it may not work in all situations.
  2. Can we modify the environment of the parent process?
    No.
  3. Can we modify the environment of the child process?
    Yes, using ProcessBuilder.

Solution 4 - Java

@Snowbuilder's answer put me in the right track, however it was mysteriously not working with Oracle JDK 1.8.0_231 (he tested with minor version _144). Even though I was able to update the underlying Map (validated by printing out System.getenv() before and after adding a new property to the Map), the changes were not reflected when the new property was retrieved using System.getenv("property").

After some investigation I found out that's because System.getenv() and System.getenv("property") end up using different static attributes of java.lang.ProcessEnvironment, which are initialized in the class' static block. So it doesn't matter if new properties are added to the Map retrieved using System.getenv(); these properties will not be available in the other Map used by System.getenv("property").

So I changed the code from the other answer to deal with this scenario and came to the code below. Please note this will only work if you retrieve properties using System.getenv("property"); if you use System.getenv().get("property") then his answer is what you need. The usage is as follows:

@SuppressWarnings("unchecked")
private static Map<String, String> getModifiableEnvironment() throws Exception
{
    Class<?> pe = Class.forName("java.lang.ProcessEnvironment");
    Method getenv = pe.getDeclaredMethod("getenv", String.class);
    getenv.setAccessible(true);
    Field props = pe.getDeclaredField("theCaseInsensitiveEnvironment");
    props.setAccessible(true);
    return (Map<String, String>) props.get(null);
}

This method should be used as follows:

getModifiableEnvironment().put("propName", "propValue");
System.getenv("propName"); // this will return "propValue"

Solution 5 - Java

I don't think so, at least not purely in Java, but why do you need to do this? In Java it's preferable to use properties via System.getProperties(), which you can modify.

If you really must, I'm sure you could wrap the C setenv function in a JNI call - in fact, I wouldn't be surprised if someone has done so already. I don't know the details of the code, though.

Solution 6 - Java

You can modify the environment of current and child processes but not of the parent process that spawned this process.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVickyView Question on Stackoverflow
Solution 1 - JavavladrView Answer on Stackoverflow
Solution 2 - JavaSnowbuilderView Answer on Stackoverflow
Solution 3 - JavaGreg HewgillView Answer on Stackoverflow
Solution 4 - JavaLeonardo ReinehrView Answer on Stackoverflow
Solution 5 - JavaDavid ZView Answer on Stackoverflow
Solution 6 - JavaMiserable VariableView Answer on Stackoverflow