Class RpcGZIPOutputStream

  • All Implemented Interfaces:
    java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable

    public class RpcGZIPOutputStream
    extends java.io.FilterOutputStream
    A fairly lightweight filter output stream that implements Perforce's GZIP-based connection stream compression for Perforce clients that have the Perforce "client compression" option set.

    The implementation here uses the JZlib package because the standard JDK GZIP packages in java.util.zip are not able to cope cleanly with the various flush options needed on a streaming connection like this. Our use of the JZlib package is pretty boring, and is basically just a transliteration of the original C++ API code that dates from about 1999 or so. The implementation here is not thread safe in that there's a buffer in each instantiation of this stream; this could probably be tightened up if there's a need for it.

    Note that this implementation requires the upper levels to ensure that the stream is flushed properly with the flush() method whenever an RPC packet is ready for sending; this ensures that GZIP block processing, etc., is done properly and the server sees correct block and stream boundaries. If this isn't done, the server may hang or you may see some very odd client-side errors. The stream should also be closed properly, but that's less of an issue.

    Note that there's quite a performance penalty for using connection (a.k.a. client) compression (especially on the server), but if that's what the customer wants, that what the customer gets.

    • Field Summary

      • Fields inherited from class java.io.FilterOutputStream

        out
    • Constructor Summary

      Constructors 
      Constructor Description
      RpcGZIPOutputStream​(java.io.OutputStream out)  
    • Method Summary

      All Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      void close()
      Cleanly close the stream and finalize deflation (compression).
      void flush()
      Flush the results of previous byte deflation (compression) downstream.
      protected java.lang.String getJZlibErrorStr​(int errNum)
      Provide a more human-readable form of the underlying JZlib compression errors.
      void write​(byte[] bytes)
      A convenience method for write(bytes, 0, bytes.length).
      void write​(byte[] bytes, int offset, int len)
      Deflate (compress) the passed-in bytes and -- if appropriate -- send the compressed bytes downstream to the filter's output stream.
      void write​(int b)
      Not used.
      • Methods inherited from class java.io.OutputStream

        nullOutputStream
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Constructor Detail

      • RpcGZIPOutputStream

        public RpcGZIPOutputStream​(java.io.OutputStream out)
                            throws java.io.IOException
        Throws:
        java.io.IOException
    • Method Detail

      • write

        public void write​(byte[] bytes)
                   throws java.io.IOException
        A convenience method for write(bytes, 0, bytes.length).
        Overrides:
        write in class java.io.FilterOutputStream
        Throws:
        java.io.IOException
        See Also:
        FilterOutputStream.write(byte[])
      • write

        public void write​(byte[] bytes,
                          int offset,
                          int len)
                   throws java.io.IOException
        Deflate (compress) the passed-in bytes and -- if appropriate -- send the compressed bytes downstream to the filter's output stream.

        This write method does not necessarily cause a write to the server -- a write will only occur when the jzBytes buffer is full, or on a later flush. This is a consequence of the way GZIP streaming works here, and means you must ensure that a suitable flush is done at a suitable (packet) boundary. See the comments for flush() below.

        Overrides:
        write in class java.io.FilterOutputStream
        Throws:
        java.io.IOException
        See Also:
        FilterOutputStream.write(byte[], int, int)
      • write

        public void write​(int b)
                   throws java.io.IOException
        Not used. Will cause a UnimplementedError to be thrown if called.
        Overrides:
        write in class java.io.FilterOutputStream
        Throws:
        java.io.IOException
        See Also:
        FilterOutputStream.write(int)
      • flush

        public void flush()
                   throws java.io.IOException
        Flush the results of previous byte deflation (compression) downstream.

        As a consequence of the way GZIP streaming works, this flush is often the only place where bytes are actually written downstream towards the server (the earlier writes may only write to the internal buffer here). Using flush causes a compression boundary, so it should only be used after a complete packet has been put onto this stream -- i.e. users of this stream must call flush appropriately, or the server may not see packets at all.

        Specified by:
        flush in interface java.io.Flushable
        Overrides:
        flush in class java.io.FilterOutputStream
        Throws:
        java.io.IOException
        See Also:
        FilterOutputStream.flush()
      • close

        public void close()
                   throws java.io.IOException
        Cleanly close the stream and finalize deflation (compression). No one dies if you don't call this properly, but it certainly helps to close the stream cleanly.
        Specified by:
        close in interface java.lang.AutoCloseable
        Specified by:
        close in interface java.io.Closeable
        Overrides:
        close in class java.io.FilterOutputStream
        Throws:
        java.io.IOException
        See Also:
        FilterOutputStream.close()
      • getJZlibErrorStr

        protected java.lang.String getJZlibErrorStr​(int errNum)
        Provide a more human-readable form of the underlying JZlib compression errors.

        Should be made even more human-readable sometime later -- HR.

        Parameters:
        errNum - errNum
        Returns:
        error message