JDK-6280339 : Reading from a JAR resource input stream doesn't read all available bytes
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2005-06-03
  • Updated: 2010-04-02
  • Resolved: 2005-07-14
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_02-b09)
Java HotSpot(TM) Client VM (build 1.5.0_02-b09, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
This bug only occurs in JRE 1.5.0_02 and 1.5.0_03. It didn't occur with JRE 1.5.0_01.

An InputStream is retrieved for a resource contained within a JAR file using the getResourceAsStream() function in the ClassLoader class.

The available() function is called to determine the number of bytes available to be read. A byte array is created of the required size and a read() with the array as a parameter is performed.

The number of bytes read is not the same as the number of bytes available.
Since the InputStream.read() documentation states "This method blocks until input data is available, end of file is detected, or an exception is thrown" and the function call neither blocks, or throws an exception it looks like it is seeing a premature end of file.

The number of bytes reported by available() is correct.
The number of bytes reported by read() is the correct number of bytes read.
This doesn't appear to happen if the jar file is not compressed. Adding the compress="false" attribute to the jar task gives the correct result.



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Build the jar file using the ant build script below and the java source code below.

A project directory should contain a "source" directory which should contain the "jarFault" package directory. The "faultTest.java" file should be placed in here.
The build.xml file below should be placed in the project directory.
The ant script should be run (default target).

Run the test application  with the following command line:
     java -jar release/jarFault.jar



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
no. of bytes available = 1022 bytes.
no. of bytes read      = 1022 bytes.
ACTUAL -
no. of bytes available = 1022 bytes.
no. of bytes read      = 1017 bytes.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package jarFault;

import java.io.IOException;
import java.io.InputStream;

public class faultTest
{
	
	public static void main(String[] args)
		throws IOException
	{
		InputStream in;
		
        in = faultTest.class.getClassLoader().getResourceAsStream("vnv1_sunlogo.gif");
        if (in == null)
            System.out.println("Failed to open input stream to file.");
        else
		{
			System.out.println(in.getClass().getName());
			int size = in.available();
	        byte[] publicKeyBuf = new byte[size];

			int count = 0;
			count= in.read(publicKeyBuf, 0, size);

			in.close();
			System.out.println("no. of bytes available = " + size +" bytes.");
			System.out.println("no. of bytes read      = " + count +" bytes.");
		}
	}
}

ANT build.xml
-------------------

<project name="template" default="rebuild">

	<property name="source"  value="source"/>
	<property name="classes" value="out/classes"/>

	<!-- The main targets called by the user -->
	<target name="rebuild" depends="clean, make" />
	<target name="make" depends="jar" />

	<!-- returns the directory structure to its original settings -->
	<target name="clean">
		<delete dir="${classes}"/>
	</target>

	<!-- prepares the directory structure for use -->
	<target name="init">
		<mkdir dir="${classes}"/>
	</target>

	<!-- compiles all the java files -->
	<target name="compile" depends="init">
		<javac srcdir="${source}" destdir="${classes}" debug="on" compiler="javac1.5"/>
	</target>

	<!-- creates the distributable JAR file containing all the classes -->
	<target name="jar" depends="compile">
		<jar jarfile="release/jarFault.jar">
			<fileset dir="${classes}"/>
			<fileset dir="jarFault"/>

			<manifest>
				<attribute name="Version" value="${version}"/>
				<attribute name="Main-Class" value="jarFault.faultTest"/>
				<attribute name="Build-Date" value="${buildDate}"/>
			</manifest>
		</jar>
	</target>

</project>




---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
There are 2 workarounds for this:

The first is to use the read() function with byte array, offset and length parameters and wrap the read() function in a loop until all the bytes have been read

			int size = in.available();
	        byte[] publicKeyBuf = new byte[size];

			int pos = 0;
			while(pos < size)
				pos += in.read(publicKeyBuf, pos, size - pos);



The second is to create an uncompressed jar file.
###@###.### 2005-06-03 18:18:26 GMT