United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4738465 : (process) Need to create subprocess environment derived from current environment

Details
Type:
Enhancement
Submit Date:
2002-08-28
Status:
Resolved
Updated Date:
2003-09-12
Project Name:
JDK
Resolved Date:
2003-09-12
Component:
core-libs
OS:
generic
Sub-Component:
java.lang
CPU:
generic
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.3.0,1.3.1
Fixed Versions:
5.0 (tiger)

Related Reports
Duplicate:
Relates:
Relates:
Relates:
Relates:

Sub Tasks

Description

Name: pa48320			Date: 08/28/2002


When we develop new feature of environment switching for Oracle Reports product, we look into jre's Runtime.exec(cmdline, envp) function to spawn a new process with a set of environment. We found once we put environment to envp, some functionality (such as orb.init()) in the sub-process stop working. We searched sun's bugdb and found related bug 4115037 and 4064912, and understood the reason of the failure. Although there is a workaround for the problem, but we think the workaround is against 100% pure java implementation for main process, because in java there is no way to get environment variable. We have to call jni into native function to get certain environment variables and add them to envp.

We (as well as from many comments in the bugdb) think it is highly desirable feature to have another flavor of Runtime.exec() which inherits the environment from main process plus new
environment specified by envp.

Following is the source code to demostrates the problem. Run it on Windows platform. If you run
java MainProc CreateOrb
it will succeed. If you run
java MainProc CreateOrb -e
it will fail because it clears the several important Windows environment variables.

1. MainProc.java - Main process:
import java.io.*;

public class MainProc
{
  static Process p;

  public static void main(String []args) throws IOException,
                                                InterruptedException
  {
    if (args.length == 0)
    {
      System.out.println("Usage: java MainProc <SubProcessClassName> [-e]");
      System.out.println("  <Default>   Spawns SubProc without environment");
      System.out.println("  -e          Spawns SubProc with environment");
      return;
    }

    Runtime rt = Runtime.getRuntime();

    String cmdLine = "javaw -cp . " + args[0];

    /*
     * Spawn the process with environment setting
     */
    if (args.length >= 2 && args[1].equalsIgnoreCase("-e"))
    {
      String[] envp = new String[1];
      envp[0] = "Foo=Bar";

      p = rt.exec(cmdLine, envp);
    }
    /*
     * Spawn the process without environment setting
     */
    else
    {
      p = rt.exec(cmdLine);
    }

    /*
     * Catch the process in action
     */
    StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(), "ERROR");
    StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), "OUTPUT");
    errorGobbler.start();
    outputGobbler.start();

    int rv = p.waitFor();

    System.out.println("Return value: "+rv);
  }

}

class StreamGobbler extends Thread
{
  InputStream is;
  String type;

  StreamGobbler(InputStream is, String type)
  {
      this.is = is;
      this.type = type;
  }

  public void run()
  {
    try
    {
      InputStreamReader isr = new InputStreamReader(is);
      BufferedReader br = new BufferedReader(isr);
      String line=null;
      while ((line = br.readLine()) != null)
        System.out.println(type+">"+line);
    }
    catch (IOException ioe)
    {
      ioe.printStackTrace();
    }
  }
}

2. CreateOrb.java - Sub process:
public class CreateOrb
{
  public static void main(String []args)
  {
    try
    {
      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);

      if (orb != null)
        System.out.println("Successfully created an ORB object");
      else
        System.out.println("orb is null");
      System.exit(0);
    }
    catch (Exception e)
    {
      e.printStackTrace();
      System.exit(-1);
    }
  }
}
(Review ID: 163687) 
======================================================================

                                    

Comments
EVALUATION

A non-jni workaround would be possible if we could get the parent process'
environment.  See bug 4642629.  In that case, we could get the environment,
modify it, and use the existing Runtime APIs.

-- iag@sfbay 2002-08-28

An even cleaner solution to this problem would be to define two new methods: A
new System.getenv() method that returns a Map<String,String> of the environment
variables of the JRE process, and a new Runtime.exec method that takes a
command and an environment-variable map.

-- ###@###.### 2003/1/31

The smallest change to the API that would satisfy the user's desires is
the addition of
Map<String,String> System.environment()
and then adding
Process Runtime.exec(String[]args,Map<String,String>environ, File dir)

The obvious problem is that Runtime.exec is already horribly overloaded,
with four methods that take a String envp[] style environment.

public Process exec(String cmd, String envp[]) 
public Process exec(String command, String envp[], File dir) 
public Process exec(String cmdarray[], String envp[])
public Process exec(String cmdarray[], String envp[], File dir)

If we were to be consistent, we would add 4 new methods that mirror the
above.  The situation will get worse in the very likely event that
we will want to add new attributes that new processes can have,
for example:

- redirect stdio streams
- change process priority
- create daemon processes
- provide byte-level access to process properties like working directory.

An example of what I fear is the huge list of parameters to the
Windows CreateProcess function.

A better way is to have a separate class to capture nascent
process attributes.  

class ProcessBuilder {
  public ProcessBuilder(String command...)
  public Map<String,String> environment()
  public void setWorkingDirectory(File dir)
}

Unix folks can can think of this class as the state of a process after
fork() and before exec().

An advantage of this design is that the map returned by environment()
can be designed in such a way that the exact byte sequences of environment
variables not modified by the user program will be perfectly preserved
in any child process.  There should be no setEnvironment(Map<String,String>)
method.

While implementing class ProcessBuilder, we also added a method
ProcessBuilder.redirectErrorStream() which addresses the
functionality described in this rfe:
4480528: (process) Need combined stderr/stdout streams

###@###.### 2003-08-03
                                     
2003-08-03
WORK AROUND



Name: pa48320			Date: 08/28/2002


Use jni to retrieve the surrounding environment and append to envp.
======================================================================
                                     
2004-06-11
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
tiger

FIXED IN:
tiger

INTEGRATED IN:
tiger
tiger-b20


                                     
2004-06-14



Hardware and Software, Engineered to Work Together