org.refcodes.io.AbstractBytesDestination 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")
// together with the GPL linking exception applied; as being applied by the GNU
// Classpath ("http://www.gnu.org/software/classpath/license.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.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.refcodes.component.AbstractConnectable;
import org.refcodes.controlflow.RetryCounter;
import org.refcodes.data.IoRetryCount;
import org.refcodes.data.SleepLoopTime;
/**
* The {@link AbstractBytesDestination} is a base abstract implementation of the
* {@link BytesDestination} interface providing common functionality for
* concrete real implementations. A blocking queue is used internally to which
* received datagrams are put via {@link #pushDatagram(byte)} and which can be
* retrieved via {@link #receiveByte()}. The {@link #pushDatagram(byte)} method
* is your hook when extending this class. Make sure your code fetches the
* datagrams quick enough to prevent filling up of the queue. In case the queue
* is filled up, adding elements via {@link #pushDatagram(byte)} to the queue is
* blocked until elements are taken from the queue via {@link #receiveByte()}.
* So cautions are taken to prevent a memory leak.
*/
public abstract class AbstractBytesDestination extends AbstractConnectable implements BytesDestination {
// /////////////////////////////////////////////////////////////////////////
// STATICS:
// /////////////////////////////////////////////////////////////////////////
private static final Logger LOGGER = Logger.getLogger( AbstractBytesDestination.class.getName() );
// /////////////////////////////////////////////////////////////////////////
// CONSTANTS:
// /////////////////////////////////////////////////////////////////////////
public static final int DATAGRAM_QUEUE_SIZE = AbstractDatagramsReceiver.DATAGRAM_QUEUE_SIZE;
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
protected LinkedBlockingQueue _datagramQueue;
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
/**
* Constructs a {@link AbstractBytesDestination} with a default sized
* blocking queue enabling a maximum of {@link #DATAGRAM_QUEUE_SIZE}
* datagrams.
* -------------------------------------------------------------------------
* Make sure your code fetches the datagrams quick enough to prevent filling
* up of the queue. In case the queue is filled up, adding elements via
* {@link #pushDatagram(byte)} to the queue is blocked until elements are
* taken from the queue via {@link #receiveByte()}.
*/
public AbstractBytesDestination() {
_datagramQueue = new LinkedBlockingQueue<>( DATAGRAM_QUEUE_SIZE );
}
/**
* Constructs a {@link AbstractBytesDestination} with a custom sized
* blocking queue enabling a maximum of datagrams as specified by the
* capacity parameter.
* -------------------------------------------------------------------------
* Make sure your code fetches the datagrams quick enough to prevent filling
* up of the queue. In case the queue is filled up, adding elements via
* {@link #pushDatagram(byte)} to the queue is blocked until elements are
* taken from the queue via {@link #receiveByte()}.
*
* @param aCapacity The capacity of the queue holding the received
* datagrams.
*/
public AbstractBytesDestination( int aCapacity ) {
if ( aCapacity == 0 ) {
_datagramQueue = new LinkedBlockingQueue<>();
}
else {
_datagramQueue = new LinkedBlockingQueue<>( aCapacity );
}
}
// /////////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public byte receiveByte() throws IOException {
try {
return _datagramQueue.take();
}
catch ( InterruptedException e ) {
throw new IOException( "Cannot receive data as of unexpected interruption!", e );
}
}
/**
* {@inheritDoc}
*/
@Override
public byte[] receiveAllBytes() throws IOException {
final List theBytes = new ArrayList<>();
_datagramQueue.drainTo( theBytes );
final byte[] theDatagrams = new byte[theBytes.size()];
for ( int i = 0; i < theDatagrams.length; i++ ) {
theDatagrams[i] = theBytes.get( i );
}
return theDatagrams;
}
/**
* {@inheritDoc}
*/
@Override
public byte[] receiveBytes( int aLength ) throws IOException {
final List theBytes = new ArrayList<>();
_datagramQueue.drainTo( theBytes, aLength );
final byte[] theDatagrams = new byte[theBytes.size()];
for ( int i = 0; i < theDatagrams.length; i++ ) {
theDatagrams[i] = theBytes.get( i );
}
return theDatagrams;
}
// /////////////////////////////////////////////////////////////////////////
// HOOKS:
// /////////////////////////////////////////////////////////////////////////
/**
* Pushes a datagram into the receiver and puts it into the blocking queue
* containing the so far received datagrams. Them datagrams can be retrieved
* via {@link #receiveByte()}: Use {@link AbstractBytesReceiver} extension's
* {@link AbstractBytesReceiver#available()} to test beforehand whether
* there is a datagram available.
*
* @param aDatagram The datagram to be pushed at the end of the blocking
* queue; to be retrieved with the {@link #receiveByte()} method.
*
* @throws IOException the open exception
*/
protected void pushDatagram( byte aDatagram ) throws IOException {
final RetryCounter theRetryCounter = new RetryCounter( IoRetryCount.MAX.getValue() );
try {
while ( !_datagramQueue.offer( aDatagram, SleepLoopTime.MAX.getTimeMillis(), TimeUnit.MILLISECONDS ) && theRetryCounter.nextRetry() ) {
LOGGER.log( Level.WARNING, "Trying to offer (add) a datagram to the datagram queue, though the queue is full, this is retry # <" + theRetryCounter.getRetryCount() + ">, aborting after <" + theRetryCounter.getRetryNumber() + "> retries. Retrying now after a delay of <" + ( SleepLoopTime.MAX.getTimeMillis() / 1000 ) + "> seconds..." );
if ( !theRetryCounter.hasNextRetry() ) {
throw new IOException( "Unable to process the datagram after <" + theRetryCounter.getRetryNumber() + "> retries, aborting retries, dismissing datagram \"" + aDatagram + "\"!" );
}
}
}
catch ( InterruptedException ignored ) {}
}
/**
* Pushes datagrams into the receiver and puts them into the blocking queue
* containing the so far received datagrams. Them datagrams can be retrieved
* via {@link #receiveByte()}: Use {@link AbstractBytesReceiver} extension's
* {@link AbstractBytesReceiver#available()} to test beforehand whether
* there is a datagram available.
*
* @param aDatagrams The datagrams to be pushed at the end of the blocking
* queue; to be retrieved with the {@link #receiveByte()} method.
*
* @throws IOException the open exception
*/
protected void pushDatagrams( byte[] aDatagrams ) throws IOException {
for ( byte eData : aDatagrams ) {
pushDatagram( eData );
}
}
/**
* Pushes datagrams into the receiver and puts them into the blocking queue
* containing the so far received datagrams. Them datagrams can be retrieved
* via {@link #receiveByte()}: Use {@link AbstractBytesReceiver} extension's
* {@link AbstractBytesReceiver#available()} to test beforehand whether
* there is a datagram available.
*
* @param aDatagrams The datagrams to be pushed at the end of the blocking
* queue; to be retrieved with the {@link #receiveByte()} method.
* @param aOffset The offset to start taking data from to be pushed.
* @param aLength The number of elements to be pushed from the offset
* onwards.
*
* @throws IOException the open exception
*/
protected void pushDatagrams( byte[] aDatagrams, int aOffset, int aLength ) throws IOException {
for ( int i = aOffset; i < aOffset + aLength; i++ ) {
pushDatagram( aDatagrams[i] );
}
}
}