org.refcodes.serial.ext.handshake.HandshakePortController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refcodes-serial-ext-handshake Show documentation
Show all versions of refcodes-serial-ext-handshake Show documentation
The refcodes-serial-ext-handshake artifact extends the refcodes-serial artifact
with handshake functionality.
The newest version!
// /////////////////////////////////////////////////////////////////////////////
// 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.serial.ext.handshake;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.refcodes.controlflow.ControlFlowUtility;
import org.refcodes.data.IoRetryCount;
import org.refcodes.data.IoSleepLoopTime;
import org.refcodes.exception.TimeoutIOException;
import org.refcodes.exception.Trap;
import org.refcodes.io.TimeoutInputStream;
import org.refcodes.numerical.ChecksumValidationMode;
import org.refcodes.numerical.ChecksumValidationModeAccessor;
import org.refcodes.numerical.CrcAlgorithm;
import org.refcodes.numerical.CrcAlgorithmAccessor;
import org.refcodes.numerical.Endianess;
import org.refcodes.numerical.EndianessAccessor;
import org.refcodes.serial.AbstractPortDecorator;
import org.refcodes.serial.AcknowledgeMagicBytesAccessor;
import org.refcodes.serial.AcknowledgeRetryNumberAccessor;
import org.refcodes.serial.AcknowledgeTimeoutMillisAccessor;
import org.refcodes.serial.AllocSectionDecoratorSegment;
import org.refcodes.serial.AssertMagicBytesSegment;
import org.refcodes.serial.ByteArraySection;
import org.refcodes.serial.ByteSegment;
import org.refcodes.serial.MagicBytesSegmentMultiplexer;
import org.refcodes.serial.Port;
import org.refcodes.serial.PortMetrics;
import org.refcodes.serial.ReceiveSegmentConsumerDaemon;
import org.refcodes.serial.ReceiveSegmentResultDaemon;
import org.refcodes.serial.Segment;
import org.refcodes.serial.SegmentConsumer;
import org.refcodes.serial.SegmentResult;
import org.refcodes.serial.Sequence;
import org.refcodes.serial.SequenceNumberAccessor;
import org.refcodes.serial.SequenceNumberInitValueAccessor;
import org.refcodes.serial.SequenceNumberWidthAccessor;
import org.refcodes.serial.SerialUtility;
import org.refcodes.serial.TransmissionException;
import org.refcodes.serial.TransmitSegmentConsumerDaemon;
import org.refcodes.serial.TransmitSegmentResultDaemon;
/**
* A {@link HandshakePortController} decorates a {@link Port} for its usage in
* Full-Duplex mode regarding the transmission of {@link Segment} messages (or
* any transmission finished off by calling {@link #flush()}). This is achieved
* by giving each {@link Segment} (or transmission) to be transmitted a sequence
* number together with magic bytes identifying the transmission as being a
* {@link Segment}. Each acknowledge message is given the sequence numbers of
* successfully received {@link Segment} transmissions alongside magic bytes
* identifying the acknowledge message as such. This way {@link Segment}
* messages and acknowledge messages can be distinguished from each other and
* transmission can take place in Full-Duplex mode. This implementation applies
* the Full-Duplex handshake on {@link Segment} messages rather than on packets
* (of a defined size) in order to enable microcontroller based implementation
* to participate easily in this kind of Full-Duplex. Caution: In case very much
* data (long transmissions) is to be transmitted, then the outbound line will
* be blocked by the transmission so that for the time of sending the
* transmission No-Acknowledge message upon receival of an inbound transmission
* can be sent over the outbound line! For such use cases sending much data in a
* single transmission, consider chunking a big transmission into packages
* (could be done by a https://www.metacodes.proization of the
* {@link HandshakePortController}).
*
* @param The actual {@link PortMetrics} type to use.
*/
public class HandshakePortController extends AbstractPortDecorator implements AcknowledgeMagicBytesAccessor, AcknowledgeTimeoutMillisAccessor, AcknowledgeRetryNumberAccessor, ReplyTimeoutMillisAccessor, ReplyRetryNumberAccessor, SequenceNumberAccessor, SequenceNumberInitValueAccessor, SequenceNumberWidthAccessor, CrcAlgorithmAccessor, ChecksumValidationModeAccessor, EndianessAccessor { // ExceptionHandlerMutator, ExceptionHandlerBuilder>
// /////////////////////////////////////////////////////////////////////////
// STATICS:
// /////////////////////////////////////////////////////////////////////////
private static final Logger LOGGER = Logger.getLogger( HandshakePortController.class.getName() );
// /////////////////////////////////////////////////////////////////////////
// CONSTANTS:
// /////////////////////////////////////////////////////////////////////////
private static final int MAX_SEQUENCE_NUMBER_COUNT = 1024;
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
private final Set _sequenceNumbers = new HashSet<>();
private LinkedBlockingQueue _inboundQueue;
private final LinkedBlockingQueue _consumerQueue = new LinkedBlockingQueue<>();
private final LinkedBlockingQueue _requestQueue = new LinkedBlockingQueue<>();
private final LinkedBlockingQueue _outboundQueue = new LinkedBlockingQueue<>();
private final AtomicInteger _sequenceNumber = new AtomicInteger( 0 );
private final Map _sequenceNumToAcknowledge = new HashMap<>();
private final Map _sequenceNumToRequest = new HashMap<>();
private HandshakeTransmissionMetrics _transmissionMetrics;
private final List> _segmentConsumerTupels = new ArrayList<>();
private final List> _requestHandlerTupels = new ArrayList<>();
private Runnable _pingHandler = null;
private ExecutorService _executorService;
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
private HandshakePortController( Builder aBuilder ) {
this( aBuilder.port, aBuilder.endianess, aBuilder.acknowledgeRetryNumber, aBuilder.acknowledgeTimeoutInMs, aBuilder.replyRetryNumber, aBuilder.replyTimeoutInMs, aBuilder.sequenceNumberInitValue, aBuilder.sequenceNumberWidth, aBuilder.crcAlgorithm, aBuilder.checksumValidationMode, aBuilder.inboundQueueCapacity, aBuilder.executorService );
}
// -------------------------------------------------------------------------
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
* be used for configuring this instance.
*/
public HandshakePortController( Port aPort, HandshakeTransmissionMetrics aTransmissionMetrics ) {
this( aPort, aTransmissionMetrics, -1, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
* be used for configuring this instance.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, HandshakeTransmissionMetrics aTransmissionMetrics, ExecutorService aExecutorService ) {
this( aPort, aTransmissionMetrics, -1, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality.
*
* @param aPort The {@link Port} to be decorated.
*/
public HandshakePortController( Port aPort ) {
this( aPort, null, -1, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aAcknowledgeRetryNumber The number of retries waiting for an ACK
* from the return channel.
* @param aAcknowledgeTimeoutInMs The timeout in milliseconds to pend till
* the next retry.
* @param aReplyRetryNumber The number of retries waiting for a reply from
* the return channel.
* @param aReplyTimeoutInMs The timeout in milliseconds to pend till the
* next retry.
* @param aSequenceNumberWidth The width in bytes to be used for the binary
* sequence number representation.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, int aAcknowledgeRetryNumber, long aAcknowledgeTimeoutInMs, int aReplyRetryNumber, long aReplyTimeoutInMs, int aSequenceNumberWidth, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode ) {
this( aPort, aEndianess, aAcknowledgeRetryNumber, aAcknowledgeTimeoutInMs, aReplyRetryNumber, aReplyTimeoutInMs, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, aSequenceNumberWidth, aCrcAlgorithm, aChecksumValidationMode, -1, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aAcknowledgeRetryNumber The number of retries waiting for an ACK
* from the return channel.
* @param aAcknowledgeTimeoutInMs The timeout in milliseconds to pend till
* the next retry.
* @param aReplyRetryNumber The number of retries waiting for a reply from
* the return channel.
* @param aReplyTimeoutInMs The timeout in milliseconds to pend till the
* next retry.
* @param aSequenceNumberWidth The width in bytes to be used for the binary
* sequence number representation.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, int aAcknowledgeRetryNumber, long aAcknowledgeTimeoutInMs, int aReplyRetryNumber, long aReplyTimeoutInMs, int aSequenceNumberWidth, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode, ExecutorService aExecutorService ) {
this( aPort, aEndianess, aAcknowledgeRetryNumber, aAcknowledgeTimeoutInMs, aReplyRetryNumber, aReplyTimeoutInMs, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, aSequenceNumberWidth, aCrcAlgorithm, aChecksumValidationMode, -1, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aAcknowledgeRetryNumber The number of retries waiting for an ACK
* from the return channel.
* @param aAcknowledgeTimeoutInMs The timeout in milliseconds to pend till
* the next retry.
* @param aReplyRetryNumber The number of retries waiting for a reply from
* the return channel.
* @param aReplyTimeoutInMs The timeout in milliseconds to pend till the
* next retry.
* @param aSequenceNumberInitValue The initialization value for the sequence
* number counter, when -1 then the lower 32 bit of the current time
* in milliseconds are taken to prevent sequence number collisions
* upon restarting one of the two communication partners.
* @param aSequenceNumberWidth The width in bytes to be used for the binary
* sequence number representation.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, int aAcknowledgeRetryNumber, long aAcknowledgeTimeoutInMs, int aReplyRetryNumber, long aReplyTimeoutInMs, int aSequenceNumberInitValue, int aSequenceNumberWidth, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode ) {
this( aPort, aEndianess, aAcknowledgeRetryNumber, aAcknowledgeTimeoutInMs, aReplyRetryNumber, aReplyTimeoutInMs, aSequenceNumberInitValue, aSequenceNumberWidth, aCrcAlgorithm, aChecksumValidationMode, -1, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, CrcAlgorithm aCrcAlgorithm ) {
this( aPort, aEndianess, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_REPLY_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_REPLY_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH, aCrcAlgorithm, HandshakeTransmissionMetrics.DEFAULT_CHECKSUM_VALIDATION_MODE, -1, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode ) {
this( aPort, aEndianess, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_REPLY_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_REPLY_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH, aCrcAlgorithm, aChecksumValidationMode, -1, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode, ExecutorService aExecutorService ) {
this( aPort, aEndianess, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_REPLY_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_REPLY_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH, aCrcAlgorithm, aChecksumValidationMode, -1, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, CrcAlgorithm aCrcAlgorithm, ExecutorService aExecutorService ) {
this( aPort, aEndianess, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_REPLY_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_REPLY_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH, aCrcAlgorithm, HandshakeTransmissionMetrics.DEFAULT_CHECKSUM_VALIDATION_MODE, -1, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality.
*
* @param aPort The {@link Port} to be decorated.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, ExecutorService aExecutorService ) {
this( aPort, HandshakeTransmissionMetrics.DEFAULT_ENDIANESS, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_REPLY_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_REPLY_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH, HandshakeTransmissionMetrics.DEFAULT_CRC_ALGORITHM, HandshakeTransmissionMetrics.DEFAULT_CHECKSUM_VALIDATION_MODE, -1, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
* be used for configuring this instance.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
*/
public HandshakePortController( Port aPort, HandshakeTransmissionMetrics aTransmissionMetrics, int aInboundQueueCapacity ) {
this( aPort, aTransmissionMetrics, aInboundQueueCapacity, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality.
*
* @param aPort The {@link Port} to be decorated.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
*/
public HandshakePortController( Port aPort, int aInboundQueueCapacity ) {
this( aPort, null, aInboundQueueCapacity, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aAcknowledgeRetryNumber The number of retries waiting for an ACK
* from the return channel.
* @param aAcknowledgeTimeoutInMs The timeout in milliseconds to pend till
* the next retry.
* @param aReplyRetryNumber The number of retries waiting for a reply from
* the return channel.
* @param aReplyTimeoutInMs The timeout in milliseconds to pend till the
* next retry.
* @param aSequenceNumberWidth The width in bytes to be used for the binary
* sequence number representation.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, int aAcknowledgeRetryNumber, long aAcknowledgeTimeoutInMs, int aReplyRetryNumber, long aReplyTimeoutInMs, int aSequenceNumberWidth, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode, int aInboundQueueCapacity ) {
this( aPort, aEndianess, aAcknowledgeRetryNumber, aAcknowledgeTimeoutInMs, aReplyRetryNumber, aReplyTimeoutInMs, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, aSequenceNumberWidth, aCrcAlgorithm, aChecksumValidationMode, aInboundQueueCapacity, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aAcknowledgeRetryNumber The number of retries waiting for an ACK
* from the return channel.
* @param aAcknowledgeTimeoutInMs The timeout in milliseconds to pend till
* the next retry.
* @param aReplyRetryNumber The number of retries waiting for a reply from
* the return channel.
* @param aReplyTimeoutInMs The timeout in milliseconds to pend till the
* next retry.
* @param aSequenceNumberWidth The width in bytes to be used for the binary
* sequence number representation.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, int aAcknowledgeRetryNumber, long aAcknowledgeTimeoutInMs, int aReplyRetryNumber, long aReplyTimeoutInMs, int aSequenceNumberWidth, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode, int aInboundQueueCapacity, ExecutorService aExecutorService ) {
this( aPort, aEndianess, aAcknowledgeRetryNumber, aAcknowledgeTimeoutInMs, aReplyRetryNumber, aReplyTimeoutInMs, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, aSequenceNumberWidth, aCrcAlgorithm, aChecksumValidationMode, aInboundQueueCapacity, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aAcknowledgeRetryNumber The number of retries waiting for an ACK
* from the return channel.
* @param aAcknowledgeTimeoutInMs The timeout in milliseconds to pend till
* the next retry.
* @param aReplyRetryNumber The number of retries waiting for a reply from
* the return channel.
* @param aReplyTimeoutInMs The timeout in milliseconds to pend till the
* next retry.
* @param aSequenceNumberInitValue The initialization value for the sequence
* number counter, when -1 then the lower 32 bit of the current time
* in milliseconds are taken to prevent sequence number collisions
* upon restarting one of the two communication partners.
* @param aSequenceNumberWidth The width in bytes to be used for the binary
* sequence number representation.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, int aAcknowledgeRetryNumber, long aAcknowledgeTimeoutInMs, int aReplyRetryNumber, long aReplyTimeoutInMs, int aSequenceNumberInitValue, int aSequenceNumberWidth, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode, int aInboundQueueCapacity ) {
this( aPort, aEndianess, aAcknowledgeRetryNumber, aAcknowledgeTimeoutInMs, aReplyRetryNumber, aReplyTimeoutInMs, aSequenceNumberInitValue, aSequenceNumberWidth, aCrcAlgorithm, aChecksumValidationMode, aInboundQueueCapacity, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, CrcAlgorithm aCrcAlgorithm, int aInboundQueueCapacity ) {
this( aPort, aEndianess, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_REPLY_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_REPLY_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH, aCrcAlgorithm, HandshakeTransmissionMetrics.DEFAULT_CHECKSUM_VALIDATION_MODE, aInboundQueueCapacity, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode, int aInboundQueueCapacity ) {
this( aPort, aEndianess, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_REPLY_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_REPLY_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH, aCrcAlgorithm, aChecksumValidationMode, aInboundQueueCapacity, null );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode, int aInboundQueueCapacity, ExecutorService aExecutorService ) {
this( aPort, aEndianess, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_REPLY_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_REPLY_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH, aCrcAlgorithm, aChecksumValidationMode, aInboundQueueCapacity, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, CrcAlgorithm aCrcAlgorithm, int aInboundQueueCapacity, ExecutorService aExecutorService ) {
this( aPort, aEndianess, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_ACKNOWLEDGE_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_REPLY_RETRY_NUMBER, HandshakeTransmissionMetrics.DEFAULT_REPLY_TIMEOUT_IN_MS, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_INIT_VALUE, HandshakeTransmissionMetrics.DEFAULT_SEQUENCE_NUMBER_WIDTH, aCrcAlgorithm, HandshakeTransmissionMetrics.DEFAULT_CHECKSUM_VALIDATION_MODE, aInboundQueueCapacity, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality.
*
* @param aPort The {@link Port} to be decorated.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, int aInboundQueueCapacity, ExecutorService aExecutorService ) {
this( aPort, null, aInboundQueueCapacity, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aAcknowledgeRetryNumber The number of retries waiting for an ACK
* from the return channel.
* @param aAcknowledgeTimeoutInMs The timeout in milliseconds to pend till
* the next retry.
* @param aReplyRetryNumber The number of retries waiting for a reply from
* the return channel.
* @param aReplyTimeoutInMs The timeout in milliseconds to pend till the
* next retry.
* @param aSequenceNumberInitValue The initialization value for the sequence
* number counter, when -1 then the lower 32 bit of the current time
* in milliseconds are taken to prevent sequence number collisions
* upon restarting one of the two communication partners.
* @param aSequenceNumberWidth The width in bytes to be used for the binary
* sequence number representation.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, int aAcknowledgeRetryNumber, long aAcknowledgeTimeoutInMs, int aReplyRetryNumber, long aReplyTimeoutInMs, int aSequenceNumberInitValue, int aSequenceNumberWidth, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode, ExecutorService aExecutorService ) {
this( aPort, aEndianess, aAcknowledgeRetryNumber, aAcknowledgeTimeoutInMs, aReplyRetryNumber, aReplyTimeoutInMs, aSequenceNumberInitValue, aSequenceNumberWidth, aCrcAlgorithm, aChecksumValidationMode, -1, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aEndianess The {@link Endianess} to use when calculating the CRC
* checksum.
* @param aAcknowledgeRetryNumber The number of retries waiting for an ACK
* from the return channel.
* @param aAcknowledgeTimeoutInMs The timeout in milliseconds to pend till
* the next retry.
* @param aReplyRetryNumber The number of retries waiting for a reply from
* the return channel.
* @param aReplyTimeoutInMs The timeout in milliseconds to pend till the
* next retry.
* @param aSequenceNumberInitValue The initialization value for the sequence
* number counter, when -1 then the lower 32 bit of the current time
* in milliseconds are taken to prevent sequence number collisions
* upon restarting one of the two communication partners.
* @param aSequenceNumberWidth The width in bytes to be used for the binary
* sequence number representation.
* @param aCrcAlgorithm The {@link CrcAlgorithm} to be used for CRC checksum
* calculation.
* @param aChecksumValidationMode The mode of operation when validating
* provided CRC checksums against calculated ones.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, Endianess aEndianess, int aAcknowledgeRetryNumber, long aAcknowledgeTimeoutInMs, int aReplyRetryNumber, long aReplyTimeoutInMs, int aSequenceNumberInitValue, int aSequenceNumberWidth, CrcAlgorithm aCrcAlgorithm, ChecksumValidationMode aChecksumValidationMode, int aInboundQueueCapacity, ExecutorService aExecutorService ) {
this( aPort, HandshakeTransmissionMetrics.builder().withEndianess( aEndianess ).withAcknowledgeRetryNumber( aAcknowledgeRetryNumber ).withAcknowledgeTimeoutMillis( aAcknowledgeTimeoutInMs ).withSequenceNumberInitValue( aSequenceNumberInitValue ).withSequenceNumberWidth( aSequenceNumberWidth ).withCrcAlgorithm( aCrcAlgorithm ).withChecksumValidationMode( aChecksumValidationMode ).build(), aInboundQueueCapacity, aExecutorService );
}
/**
* Decorates the given {@link Port} with full duplex {@link Segment}
* multiplexer functionality as of the given arguments.
*
* @param aPort The {@link Port} to be decorated.
* @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
* be used for configuring this instance.
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and output
* data simultaneously.
*/
public HandshakePortController( Port aPort, HandshakeTransmissionMetrics aTransmissionMetrics, int aInboundQueueCapacity, ExecutorService aExecutorService ) {
super( aPort );
_inboundQueue = new LinkedBlockingQueue<>( aInboundQueueCapacity != -1 ? aInboundQueueCapacity : Integer.MAX_VALUE );
_transmissionMetrics = aTransmissionMetrics != null ? aTransmissionMetrics : new HandshakeTransmissionMetrics();
_sequenceNumber.set( _transmissionMetrics.getSequenceNumberInitValue() != -1 ? _transmissionMetrics.getSequenceNumberInitValue() : (int) System.currentTimeMillis() );
_executorService = ( aExecutorService != null ) ? aExecutorService : ControlFlowUtility.createCachedExecutorService( true );
if ( aPort.isOpened() ) { // Is the provided port already open?
start();
}
}
// /////////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////////
/**
* Probes the connection by sending a "ping" to the attached remote station
* ({@link HandshakePortController}) expecting a "pong" reply within a
* timeout (shorter than the timeout of usual transmissions).
*
* @throws IOException thrown in case the "ping" failed, e.g. the remote
* station ({@link HandshakePortController}) is not ready
* (available).
*/
public void ping() throws IOException {
pingWithin( _transmissionMetrics.getPingTimeoutMillis(), _transmissionMetrics.getPingRetryNumber() );
}
/**
* Probes the connection by sending a "ping" to the attached remote station
* ({@link HandshakePortController}) expecting a "pong" reply within a
* timeout (shorter than the timeout of usual transmissions).
*
* @param the generic type
* @param aTimeoutMillis The timeout to wait for sending the ping and
* receiving the pong till aborting.
* @param aRetryNumber The number of retries each of the given timeout in
* milliseconds.
*
* @throws IOException thrown in case the "ping" failed, e.g. the remote
* station ({@link HandshakePortController}) is not ready
* (available).
*/
public void pingWithin( long aTimeoutMillis, int aRetryNumber ) throws IOException {
transmitSegmentWithin( aTimeoutMillis, aRetryNumber, new TransmissionMessage( _sequenceNumber.getAndIncrement(), TransmissionType.PING, _transmissionMetrics ) );
}
/**
* Registers a handler invoked upon incoming remote ping requests (via a
* remote's invocation of {@link #ping()} or similar). The according
* acknowledgement ("pong") is sent automatically, though the ping handler
* should be short running as the ping's acknowledgment timeouts are usually
* shorter than other transmission related timeouts.
*
* @param aPingHandler The handler to be notified upon incoming ping
* requests.
*/
public void onPing( Runnable aPingHandler ) {
_pingHandler = aPingHandler;
}
/**
* Adds a {@link SegmentConsumer} for receiving and handling transmissions.
*
* @param The type of {@link Segment} for which to register.
* @param aSegment The {@link Segment} used for deserializing the
* transmission. A good practice is to use some kind of
* {@link AssertMagicBytesSegment} for uniquely identifying a
* responsible {@link SegmentConsumer} for an according
* {@link Segment}.
* @param aSegmentConsumer The {@link SegmentConsumer} processing the
* according transmission.
*
* @return True in case the {@link SegmentConsumer} has been registered to
* the given {@link Segment}, false in case a consumer has already
* been registered to the given segment.
*/
public boolean onSegment( SEGMENT aSegment, SegmentConsumer aSegmentConsumer ) {
for ( SegmentConsumerTupel> eTupel : _segmentConsumerTupels ) {
if ( eTupel.segment == aSegment || eTupel.segment.equals( aSegment ) ) {
return false;
}
}
_segmentConsumerTupels.add( new SegmentConsumerTupel<>( aSegment, aSegmentConsumer ) );
return true;
}
/**
* Removes a registered {@link SegmentConsumer} (as of
* {@link #subscribeSegmentConsumer(Segment, SegmentConsumer)}).
*
* @param the generic type
* @param aRequest the request
* @param aRequestHandler the request handler
*
* @return True in case the {@link SegmentConsumer} was removed, false in
* case no such {@link SegmentConsumer} was found.
*/
// public boolean unsubscribeSegmentConsumer( SEGMENT aSegment ) {
// Iterator> e = _segmentConsumerTupels.iterator();
// while ( e.hasNext() ) {
// if ( e.next().segment == aSegment ) {
// e.remove();
// return true;
// }
// }
// return false;
// }
/**
* Adds a {@link RequestHandler} for processing requests issued as of
* {@link #requestSegment(Segment, Segment)} (and the like methods).
*
* @param The type of {@link Segment} for which to register.
* @param aRequest The request {@link Segment} used for deserialize the
* request. A good practice is to use some kind of
* {@link AssertMagicBytesSegment} for uniquely identify a
* responsible {@link RequestHandler} for an according
* {@link Segment}.
* @param aRequestHandler The {@link RequestHandler} processing the
* according request.
*
* @return True in case the {@link RequestHandler} has been registered to
* the given {@link Segment}, false in case a handler has already
* been registered to the given request.
*
*/
public boolean onRequest( REQUEST aRequest, RequestHandler aRequestHandler ) {
for ( RequestHandlerTupel> eTupel : _requestHandlerTupels ) {
if ( eTupel.request == aRequest || eTupel.request.equals( aRequest ) ) {
return false;
}
}
_requestHandlerTupels.add( new RequestHandlerTupel<>( aRequest, aRequestHandler ) );
return true;
}
/**
* Removes a registered {@link RequestHandler} (as of
* {@link #subscribeRequestHandler(Segment, RequestHandler)}).
*
* @return True in case the {@link RequestHandler} was removed, false in
* case no such {@link RequestHandler} was found.
*
* @throws IOException Signals that an I/O exception has occurred.
*/
// public boolean unsubscribeRequestHandler( REQUEST aRequest ) {
// Iterator> e = _requestHandlerTupels.iterator();
// while ( e.hasNext() ) {
// if ( e.next().request == aRequest ) {
// e.remove();
// return true;
// }
// }
// return false;
// }
// /**
// * {@inheritDoc}
// */
// @Override
// public FullDuplexHandshakePortDecorator withExceptionHandler( ExceptionHandler aExceptionHandler ) {
// _exceptionHandler = aExceptionHandler;
// return this;
// }
// /**
// * {@inheritDoc}
// */
// @Override
// public void setExceptionHandler( ExceptionHandler aExceptionHandler ) {
// _exceptionHandler = aExceptionHandler;
// }
/**
* {@inheritDoc}
*/
@Override
public int available() throws IOException {
final List theSegments = new ArrayList<>( _inboundQueue );
int theLength = 0;
for ( Segment eSegment : theSegments ) {
theLength += eSegment.getLength();
}
return theLength;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void close() throws IOException {
if ( _port != null ) {
_port.close();
}
super.close();
_executorService.shutdownNow();
}
/**
* {@inheritDoc}
*/
@Override
public SegmentResult doTransmitSegment( SEGMENT aSegment ) throws IOException {
if ( !isOpened() ) {
throw new IOException( "Cannot receive a segment as the connection is in status <" + getConnectionStatus() + ">!" );
}
final TransmitSegmentResultDaemon theDaemon = new TransmitSegmentResultDaemon<>( aSegment, this );
_executorService.execute( theDaemon );
return theDaemon.getSegmentResult();
}
/**
* {@inheritDoc}
*/
@Override
public void doTransmitSegment( SEGMENT aSegment, SegmentConsumer aSegmentConsumer ) throws IOException {
if ( !isOpened() ) {
throw new IOException( "Cannot transmit a segment as the connection is in status <" + getConnectionStatus() + ">!" );
}
_executorService.execute( new TransmitSegmentConsumerDaemon( aSegmentConsumer, aSegment, this ) );
}
/**
* {@inheritDoc}
*/
@Override
public byte[] getAcknowledgeMagicBytes() {
return _transmissionMetrics.getAcknowledgeMagicBytes();
}
/**
* {@inheritDoc}
*/
@Override
public int getAcknowledgeRetryNumber() {
return _transmissionMetrics.getAcknowledgeRetryNumber();
}
/**
* {@inheritDoc}
*/
@Override
public long getAcknowledgeTimeoutMillis() {
return _transmissionMetrics.getAcknowledgeTimeoutMillis();
}
/**
* {@inheritDoc}
*/
@Override
public int getReplyRetryNumber() {
return _transmissionMetrics.getReplyRetryNumber();
}
/**
* {@inheritDoc}
*/
@Override
public long getReplyTimeoutMillis() {
return _transmissionMetrics.getReplyTimeoutMillis();
}
/**
* {@inheritDoc}
*/
@Override
public CrcAlgorithm getCrcAlgorithm() {
return _transmissionMetrics.getCrcAlgorithm();
}
/**
* {@inheritDoc}
*/
@Override
public ChecksumValidationMode getChecksumValidationMode() {
return _transmissionMetrics.getChecksumValidationMode();
}
/**
* {@inheritDoc}
*/
@Override
public Endianess getEndianess() {
return _transmissionMetrics.getEndianess();
}
/**
* {@inheritDoc}
*/
@Override
public InputStream getInputStream() {
final TransmissionMessage theTransmissionMessage;
InputStream thePayloadStream;
try {
theTransmissionMessage = _inboundQueue.take();
thePayloadStream = theTransmissionMessage.getPayload().toSequence().getInputStream();
}
catch ( InterruptedException e ) {
thePayloadStream = new ByteArrayInputStream( new byte[0] );
}
return thePayloadStream;
}
/**
* {@inheritDoc}
*/
@Override
public TimeoutInputStream getInputStream( long aTimeoutMillis ) {
return SerialUtility.createTimeoutInputStream( getInputStream(), aTimeoutMillis );
}
/**
* {@inheritDoc}
*/
@Override
public int getSequenceNumber() {
return _sequenceNumber.get();
}
/**
* {@inheritDoc}
*/
@Override
public int getSequenceNumberInitValue() {
return _transmissionMetrics.getSequenceNumberInitValue();
}
/**
* {@inheritDoc}
*/
@Override
public int getSequenceNumberWidth() {
return _transmissionMetrics.getSequenceNumberWidth();
}
/**
* {@inheritDoc}
*/
@Override
public SegmentResult onReceiveSegment( SEGMENT aSegment ) throws IOException {
if ( !isOpened() ) {
throw new IOException( "Cannot receive a segment as the connection is in status <" + getConnectionStatus() + ">!" );
}
final ReceiveSegmentResultDaemon theDaemon = new ReceiveSegmentResultDaemon<>( aSegment, this );
_executorService.execute( theDaemon );
return theDaemon.getSegmentResult();
}
/**
* {@inheritDoc}
*/
@Override
public void onReceiveSegment( SEGMENT aSegment, SegmentConsumer aSegmentConsumer ) throws IOException {
if ( !isOpened() ) {
throw new IOException( "Cannot receive a segment as the connection is in status <" + getConnectionStatus() + ">!" );
}
_executorService.execute( new ReceiveSegmentConsumerDaemon( aSegmentConsumer, aSegment, this ) );
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void open() throws IOException {
super.open();
start();
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void open( PM aPortMetrics ) throws IOException {
super.open( aPortMetrics );
start();
}
/**
* {@inheritDoc}
*
* Uses a {@link ByteSegment} instance for wrapping up the receival of a
* single byte.
*/
@Override
public byte receiveByte() throws IOException {
return receiveByteWithin( -1 );
}
/**
* {@inheritDoc}
*
* Uses a {@link ByteSegment} instance for wrapping up the receival of a
* single byte.
*/
@Override
public byte receiveByteWithin( long aTimeoutMillis ) throws IOException {
ByteSegment theSegment = new ByteSegment();
receiveSegmentWithin( aTimeoutMillis, theSegment );
return theSegment.getPayload();
}
/**
* {@inheritDoc}
*
* Uses an {@link AllocSectionDecoratorSegment} instance wrapping a
* {@link ByteArraySection} instance for receiving all currently available
* (as of {@link #available()}) bytes. Be sure you really want to use this
* method as the available number of bytes usually is very depended on the
* point in time when this method is called!
*/
@Override
public byte[] receiveAllBytes() throws IOException {
ByteArraySection theSection = new ByteArraySection( new byte[available()] );
AllocSectionDecoratorSegment theSegment = new AllocSectionDecoratorSegment( theSection, _transmissionMetrics );
receiveSegmentWithin( -1, theSegment );
return theSection.getPayload();
}
/**
* {@inheritDoc}
*
* Uses an {@link AllocSectionDecoratorSegment} instance wrapping a
* {@link ByteArraySection} instance for receiving the number of specified
* bytes.
*/
@Override
public byte[] receiveBytes( int aLength ) throws IOException {
return receiveBytesWithin( -1, aLength );
}
/**
* {@inheritDoc}
*
* Uses an {@link AllocSectionDecoratorSegment} instance wrapping a
* {@link ByteArraySection} instance for receiving the number of specified
* bytes.
*/
@Override
public byte[] receiveBytesWithin( long aTimeoutMillis, int aLength ) throws IOException {
ByteArraySection theSection = new ByteArraySection( new byte[aLength] );
AllocSectionDecoratorSegment theSegment = new AllocSectionDecoratorSegment( theSection, _transmissionMetrics );
receiveSegmentWithin( -1, theSegment );
return theSection.getPayload();
}
/**
* {@inheritDoc}
*
* Uses an {@link AllocSectionDecoratorSegment} instance wrapping a
* {@link ByteArraySection} instance for receiving the specified number of
* bytes.
*/
@Override
public void receiveBytes( byte[] aBuffer, int aOffset, int aLength ) throws IOException {
receiveBytesWithin( -1, aBuffer, aOffset, aLength );
}
/**
* {@inheritDoc}
*
* Uses an {@link AllocSectionDecoratorSegment} instance wrapping a
* {@link ByteArraySection} instance for receiving the specified number of
* bytes.
*/
@Override
public void receiveBytesWithin( long aTimeoutMillis, byte[] aBuffer, int aOffset, int aLength ) throws IOException {
ByteArraySection theSection = new ByteArraySection( new byte[available()] );
AllocSectionDecoratorSegment theSegment = new AllocSectionDecoratorSegment( theSection, _transmissionMetrics );
receiveSegmentWithin( aTimeoutMillis, theSegment );
byte[] theBuffer = theSection.getPayload();
System.arraycopy( theBuffer, 0, aBuffer, aOffset, aLength );
}
/**
* {@inheritDoc}
*
* Uses an {@link AllocSectionDecoratorSegment} instance wrapping a
* {@link ByteArraySection} instance for receiving the given amount of
* bytes.
*/
@Override
public Sequence receiveSequence( int aLength ) throws IOException {
return receiveSequenceWithin( -1, aLength );
}
/**
* {@inheritDoc}
*
* Uses an {@link AllocSectionDecoratorSegment} instance wrapping a
* {@link ByteArraySection} instance for receiving the given amount of
* bytes.
*/
@Override
public Sequence receiveSequenceWithin( long aTimeoutMillis, int aLength ) throws IOException {
ByteArraySection theSection = new ByteArraySection( new byte[aLength] );
AllocSectionDecoratorSegment theSegment = new AllocSectionDecoratorSegment( theSection, _transmissionMetrics );
receiveSegmentWithin( aTimeoutMillis, theSegment );
return theSection.toSequence();
}
/**
* {@inheritDoc}
*/
@Override
public void receiveSegment( SEGMENT aSegment ) throws IOException {
receiveSegmentWithin( -1, aSegment );
}
/**
* {@inheritDoc}
*/
@Override
public void receiveSegmentWithin( long aTimeoutMillis, SEGMENT aSegment ) throws IOException {
try {
final TransmissionMessage theHandshakeMsg = aTimeoutMillis > 0 ? _inboundQueue.poll( aTimeoutMillis, TimeUnit.MILLISECONDS ) : _inboundQueue.take();
if ( theHandshakeMsg == null ) {
throw new TimeoutIOException( aTimeoutMillis, "Unable to retrieve a transmission within <" + aTimeoutMillis + "> milliseconds!" );
}
theHandshakeMsg.toPayloadSegment( aSegment );
}
catch ( InterruptedException e ) {
throw new IOException( "I/O operation has unexpectedly been interrupted upon receiving a segment!", e );
}
}
/**
* Sends a request expecting a response.
*
* @param The {@link Segment} (sub-)type of the request.
* @param the generic type
* @param aRequest The request to be sent.
* @param aResponse The response being provisioned with the response.
*
* @throws IOException thrown in case of I/O issues while sending.
*/
public void requestSegment( REQUEST aRequest, RESPONSE aResponse ) throws IOException {
requestSegmentWithin( -1, aRequest, aResponse );
}
/**
* Sends a request expecting a response.
*
* @param The {@link Segment} (sub-)type of the request.
* @param the generic type
* @param aTimeoutMillis The timeout to wait for sending the request and
* receiving the response till aborting.
* @param aRequest The request to be sent.
* @param aResponse The response being provisioned with the response.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* sending.
*/
public void requestSegmentWithin( long aTimeoutMillis, REQUEST aRequest, RESPONSE aResponse ) throws IOException {
requestSegmentWithin( aTimeoutMillis, new TransmissionMessage( _sequenceNumber.getAndIncrement(), TransmissionType.ACKNOWLEDGEABLE_REQUEST, aRequest, aResponse, _transmissionMetrics ) );
}
/**
* Sends a request expecting a response.
*
* @param The {@link Segment} (sub-)type of the request.
* @param the generic type
* @param aRequest The request to be sent.
* @param aResponse The response being provisioned with the response.
* @param isAcknowledgeable True in case an acknowledgment reply is
* required.
*
* @throws IOException thrown in case of I/O issues while sending.
*/
public void requestSegment( REQUEST aRequest, RESPONSE aResponse, boolean isAcknowledgeable ) throws IOException {
requestSegmentWithin( -1, aRequest, aResponse, isAcknowledgeable );
}
/**
* Sends a request expecting a response.
*
* @param The {@link Segment} (sub-)type of the request.
* @param the generic type
* @param aTimeoutMillis The timeout to wait for sending the request and
* receiving the response till aborting.
* @param aRequest The request to be sent.
* @param aResponse The response being provisioned with the response.
* @param isAcknowledgeable True in case an acknowledgment reply is
* required.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* sending.
*/
public void requestSegmentWithin( long aTimeoutMillis, REQUEST aRequest, RESPONSE aResponse, boolean isAcknowledgeable ) throws IOException {
requestSegmentWithin( aTimeoutMillis, new TransmissionMessage( _sequenceNumber.getAndIncrement(), isAcknowledgeable ? TransmissionType.ACKNOWLEDGEABLE_REQUEST : TransmissionType.REQUEST, aRequest, aResponse, _transmissionMetrics ) );
}
/**
* {@inheritDoc}
*/
@Override
public void transmitSegment( SEGMENT aSegment ) throws IOException {
transmitSegmentWithin( _transmissionMetrics.getAcknowledgeTimeoutMillis(), _transmissionMetrics.getAcknowledgeRetryNumber(), new TransmissionMessage( _sequenceNumber.getAndIncrement(), TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION, aSegment, _transmissionMetrics ) );
}
/**
* {@inheritDoc}
*/
@Override
public void transmitSequence( Sequence aSequence ) throws IOException {
transmitSegmentWithin( _transmissionMetrics.getAcknowledgeTimeoutMillis(), _transmissionMetrics.getAcknowledgeRetryNumber(), new TransmissionMessage( _sequenceNumber.getAndIncrement(), TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION, aSequence, _transmissionMetrics ) );
}
/**
* Transmits a {@link Segment} (and blocks this thread) till all it's
* {@link Sequence} data (as of {@link Segment#toSequence()}) has been sent.
*
* @param The {@link Segment} type describing the {@link Segment}
* subclass used.
* @param aTimeoutMillis The timeout to wait for sending the request and
* receiving the response till aborting.
* @param aRetryNumber The number of retries each of the given timeout in
* milliseconds.
* @param aSegment The {@link Segment}'s data to be sent.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* sending.
*/
public void transmitSegmentWithin( long aTimeoutMillis, int aRetryNumber, SEGMENT aSegment ) throws IOException {
transmitSegmentWithin( aTimeoutMillis, aRetryNumber, new TransmissionMessage( _sequenceNumber.getAndIncrement(), TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION, aSegment, _transmissionMetrics ) );
}
/**
* Transmits a {@link Segment} (and blocks this thread) till all it's
* {@link Sequence} data (as of {@link Segment#toSequence()}) has been sent.
*
* @param The {@link Segment} type describing the {@link Segment}
* subclass used.
* @param aTimeoutMillis The timeout to wait for sending the request and
* receiving the response till aborting.
* @param aSegment The {@link Segment}'s data to be sent.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* sending.
*/
public void transmitSegmentWithin( long aTimeoutMillis, SEGMENT aSegment ) throws IOException {
transmitSegmentWithin( aTimeoutMillis, 1, new TransmissionMessage( _sequenceNumber.getAndIncrement(), TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION, aSegment, _transmissionMetrics ) );
}
/**
* Transmits a {@link Sequence} (and blocks this thread) till all it's data
* (as of {@link Segment#toSequence()}) has been sent.
*
* @param aTimeoutMillis The timeout to wait for sending the request and
* receiving the response till aborting.
* @param aRetryNumber The number of retries each of the given timeout in
* milliseconds.
* @param aSequence The {@link Sequence} containing the data to be send.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* sending.
*/
public void transmitSequenceWithin( long aTimeoutMillis, int aRetryNumber, Sequence aSequence ) throws IOException {
transmitSegmentWithin( aTimeoutMillis, aRetryNumber, new TransmissionMessage( _sequenceNumber.getAndIncrement(), TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION, aSequence, _transmissionMetrics ) );
}
/**
* Transmits a {@link Sequence} (and blocks this thread) till all it's data
* (as of {@link Segment#toSequence()}) has been sent.
*
* @param aTimeoutMillis The timeout to wait for sending the request and
* receiving the response till aborting.
* @param aSequence The {@link Sequence} containing the data to be send.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* sending.
*/
public void transmitSequenceWithin( long aTimeoutMillis, Sequence aSequence ) throws IOException {
transmitSegmentWithin( aTimeoutMillis, 1, new TransmissionMessage( _sequenceNumber.getAndIncrement(), TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION, aSequence, _transmissionMetrics ) );
}
/**
* Transmits a {@link Segment} (and blocks this thread) till all it's
* {@link Sequence} data (as of {@link Segment#toSequence()}) has been sent.
*
* @param The {@link Segment} type describing the {@link Segment}
* subclass used.
* @param aSegment The {@link Segment}'s data to be sent.
*
* @param isAcknowledgeable True in case an acknowledgment reply is
* required.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* sending.
*/
public void transmitSegment( SEGMENT aSegment, boolean isAcknowledgeable ) throws IOException {
transmitSegmentWithin( _transmissionMetrics.getAcknowledgeTimeoutMillis(), _transmissionMetrics.getAcknowledgeRetryNumber(), new TransmissionMessage( _sequenceNumber.getAndIncrement(), isAcknowledgeable ? TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION : TransmissionType.TRANSMISSION, aSegment, _transmissionMetrics ) );
}
/**
* Transmits a {@link Sequence} (and blocks this thread) till all it's data
* (as of {@link Segment#toSequence()}) has been sent.
*
* @param aSequence The {@link Sequence} containing the data to be send.
*
* @param isAcknowledgeable True in case an acknowledgment reply is
* required.
*
* @throws IOException thrown in case of I/O issues (e.g. a timeout) while
* sending.
*/
public void transmitSequence( Sequence aSequence, boolean isAcknowledgeable ) throws IOException {
transmitSegmentWithin( _transmissionMetrics.getAcknowledgeTimeoutMillis(), _transmissionMetrics.getAcknowledgeRetryNumber(), new TransmissionMessage( _sequenceNumber.getAndIncrement(), isAcknowledgeable ? TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION : TransmissionType.TRANSMISSION, aSequence, _transmissionMetrics ) );
}
/**
* {@inheritDoc}
*
* Uses a {@link ByteSegment} instance for wrapping up the transmission of a
* single byte.
*/
@Override
public void transmitByte( byte aByte ) throws IOException {
ByteSegment theSegment = new ByteSegment( aByte );
transmitSegment( theSegment );
}
/**
* {@inheritDoc}
*
* Uses an {@link AllocSectionDecoratorSegment} instance wrapping a
* {@link ByteArraySection} instance for receiving all currently available
* (as of {@link #available()}) bytes.
*/
@Override
public void transmitBytes( byte[] aBytes ) throws IOException {
ByteArraySection theSection = new ByteArraySection( aBytes );
AllocSectionDecoratorSegment theSegment = new AllocSectionDecoratorSegment( theSection, _transmissionMetrics );
transmitSegment( theSegment );
}
/**
* {@inheritDoc}
*
* Uses an {@link AllocSectionDecoratorSegment} instance wrapping a
* {@link ByteArraySection} instance for receiving the number of specified
* bytes.
*/
@Override
public void transmitBytes( byte[] aBytes, int aOffset, int aLength ) throws IOException {
byte[] theBuffer = new byte[aLength];
System.arraycopy( aBytes, aOffset, theBuffer, 0, aLength );
ByteArraySection theSection = new ByteArraySection( theBuffer );
AllocSectionDecoratorSegment theSegment = new AllocSectionDecoratorSegment( theSection, _transmissionMetrics );
transmitSegment( theSegment );
}
/**
* {@inheritDoc}
*/
@Override
public HandshakePortController withOpen() throws IOException {
open();
return this;
}
/**
* {@inheritDoc}
*/
@Override
public HandshakePortController withOpen( PM aPortMetrics ) throws IOException {
open( aPortMetrics );
return this;
}
/**
* {@inheritDoc}
*/
@Override
public HandshakePortController withOpenUnchecked( PM aPortMetrics ) {
openUnchecked( aPortMetrics );
return this;
}
/**
* {@inheritDoc}
*/
@Override
public HandshakePortController withOpenUnchecked() {
openUnchecked();
return this;
}
/**
* Creates builder to build {@link HandshakePortController}.
*
* @param The actual {@link PortMetrics} type to use.
*
* @return The accordingly created builder.
*/
public static Builder builder() {
return new Builder<>();
}
// /////////////////////////////////////////////////////////////////////////
// HELPER:
// /////////////////////////////////////////////////////////////////////////
private void start() {
_executorService.execute( new OutboundQueueDaemon() );
_executorService.execute( new InboundQueueDaemon() );
_executorService.execute( new RequestQueueDaemon() );
_executorService.execute( new ConsumerQueueDaemon() );
}
private void transmitSegmentWithin( long aTimeoutMillis, int aRetryNumber, TransmissionMessage aTransmissionMessage ) throws IOException {
aRetryNumber = aRetryNumber != -1 ? aRetryNumber : _transmissionMetrics.getAcknowledgeRetryNumber();
aTimeoutMillis = aTimeoutMillis != -1 ? aTimeoutMillis : _transmissionMetrics.getAcknowledgeTimeoutMillis();
if ( aTransmissionMessage.getTransmissionType().isAcknowledgeable() ) {
final int theSequenceNumber = aTransmissionMessage.getSequenceNumber();
for ( int i = 0; i < aRetryNumber; i++ ) {
if ( !_outboundQueue.contains( aTransmissionMessage ) ) {
_outboundQueue.offer( aTransmissionMessage );
}
try {
synchronized ( aTransmissionMessage ) {
aTransmissionMessage.wait( aTimeoutMillis );
}
}
catch ( InterruptedException interrupted ) {}
if ( aTransmissionMessage.getException() != null ) {
_outboundQueue.remove( aTransmissionMessage );
_sequenceNumToAcknowledge.remove( theSequenceNumber );
throw aTransmissionMessage.getException();
}
else if ( aTransmissionMessage.hasAcknowledge() ) {
_sequenceNumToAcknowledge.remove( theSequenceNumber );
_outboundQueue.remove( aTransmissionMessage );
return;
}
}
_outboundQueue.remove( aTransmissionMessage );
_sequenceNumToAcknowledge.remove( theSequenceNumber );
throw new TimeoutIOException( _transmissionMetrics.getAcknowledgeTimeoutMillis(), "Aborting transmission after <" + aRetryNumber + "> retries without an acknowledge with timeouts each of <" + _transmissionMetrics.getAcknowledgeTimeoutMillis() + "> milliseconds duration! Failed transmission: " + ( aTransmissionMessage != null && aTransmissionMessage.getPayload() != null ? " Failed transmission: " + aTransmissionMessage.getPayload().toString() : "" ) );
}
else {
for ( int i = 0; i < aRetryNumber; i++ ) {
if ( !_outboundQueue.contains( aTransmissionMessage ) ) {
_outboundQueue.offer( aTransmissionMessage );
}
try {
synchronized ( aTransmissionMessage ) {
aTransmissionMessage.wait( aTimeoutMillis );
}
}
catch ( InterruptedException interrupted ) {}
if ( aTransmissionMessage.getException() != null ) {
_outboundQueue.remove( aTransmissionMessage );
throw aTransmissionMessage.getException();
}
}
_outboundQueue.remove( aTransmissionMessage );
}
}
private void requestSegmentWithin( long aTimeoutMillis, TransmissionMessage aRequestMsg ) throws IOException {
final int theSequenceNumber = aRequestMsg.getSequenceNumber();
for ( int i = 0; i < _transmissionMetrics.getReplyRetryNumber(); i++ ) {
if ( !_outboundQueue.contains( aRequestMsg ) ) {
_outboundQueue.offer( aRequestMsg );
}
try {
synchronized ( aRequestMsg ) {
aRequestMsg.wait( aTimeoutMillis != -1 ? aTimeoutMillis : _transmissionMetrics.getReplyTimeoutMillis() );
}
}
catch ( InterruptedException interrupted ) {}
if ( aRequestMsg.getException() != null ) {
_sequenceNumToRequest.remove( theSequenceNumber );
_outboundQueue.remove( aRequestMsg );
throw aRequestMsg.getException();
}
else if ( aRequestMsg.hasResponse() ) {
_sequenceNumToRequest.remove( theSequenceNumber );
_outboundQueue.remove( aRequestMsg );
return;
}
}
_sequenceNumToRequest.remove( theSequenceNumber );
_outboundQueue.remove( aRequestMsg );
throw new TimeoutIOException( _transmissionMetrics.getReplyTimeoutMillis(), "Aborting transmission after <" + _transmissionMetrics.getReplyRetryNumber() + "> retries without an acknowledge with timeouts each of <" + _transmissionMetrics.getReplyTimeoutMillis() + "> milliseconds duration! Failed transmission: " + aRequestMsg.getPayload().toString() );
}
private void offerInbound( TransmissionMessage aTransmissionMsg ) {
if ( !_inboundQueue.offer( aTransmissionMsg ) ) {
if ( aTransmissionMsg.getTransmissionType().isAcknowledgeable() ) {
_outboundQueue.offer( new AcknowledgeMessage( AcknowledgeType.TRANSMISSION_DISMISSED, aTransmissionMsg.getSequenceNumber(), _transmissionMetrics ) );
}
LOGGER.log( Level.WARNING, "Skipping inbound transmission for session ID <" + aTransmissionMsg.getSequenceNumber() + "> as the queue capacity has been exceeded." );
}
else {
if ( aTransmissionMsg.getTransmissionType().isAcknowledgeable() ) {
_outboundQueue.offer( new AcknowledgeMessage( AcknowledgeType.ACKNOWLEDGE, aTransmissionMsg.getSequenceNumber(), _transmissionMetrics ) );
}
}
}
private boolean addSequenceNumber( TransmissionMessage eTransmissionMsg ) {
if ( !_sequenceNumbers.contains( eTransmissionMsg.getSequenceNumber() ) ) {
synchronized ( _sequenceNumbers ) {
if ( !_sequenceNumbers.contains( eTransmissionMsg.getSequenceNumber() ) ) {
_sequenceNumbers.add( eTransmissionMsg.getSequenceNumber() );
if ( _sequenceNumbers.size() > MAX_SEQUENCE_NUMBER_COUNT ) {
_sequenceNumbers.remove( 0 );
}
return true;
}
}
}
return false;
}
// /////////////////////////////////////////////////////////////////////////
// INNER CLASSES:
// /////////////////////////////////////////////////////////////////////////
/**
* Builder for building {@link HandshakePortController} instances.
*
* @param The actual {@link PortMetrics} type to use.
*/
public static final class Builder implements AcknowledgeTimeoutMillisBuilder>, AcknowledgeRetryNumberBuilder>, ReplyTimeoutMillisBuilder>, ReplyRetryNumberBuilder>, CrcAlgorithmBuilder>, ChecksumValidationModeBuilder>, EndianessBuilder>, SequenceNumberInitValueBuilder>, SequenceNumberWidthBuilder> {
private int acknowledgeRetryNumber;
private long acknowledgeTimeoutInMs;
private int replyRetryNumber;
private long replyTimeoutInMs;
private CrcAlgorithm crcAlgorithm;
private ChecksumValidationMode checksumValidationMode;
private Endianess endianess;
private ExecutorService executorService;
private Port port;
private int sequenceNumberInitValue;
private int sequenceNumberWidth;
private int inboundQueueCapacity;
private Builder() {}
/**
* {@inheritDoc}
*/
@Override
public Builder withReplyRetryNumber( int aReplyRetryNumber ) {
replyRetryNumber = aReplyRetryNumber;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withReplyTimeoutMillis( long aReplyTimeoutInMs ) {
replyTimeoutInMs = aReplyTimeoutInMs;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withAcknowledgeRetryNumber( int aAcknowledgeRetryNumber ) {
acknowledgeRetryNumber = aAcknowledgeRetryNumber;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withAcknowledgeTimeoutMillis( long aAcknowledgeTimeoutInMs ) {
acknowledgeTimeoutInMs = aAcknowledgeTimeoutInMs;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withCrcAlgorithm( CrcAlgorithm aCrcAlgorithm ) {
crcAlgorithm = aCrcAlgorithm;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withChecksumValidationMode( ChecksumValidationMode aChecksumValidationMode ) {
checksumValidationMode = aChecksumValidationMode;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withEndianess( Endianess aEndianess ) {
endianess = aEndianess;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withSequenceNumberInitValue( int aSequenceNumberInitValue ) {
sequenceNumberInitValue = aSequenceNumberInitValue;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Builder withSequenceNumberWidth( int aSequenceNumberWidth ) {
sequenceNumberWidth = aSequenceNumberWidth;
return this;
}
/**
* Sets the queue capacity of unprocessed inbound messages.
*
* @param aInboundQueueCapacity The capacity of the inbound queue, any
* exceeding inbound transmissions are skipped.
*
* @return The {@link Builder} to chain other operations as of the
* builder pattern.
*/
public Builder withInboundQueueCapacity( int aInboundQueueCapacity ) {
inboundQueueCapacity = aInboundQueueCapacity;
return this;
}
/**
* Sets the according property for configuring the
* {@link HandshakePortController} upon invoking {@link #build()}.
*
* @param aExecutorService The {@link ExecutorService} to be used when
* creating {@link Thread} instances for handling input and
* output data simultaneously.
*
* @return The {@link Builder} to chain other operations as of the
* builder pattern.
*/
public Builder withExecutorService( ExecutorService aExecutorService ) {
executorService = aExecutorService;
return this;
}
/**
* Sets the according property for configuring the
* {@link HandshakePortController} upon invoking {@link #build()}.
*
* @param aPort the a port
*
* @return The {@link Builder} to chain other operations as of the
* builder pattern.
*/
public Builder withPort( Port aPort ) {
port = aPort;
return this;
}
/**
* Builds the.
*
* @return the full duplex segment multiplexer
*/
public HandshakePortController build() {
return new HandshakePortController<>( this );
}
}
private class InboundQueueDaemon implements Runnable {
/**
* {@inheritDoc}
*/
@Override
public void run() {
TransmissionMessage ePingMsg;
AcknowledgeMessage ePongAck;
TransmissionMessage eTransmissionMsg;
TransmissionMessage eRequestMsg;
TransmissionMessage eResponseMsg = null;
TransmissionMessage eAcknowledgeableTransmissionMsg;
AcknowledgeMessage eAcknowledgeableTransmissionAck;
AcknowledgeMessage eAcknowledgeableTransmissionDismissedAck;
TransmissionMessage eAcknowledgeableRequestMsg;
TransmissionMessage eAcknowledgeableResponseMsg;
AcknowledgeMessage eAcknowledgeableResponseAck;
AcknowledgeMessage eAcknowledgeableRequestDismissedAck;
MagicBytesSegmentMultiplexer eMultiplexer;
while ( !isClosed() ) {
try {
// @formatter:off
eMultiplexer = new MagicBytesSegmentMultiplexer(
ePingMsg = new TransmissionMessage( TransmissionType.PING, _transmissionMetrics ),
eTransmissionMsg = new TransmissionMessage( TransmissionType.TRANSMISSION, _transmissionMetrics ),
eAcknowledgeableTransmissionMsg = new TransmissionMessage( TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION, _transmissionMetrics ),
eRequestMsg = new TransmissionMessage(TransmissionType.REQUEST, _transmissionMetrics ),
eResponseMsg = new TransmissionMessage( TransmissionType.RESPONSE, _transmissionMetrics ),
eAcknowledgeableRequestMsg = new TransmissionMessage(TransmissionType.ACKNOWLEDGEABLE_REQUEST, _transmissionMetrics ),
eAcknowledgeableResponseMsg = new TransmissionMessage( TransmissionType.ACKNOWLEDGEABLE_RESPONSE, _transmissionMetrics ),
ePongAck = new AcknowledgeMessage( AcknowledgeType.PONG, _transmissionMetrics ),
eAcknowledgeableTransmissionAck = new AcknowledgeMessage( AcknowledgeType.ACKNOWLEDGE, _transmissionMetrics ),
eAcknowledgeableTransmissionDismissedAck = new AcknowledgeMessage( AcknowledgeType.TRANSMISSION_DISMISSED, _transmissionMetrics ),
eAcknowledgeableResponseAck = new AcknowledgeMessage( AcknowledgeType.RESPONSE, _transmissionMetrics ),
eAcknowledgeableRequestDismissedAck= new AcknowledgeMessage( AcknowledgeType.REQUEST_DISMISSED, _transmissionMetrics )
);
// @formatter:on
_port.receiveSegment( eMultiplexer );
// PING_MSG:
if ( eMultiplexer.getCallee() == ePingMsg ) {
onPingMsg( ePingMsg );
}
// PONG_ACK:
else if ( eMultiplexer.getCallee() == ePongAck ) {
onPongAck( ePongAck );
}
// TRANSMISSION_MSG:
else if ( eMultiplexer.getCallee() == eTransmissionMsg ) {
onTransmissionMsg( eTransmissionMsg );
}
// ACKNOWLEDGEABLE_TRANSMISSION_MSG:
else if ( eMultiplexer.getCallee() == eAcknowledgeableTransmissionMsg ) {
onAcknowledgeableTransmissionMsg( eAcknowledgeableTransmissionMsg );
}
// TRANSMISSION_ACK:
else if ( eMultiplexer.getCallee() == eAcknowledgeableTransmissionAck ) {
onAcknowledgeTransmissionAck( eAcknowledgeableTransmissionAck );
}
// TRANSMISSION_DISMISSED_ACK:
else if ( eMultiplexer.getCallee() == eAcknowledgeableTransmissionDismissedAck ) {
onAcknowledgeableTransmissionDismissedAck( eAcknowledgeableTransmissionDismissedAck );
}
// ACKNOWLEDGEABLE_REQUEST_MSG:
else if ( eMultiplexer.getCallee() == eAcknowledgeableRequestMsg ) {
onAcknowledgeableRequestMsg( eAcknowledgeableRequestMsg );
}
// ACKNOWLEDGEABLE_RESPONSE_MSG:
else if ( eMultiplexer.getCallee() == eAcknowledgeableResponseMsg ) {
onAcknowledgeableResponseMsg( eAcknowledgeableResponseMsg );
}
// ACLNOWLEDGEABLE_REQUEST_DISMISSED_ACK:
else if ( eMultiplexer.getCallee() == eAcknowledgeableRequestDismissedAck ) {
onAcknowledgeableRequestDismissedAck( eAcknowledgeableRequestDismissedAck );
}
// ACKNOWLEDGEBALE_RESPONSE_ACK:
else if ( eMultiplexer.getCallee() == eAcknowledgeableResponseAck ) {
onAcknowledgeableResponseAck( eAcknowledgeableResponseAck );
}
// REQUEST_MSG:
else if ( eMultiplexer.getCallee() == eRequestMsg ) {
onRequestMsg( eRequestMsg );
}
// RESPONSE_MSG:
else if ( eMultiplexer.getCallee() == eResponseMsg ) {
onResponseMsg( eResponseMsg );
}
}
catch ( IOException e ) {
if ( !isClosed() ) {
try {
if ( _port.available() > 0 ) {
LOGGER.log( Level.WARNING, Trap.asMessage( e ), e );
// Cleanup buffer, try to get back to sync |-->
_port.skipAvailableWithin( IoRetryCount.MIN.getValue(), IoSleepLoopTime.MIN.getTimeMillis() );
// for ( int i = 0; i < IoRetryCount.MIN.getValue(); i++ ) {
// try {
// Thread.sleep( IoSleepLoopTime.MIN.getTimeInMs() );
// }
// catch ( InterruptedException ignore ) {}
// _port.skipAvailable();
// }
// Cleanup buffer, try to get back to sync <--|
}
}
catch ( IOException ignore ) {}
}
}
}
}
private void onTransmissionMsg( TransmissionMessage eTransmissionMsg ) {
if ( !_segmentConsumerTupels.isEmpty() ) {
_consumerQueue.offer( eTransmissionMsg );
}
else {
offerInbound( eTransmissionMsg );
}
}
private void onRequestMsg( TransmissionMessage aRequestMsg ) {
if ( addSequenceNumber( aRequestMsg ) ) {
_requestQueue.offer( aRequestMsg );
}
}
private void onResponseMsg( TransmissionMessage aResponseMsg ) throws TransmissionException {
final TransmissionMessage theRequestMsg = _sequenceNumToRequest.remove( aResponseMsg.getSequenceNumber() );
if ( theRequestMsg != null ) {
theRequestMsg.setResponse( aResponseMsg.getPayload().toSequence() );
synchronized ( theRequestMsg ) {
theRequestMsg.notifyAll();
}
}
}
private void onAcknowledgeableResponseAck( AcknowledgeMessage aAcknowledgeableResponseAck ) {
final TransmissionMessage theAcknowledgeableResponseMsg = _sequenceNumToAcknowledge.remove( aAcknowledgeableResponseAck.getSequenceNumber() );
if ( aAcknowledgeableResponseAck != null ) {
theAcknowledgeableResponseMsg.acknowledge();
synchronized ( theAcknowledgeableResponseMsg ) {
theAcknowledgeableResponseMsg.notifyAll();
}
}
}
private void onAcknowledgeableRequestDismissedAck( AcknowledgeMessage aAcknowledgeableRequestDismissedAck ) {
final TransmissionMessage theRequestMsg = _sequenceNumToRequest.remove( aAcknowledgeableRequestDismissedAck.getSequenceNumber() );
if ( theRequestMsg != null ) {
theRequestMsg.setException( new IOException( new IllegalArgumentException( "Bad request, no remote <" + RequestHandler.class.getSimpleName() + "> has been found for given request <" + theRequestMsg.getPayload() + ">!" ) ) );
synchronized ( theRequestMsg ) {
theRequestMsg.notifyAll();
}
}
}
private void onAcknowledgeableResponseMsg( TransmissionMessage aAcknowledgeableResponseMsg ) throws TransmissionException {
final TransmissionMessage theRequestMsg = _sequenceNumToRequest.remove( aAcknowledgeableResponseMsg.getSequenceNumber() );
if ( theRequestMsg != null ) {
theRequestMsg.setResponse( aAcknowledgeableResponseMsg.getPayload().toSequence() );
synchronized ( theRequestMsg ) {
theRequestMsg.notifyAll();
}
_outboundQueue.offer( new AcknowledgeMessage( AcknowledgeType.RESPONSE, aAcknowledgeableResponseMsg.getSequenceNumber(), _transmissionMetrics ) );
}
}
private void onAcknowledgeableRequestMsg( TransmissionMessage aAcknowledgeableRequestMsg ) {
if ( addSequenceNumber( aAcknowledgeableRequestMsg ) ) {
_requestQueue.offer( aAcknowledgeableRequestMsg );
}
}
private void onAcknowledgeableTransmissionDismissedAck( AcknowledgeMessage aAcknowledgeableTransmissionDismissedAck ) {
final TransmissionMessage theAcknowledgeableTransmissionMsg = _sequenceNumToAcknowledge.remove( aAcknowledgeableTransmissionDismissedAck.getSequenceNumber() );
if ( theAcknowledgeableTransmissionMsg != null ) {
theAcknowledgeableTransmissionMsg.setException( new IOException( "Receiver dismissed transmission with sequence number <" + aAcknowledgeableTransmissionDismissedAck.getSequenceNumber() + ">!" ) );
synchronized ( theAcknowledgeableTransmissionMsg ) {
theAcknowledgeableTransmissionMsg.notifyAll();
}
}
}
private void onAcknowledgeTransmissionAck( AcknowledgeMessage aAcknowledgeTransmissionAck ) {
final TransmissionMessage theAcknowledgeableTransmissionMsg = _sequenceNumToAcknowledge.remove( aAcknowledgeTransmissionAck.getSequenceNumber() );
if ( theAcknowledgeableTransmissionMsg != null ) {
theAcknowledgeableTransmissionMsg.acknowledge();
synchronized ( theAcknowledgeableTransmissionMsg ) {
theAcknowledgeableTransmissionMsg.notifyAll();
}
}
}
private void onAcknowledgeableTransmissionMsg( TransmissionMessage aAcknowledgeableTransmissionMsg ) {
if ( addSequenceNumber( aAcknowledgeableTransmissionMsg ) ) {
onTransmissionMsg( aAcknowledgeableTransmissionMsg );
_outboundQueue.offer( new AcknowledgeMessage( AcknowledgeType.ACKNOWLEDGE, aAcknowledgeableTransmissionMsg.getSequenceNumber(), _transmissionMetrics ) );
}
}
private void onPingMsg( TransmissionMessage aPingMsg ) {
if ( addSequenceNumber( aPingMsg ) ) {
_outboundQueue.offer( new AcknowledgeMessage( AcknowledgeType.PONG, aPingMsg.getSequenceNumber(), _transmissionMetrics ) );
if ( _pingHandler != null ) {
_pingHandler.run();
}
}
}
private void onPongAck( AcknowledgeMessage aPongAck ) {
final TransmissionMessage thePingMsg = _sequenceNumToAcknowledge.remove( aPongAck.getSequenceNumber() );
if ( thePingMsg != null ) {
thePingMsg.acknowledge();
synchronized ( thePingMsg ) {
thePingMsg.notifyAll();
}
}
}
}
private class ConsumerQueueDaemon implements Runnable {
/**
* {@inheritDoc}
*/
@Override
public void run() {
TransmissionMessage eConsumerMsg;
while ( !isClosed() ) {
try {
eConsumerMsg = _consumerQueue.take();
try {
onSegment( eConsumerMsg );
if ( eConsumerMsg.getTransmissionType().isAcknowledgeable() ) {
_outboundQueue.offer( new AcknowledgeMessage( AcknowledgeType.ACKNOWLEDGE, eConsumerMsg.getSequenceNumber(), _transmissionMetrics ) );
}
}
catch ( IOException | IllegalArgumentException e ) {
offerInbound( eConsumerMsg );
LOGGER.log( Level.WARNING, Trap.asMessage( e ), e );
}
}
catch ( InterruptedException ignore ) {}
}
}
private void onSegment( TransmissionMessage aConsumerMsg ) throws IOException {
final Sequence theSequence = aConsumerMsg.getPayload().toSequence();
IOException theCause = null;
for ( SegmentConsumerTupel> eSegmentConsumerTupel : _segmentConsumerTupels ) {
try {
eSegmentConsumerTupel.segment.fromTransmission( theSequence );
eSegmentConsumerTupel.onSegment();
return;
}
catch ( IOException e ) {
theCause = theCause == null ? e : theCause;
}
}
throw new IllegalArgumentException( "Bad transmission, no <" + SegmentConsumer.class.getSimpleName() + "> has been found!" + ( theCause != null ? " Cause: " + theCause.getMessage() : "" ), theCause );
}
}
private class RequestQueueDaemon implements Runnable {
/**
* {@inheritDoc}
*/
@Override
public void run() {
TransmissionMessage eRequestMsg;
TransmissionMessage eResponseMsg;
while ( !isClosed() ) {
try {
eRequestMsg = _requestQueue.take();
try {
eResponseMsg = new TransmissionMessage( eRequestMsg.getSequenceNumber(), eRequestMsg.getTransmissionType().isAcknowledgeable() ? TransmissionType.ACKNOWLEDGEABLE_RESPONSE : TransmissionType.RESPONSE, onRequest( eRequestMsg ), _transmissionMetrics );
transmitSegmentWithin( _transmissionMetrics.getAcknowledgeTimeoutMillis(), _transmissionMetrics.getAcknowledgeRetryNumber(), eResponseMsg );
}
catch ( IOException | IllegalArgumentException e ) {
LOGGER.log( Level.WARNING, Trap.asMessage( e ), e );
}
}
catch ( InterruptedException ignore ) {}
}
}
private Segment onRequest( TransmissionMessage aRequestMsg ) throws IOException {
final Sequence theSequence = aRequestMsg.getPayload().toSequence();
IOException theCause = null;
for ( RequestHandlerTupel> eRequestHandlerTupel : _requestHandlerTupels ) {
try {
eRequestHandlerTupel.request.fromTransmission( theSequence );
return eRequestHandlerTupel.onRequest();
}
catch ( IOException e ) {
theCause = theCause == null ? e : theCause;
}
}
_outboundQueue.offer( new AcknowledgeMessage( AcknowledgeType.REQUEST_DISMISSED, aRequestMsg.getSequenceNumber() ) );
throw new IllegalArgumentException( "Bad request, no <" + RequestHandler.class.getSimpleName() + "> has been found!" + ( theCause != null ? " Cause: " + theCause.getMessage() : "" ), theCause );
}
}
private class OutboundQueueDaemon implements Runnable {
/**
* {@inheritDoc}
*/
@Override
public void run() {
Message eOut;
while ( !isClosed() ) {
try {
eOut = _outboundQueue.take();
// PING:
if ( eOut instanceof TransmissionMessage && ( (TransmissionMessage) eOut ).getTransmissionType() == TransmissionType.PING ) {
onPingMsg( (TransmissionMessage) eOut );
}
// TRANSMISSION:
else if ( eOut instanceof TransmissionMessage && ( (TransmissionMessage) eOut ).getTransmissionType() == TransmissionType.TRANSMISSION ) {
onTransmissionMsg( (TransmissionMessage) eOut );
}
// REQUEST:
else if ( eOut instanceof TransmissionMessage && ( (TransmissionMessage) eOut ).getTransmissionType() == TransmissionType.REQUEST ) {
onRequestMsg( (TransmissionMessage) eOut );
}
// RESPONSE:
else if ( eOut instanceof TransmissionMessage && ( (TransmissionMessage) eOut ).getTransmissionType() == TransmissionType.RESPONSE ) {
onResponseMsg( (TransmissionMessage) eOut );
}
//ACKNOWLEDGEABLE_TRANSMISSION:
else if ( eOut instanceof TransmissionMessage && ( (TransmissionMessage) eOut ).getTransmissionType() == TransmissionType.ACKNOWLEDGEABLE_TRANSMISSION ) {
onAcknowledgeableTransmissionMsg( (TransmissionMessage) eOut );
}
// ACKNOWLEDGEABLE_REQUEST:
else if ( eOut instanceof TransmissionMessage && ( (TransmissionMessage) eOut ).getTransmissionType() == TransmissionType.ACKNOWLEDGEABLE_REQUEST ) {
onAcknowledgeableRequestMsg( (TransmissionMessage) eOut );
}
// ACKNOWLEDGEABLE_RESPONSE:
else if ( eOut instanceof TransmissionMessage && ( (TransmissionMessage) eOut ).getTransmissionType() == TransmissionType.ACKNOWLEDGEABLE_RESPONSE ) {
onAcknowledgeableResponseMsg( (TransmissionMessage) eOut );
}
// ACKNOWLEDGE:
else {
onAcknowledge( eOut );
}
}
catch ( InterruptedException e ) { /* Probably closed */ }
}
}
private void onAcknowledge( Message aAcknowledge ) {
try {
_port.transmitSegment( aAcknowledge );
}
catch ( IOException e ) {
LOGGER.log( Level.WARNING, Trap.asMessage( e ), e );
}
}
private void onAcknowledgeableResponseMsg( TransmissionMessage aAcknowledgeableResponseMsg ) {
_sequenceNumToAcknowledge.put( aAcknowledgeableResponseMsg.getSequenceNumber(), aAcknowledgeableResponseMsg );
try {
_port.transmitSegment( aAcknowledgeableResponseMsg );
}
catch ( IOException e ) {
_sequenceNumToAcknowledge.remove( aAcknowledgeableResponseMsg.getSequenceNumber() );
aAcknowledgeableResponseMsg.setException( e );
synchronized ( aAcknowledgeableResponseMsg ) {
aAcknowledgeableResponseMsg.notifyAll();
}
}
}
private void onAcknowledgeableRequestMsg( TransmissionMessage aAcknowledgeableRequestMsg ) {
_sequenceNumToRequest.put( aAcknowledgeableRequestMsg.getSequenceNumber(), aAcknowledgeableRequestMsg );
try {
_port.transmitSegment( aAcknowledgeableRequestMsg );
}
catch ( IOException e ) {
_sequenceNumToRequest.remove( aAcknowledgeableRequestMsg.getSequenceNumber() );
aAcknowledgeableRequestMsg.setException( e );
synchronized ( aAcknowledgeableRequestMsg ) {
aAcknowledgeableRequestMsg.notifyAll();
}
}
}
private void onAcknowledgeableTransmissionMsg( TransmissionMessage aAcknowledgeableTransmissionMsg ) {
_sequenceNumToAcknowledge.put( aAcknowledgeableTransmissionMsg.getSequenceNumber(), aAcknowledgeableTransmissionMsg );
try {
_port.transmitSegment( aAcknowledgeableTransmissionMsg );
}
catch ( IOException e ) {
_sequenceNumToAcknowledge.remove( aAcknowledgeableTransmissionMsg.getSequenceNumber() );
aAcknowledgeableTransmissionMsg.setException( e );
synchronized ( aAcknowledgeableTransmissionMsg ) {
aAcknowledgeableTransmissionMsg.notifyAll();
}
}
}
private void onTransmissionMsg( TransmissionMessage aTransmissionMsg ) {
try {
_port.transmitSegment( aTransmissionMsg );
}
catch ( IOException e ) {
aTransmissionMsg.setException( e );
}
synchronized ( aTransmissionMsg ) {
aTransmissionMsg.notifyAll();
}
}
private void onRequestMsg( TransmissionMessage aRequestMsg ) {
_sequenceNumToRequest.put( aRequestMsg.getSequenceNumber(), aRequestMsg );
try {
_port.transmitSegment( aRequestMsg );
}
catch ( IOException e ) {
_sequenceNumToRequest.remove( aRequestMsg.getSequenceNumber() );
aRequestMsg.setException( e );
synchronized ( aRequestMsg ) {
aRequestMsg.notifyAll();
}
}
}
private void onResponseMsg( TransmissionMessage aResponseMsg ) {
try {
_port.transmitSegment( aResponseMsg );
}
catch ( IOException e ) {
aResponseMsg.setException( e );
}
synchronized ( aResponseMsg ) {
aResponseMsg.notifyAll();
}
}
private void onPingMsg( TransmissionMessage aPingMsg ) {
_sequenceNumToAcknowledge.put( aPingMsg.getSequenceNumber(), aPingMsg );
try {
_port.transmitSegment( aPingMsg );
}
catch ( IOException e ) {
_sequenceNumToAcknowledge.remove( aPingMsg.getSequenceNumber() );
aPingMsg.setException( e );
synchronized ( aPingMsg ) {
aPingMsg.notifyAll();
}
}
}
}
private static class SegmentConsumerTupel {
SEGMENT segment;
SegmentConsumer consumer;
SegmentConsumerTupel( SEGMENT aSegment, SegmentConsumer aConsumer ) {
segment = aSegment;
consumer = aConsumer;
}
void onSegment() {
consumer.onSegment( segment );
}
}
private static class RequestHandlerTupel {
REQUEST request;
RequestHandler handler;
RequestHandlerTupel( REQUEST aRequest, RequestHandler aHandler ) {
request = aRequest;
handler = aHandler;
}
public Segment onRequest() {
return handler.onRequest( request );
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return getClass().getSimpleName() + " [request=" + request + ", handler=" + handler + "]";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy