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

net.jxta.socket.JxtaSocketInputStream 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.socket;


import net.jxta.endpoint.MessageElement;
import net.jxta.endpoint.StringMessageElement;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.SocketTimeoutException;
import java.util.LinkedList;
import java.util.Queue;


/**
 * Provides the stream data source for JxtaSocket.
 *
 * @author Athomas Goldberg
 */
class JxtaSocketInputStream extends InputStream {

    /**
     * We push this "poison" value into the accept backlog queue in order to
     * signal that the queue has been closed.
     */
    protected static final MessageElement QUEUE_END = new StringMessageElement("Terminal", "Terminal", null);

    /**
     * Our read timeout.
     */
    private long timeout = 60 * 1000;

    /**
     * The associated socket.
     */
    private final JxtaSocket socket;

    /**
     * Our queue of message elements waiting to be read.
     */
    protected final Queue queue;

    /**
     * The maximum number of message elements we will allow in the queue.
     */
    protected final int queueSize;

    /**
     * The current message element input stream we are processing.
     */
    private InputStream currentMsgStream = null;

    /**
     * Construct an InputStream for a specified JxtaSocket.
     *
     * @param socket  the JxtaSocket
     * @param queueSize the queue size
     */
    JxtaSocketInputStream(JxtaSocket socket, int queueSize) {
        this.socket = socket;
        this.queueSize = queueSize;
        queue = new LinkedList();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized int available() throws IOException {
        int result;
        InputStream in = getCurrentStream(false);

        if (in != null) {
            result = in.available();
        } else {
            // We chose not to block, if we have no inputstream then
            // that means there are no bytes available.
            result = 0;
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized int read() throws IOException {
        byte[] b = new byte[1];
        int result = 0;

        // The result of read() can be -1 (EOF), 0 (yes, its true) or 1.
        while (0 == result) {
            result = read(b, 0, 1);
        }

        if (-1 != result) {
            result = (int) b[0];
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized int read(byte b[], int off, int len) throws IOException {
        if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        }

        while (true) {
            int result = -1;
            InputStream in = getCurrentStream(true);

            if (null == in) {
                return -1;
            }

            result = in.read(b, off, len);
            if (0 == result) {
                // Some streams annoyingly return 0 result. We won't
                // perpetuate this behaviour.
                continue;
            }

            if (result == -1) {
                closeCurrentStream();
                continue;
            }
            return result;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized void close() {
        queue.clear();
        closeCurrentStream();
        queue.offer(QUEUE_END);
        notify();
    }

    /**
     * Rather than force the InputStream closed we add the EOF at the end of
     * any current data.
     */
    synchronized void softClose() {
        queue.offer(QUEUE_END);
        notify();
    }

    /**
     * Get the input stream for the current segment and optionally block until
     * a segment is available.
     *
     * @param block If {@code true} then block until a segment is available.
     * @return the InputStream
     * @throws IOException if an io error occurs
     */
    private InputStream getCurrentStream(boolean block) throws IOException {

        if (currentMsgStream == null) {

            if (QUEUE_END == queue.peek()) {
                // We are at the end of the queue.
                return null;
            }

            MessageElement me = null;
            long pollUntil = (Long.MAX_VALUE == timeout) ? Long.MAX_VALUE : System.currentTimeMillis() + timeout;

            while (pollUntil >= System.currentTimeMillis()) {
                try {
                    me = queue.poll();

                    if (null == me) {
                        long sleepFor = pollUntil - System.currentTimeMillis();

                        if (sleepFor > 0) {
                            wait(sleepFor);
                        }
                    } else {
                        break;
                    }
                } catch (InterruptedException woken) {
                    InterruptedIOException incomplete = new InterruptedIOException("Interrupted waiting for data.");

                    incomplete.initCause(woken);
                    incomplete.bytesTransferred = 0;
                    throw incomplete;
                }
            }

            if (block && (null == me)) {
                throw new SocketTimeoutException("Socket timeout during read.");
            }

            if (me != null) {
                currentMsgStream = me.getStream();
            }
        }
        return currentMsgStream;
    }

    private void closeCurrentStream() {
        if (currentMsgStream != null) {
            try {
                currentMsgStream.close();
            } catch (IOException ignored) {// ignored
            }
            currentMsgStream = null;
        }
    }

    synchronized void enqueue(MessageElement element) {
        if (queue.contains(QUEUE_END)) {
            // We have already marked the end of the queue.
            return;
        }
        if (queue.size() < queueSize) {
            queue.offer(element);
        }
        notify();
    }

    /**
     * Returns the timeout value for this socket. This is the amount of time in
     * relative milliseconds which we will wait for read() operations to
     * complete.
     *
     * @return The timeout value in milliseconds or 0 (zero) for
     *         infinite timeout.
     */
    long getTimeout() {
        if (timeout < Long.MAX_VALUE) {
            return timeout;
        } else {
            return 0;
        }
    }

    /**
     * Returns the timeout value for this socket. This is the amount of time in
     * relative milliseconds which we will wait for read() operations to
     * operations to complete.
     *
     * @param timeout The timeout value in milliseconds or 0 (zero) for
     *                infinite timeout.
     */
    void setTimeout(long timeout) {
        if (timeout < 0) {
            throw new IllegalArgumentException("Negative timeout not allowed.");
        }

        if (0 == timeout) {
            timeout = Long.MAX_VALUE;
        }
        this.timeout = timeout;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy