Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4829325

Mapped read prevents file delete

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P3 P3
    • None
    • 1.4.1_01
    • core-libs
    • None
    • x86
    • windows_2000



      Name: eaC66865 Date: 03/07/2003


      When FileChannel.map() is used to read a file under
      windows 2000, the map never goes away, so it prevents
      additional operations like File.delete(). Any of those
      operations appear to be sufficient under Solaris,
      however. (Solaris may even be closing the channel
      automatically when a File operation is undertaken.)

      The following code illustrates the problem.
      closing the channel, closing the stream, and setting
      the ByteBuffer to null do not release the map. Only
      replacing the map() operation with a read() allows the
      File.delete() to work on Windows 2000.

      Note:
        * I've run the program on the local file system, and
          the solaris drive mapped to win2k at p:\, with the
          same results.

      NioMappingBug.java
      ------------------
      import java.io.*;
      import java.nio.*;
      import java.nio.channels.*;
      import java.nio.charset.*;

      import java.util.*;

      public class NioMappingBug {
        String testData = "test data";
        File testFile = new File("testFile");

        Charset charset = Charset.forName("UTF-8");
        CharBuffer charBuffer;

        FileChannel testChannel;
        ByteBuffer byteBuffer;

        FileOutputStream fos;

        public static void main(String[] argv) {
          NioMappingBug app = new NioMappingBug();
          app.run();
        }

        public void run() {
          setup();
          writeFile();

          // CHANGE THIS FOR A MAPPED OR UNMAPPED READ
          // --WHEN TRUE, THE DELETE FAILS ON WINDOWS 2000 (succeeds on
      Solaris)
          // --WHEN FALSE, IT SUCCEEDS ON BOTH SYSTEMS
          boolean mapped = true;

          String result = readFile(testFile, mapped).toString();
          assert result.equals(testData);
          deleteFile();
        }

        public void setup() {
          charBuffer = CharBuffer.allocate(16);
          testChannel = null;
          byteBuffer = null;
          fos = null;
        }

        /**
         * Attempt to write to a file that doesn't exist.
         */
        public void writeFile() {
          createOutputStream();
          assert testFile.exists(); // Creating an output stream also creates
      the file
          testChannel = fos.getChannel();
          charBuffer.put(testData); // position=9, limit=16
          charBuffer.flip(); // position=0, limit=9
          byteBuffer = charset.encode(charBuffer);
          try {
            output(byteBuffer, testChannel);
          }
          catch (IOException ioe) {
            ioe.printStackTrace();
            assert false : "Error writing the file.";
          }
          try {
            testChannel.close();
          }
          catch (IOException ioe) {
            ioe.printStackTrace();
            assert false : "Could not close the newly written file.";
          }
        }

        private void createOutputStream() {
          try {
            fos = new FileOutputStream(testFile);
          }
          catch (IOException ioe) {
            ioe.printStackTrace();
            assert false : "The attempt to create an output stream failed";
          }
        }

        /**
         * This is the <i>right</i> way to write.
         * (Courtesy of Mike McCloskey)
         */
        public void output(ByteBuffer byteBuffer, WritableByteChannel channel)
          throws IOException {
          int bytesWritten = 0;
          while (bytesWritten < byteBuffer.remaining()) {
            // Loop if only part of the buffer contents get written.
            bytesWritten = channel.write(byteBuffer);
            if (bytesWritten == 0) {
              // Media full? We could do some retries here, or throw an
      exception
              throw new IOException("Unable to write to channel. Media may be
      full.");
            }
          }
        }

        public CharBuffer readFile(File file, boolean mapped) {
          CharBuffer resultBuffer = null;
          try {
            FileInputStream stream = new FileInputStream(file);
            FileChannel fileChannel = stream.getChannel();
            int max = Integer.MAX_VALUE;
            if (fileChannel.size() > max) {
              throw new IOException("File too large: " + file.getPath());
            }
            int size = (int) fileChannel.size();
            if (mapped) {
              // From FileChannel.map:
              // For most operating systems, mapping a file into memory is
      more
              // expensive than reading or writing a few tens of kilobytes of
      data
              // via the usual read and write methods. From the standpoint of
              // performance it is generally only worth mapping relatively
      large files
              // into memory.
              //
              // More importantly, this mapping causes File.delete() to return
      false
              // on Windows 2000 (though not on Solaris). There is no unmap()
      command,
              // setting byteBuffer to null doesn't help, and closing the
      stream (which
              // closes the channel) is also ineffective.
              byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0,
      size);
            }
            else {
              // This code solves the problem
              byteBuffer = ByteBuffer.allocate(size);
              while (byteBuffer.hasRemaining()) {
                int bytesRead = fileChannel.read(byteBuffer); // increments
      position
                if (bytesRead == 0) {
                  throw new IOException("Read failure: " + file.getPath());
                }
              }
              byteBuffer.flip(); // position -> 0, limit -> capacity
            }
            resultBuffer = charset.decode(byteBuffer);
            fileChannel.close();
            stream.close(); //closes the associated channel as well
            byteBuffer = null; // attempt to "unmap" the buffer
            report(" read: |" + resultBuffer.toString() + "|");
            report("ch size: " + size + ", buff capacity: " +
      resultBuffer.capacity()
                   + ", pos: " + resultBuffer.position() + ", lim: " +
      resultBuffer.limit());
          }
          catch (IOException ioe) {
            ioe.printStackTrace();
            assert false : "readFile: Error reading file: " +
      testFile.getPath();
          }
          return resultBuffer;
        }

        public void report(String s) {
          System.out.println(s);
        }

        public void deleteFile() {
          boolean success = testFile.delete();
          if (!success) {
            assert false : "Delete failed: "+ testFile.getPath();
          }
          assert !testFile.exists();
        }

      }


      ======================================================================

            mr Mark Reinhold
            earmstrosunw Eric Armstrong (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: