Name: mf23781 Date: 07/02/99
Test Case and Failure Data:
Description of Problem:
The user was trying to issue an command and redirect the output to a file.
Issuing a runtime exec with one of the following commands will cause a problem
cmd /c netstat > myfile for example. myfile would be created but would be empty.
An internal command such as dir, for example cmd /c dir > myFile will work
as excepted, myfile would contain the directory listing.
The problem was not present in JDK1.2.x. Looking at the source code showed only
one difference. This was a change to the flags in the CreateProcess
call. DETATCHED_PROCESS was changed to 0. (normal process). This allows the redirection
to occur as expected.
We attempted to put this fix into 1.7.1. This generates a problem in that for jrew a console
window will appear. This also occurs with JDK1.2.2. The latest drop of Kestrel has no changes
in the source code. (This is Sunbug 4188007)
Ways of preventing this have been investigated. This involded creating a console
but not showing it. Use the CREATE_NEW_CONSOLE flag but set the wShowWindow flag to SW_HIDE.
However for GUI applications this blocks the interface all together.
Problem Analysis:
A change was made so that the child process was nolonger DETATCHED_PROCESS. This fixes
the redirection problem and also has some effect on the running of 16 bit applications. SunBug
4079419 indicates that the change from DETATCHED_PROCESS to 0 was made in 1.2beta4 to enable
16 bit applications to be run.
This change also allows the redirection to occur successfully. It generates the console window
generation problem.
Abstracting away from the JVM, how is it possible to create the process (allowing for 16bit
applications/redirection) and preventing the console window from appearing for console applications
when run from a Windows Application. javaw or jrew are the same as the non-w counterparts
except that they are linked differently. javaw is linked with -subsystem:windows java is linked
with -sunsystem:console.
Test Case:
To deal with the problem, some code is required that can execute any external command.
The code below will execute the first command line arguement sending the data from the file
specifed in the second arguement. This filename is optional.
----------------------------------Start of test case ---------------------------------------------------------
// IBM Java Technology Centre
// Matthew B White
// Please note that this code is offered with no warranty
// and is offered for examples only!
import java.io.*;
public class StoreExec {
// Main functions
// First cmd line arg = command to run
// Second is the file to send to the stdin [optional]
public static void main (String[] args)
{
try {
// Create threads to process the stdin/stdout/stderr of the
// process.
ExecStorage stdoutStorage;
ExecStorage stderrStorage;
Write fromFile;
if (args.length != 1 && args.length != 2) {
System.out.println("Correct Usage is ");
System.out.println("\t java StoreExec <command> [<filename>]");
System.out.println("\t <command> The command to execute- double");
System.out.println("\t quotes if it contains spaces");
System.out.println("\t <filename> Name of the file to send to the ");
System.out.println("\t stdin of the command - optional");
System.exit(1);
}
// Set up command, arguments, and environment variables
String prog = args[0];
// get the runtime object
Runtime rt = Runtime.getRuntime();
System.out.println("Execing the process... "+prog);
// execute the processes
Process p = rt.exec(prog);
// Running the storage thread for the stdout of the command
stdoutStorage = new ExecStorage(p.getInputStream());
stdoutStorage.start();
// Running the storage thread for the stderr of the command
stderrStorage = new ExecStorage(p.getErrorStream());
stderrStorage.start();
// if an arguement has been specified than feed that data
// to the stdin of the command
if (args.length==2) {
fromFile = new Write(args[1],p.getOutputStream());
fromFile.start();
}
// Wait for the process to finish and get the return code
int s = p.waitFor();
// wait for all the data to be read - should be
// synchronised on the multiple threads
System.out.println("Sleeping....");
Thread.sleep(1000);
// Dispaly the return value and the data captured from the process
System.out.println("\n\nProcess Finished - Return value from process = "+s);
System.out.println("Data from the command is on the next lines");
String lines[] = stdoutStorage.extract();
for (int count=0; count<lines.length; count++) {
System.out.println("stdout>>>"+lines[count]);
}
System.out.println("\n\n");
String lines2[] = stderrStorage.extract();
for (int count=0; count<lines2.length; count++) {
System.out.println("stderr>>>"+lines2[count]);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// Reads in from a file and sends to the process
class Write extends Thread {
String filename;
OutputStream out;
public Write(String filename,OutputStream out)
{
this.filename = filename;
this.out=out;
}
public void run()
{
try {
FileReader inputfile = new FileReader(filename);
BufferedReader buffered = new BufferedReader(inputfile);
PrintWriter writer = new PrintWriter(out);
String line;
while ( (line = buffered.readLine()) != null) {
writer.println(line);
}
buffered.close();
writer.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
// Read from the InputStream specified and store
class ExecStorage extends Thread {
// Storage for the data returned from the command
java.util.Vector lines;
//The input stream from the process to handle
InputStream is;
// Store the process
public ExecStorage(InputStream is)
{
this.is=is;
}
// Normal run method of the thread
// Will get the input stream from the process
// and read data until there is no more data to read.
public void run()
{
// use the stream specified from the constructor
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
// The line to read
String line;
// Setup the vector to hold the data
// Set the inital size of the vector if you know how much data
// will be comming - would help to improve performance as the
// vector will not have to expand its size.
lines = new java.util.Vector();
// System.out.print("\nEvery dot is another line read>");
try {
// read from the buffered reader until nothing more exists
while ( ( line=br.readLine()) != null) {
// add this to the vector
//System.out.print(".");
lines.addElement(line);
}
br.close();
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
// Copy the vector to a string array
// for printing out
public String[] extract()
{
String data[] = new String[lines.size()];
lines.copyInto(data);
return data;
}
}
-------------------------- end of test case ---------------------------------
(Review ID: 85121)
======================================================================