United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4712766 : Channels.newOutputStream stream can fail w/ non-zero offsets

Details
Type:
Bug
Submit Date:
2002-07-10
Status:
Closed
Updated Date:
2003-04-12
Project Name:
JDK
Resolved Date:
2002-09-02
Component:
core-libs
OS:
windows_2000
Sub-Component:
java.nio
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.4.1
Fixed Versions:
1.4.2 (mantis)

Related Reports

Sub Tasks

Description

Name: nt126004			Date: 07/10/2002


FULL PRODUCT VERSION :
java version "1.4.1-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-beta-
b14)
Java HotSpot(TM) Client VM (build 1.4.1-beta-b14, mixed mode)


FULL OPERATING SYSTEM VERSION :
Windows 2000 CZ sp2 (Microsoft Windows
2000 [Verze 5.00.2195])

ADDITIONAL OPERATING SYSTEMS :
Linux


A DESCRIPTION OF THE PROBLEM :
OutputStream subclass returned by
java.nio.channels.Channels.newOutputStream() throws
IllegalArgumentException under certain circumstances on perfectly
legal request.
Problem stems from internally cahced instance of
ByteBuffer used to write byte[] into channel. When setting parameters of
ByteBuffer to match that of array fragment passed to be written to
channel, ByteBuffer's position is incorrectly set BEFORE it's limit.
This results in IllegalArgumentException when one part of array at the
beginning of array is written first, then another part of the same array
which starts after the fist part (+1) in the array. Cached ByteBuffer's
limit is set below start of the second fragment, at the end of the first
fragment, which relults in IllegalArgumentException when trying to
call ByteBuffer.position() with offset higher than current
limit.

  Fix:

In the implementation of anonymous OutputStream
subclass in Channels.newOutputStream(), following two lines must be
swapped to make it work:

       bb.position(off);
       
bb.limit(Math.min(off + len, bb.capacity()));


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create any WritableByteChannel
2. Call
java.nio.channels.Channels.newOutputStream() on it to get
OutputStream instance (out)
3. let's say we have an_array =
byte[3]
4. call out.write(an_array,0,1)
5. call
out.write(an_array,2,1) -> IllegalArgumentException

See
attached code for a working example.


EXPECTED VERSUS ACTUAL BEHAVIOR :
Bytes should have been written into channel.
Instead,
IllegalArgumentException is thrown.


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.*;
import java.nio.*;
import java.nio.channels.*;

/**
 * Test case
to demonstrate bug in Channels.newOutputStream() in JDK 1.4.0/1.4.1beta.
 * It will create
file testfile.txt in current directory and try to write chunks of byte array
 * to it, using
OutputStream returned by Channels.newOutputStream() .
 *
 * @author Jan Hlavat?,
<###@###.###>
 */
public class NioBug {
    
    public static void main(String[] args) {

        try {
        
        // create test byte buffer and fill it in with data:
        byte[] testbuffer = new byte[3];
        testbuffer[0]=0x31;
        testbuffer[1]=0x32;
        testbuffer[2]=0x33;
        
        // create a Channel
        FileOutputStream ofs = new FileOutputStream("testfile.txt");
        FileChannel fch = ofs.getChannel();
        // create an OutputStream from Channel
        OutputStream out = Channels.newOutputStream(fch);
        
        //------ important part: ------------------
        // write part of array to the output stream, setting limit of cached ByteBuffer instance to 1
        out.write(testbuffer,0,1);
        try {
        // write the same array into output stream again, this time starting beyond limit at offset 2
        out.write(testbuffer,2,1);
        System.out.println("It works OK");
        } catch (IllegalArgumentException e) {
        System.out.println("It failed");    
		// this line gets executed, because cached ByteBuffer's
        // limit is 1 and position(2) will throw exception as it should.
        }
        //----- end of important part -------------
        
        // close OutputStream
        out.close();
        
        // close channel
        fch.close();
        ofs.close();
        

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}
---------- END SOURCE ----------

CUSTOMER WORKAROUND :
Don't use Channels.newOutputStream(), make equivalent but working
replacement
(Review ID: 159141) 
======================================================================

                                    

Comments
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
mantis
mantis-b02

FIXED IN:
mantis
mantis-b02

INTEGRATED IN:
mantis
mantis-b02

VERIFIED IN:
mantis


                                     
2004-06-14
SUGGESTED FIX

Below is correct code for Channels.newOutputStream.
###@###.### 2002-07-11

    public static OutputStream newOutputStream(final WritableByteChannel ch) {
	return new OutputStream() {

		private ByteBuffer bb = null;
		private byte[] bs = null; 	// Invoker's previous array
		private byte[] b1 = null;

                public synchronized void write(int b) throws IOException {
                   if (b1 == null)
                        b1 = new byte[1];
                    b1[0] = (byte)b;
                    this.write(b1);
                }

                public synchronized void write(byte[] bs, int off, int len)
                    throws IOException
                {
                    if ((off < 0) || (off > bs.length) || (len < 0) ||
                        ((off + len) > bs.length) || ((off + len) < 0)) {
                        throw new IndexOutOfBoundsException();
                    } else if (len == 0) {
                        return;
                    }
                    ByteBuffer bb = ((this.bs == bs)
                                     ? this.bb
                                     : ByteBuffer.wrap(bs));
                    bb.limit(Math.min(off + len, bb.capacity()));
                    bb.position(off);
                    this.bb = bb;
                    this.bs = bs;
                    Channels.write(ch, bb);
                }

		public void close() throws IOException {
		    ch.close();
		}

	    };
    }
                                     
2004-06-11
EVALUATION

Submitter is correct.
###@###.### 2002-07-10
                                     
2002-07-10



Hardware and Software, Engineered to Work Together