JDK-4994125 : FileOutputStream.write() throws IOException when redirecting stdout in native code (1.4.2, win)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 1.4.2_02
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2004-02-13
  • Updated: 2006-03-31
  • Resolved: 2006-03-31
Description
There is a Windows C problem, using JNI when creating a JVM to run a simple 
Java problem (System.out.println("Hello World")).

If redirection of stdout to a file is done on the C side before creating the 
JVM, the output does not appear in the file and an IOException is thrown

Detail Analysis:

Customer has written a simple java class which simulates the
this problem. It will print out the IOException whenever write() fails. 
The following exception is seen:

java.io.IOException: The handle is invalid
     at java.io.FileOutputStream.writeBytes(Native Method)
     at java.io.FileOutputStream.write(FileOutputStream.java:260)
     at java.io.BufferedOutputStream.flushBuffer
BufferedOutputStream.java:66)
     at
java.io.BufferedOutputStream.flush(BufferedOutputStream.java:124)
     at IntClient_jni.Los(PrintStream_Sim.java:12)




STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) Write a simple "Hello World" Java program which uses
System.out.println("Hello World");

2) Write a C program that does the following:
     a)redirect stdout to a log file (freopen)
     b)using JNI to create JVM
     c)run the main() method of the Java program by using JNI.

You will not see the "Hello World" in the file. But if you take out
the stdout redirection from the C program, you will see the Hello World from
console and no exception on Java side.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
You should see the System.out redirect output to the log file if we
redirect the stdout from C side before creating JVM. 

ACTUAL BEHAVIOR - There is printout in the redirected log file and 
 an exception is seen: java.io.IOException: The handle is invalid
     at java.io.FileOutputStream.writeBytes(Native Method)
     at java.io.FileOutputStream.write(FileOutputStream.java:260)
     at
java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:66)
     at
java.io.BufferedOutputStream.flush(BufferedOutputStream.java:124)
     at IntClient_jni.Los(PrintStream_Sim.java:12)

exception on Java side.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.io.IOException: The handle is invalid
     at java.io.FileOutputStream.writeBytes(Native Method)
     at java.io.FileOutputStream.write(FileOutputStream.java:260)
     at
java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:66)
     at
java.io.BufferedOutputStream.flush(BufferedOutputStream.java:124)
     at IntClient_jni.Los(PrintStream_Sim.java:12)


REPRODUCIBILITY :
This bug is easily reproduced.

---------- BEGIN SOURCE ----------
1) Java Side "Hello World" Program:
    ===========================

import java.io.*;

public class PrintStream_Sim{

   public static void main(String [] argv)   {
      try {
         FileOutputStream fdOut = new
FileOutputStream(FileDescriptor.out);
         BufferedOutputStream bout = new BufferedOutputStream(fdOut,
128);

         bout.write(45);
         bout.flush();
           } catch (Throwable t) {
         try {
         PrintStream o = new PrintStream(new
FileOutputStream("mythrowable.txt"));
         t.printStackTrace(o);
         o.close();
                 } catch (Exception e) {}
      }
   }
}

2) The C program that creates the JVM and run the Hello World:
    ================================================

int main(int argc, char* argv[])
{
        FILE * t = freopen("D:\\temp\\stdout.log", "w", stdout);
                     t = freopen("D:\\temp\\stderr.log", "w", stderr);

        JavaVM *jvm;

        JNIEnv *env;

        JavaVMInitArgs vm_args;

        jclass cls;

        jmethodID mid;

        printf("beginning execution...\n");
        JavaVMOption options[2];
    options[0].optionString =
         "-Djava.class.path=.";
     
        options[1].optionString = "vfprintf";
        options[1].extraInfo = (void*)jio_vfprintf;
     
        vm_args.version = JNI_VERSION_1_2;
        vm_args.options = options;
        vm_args.nOptions = 2;
        vm_args.ignoreUnrecognized = JNI_TRUE;

        jint res = JNI_CreateJavaVM(&jvm, (void **)&env,&vm_args);
        if (res < 0) {
                printf("Can't create Java VM\n");
                fflush(stdout);
                return 1;
        }

        
        /* Find the class */

        cls = env->FindClass("PrintStream_Sim");
        if (cls == 0) {

        fprintf(stderr,
        "Could not locate class your CLASSPATH.\n");
         return 1 ;
        }

        /* Find the method */
        mid = env->GetStaticMethodID(cls, "main",
"(Ljava/lang/String;)V");
        if (mid == 0) {
        fprintf(stderr, "Could not locate method main with signature
\n");
        return 1;
        }

        /* Invoke the method */

        env->CallStaticVoidMethod(cls, mid, NULL);

        /* we are done */
        jvm->DestroyJavaVM();
        
        return 0;
}

---------- END SOURCE ----------
workaround:  None 

Comments
EVALUATION Since there has been no further information after more than 6 months, we assume that the reported problem is a result of mixed model compilation of the C Runtime Libraries. Closing this issue as "not a bug" in the jdk.
31-03-2006

EVALUATION The likely cause of this problem is that the test C program may have been compiled with a mixed model of C Runtme Libraries. This is known to cause some problems with file descriptors as noted in the following Microsoft Knowledge Base article, Section 5: Mixing Library Types: http://support.microsoft.com/default.aspx?scid=kb;en-us;94248 However we can not be absolutely certain because the problem description does not contproblem description does not contain the makefile or other indication of the compiler and linker options used. It is quite likely that if this is the cause, it exists for all versions of the jdk. If this is a new failure (and there is no indication that it is) then it probably means that the JNI library has been compiled differently and thus requires the user to use different linking options. Marking the bug "incomplete" until complete compilation and linking details are provided. iag@sfbay 2005-08-22 jdk1.4.2's .dlls link dynmaically with msvcrt.dll (i.e. they were all compiled with /MD in VC6.0). The link option is important because our implementation of file I/O in 1.4.2 (and likely earlier releases) depends on C runtime operations such as write(). If the provided example is compiled using the same compiler version and linking options as jdk1.4.2, the output is correctly redirected. If the linking options are different, then the problem is reproducible. In fact, if you replace all references to the jdk with calls to any function in a .dll that is compiled with a different C runtime than the calling program, then the problem is reproducible. This is not a bug in the jdk. It is a known problem with mixing C runtime libraries as described above. The only other possible workaround for jdk1.4.2 would require the calling program to launch a separate wrapper executable which itself is compiled with options compatible with the jdk. This behaviour does not exist in jdk1.5 (Tiger) because it was resolved as a side-effect of fixing the following bug: 4189011: java.io: Cannot open more than 2035 files (win32) The fix for this bug required us to completely re-write a lot of the file output code to use native windows handles rather than file descriptors. Based on the above, it is unlikely that any other problem is the cause of this failure; however, we can not be absolutely certain because complete compilation and linking information has not been provided. Therefore, this bug remains "incomplete".
23-08-2005