Index: core/src/main/java/hudson/model/Run.java =================================================================== --- core/src/main/java/hudson/model/Run.java (revision 16755) +++ core/src/main/java/hudson/model/Run.java (working copy) @@ -78,7 +78,7 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.zip.GZIPInputStream; +import com.sun.bugs.id4691425.MultiMemberGZIPInputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; @@ -758,7 +758,7 @@ File compressedLogFile = new File(logFile.getParentFile(), logFile.getName()+ ".gz"); if (compressedLogFile.exists()) { return new InputStreamReader( - new GZIPInputStream( + new MultiMemberGZIPInputStream( new FileInputStream(compressedLogFile))); } Index: core/src/main/java/hudson/FilePath.java =================================================================== --- core/src/main/java/hudson/FilePath.java (revision 16755) +++ core/src/main/java/hudson/FilePath.java (working copy) @@ -83,7 +83,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.zip.GZIPOutputStream; -import java.util.zip.GZIPInputStream; +import com.sun.bugs.id4691425.MultiMemberGZIPInputStream; import java.util.zip.ZipInputStream; /** @@ -422,7 +422,7 @@ public InputStream extract(InputStream _in) throws IOException { HeadBufferingStream in = new HeadBufferingStream(_in,SIDE_BUFFER_SIZE); try { - return new GZIPInputStream(in); + return new MultiMemberGZIPInputStream(in); } catch (IOException e) { // various people reported "java.io.IOException: Not in GZIP format" here, so diagnose this problem better in.fillSide(); @@ -1077,7 +1077,7 @@ Future future = target.actAsync(new FileCallable() { public Void invoke(File f, VirtualChannel channel) throws IOException { try { - readFromTar(remote+'/'+fileMask, f,new GZIPInputStream(pipe.getIn())); + readFromTar(remote+'/'+fileMask, f,new MultiMemberGZIPInputStream(pipe.getIn())); return null; } finally { pipe.getIn().close(); @@ -1105,7 +1105,7 @@ } }); try { - readFromTar(remote+'/'+fileMask,new File(target.remote),new GZIPInputStream(pipe.getIn())); + readFromTar(remote+'/'+fileMask,new File(target.remote),new MultiMemberGZIPInputStream(pipe.getIn())); } catch (IOException e) {// BuildException or IOException try { future.get(3,TimeUnit.SECONDS); Index: core/src/main/java/hudson/util/CompressedFile.java =================================================================== --- core/src/main/java/hudson/util/CompressedFile.java (revision 16755) +++ core/src/main/java/hudson/util/CompressedFile.java (working copy) @@ -40,7 +40,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.zip.GZIPInputStream; +import com.sun.bugs.id4691425.MultiMemberGZIPInputStream; import java.util.zip.GZIPOutputStream; /** @@ -93,7 +93,7 @@ // check if the compressed file exists if(gz.exists()) - return new GZIPInputStream(new FileInputStream(gz)); + return new MultiMemberGZIPInputStream(new FileInputStream(gz)); // no such file throw new FileNotFoundException(file.getName()); Index: core/src/main/java/com/sun/bugs/id4691425/MultiMemberGZIPInputStream.java =================================================================== --- core/src/main/java/com/sun/bugs/id4691425/MultiMemberGZIPInputStream.java (revision 0) +++ core/src/main/java/com/sun/bugs/id4691425/MultiMemberGZIPInputStream.java (revision 0) @@ -0,0 +1,99 @@ +package com.sun.bugs.id4691425; + +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; + +/** + * This is a workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4691425 + */ +public class MultiMemberGZIPInputStream extends GZIPInputStream { + + public MultiMemberGZIPInputStream(InputStream in, int size) throws IOException + { + // Wrap the stream in a PushbackInputStream... + super(new PushbackInputStream(in, size), size); + this.size=size; + } + + public MultiMemberGZIPInputStream(InputStream in) throws IOException + { + // Wrap the stream in a PushbackInputStream... + super(new PushbackInputStream(in, 1024)); + this.size=-1; + } + + private MultiMemberGZIPInputStream(MultiMemberGZIPInputStream parent) throws IOException + { + super(parent.in); + this.size=-1; + this.parent=parent.parent==null ? parent : parent.parent; + this.parent.child=this; + } + + private MultiMemberGZIPInputStream(MultiMemberGZIPInputStream parent, int size) throws IOException + { + super(parent.in, size); + this.size=size; + this.parent=parent.parent==null ? parent : parent.parent; + this.parent.child=this; + } + + private MultiMemberGZIPInputStream parent; + private MultiMemberGZIPInputStream child; + private int size; + private boolean eos; + + public int read(byte[] inputBuffer, int inputBufferOffset, int inputBufferLen) throws IOException { + + if (eos) { return -1;} + if (this.child!=null) + return this.child.read(inputBuffer, inputBufferOffset, inputBufferLen); + + int charsRead=super.read(inputBuffer, inputBufferOffset, inputBufferLen); + if (charsRead==-1) + { + // Push any remaining buffered data back onto the stream + // If the stream is then not empty, use it to construct + // a new instance of this class and delegate this and any + // future calls to it... + int n = inf.getRemaining() - 8; + if (n > 0) + { + // More than 8 bytes remaining in deflater + // First 8 are gzip trailer. Add the rest to + // any un-read data... + ((PushbackInputStream)this.in).unread(buf, len-n, n); + } + else + { + // Nothing in the buffer. We need to know whether or not + // there is unread data available in the underlying stream + // since the base class will not handle an empty file. + // Read a byte to see if there is data and if so, + // push it back onto the stream... + byte[] b=new byte[1]; + int ret=in.read(b,0,1); + if (ret==-1) + { + eos=true; + return -1; + } + else + ((PushbackInputStream)this.in).unread(b, 0, 1); + } + + MultiMemberGZIPInputStream child; + if (this.size==-1) + child=new MultiMemberGZIPInputStream(this); + else + child=new MultiMemberGZIPInputStream(this, this.size); + return child.read(inputBuffer, inputBufferOffset, inputBufferLen); + } + else + return charsRead; + } + +} +