package org.limewire.io; import java.io.IOException; import java.io.OutputStream; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; /** * Ensures that data in the stream can be flushed at any given moment without * requiring the stream to be closed. Works around known Java bugs, * <a href="http://developer.java.sun.com/developer/bugParade/bugs/4255743.html"> * Java bug 4255743</a> and * <a href ="http://developer.java.sun.com/developer/bugParade/bugs/4206909.html"> * Java bug 4206909</a>. * <p> * <code>CompressingOutputStream</code> simulates * <a href="http://www.zlib.net/">zlib</a>'s Z_PARTIAL_FLUSH and * Z_SYNC_FLUSH behavior. */ public final class CompressingOutputStream extends DeflaterOutputStream { public CompressingOutputStream (final OutputStream out, final Deflater flate) { super(out, flate); } private static final byte [] EMPTYBYTEARRAY = new byte [0]; /** * Insure all remaining data will be output. */ @Override public void flush() throws IOException { if( def.finished() ) return; /** * Now this is tricky: We force the Deflater to flush * its data by switching compression level. * As yet, a perplexingly simple workaround for * http://developer.java.sun.com/developer/bugParade/bugs/4255743.html */ def.setInput(EMPTYBYTEARRAY, 0, 0); def.setLevel(Deflater.NO_COMPRESSION); deflate(); def.setLevel(Deflater.DEFAULT_COMPRESSION); deflate(); super.flush(); } @Override protected void deflate() throws IOException { try { // DO NOT CALL super.deflate(), it is wrong. // It incorrectly assumes that its buffer will be large enough // to hold all data from a single deflate call. That is wrong. // We need to loop until deflate returns <= 0, saying it couldn't // deflate. int deflated; while( (deflated = def.deflate(buf, 0, buf.length)) > 0) out.write(buf, 0, deflated); } catch(NullPointerException e) { //This will happen if 'end' was called on the deflater //while we were deflating. throw new IOException("deflater was ended"); } } } // class