All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jxta.endpoint.InputStreamMessageElement Maven / Gradle / Ivy

/*
 * Copyright (c) 2001-2007 Sun Microsystems, Inc.  All rights reserved.
 *  
 *  The Sun Project JXTA(TM) Software License
 *  
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions are met:
 *  
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  
 *  2. Redistributions in binary form must reproduce the above copyright notice, 
 *     this list of conditions and the following disclaimer in the documentation 
 *     and/or other materials provided with the distribution.
 *  
 *  3. The end-user documentation included with the redistribution, if any, must 
 *     include the following acknowledgment: "This product includes software 
 *     developed by Sun Microsystems, Inc. for JXTA(TM) technology." 
 *     Alternately, this acknowledgment may appear in the software itself, if 
 *     and wherever such third-party acknowledgments normally appear.
 *  
 *  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must 
 *     not be used to endorse or promote products derived from this software 
 *     without prior written permission. For written permission, please contact 
 *     Project JXTA at http://www.jxta.org.
 *  
 *  5. Products derived from this software may not be called "JXTA", nor may 
 *     "JXTA" appear in their name, without prior written permission of Sun.
 *  
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
 *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN 
 *  MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *  
 *  JXTA is a registered trademark of Sun Microsystems, Inc. in the United 
 *  States and other countries.
 *  
 *  Please see the license information page at :
 *   for instructions on use of 
 *  the license in source files.
 *  
 *  ====================================================================
 *  
 *  This software consists of voluntary contributions made by many individuals 
 *  on behalf of Project JXTA. For more information on Project JXTA, please see 
 *  http://www.jxta.org.
 *  
 *  This license is based on the BSD license adopted by the Apache Foundation. 
 */

package net.jxta.endpoint;


import net.jxta.document.MimeMediaType;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.Checksum;


/**
 * A Message Element using {@link java.io.InputStream} as the source for the
 * element data. This implementation copies all of the data from the stream at
 * the time of creation.
 * 

* InputStreamMessageElement is not as efficient as other message element types * and should only be used when an input stream is the only available source for * the element data. */ public class InputStreamMessageElement extends MessageElement { /** * The bytes of this element. */ protected final List databytes; /** * The length of the data. */ protected final long length; /** * Cached Hash Code */ protected transient int cachedHashCode = 0; /** * Create a new MessageElement. This constructor copies the data as needed * and closes the stream upon completion. * * @param name Name of the MessageElement. May be the empty string ("") if * the MessageElement is not named. * @param type Type of the MessageElement. null is the same as specifying * the type "Application/Octet-stream". * @param in the stream containing the body of the MessageElement. The * stream will be closed by the MessageElement. * @param sig optional message digest/digital signature element or null if * no signature is desired. * @throws IOException If there is a problem reading from the source stream. */ public InputStreamMessageElement(String name, MimeMediaType type, InputStream in, MessageElement sig) throws IOException { this(name, type, in, Long.MAX_VALUE, sig); } /** * Create a new Message Element. * * @param name Name of the MessageElement. May be the empty string ("") if * the MessageElement is not named. * @param type Type of the MessageElement. null is the same as specifying * the type "Application/Octet-stream". * @param in the stream containing the body of the MessageElement. * The stream will NOT be closed unless EOF is unexpectedly reached. * @param len The size of the Element will be limited to len bytes * from the stream. If you are using the stream interface and know * the size of the stream, specifying it here improves performance * and space efficiency a lot. The stream must contain at least * len bytes. * @param sig optional message digest/digital signature element or null if * no signature is desired. * @throws IOException if there is a problem reading from the source stream */ public InputStreamMessageElement(String name, MimeMediaType type, InputStream in, long len, MessageElement sig) throws IOException { super(name, type, sig); if ((len < 0)) { throw new IllegalArgumentException("len must be >= 0"); } // copy the data from the stream databytes = CopyToDataBytes(in, len); // calculate the length long buffersSum = 0; for (byte[] aBuffer : databytes) { buffersSum += aBuffer.length; } length = buffersSum; // fail if the length is not as promised. if ((len != Long.MAX_VALUE) && (len != length)) { throw new IllegalArgumentException("Stream was shorter than promised length."); } } /** * {@inheritDoc} */ @Override public boolean equals(Object target) { if (this == target) { return true; } if (target instanceof MessageElement) { if (!super.equals(target)) { return false; } if (target instanceof InputStreamMessageElement) { // have to do a slow stream comparison. // XXX 20020615 [email protected] the performance of this could be much improved. MessageElement likeMe = (MessageElement) target; try { InputStream myStream = getStream(); InputStream itsStream = likeMe.getStream(); int mine; int its; do { mine = myStream.read(); its = itsStream.read(); if (mine != its) { return false; } // content didn't match } while ((-1 != mine) && (-1 != its)); return ((-1 == mine) && (-1 == its)); // end at the same time? } catch (IOException fatal) { throw new IllegalStateException("MessageElements could not be compared." + fatal); } } } return false; // not a new message element } /** * {@inheritDoc} */ @Override public synchronized int hashCode() { if (0 == cachedHashCode) { Checksum crc = new CRC32(); for (byte[] aBuffer : databytes) { crc.update(aBuffer, 0, aBuffer.length); } int result = super.hashCode() + (int) crc.getValue() * 6037; // a prime cachedHashCode = 0 != result ? result : 1; } return cachedHashCode; } /** * {@inheritDoc} */ @Override public long getByteLength() { return length; } /** * {@inheritDoc} */ public InputStream getStream() throws IOException { List buffers = new ArrayList(); for (byte[] aBuffer : databytes) { buffers.add(new ByteArrayInputStream(aBuffer)); } return new SequenceInputStream(Collections.enumeration(buffers)); } /** * {@inheritDoc} */ @Override public void sendToStream(OutputStream sendTo) throws IOException { for (byte[] aBuffer : databytes) { sendTo.write(aBuffer); } } /** * Copy data from a stream with best possible efficiency. Unfortunately, * this still results in a lot of copying since we have often have no * fore-knowledge of the length of the stream. * * @param in the stream to copy from * @param limit the maximum number of bytes to copy from the stream. * Long.LONG_MAX will read until EOF. * @return A list of buffers. * @throws IOException if there is a problem reading from the stream. */ protected List CopyToDataBytes(InputStream in, long limit) throws IOException { final long INITIAL_INTERMEDIATE_BUFFERSIZE = 6; final long MAXIMUM_INTERMEDIATE_BUFFERSIZE = 18; List buffs = new ArrayList(); boolean atEOF = false; long read = 0; long currentIntermediateBufferSize = INITIAL_INTERMEDIATE_BUFFERSIZE; // build a list of buffers containing all the element data. do { long readRequest = (limit - read); if (Long.MAX_VALUE == limit) { readRequest = Math.min(readRequest, (1L << currentIntermediateBufferSize)); } readRequest = Math.min(readRequest, Integer.MAX_VALUE); // limited by size of arrays which are Integer indexed. byte[] nextBuffer = new byte[(int) readRequest]; int offsetInThisBuffer = 0; // fully read the buffer if we can. do { int readLength = in.read(nextBuffer, offsetInThisBuffer, nextBuffer.length - offsetInThisBuffer); if (readLength == -1) { atEOF = true; break; } offsetInThisBuffer += readLength; } while (offsetInThisBuffer < nextBuffer.length); // handle the final buffer. if (atEOF) { byte[] anotherBuffer = new byte[offsetInThisBuffer]; System.arraycopy(nextBuffer, 0, anotherBuffer, 0, offsetInThisBuffer); nextBuffer = anotherBuffer; } read += nextBuffer.length; buffs.add(nextBuffer); if (currentIntermediateBufferSize < MAXIMUM_INTERMEDIATE_BUFFERSIZE) { currentIntermediateBufferSize++; } } while (!atEOF && (read < limit)); // we are done, close if we are at EOF. if (atEOF) { in.close(); in = null; } return buffs; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy