Skip to main content
Hitachi Vantara Knowledge

Java classes for examples

This section of the Help contains the implementation of these Java classes that are used in examples in this book.

GZIPCompressedInputStream class

o.IOException;
import java.io.InputStream;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.DeflaterInputStream;

public class GZIPCompressedInputStream extends DeflaterInputStream {

  /**
   * This static class is used to hijack the InputStream
   * read(b, off, len) function to be able to compute the CRC32
   * checksum of the content as it is read.
   */
 static private class CRCWrappedInputStream extends InputStream {
  private InputStream inputStream;

  /**
   * CRC32 of uncompressed data.
   */
  protected CRC32 crc = new CRC32();

  /**
   * Construct the object with the InputStream provided.
   * @param pInputStream - Any class derived from InputStream class.
   */
  public CRCWrappedInputStream(InputStream pInputStream) {
    inputStream = pInputStream;

    crc.reset(); // Reset the CRC value.
  }

  /**
   * Methods in this group are the InputStream equivalent methods
   * that just call the method on the InputStream provided during
   * construction.
   */
  public int available() throws IOException
    { return inputStream.available(); };
  public void close() throws IOException { inputStream.close(); };
  public void mark(int readlimit) { inputStream.mark(readlimit); };
  public boolean markSupported()
    { return inputStream.markSupported(); };
  public int read() throws IOException { return inputStream.read(); };
  public int read(byte[] b) throws IOException
    { return inputStream.read(b); };
  public void reset() throws IOException { inputStream.reset(); };
  public long skip(long n) throws IOException
    { return inputStream.skip(n); };

    /*
     * This function intercepts all read requests in order to
     * calculate the CRC value that is stored in this object.
     */
    public int read(byte b[], int off, int len) throws IOException {
      // Do the actual read from the input stream.
      int retval = inputStream.read(b, off, len);

      // If we successfully read something, compute the CRC value
      // of it.
      if (0 <= retval) {
        crc.update(b, off, retval);
      }

      // All done with the intercept. Return the value.
      return retval;
    };

    /*
     * Function to retrieve the CRC value computed thus far while the
     * stream was processed.
     */
    public long getCRCValue() { return crc.getValue(); };
  } // End class CRCWrappedInputStream.

  /**
   * Create a new input stream with the default buffer size of
   * 512 bytes.
   * @param pInputStream - InputStream to read content for
   * compression.
   * @throws IOException if an I/O error has occurred.
   */
  public GZIPCompressedInputStream(InputStream pInputStream)
    throws IOException {
      this(pInputStream, 512);
  }

  /**
   * Create a new input stream with the specified buffer size.
   * @param pInputStream - InputStream to read content for
   * compression.
   * @param size - The output buffer size.
   * @exception - IOException if an I/O error has occurred.
   */
  public GZIPCompressedInputStream(InputStream pInputStream, int size)
    throws IOException {
      super(new CRCWrappedInputStream(pInputStream),
        new Deflater(Deflater.DEFAULT_COMPRESSION, true), size);

      mCRCInputStream = (CRCWrappedInputStream) super.in;
  }

  // Indicator for if EOF has been reached for this stream.
  private boolean mReachedEOF = false;

  // Holder for the hijacked InputStream that computes the
  // CRC32 value.
  private CRCWrappedInputStream mCRCInputStream;

  /*
   * GZIP header structure and positional variable.
   */
  private final static int GZIP_MAGIC = 0x8b1f;

  private final static byte[] mHeader = {
      (byte) GZIP_MAGIC, // Magic number (short)
      (byte)(GZIP_MAGIC >> 8), // Magic number (short)
      Deflater.DEFLATED, // Compression method (CM)
      0, // Flags (FLG)
      0, // Modification time MTIME (int)
      0, // Modification time MTIME (int)
      0, // Modification time MTIME (int)
      0, // Modification time MTIME (int)
      0, // Extra flags (XFLG)
      0 // Operating system (OS) FYI. UNIX/Linux OS is 3
  };

  private int mHeaderPos = 0; // Keeps track of how much of the
                              // header has already been read.

  /*
   * GZIP trailer structure and positional indicator.
   *
   * Trailer consists of 2 integers: CRC32 value and original file
   * size.
   */
  private final static int TRAILER_SIZE = 8;
  private byte mTrailer[] = null;
  private int mTrailerPos = 0;

  /***
   * Overridden functions against the DeflatorInputStream.
   */

  /*
   * Function to indicate whether there is any content available to
   * read. It is overridden because there are the GZIP header and
   * trailer to think about.
   */
  public int available() throws IOException {
    return (mReachedEOF ? 0 : 1);
  }

  /*
   * This read function is the meat of the class. It handles passing
   * back the GZIP header, GZIP content, and GZIP trailer in that
   * order to the caller.
   */
  public int read(byte[] outBuffer, int offset, int maxLength)
    throws IOException, IndexOutOfBoundsException {

    int retval = 0; // Contains the number of bytes read into
                    // outBuffer and will be the return value of
                    // the function.
    int bIndex = offset; // Used as current index into outBuffer.
    int dataBytesCount = 0; // Used to indicate how many data bytes
                            // are in the outBuffer array.

    // Make sure we have a buffer.
    if (null == outBuffer) {
      throw new NullPointerException("Null buffer for read");
    }

    // Make sure offset is valid.
    if (0 > offset || offset >= outBuffer.length)
    {
      throw new IndexOutOfBoundsException(
        "Invalid offset parameter value passed into function");
    }

    // Make sure the maxLength is valid.
    if (0 > maxLength || outBuffer.length - offset < maxLength)
      throw new IndexOutOfBoundsException(
        "Invalid maxLength parameter value passed into function");

    // Asked for nothing; you get nothing.
    if (0 == maxLength)
      return retval;

    /**
     * Put any GZIP header in the buffer if we haven't already returned
     * it from previous calls.
     */
    if (mHeaderPos < mHeader.length)
    {
      // Get how much will fit.
      retval = Math.min(mHeader.length - mHeaderPos, maxLength);

      // Put it there.
      for (int i = retval; i > 0; i--)
      {
        outBuffer[bIndex++] = mHeader[mHeaderPos++];
      }

      // Return the number of bytes copied if we exhausted the
      // maxLength specified.
      // NOTE: Should never be >, but...
      if (retval >= maxLength) {
        return retval;
      }
    }

    /**
     * At this point, the header has all been read or put into the
     * buffer.
     *
     * Time to add some GZIP compressed data, if there is still some
     * left.
     */
    if (0 != super.available()) {

      // Get some data bytes from the DeflaterInputStream.
      dataBytesCount = super.read(outBuffer, offset+retval,
                                  maxLength-retval);

      // As long as we didn't get EOF (-1), update the buffer index and
      // retval.
      if (0 <= dataBytesCount) {
        bIndex += dataBytesCount;
        retval += dataBytesCount;
      }

      // Return the number of bytes copied during this call if we
      // exhausted the maxLength requested.
      // NOTE: Should never be >, but...
      if (retval == maxLength) {
        return retval;
      }

      // If we got here, we should have read all that can be read from
      // the input stream, so make sure the input stream is at EOF just
      // in case someone tries to read it outside this class.
      byte[] junk = new byte[1];
      if (-1 != super.read(junk, 0, junk.length)) {
        // Should never happen, but...
        throw new IOException(
        "Unexpected content read from input stream when EOF expected");
      }
    }

    /**
     * Got this far; time to write out the GZIP trailer.
     */

    // Have we already set up the GZIP trailer in a previous
    // invocation?
    if (null == mTrailer) {
      // Time to prepare the trailer.
      mTrailer = new byte[TRAILER_SIZE];

    // Put the content in it.
    writeTrailer(mTrailer, 0);
  }

  // If there are still GZIP trailer bytes to be returned to the
  // caller, do as much as will fit in the outBuffer.
  if (mTrailerPos < mTrailer.length) {

    // Get the number of bytes that will fit in the outBuffer.
    int trailerSize = Math.min(mTrailer.length - mTrailerPos,
                               maxLength - bIndex);

    // Move them in.
    for (int i = trailerSize; i > 0; i--)
    {
      outBuffer[bIndex++] = mTrailer[mTrailerPos++];
    }

    // Return the total number of bytes written during this call.
    return retval + trailerSize;
  }

  /**
   * If we got this far, we have already been asked to read
   * all content that is available.
   *
   * So we are at EOF.
   */
  mReachedEOF = true;
  return -1;
 }

  /***
  * Helper functions to construct the trailer.
  */

  /*
   * Write GZIP member trailer to a byte array, starting at a given
   * offset.
   */
  private void writeTrailer(byte[] buf, int offset) throws IOException
  {
    writeInt((int)mCRCInputStream.getCRCValue(), buf, offset);
                                      // CRC32 of uncompr. data
    writeInt(def.getTotalIn(), buf, offset + 4);
                                      // Number of uncompr. bytes
  }

  /*
   * Write integer in Intel byte order to a byte array, starting at
   * a given offset.
   */
  private void writeInt(int i, byte[] buf, int offset)
    throws IOException {
    writeShort(i & 0xffff, buf, offset);
    writeShort((i >> 16) & 0xffff, buf, offset + 2);
  }
  /*
   * Write short integer in Intel byte order to a byte array,
   * starting at a given offset
   */
  private void writeShort(int s, byte[] buf, int offset)
    throws IOException {
      buf[offset] = (byte)(s & 0xff);
      buf[offset + 1] = (byte)((s >> 8) & 0xff);
  }
}

WholeIOOutputStream class

package com.hds.hcp.apihelpers;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* This class defines an OutputStream that will create both the data
* file and the custom metadata file for an object. The copy() method
* is used to read an InputStream and create the two output files based
* on the indicated size of the data file portion of the stream.
*
* The class is used to split and create content retrieved over REST 
* API as a single stream for type=whole-object GET operations.
*/
public class WholeIOOutputStream extends OutputStream {

   // Constructor. Passed output streams for the data file and the
   // custom metadata file. Allows specification of whether the custom
   // metadata comes before the object data.
   public WholeIOOutputStream(OutputStream inDataFile,
          OutputStream inCustomMetadataFile,
          Boolean inCustomMetadataFirst) {

      bCustomMetadataFirst = inCustomMetadataFirst;

      // Set up first and second file Output Streams based on whether
      // custom metadata is first in the stream.
      if (bCustomMetadataFirst) {
         mFirstFile = inCustomMetadataFile;
         mSecondFile = inDataFile;
      } else {
         mFirstFile = inDataFile;
         mSecondFile = inCustomMetadataFile;
      }

       bFinishedFirstPart = false;
   }

   // Member variables.
   private Boolean bFinishedFirstPart;
   private Boolean bCustomMetadataFirst;
   private OutputStream mFirstFile, mSecondFile;

   /**
     * This routine copies content in an InputStream to this
     * output Stream. The first inDataSize number of bytes are written
     * to the data file output stream.
     *
     * @param inStream - InputStream to copy content from.
     * @param inFirstPartSize - number of bytes of inStream that should
     * be written to the first output stream.
     * @throws IOException
     */
   public void copy(InputStream inStream, Integer inFirstPartSize)
       throws IOException {
       int streamPos = 0;
       byte buffer[] = new byte[2048];

       int readLength = 0;

       // Keep reading bytes until EOF has been reached.
       while (-1 != (readLength = inStream.read(buffer, 0,
          Math.min(buffer.length, (bFinishedFirstPart ? buffer.length :
          inFirstPartSize - streamPos ) )))) {

       // Update the position in the stream.
       streamPos += readLength;

       // Write the bytes read.
       write(buffer, 0, readLength);

       // Was all the content for the first file written?
       if ( streamPos == inFirstPartSize)
       {
       // Yes. Flag that the next write should be to the second file.
          bFinishedFirstPart = true;
       }
    }
}

/**
  * This is the core buffer write function for the OutputStream
  * implementation. It writes to either the first or second file
  * stream depending on where it is in the stream.
  */
public void write(byte[] b, int offset, int length) throws
    IOException {
    // Write to first or second file depending on where we are in the
    // stream.
    if (! bFinishedFirstPart ) {
      mFirstFile.write(b, offset, length);
   } else {
      mSecondFile.write(b, offset, length);
   }
}

/**
  * This version of the write function takes a single int parameter.
  */
public void write(int b) throws IOException {
     // Write to first or second file depending on where we are in the
     // stream.
     if (! bFinishedFirstPart ) {
        mFirstFile.write(b);
     } else {
        mSecondFile.write(b);
     }
}

/**
  * flush() method to flush all files involved.
*/
public void flush() throws IOException {
    mFirstFile.flush();
    mSecondFile.flush();
    super.flush();
}

/**
* close() method to first close the data file and custom metadata
* file. Then close itself.
*/
public void close() throws IOException {
    mFirstFile.close();
    mSecondFile.close();
    super.close();
 }
}

 

  • Was this article helpful?