org.refcodes.io.BytesReceiver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refcodes-io Show documentation
Show all versions of refcodes-io Show documentation
Artifact with commonly used I/O functionality and for connection related
issues such as receiving or transmitting data in a unified way.
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany, distributed
// on an "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, and licen-
// sed under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// -----------------------------------------------------------------------------
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// -----------------------------------------------------------------------------
// Apache License, v2.0 ("http://www.apache.org/licenses/TEXT-2.0")
// -----------------------------------------------------------------------------
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////
package org.refcodes.io;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.refcodes.data.IoRetryCount;
import org.refcodes.data.IoSleepLoopTime;
import org.refcodes.data.IoTimeout;
import org.refcodes.data.SleepLoopTime;
/**
* The Interface BytesReceiver.
*/
public interface BytesReceiver extends BytesDestination, ByteReceiver, TimeoutInputStreamAccessor, Skippable {
/**
* {@inheritDoc}
*/
@Override
default long skip( long aLength ) throws IOException {
int count = 0;
for ( int i = 0; i < aLength && available() >= aLength - i; i++ ) {
receiveByte();
count++;
}
return count;
}
/**
* Retrieves the input stream from the underlying implementation as is (no
* {@link TimeoutInputStream} wrapping the original {@link InputStream}).
*
* @return The according underlying {@link InputStream}.
*/
@Override
default InputStream getInputStream() {
return new ReceiverInputStream( this );
}
/**
* Retrieves the {@link InputStream} as provided by
* {@link #getInputStream()} wrapped in a {@link TimeoutInputStream} or an
* implementation depended implementation of a {@link TimeoutInputStream}.
*
* @param aTimeoutMillis the a timeout in ms
*
* @return The according {@link TimeoutInputStream} from the underlying
* {@link InputStream}.
*/
@Override
default TimeoutInputStream getInputStream( long aTimeoutMillis ) {
return new TimeoutInputStream( new ReceiverInputStream( this ), aTimeoutMillis );
}
/**
* {@inheritDoc}
*/
@Override
default byte[] receiveAllBytes() throws IOException {
final List theData = new ArrayList<>();
while ( hasAvailable() ) {
theData.add( receiveByte() );
}
final byte[] theBytes = new byte[theData.size()];
for ( int i = 0; i < theBytes.length; i++ ) {
theBytes[i] = theData.get( i );
}
return theBytes;
}
/**
* {@inheritDoc}
*/
@Override
default byte[] receiveBytes( int aLength ) throws IOException {
final byte[] theBlock = new byte[aLength];
int i = 0;
while ( hasAvailable() && i < aLength ) {
theBlock[i] = receiveByte();
i++;
}
if ( i == 0 ) {
while ( !hasAvailable() ) {
try {
Thread.sleep( SleepLoopTime.NORM.getTimeMillis() );
}
catch ( InterruptedException ignore ) {}
}
while ( hasAvailable() && i < aLength ) {
theBlock[i] = receiveByte();
i++;
}
}
if ( i == aLength ) {
return theBlock;
}
return Arrays.copyOfRange( theBlock, 0, i );
}
/**
* Receives a byte. This method blocks till a byte is available or the
* timeout has been reached.
*
* @param aTimeoutMillis The default timeout for read operations not
* explicitly called with a timeout argument. With a value of -1
* timeout handling is disabled (blocking mode) or a technical
* timeout occurs (implementation depended).
*
* @return A accordingly received byte.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* receiving.
*/
default byte receiveByteWithin( long aTimeoutMillis ) throws IOException {
// waitForBytesAvailableWithin( aTimeoutMillis, 1 );
// return receiveByte();
byte[] theBuffer = new byte[1];
receiveBytesWithin( aTimeoutMillis, theBuffer, 0, 1 );
return theBuffer[0];
}
/**
* Receives a byte array with the number of bytes specified. This method
* blocks till all bytes are read or the timeout has been reached.
*
* @param aTimeoutMillis The default timeout for read operations not
* explicitly called with a timeout argument. With a value of -1
* timeout handling is disabled (blocking mode) or a technical
* timeout occurs (implementation depended).
* @param aLength The number of bytes to receive.
*
* @return A byte array containing the accordingly received bytes.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* receiving.
*/
default byte[] receiveBytesWithin( long aTimeoutMillis, int aLength ) throws IOException {
// waitForBytesAvailableWithin( aTimeoutMillis, aLength );
// return receiveBytes( aLength );
byte[] theBuffer = new byte[aLength];
receiveBytesWithin( aTimeoutMillis, theBuffer, 0, aLength );
return theBuffer;
}
/**
* Receives a byte array with the number of bytes specified inserted at the
* given offset. This method blocks till all bytes are read or the timeout
* has been reached.
*
* @param aTimeoutMillis The default timeout for read operations not
* explicitly called with a timeout argument. With a value of -1
* timeout handling is disabled (blocking mode) or a technical
* timeout occurs (implementation depended).
* @param aBuffer The byte array where to store the bytes at.
* @param aOffset The offset where to start storing the received bytes.
* @param aLength The number of bytes to receive.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* receiving.
*/
default void receiveBytesWithin( long aTimeoutMillis, byte[] aBuffer, int aOffset, int aLength ) throws IOException {
// waitForBytesAvailableWithin( aTimeoutMillis, aLength );
// receiveBytes( aBuffer, aOffset, aLength );
if ( isClosed() ) {
throw new IOException( "The receiver connection is already closed!" );
}
final long thePollTimeInMs = IoTimeout.toTimeoutSleepLoopTimeInMs( aTimeoutMillis );
int theOffset = 0;
int eAvaialble;
int eRemainder;
if ( aTimeoutMillis != -1 ) {
final long theStartTimeMs = System.currentTimeMillis();
while ( isOpened() && theOffset < aLength && ( aTimeoutMillis == -1 || System.currentTimeMillis() - theStartTimeMs < aTimeoutMillis ) ) {
eRemainder = aLength - theOffset;
if ( eRemainder > 0 ) {
eAvaialble = available();
if ( eAvaialble > 0 ) {
if ( eAvaialble > eRemainder ) {
eAvaialble = eRemainder;
}
receiveBytes( aBuffer, theOffset, eAvaialble );
theOffset += eAvaialble;
}
}
if ( theOffset == aLength ) {
return;
}
synchronized ( this ) {
try {
wait( thePollTimeInMs );
}
catch ( InterruptedException e ) {
throw new IOException( "Interrupted while trying to read <" + aLength + "> number of bytes after <" + ( System.currentTimeMillis() - theStartTimeMs ) + "> milliseconds (with a given timeout of <" + aTimeoutMillis + "> milliseconds)!", e );
}
}
}
if ( isClosed() ) {
throw new IOException( "Connection was closed after <" + ( System.currentTimeMillis() - theStartTimeMs ) + "> milliseconds (with a given timeout of <" + aTimeoutMillis + "> milliseconds) while trying to read <" + aLength + "> number of bytes!" );
}
}
if ( theOffset < aLength ) {
throw new IOException( "Operation timed out after <" + aTimeoutMillis + "> milliseconds while trying to read <" + aLength + "> number of bytes." );
}
}
// /////////////////////////////////////////////////////////////////////////
// HELPER:
// /////////////////////////////////////////////////////////////////////////
@SuppressWarnings("unused")
private void waitForBytesAvailableWithin( long aTimeoutMillis, int aNumberOfBytes ) throws IOException {
if ( isClosed() ) {
throw new IOException( "The receiver connection is already closed!" );
}
final long thePollTimeInMs = IoTimeout.toTimeoutSleepLoopTimeInMs( aTimeoutMillis );
if ( aTimeoutMillis != -1 ) {
final long theStartTimeMs = System.currentTimeMillis();
while ( isOpened() && available() < aNumberOfBytes && System.currentTimeMillis() - theStartTimeMs < aTimeoutMillis ) {
synchronized ( this ) {
try {
wait( thePollTimeInMs );
}
catch ( InterruptedException e ) {
throw new IOException( "Interrupted while trying to read <" + aNumberOfBytes + "> number of bytes after <" + ( System.currentTimeMillis() - theStartTimeMs ) + "> milliseconds (with a given timeout of <" + aTimeoutMillis + "> milliseconds)!", e );
}
}
}
if ( isClosed() ) {
throw new IOException( "Connection was closed after <" + ( System.currentTimeMillis() - theStartTimeMs ) + "> milliseconds (with a given timeout of <" + aTimeoutMillis + "> milliseconds) while trying to read <" + aNumberOfBytes + "> number of bytes!" );
}
}
else {
while ( isOpened() && available() < aNumberOfBytes ) {
synchronized ( this ) {
try {
wait( thePollTimeInMs );
}
catch ( InterruptedException e ) {
throw new IOException( "Interrupted while trying to read <" + aNumberOfBytes + "> number of bytes (in blocking mode)!", e );
}
}
}
if ( isClosed() ) {
throw new IOException( "Connection was closed while trying to read <" + aNumberOfBytes + "> number of bytes (ib blocking mode)." );
}
}
if ( available() < aNumberOfBytes ) {
throw new IOException( "Operation timed out after <" + aTimeoutMillis + "> milliseconds while trying to read <" + aNumberOfBytes + "> number of bytes." );
}
}
/**
* The {@link ReceiverInputStream} constructs an {@link InputStream} from a
* {@link BytesReceiver}.
*/
static class ReceiverInputStream extends InputStream {
// /////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////
private final BytesReceiver _receiver;
private boolean _isClosed = false;
// /////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////
/**
* Uses the provided {@link BytesReceiver} to provide
* {@link InputStream} functionality.
*
* @param aBytesReceiver The receiver to use.
*/
public ReceiverInputStream( BytesReceiver aBytesReceiver ) {
_receiver = aBytesReceiver;
}
// /////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public int available() throws IOException {
if ( _isClosed ) {
throw new IOException( "The stream has already been closed!" );
}
return _receiver.available();
}
/**
* {@inheritDoc}
*/
@Override
public int read() throws IOException {
if ( _receiver.isClosed() || _isClosed ) {
return -1;
}
return _receiver.receiveByte();
}
/**
* {@inheritDoc}
*/
@Override
public int read( byte[] b, int off, int len ) throws IOException {
if ( _isClosed ) {
throw new IOException( "The stream has already been closed!" );
}
final Integer theRetries = IoRetryCount.NORM.getValue();
for ( int i = 0; i < theRetries && available() < len; i++ ) {
synchronized ( this ) {
try {
// Evaluate whether we have a valid timeout |-->
wait( IoSleepLoopTime.NORM.getTimeMillis() );
// Evaluate whether we have a valid timeout <--|
}
catch ( InterruptedException ignore ) {}
}
}
final int theAvailable = available();
len = theAvailable <= len ? theAvailable : len;
_receiver.receiveBytes( b, off, len );
return len;
}
/**
* {@inheritDoc}
*/
@Override
public void close() throws IOException {
_isClosed = true;
super.close();
}
}
}