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 HTTP 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(); } }