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

org.refcodes.serial.ext.handshake.TransmissionMessage Maven / Gradle / Ivy

Go to download

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/LICENSE-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 static org.refcodes.serial.SerialSugar.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.refcodes.exception.ExceptionAccessor.ExceptionProperty;
import org.refcodes.mixin.PayloadAccessor;
import org.refcodes.serial.AllocSegmentBody;
import org.refcodes.serial.AllocSegmentHead;
import org.refcodes.serial.AssertMagicBytesSegment;
import org.refcodes.serial.ComplexTypeSegment;
import org.refcodes.serial.PayloadSegment;
import org.refcodes.serial.Section;
import org.refcodes.serial.Segment;
import org.refcodes.serial.Sequence;
import org.refcodes.serial.SequenceNumberSegment;
import org.refcodes.serial.SequenceSection;
import org.refcodes.serial.SerialSchema;
import org.refcodes.serial.Transmission;
import org.refcodes.serial.TransmissionException;
import org.refcodes.struct.SimpleTypeMap;
import org.refcodes.struct.SimpleTypeMapImpl;

/**
 * Implementation for a transmission {@link Message}.
 */
class TransmissionMessage implements HandshakeMessage, ExceptionProperty, PayloadAccessor, TransmissionTypeAccessor {

	// /////////////////////////////////////////////////////////////////////////
	// STATICS:
	// /////////////////////////////////////////////////////////////////////////

	private static final long serialVersionUID = 1L;

	// /////////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////////

	private IOException _exception = null;
	private boolean _hasAcknowledge = false;
	private boolean _hasResponse = false;
	private Segment _delegatee = null;
	private Transmission _cargo;
	private SequenceNumberSegment _sequenceNumberSegment;
	private AssertMagicBytesSegment _assertMagicBytesSegment;
	private HandshakeTransmissionMetrics _transmissionMetrics;
	private Segment _responseSegment;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Instantiates a new {@link TransmissionMessage} with a plain
	 * {@link Sequence} as payload ready to receive an according cargo.
	 *
	 * @param aTransmissionType the transmission type
	 * @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
	 *        use.
	 */
	public TransmissionMessage( TransmissionType aTransmissionType, HandshakeTransmissionMetrics aTransmissionMetrics ) {
		if ( aTransmissionType == TransmissionType.PING ) {
			toPingMessage( -1, aTransmissionType, aTransmissionMetrics );
		}
		else {
			toTransmissionMessage( -1, aTransmissionType, (Sequence) null, aTransmissionMetrics );
		}
	}

	/**
	 * Instantiates a new {@link TransmissionMessage} with the provided payload.
	 * It is up to the {@link TransmissionMessage} how to encode and (decode as
	 * of {@link #toPayload(Class)} the provided payload!
	 *
	 * @param  The generic type of the payload to be carried by
	 *        {@link TransmissionMessage}.
	 * @param aSequenceNumber The sequence number to be assigned to the
	 *        {@link TransmissionMessage}.
	 * @param aTransmissionType The magic bytes identifying the transmission
	 *        message.
	 * @param aPayload The payload to be carried by the
	 *        {@link TransmissionMessage}.
	 * @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
	 *        use.
	 */
	public  TransmissionMessage( int aSequenceNumber, TransmissionType aTransmissionType, T aPayload, HandshakeTransmissionMetrics aTransmissionMetrics ) {
		if ( aTransmissionType != TransmissionType.PING ) {
			toTransmissionMessage( aSequenceNumber, aTransmissionType, new ComplexTypeSegment<>( aPayload, aTransmissionMetrics ), aTransmissionMetrics );
		}
		else {
			throw new IllegalArgumentException( "This constructor must not(!) be invoked for a transmission type <" + TransmissionType.PING + ">!" );
		}
	}

	/**
	 * Instantiates a new {@link TransmissionMessage} containing the given cargo
	 * {@link Segment}.
	 * 
	 * @param aSequenceNumber The sequence number to be assigned to the
	 *        {@link TransmissionMessage}.
	 * @param aTransmissionType The magic bytes identifying the transmission
	 *        message.
	 * @param aRequestSegment The request {@link Segment} to be carried.
	 * @param aResponseSegment The {@link Segment} representing the response.
	 * @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
	 *        use.
	 */
	public TransmissionMessage( int aSequenceNumber, TransmissionType aTransmissionType, Segment aRequestSegment, Segment aResponseSegment, HandshakeTransmissionMetrics aTransmissionMetrics ) {
		if ( aTransmissionType != TransmissionType.PING ) {
			toTransmissionMessage( aSequenceNumber, aTransmissionType, aRequestSegment, aTransmissionMetrics );
			_responseSegment = aResponseSegment;
		}
		else {
			throw new IllegalArgumentException( "This constructor must not(!) be invoked for a transmission type <" + TransmissionType.PING + ">!" );
		}
	}

	/**
	 * Instantiates a new {@link TransmissionMessage} containing the given cargo
	 * {@link Segment}.
	 * 
	 * @param aSequenceNumber The sequence number to be assigned to the
	 *        {@link TransmissionMessage}.
	 * @param aTransmissionType The magic bytes identifying the transmission
	 *        message.
	 * @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
	 *        use.
	 */
	public TransmissionMessage( int aSequenceNumber, TransmissionType aTransmissionType, HandshakeTransmissionMetrics aTransmissionMetrics ) {
		if ( aTransmissionType == TransmissionType.PING ) {
			toPingMessage( aSequenceNumber, aTransmissionType, aTransmissionMetrics );
		}
		else {
			throw new IllegalArgumentException( "This constructor must only be invoked for a transmission type <" + TransmissionType.PING + ">!" );
		}
	}

	/**
	 * Instantiates a new {@link TransmissionMessage} containing the given cargo
	 * {@link Segment}.
	 * 
	 * @param aSequenceNumber The sequence number to be assigned to the
	 *        {@link TransmissionMessage}.
	 * @param aTransmissionType The {@link TransmissionType} and therewith the
	 *        magic bytes identifying the transmission message.
	 * @param aCargoSegment The cargo {@link Segment} to be carried.
	 * @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
	 *        use.
	 */
	public TransmissionMessage( int aSequenceNumber, TransmissionType aTransmissionType, Segment aCargoSegment, HandshakeTransmissionMetrics aTransmissionMetrics ) {
		if ( aTransmissionType != TransmissionType.PING ) {
			toTransmissionMessage( aSequenceNumber, aTransmissionType, aCargoSegment, aTransmissionMetrics );
		}
		else {
			throw new IllegalArgumentException( "This constructor must not(!) be invoked for a transmission type <" + TransmissionType.PING + ">!" );
		}
	}

	/**
	 * Instantiates a new {@link TransmissionMessage} containing the given cargo
	 * {@link Sequence}. The cargo {@link Sequence} is automatically labeled
	 * with the according cargo's length.
	 * 
	 * @param aSequenceNumber The sequence number to be assigned to the
	 *        {@link TransmissionMessage}.
	 * @param aTransmissionType The {@link TransmissionType} and therewith the
	 *        magic bytes identifying the transmission message.
	 * @param aCargoSequence The cargo {@link Sequence} to be carried.
	 * @param aTransmissionMetrics The {@link HandshakeTransmissionMetrics} to
	 *        use.
	 */
	public TransmissionMessage( int aSequenceNumber, TransmissionType aTransmissionType, Sequence aCargoSequence, HandshakeTransmissionMetrics aTransmissionMetrics ) {
		if ( aTransmissionType != TransmissionType.PING ) {
			toTransmissionMessage( aSequenceNumber, aTransmissionType, aCargoSequence, aTransmissionMetrics );
		}
		else {
			throw new IllegalArgumentException( "This constructor must not(!) be invoked for a transmission type <" + TransmissionType.PING + ">!" );
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getLength() {
		return _delegatee.getLength();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Sequence toSequence() {
		return _delegatee.toSequence();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return getClass().getSimpleName() + " [segment=" + _delegatee + "]";
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public SimpleTypeMap toSimpleTypeMap() {
		return _delegatee != null ? _delegatee.toSimpleTypeMap() : new SimpleTypeMapImpl();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void transmitTo( OutputStream aOutputStream, InputStream aReturnStream ) throws IOException {
		_delegatee.transmitTo( aOutputStream, aReturnStream );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void reset() {
		_assertMagicBytesSegment.reset();
		_cargo.reset();
		_delegatee.reset();
		_exception = null;
		_hasAcknowledge = false;
		_hasResponse = false;
		_responseSegment.reset();
		_sequenceNumberSegment.reset();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public SerialSchema toSchema() {
		return _delegatee.toSchema();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int fromTransmission( Sequence aSequence, int aOffset ) throws TransmissionException {
		return _delegatee.fromTransmission( aSequence, aOffset );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void receiveFrom( InputStream aInputStream, OutputStream aReturnStream ) throws IOException {
		_delegatee.receiveFrom( aInputStream, aReturnStream );

	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getSequenceNumber() {
		if ( _sequenceNumberSegment != null ) {
			return _sequenceNumberSegment.getValue() != null ? _sequenceNumberSegment.getValue().intValue() : -1;
		}
		throw new IllegalStateException( "A transmission segment of type <" + getTransmissionType() + "> does not have a sequence nummber!" );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public byte[] getMagicBytes() {
		return _assertMagicBytesSegment.getMagicBytes();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Transmission getPayload() {
		return _cargo;
	}

	/**
	 * Initializes the provided {@link Segment} from this
	 * {@link TransmissionMessage}'s explicitly carried {@link Transmission}.
	 * 
	 * @param  The generic type of the {@link Segment} to be extracted.
	 * 
	 * @param aPayloadSegment The preconfigured {@link Segment} to be
	 *        initialized from this {@link TransmissionMessage}'s explicitly
	 *        carried {@link Transmission}.
	 * 
	 * @return The provided {@link Segment} initialized with this
	 *         {@link TransmissionMessage}'s explicitly carried
	 *         {@link Transmission}.
	 * 
	 * @throws TransmissionException thrown in case the {@link Transmission}
	 *         cannot be processed by the provided {@link Segment}.
	 */
	public  SEGMENT toPayloadSegment( SEGMENT aPayloadSegment ) throws TransmissionException {
		aPayloadSegment.fromTransmission( _cargo.toSequence() );
		return aPayloadSegment;
	}

	/**
	 * Extracts the payload from this {@link TransmissionMessage}'s explicitly
	 * carried {@link Transmission} as of the provided preconfigured
	 * {@link PayloadSegment}.
	 * 
	 * @param  The generic type of the {@link PayloadSegment} from
	 *        which to extract the payload.
	 * @param  The generic type of the actual payload.
	 * 
	 * @param aPayloadSegment The preconfigured {@link PayloadSegment} to use
	 *        for extracting the payload from this {@link TransmissionMessage}
	 * 
	 * @return The payload as of this {@link TransmissionMessage}'s encapsulated
	 *         {@link Transmission}.
	 * 
	 * @throws TransmissionException thrown in case the {@link Transmission}
	 *         cannot be processed by the provided {@link PayloadSegment}.
	 */
	public , T> T toPayload( SEGMENT aPayloadSegment ) throws TransmissionException {
		aPayloadSegment.fromTransmission( _cargo.toSequence() );
		return aPayloadSegment.getPayload();
	}

	/**
	 * Decodes the payload to an instance of the given type. The payload must be
	 * either be passed to one of the constructors such as
	 * {@link #TransmissionMessage(Object)} or be encoded by a
	 * {@link ComplexTypeSegment} constructed with the same
	 * {@link HandshakeTransmissionMetrics} as this {@link TransmissionMessage}
	 * instance and passed to one of the other constructors such as
	 * {@link #TransmissionMessage(Segment )} or
	 * {@link #TransmissionMessage(Section)}.
	 * 
	 * @param  The generic type of the actual payload.
	 * @param aType The type of the payload for which to retrieve an instance
	 *        created from the {@link TransmissionMessage} instance's cargo.
	 * 
	 * @return The according instance created from the
	 *         {@link TransmissionMessage} instance's cargo.
	 * 
	 * @throws TransmissionException in case creating the given instance failed
	 *         in case a given {@link Transmission} cannot be processed
	 *         accordingly.
	 */
	public  T toPayload( Class aType ) throws TransmissionException {
		return toPayload( new ComplexTypeSegment<>( aType, _transmissionMetrics ) );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ( ( _delegatee == null ) ? 0 : _delegatee.hashCode() );
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals( Object obj ) {
		if ( this == obj ) {
			return true;
		}
		if ( obj == null ) {
			return false;
		}
		if ( getClass() != obj.getClass() ) {
			return false;
		}
		final TransmissionMessage other = (TransmissionMessage) obj;
		if ( _delegatee == null ) {
			if ( other._delegatee != null ) {
				return false;
			}
		}
		else if ( !_delegatee.equals( other._delegatee ) ) {
			return false;
		}
		return true;
	}

	/**
	 * Sets the acknowledgement status for to "acknowledged", e.g. the
	 * acknowledgement for this message is to be set.
	 */
	public void acknowledge() {
		setAcknowledge( true );
	}

	/**
	 * Determines the acknowledgement status, e.g. whether there is already an
	 * acknowledgement for this message.
	 * 
	 * @return The according acknowledgement status.
	 */
	public boolean hasAcknowledge() {
		return _hasAcknowledge;
	}

	/**
	 * Sets the acknowledgement status for the message, e.g. whether there is
	 * already an acknowledgement for this message.
	 *
	 * @param hasAcknowledge the new acknowledge
	 */
	public void setAcknowledge( boolean hasAcknowledge ) {
		_hasAcknowledge = hasAcknowledge;
	}

	/**
	 * Determines the response status, e.g. whether there is already a response
	 * for this message.
	 * 
	 * @return The according response status.
	 */
	public boolean hasResponse() {
		return _hasResponse;
	}

	/**
	 * Sets the response for this message.
	 * 
	 * @param aResponse The {@link Sequence} representing the response.
	 * 
	 * @throws TransmissionException in case the response {@link Sequence}
	 *         cannot be deserialized by the response {@link Segment}.
	 */
	public void setResponse( Sequence aResponse ) throws TransmissionException {
		_responseSegment.fromTransmission( aResponse );
		_hasResponse = true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IOException getException() {
		return _exception;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setException( IOException aException ) {
		_exception = aException;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public TransmissionType getTransmissionType() {
		return _transmissionMetrics.toTransmissionType( _assertMagicBytesSegment.getMagicBytes() );
	}

	// /////////////////////////////////////////////////////////////////////////
	// HELPER:
	// /////////////////////////////////////////////////////////////////////////

	private void toPingMessage( int aSequenceNumber, TransmissionType aTransmissionType, HandshakeTransmissionMetrics aTransmissionMetrics ) {
		// @formatter:off
		_delegatee = segmentComposite(  
			crcSegment(
				segmentComposite(
					_assertMagicBytesSegment = assertMagicBytesSegment( aTransmissionMetrics.toMagicBytes(  aTransmissionType ), aTransmissionMetrics ),
					_sequenceNumberSegment = sequenceNumberSegment( aSequenceNumber, aTransmissionMetrics )  
				), 
				aTransmissionMetrics
			)
		);
			// @formatter:on
		_transmissionMetrics = aTransmissionMetrics;
	}

	private void toTransmissionMessage( int aSequenceNumber, TransmissionType aTransmissionType, Segment aCargoSegment, HandshakeTransmissionMetrics aTransmissionMetrics ) {
		// @formatter:off
		_delegatee = segmentComposite(  
			crcSegment(
				segmentComposite(
					_assertMagicBytesSegment = assertMagicBytesSegment( aTransmissionMetrics.toMagicBytes(  aTransmissionType ), aTransmissionMetrics ),
					segmentLength( aCargoSegment, aTransmissionMetrics ),
					_sequenceNumberSegment = sequenceNumberSegment( aSequenceNumber, aTransmissionMetrics ) 
				), 
				aTransmissionMetrics
			),
			crcSegment(	aCargoSegment, aTransmissionMetrics	)
		);
		// @formatter:on
		_cargo = aCargoSegment;
		_transmissionMetrics = aTransmissionMetrics;
	}

	private void toTransmissionMessage( int aSequenceNumber, TransmissionType aTransmissionType, Sequence aCargoSequence, HandshakeTransmissionMetrics aTransmissionMetrics ) {
		aTransmissionMetrics = aTransmissionMetrics != null ? aTransmissionMetrics : new HandshakeTransmissionMetrics();
		final SequenceSection theSequenceSection = aCargoSequence != null ? sequenceSection( aCargoSequence ) : sequenceSection();
		final AllocSegmentBody theAllocBody = allocSegmentBody( theSequenceSection );
		final AllocSegmentHead theAllocHead = allocSegmentHead( theAllocBody, aTransmissionMetrics );
		// @formatter:off
		_delegatee = segmentComposite(  
			crcSegment(
				segmentComposite(
					_assertMagicBytesSegment = assertMagicBytesSegment( aTransmissionMetrics.toMagicBytes(  aTransmissionType ), aTransmissionMetrics),
					theAllocHead, 
					_sequenceNumberSegment = sequenceNumberSegment( aSequenceNumber, aTransmissionMetrics ) 
				), 
				aTransmissionMetrics
			),
			crcSegment(
				theAllocBody, aTransmissionMetrics
			)
		);
		// @formatter:on
		_cargo = theAllocBody;
		_transmissionMetrics = aTransmissionMetrics;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy