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

org.refcodes.io.ext.observer.ObservableConnectionRequestReceiver Maven / Gradle / Ivy

// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// =============================================================================
// This code is copyright (c) by Siegfried Steiner, Munich, Germany, distributed
// on an "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, and licen-
// sed under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// =============================================================================
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// together with the GPL linking exception applied; as being applied by the GNU
// Classpath ("http://www.gnu.org/software/classpath/license.html")
// =============================================================================
// Apache License, v2.0 ("http://www.apache.org/licenses/TEXT-2.0")
// =============================================================================
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.io.ext.observer;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.refcodes.component.Component;
import org.refcodes.component.ConnectionStatus;
import org.refcodes.component.ext.observer.CloseEvent;
import org.refcodes.component.ext.observer.ClosedEvent;
import org.refcodes.component.ext.observer.ConnectionEvent;
import org.refcodes.component.ext.observer.ConnectionObserver.ConnectionRequestObserver;
import org.refcodes.component.ext.observer.ConnectionStatusEvent;
import org.refcodes.component.ext.observer.OpenEvent;
import org.refcodes.component.ext.observer.OpenedEvent;
import org.refcodes.controlflow.ExecutionStrategy;
import org.refcodes.exception.VetoException;
import org.refcodes.exception.VetoException.VetoRuntimeException;
import org.refcodes.io.ConnectionDatagramsReceiver;
import org.refcodes.observer.AbstractObservable;
import org.refcodes.observer.ActionEvent;
import org.refcodes.observer.EventMetaData;
import org.refcodes.observer.Observable;

/**
 * The {@link ObservableConnectionRequestReceiver} extends the
 * {@link ConnectionDatagramsReceiver} with {@link ConnectionRequestObserver}
 * functionality.
 *
 * @param  The type of the datagram to be operated with.
 * @param  The type of the connection to be used.
 */
public class ObservableConnectionRequestReceiver implements ConnectionDatagramsReceiver, Observable>, Component {

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

	private static final Logger LOGGER = Logger.getLogger( ObservableConnectionReceiver.class.getName() );

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

	private ConnectionDatagramsReceiver _connectionReceiver;
	private EventMetaData _eventMetaData;
	private boolean _isDestroyed = false;
	private ConnectionObservable _observable;
	private CON _source;

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

	/**
	 * Constructs a {@link ObservableConnectionRequestReceiver} with the given
	 * attributes.
	 * 
	 * @param aConnectionReceiver The {@link ConnectionDatagramsReceiver} to
	 *        which the connection method calls are to be delegated to.
	 */
	public ObservableConnectionRequestReceiver( ConnectionDatagramsReceiver aConnectionReceiver ) {
		_connectionReceiver = aConnectionReceiver;
		_eventMetaData = new EventMetaData( this.getClass() );
		_source = toSource();
		_observable = new ConnectionObservable();
	}

	/**
	 * Constructs a {@link ObservableConnectionRequestReceiver} with the given
	 * attributes.
	 * 
	 * @param aConnectionReceiver The {@link ConnectionDatagramsReceiver} to
	 *        which the connection method calls are to be delegated to.
	 * @param aEventMetaData The {@link EventMetaData} to be used when firing
	 *        events in case the {@link EventMetaData} is to be different from
	 *        the auto-generated {@link EventMetaData}.
	 */
	public ObservableConnectionRequestReceiver( ConnectionDatagramsReceiver aConnectionReceiver, EventMetaData aEventMetaData ) {
		_connectionReceiver = aConnectionReceiver;
		_eventMetaData = aEventMetaData;
		_source = toSource();
		_observable = new ConnectionObservable();
	}

	/**
	 * Constructs a {@link ObservableConnectionRequestReceiver} with the given
	 * attributes.
	 * 
	 * @param aConnectionReceiver The {@link ConnectionDatagramsReceiver} to
	 *        which the connection method calls are to be delegated to.
	 * @param aEventMetaData The {@link EventMetaData} to be used when firing
	 *        events in case the {@link EventMetaData} is to be different from
	 *        the auto-generated {@link EventMetaData}.
	 * @param aExecutorService The executor service to be used when firing
	 *        {@link ActionEvent} instances in multiple threads (if null then a
	 *        default one is used).
	 * @param aExecutionStrategy The {@link ExecutionStrategy} to be used when
	 *        firing {@link ActionEvent} instance (if null then the default
	 *        {@link ExecutionStrategy#SEQUENTIAL} is used).
	 */
	public ObservableConnectionRequestReceiver( ConnectionDatagramsReceiver aConnectionReceiver, EventMetaData aEventMetaData, ExecutorService aExecutorService, ExecutionStrategy aExecutionStrategy ) {
		_connectionReceiver = aConnectionReceiver;
		_eventMetaData = aEventMetaData;
		_source = toSource();
		_observable = new ConnectionObservable( aExecutorService, aExecutionStrategy );
	}

	/**
	 * Constructs a {@link ObservableConnectionRequestReceiver} with the given
	 * attributes.
	 * 
	 * @param aConnectionReceiver The {@link ConnectionDatagramsReceiver} to
	 *        which the connection method calls are to be delegated to.
	 * @param aEventMetaData The {@link EventMetaData} to be used when firing
	 *        events in case the {@link EventMetaData} is to be different from
	 *        the auto-generated {@link EventMetaData}.
	 * @param aSource The source instance to be used when firing events in case
	 *        the source is to be different from this class' instance.
	 */
	public ObservableConnectionRequestReceiver( ConnectionDatagramsReceiver aConnectionReceiver, EventMetaData aEventMetaData, Object aSource ) {
		_connectionReceiver = aConnectionReceiver;
		_eventMetaData = aEventMetaData;
		_source = toSource();
		_observable = new ConnectionObservable();
	}

	/**
	 * Constructs a {@link ObservableConnectionRequestReceiver} with the given
	 * attributes.
	 * 
	 * @param aConnectionReceiver The {@link ConnectionDatagramsReceiver} to
	 *        which the connection method calls are to be delegated to.
	 * @param aEventMetaData The {@link EventMetaData} to be used when firing
	 *        events in case the {@link EventMetaData} is to be different from
	 *        the auto-generated {@link EventMetaData}.
	 * @param aSource The source instance to be used when firing events in case
	 *        the source is to be different from this class' instance.
	 * @param aExecutorService The executor service to be used when firing
	 *        {@link ActionEvent} instances in multiple threads (if null then a
	 *        default one is used).
	 * @param aExecutionStrategy The {@link ExecutionStrategy} to be used when
	 *        firing {@link ActionEvent} instance (if null then the default
	 *        {@link ExecutionStrategy#SEQUENTIAL} is used).
	 */
	public ObservableConnectionRequestReceiver( ConnectionDatagramsReceiver aConnectionReceiver, EventMetaData aEventMetaData, Object aSource, ExecutorService aExecutorService, ExecutionStrategy aExecutionStrategy ) {
		_connectionReceiver = aConnectionReceiver;
		_eventMetaData = aEventMetaData;
		_source = toSource();
		_observable = new ConnectionObservable( aExecutorService, aExecutionStrategy );
	}

	/**
	 * Constructs a {@link ObservableConnectionRequestReceiver} with the given
	 * attributes.
	 * 
	 * @param aConnectionReceiver The {@link ConnectionDatagramsReceiver} to
	 *        which the connection method calls are to be delegated to.
	 * @param aExecutorService The executor service to be used when firing
	 *        {@link ActionEvent} instances in multiple threads (if null then a
	 *        default one is used).
	 * @param aExecutionStrategy The {@link ExecutionStrategy} to be used when
	 *        firing {@link ActionEvent} instance (if null then the default
	 *        {@link ExecutionStrategy#SEQUENTIAL} is used).
	 */
	public ObservableConnectionRequestReceiver( ConnectionDatagramsReceiver aConnectionReceiver, ExecutorService aExecutorService, ExecutionStrategy aExecutionStrategy ) {
		_connectionReceiver = aConnectionReceiver;
		_eventMetaData = new EventMetaData( this.getClass() );
		_source = toSource();
		_observable = new ConnectionObservable( aExecutorService, aExecutionStrategy );
	}

	/**
	 * Constructs a {@link ObservableConnectionRequestReceiver} with the given
	 * attributes.
	 * 
	 * @param aConnectionReceiver The {@link ConnectionDatagramsReceiver} to
	 *        which the connection method calls are to be delegated to.
	 * @param aSource The source instance to be used when firing events in case
	 *        the source is to be different from this class' instance.
	 */
	public ObservableConnectionRequestReceiver( ConnectionDatagramsReceiver aConnectionReceiver, Object aSource ) {
		_connectionReceiver = aConnectionReceiver;
		_eventMetaData = new EventMetaData( this.getClass() );
		_source = toSource();
		_observable = new ConnectionObservable();
	}

	/**
	 * Constructs a {@link ObservableConnectionRequestReceiver} with the given
	 * attributes.
	 * 
	 * @param aConnectionReceiver The {@link ConnectionDatagramsReceiver} to
	 *        which the connection method calls are to be delegated to.
	 * @param aSource The source instance to be used when firing events in case
	 *        the source is to be different from this class' instance.
	 * @param aExecutorService The executor service to be used when firing
	 *        {@link ActionEvent} instances in multiple threads (if null then a
	 *        default one is used).
	 * @param aExecutionStrategy The {@link ExecutionStrategy} to be used when
	 *        firing {@link ActionEvent} instance (if null then the default
	 *        {@link ExecutionStrategy#SEQUENTIAL} is used).
	 */
	public ObservableConnectionRequestReceiver( ConnectionDatagramsReceiver aConnectionReceiver, Object aSource, ExecutorService aExecutorService, ExecutionStrategy aExecutionStrategy ) {
		_connectionReceiver = aConnectionReceiver;
		_eventMetaData = new EventMetaData( this.getClass() );
		_source = toSource();
		_observable = new ConnectionObservable( aExecutorService, aExecutionStrategy );
	}

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int available() throws IOException {
		return _connectionReceiver.available();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void close() throws IOException {
		try {
			_observable.fireEvent( new CloseEvent<>( _eventMetaData, _source ) );
		}
		catch ( VetoException aException ) {
			throw new VetoRuntimeException( aException );
		}
		_connectionReceiver.close();
		try {
			_observable.fireEvent( new ClosedEvent( _eventMetaData, _source ) );
		}
		catch ( VetoException ignore ) {
			/* Cannot happen here */
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void destroy() {
		if ( !_isDestroyed ) {
			_isDestroyed = true;
			try {
				close();
			}
			catch ( IOException e ) {
				LOGGER.log( Level.WARNING, "Unable to close malfunctioning connection.", e );
			}
			_connectionReceiver = null;
			_observable.clear();
			_observable = null;
			_source = null;
			_eventMetaData = null;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ConnectionStatus getConnectionStatus() {
		return _connectionReceiver.getConnectionStatus();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean hasObserver( ConnectionRequestObserver aObserver ) {
		return _observable.hasObserver( aObserver );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isClosable() {
		return _connectionReceiver.isClosable();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isClosed() {
		return _connectionReceiver.isClosed();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isOpenable( CON aConnection ) {
		return _connectionReceiver.isOpenable( aConnection );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isOpened() {
		return _connectionReceiver.isOpened();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void open( CON aConnection ) throws IOException {
		try {
			_observable.fireEvent( new OpenEvent<>( _eventMetaData, _source ) );
		}
		catch ( VetoException aException ) {
			throw new IOException( aException );
		}
		_connectionReceiver.open( aConnection );
		try {
			_observable.fireEvent( new OpenedEvent<>( _eventMetaData, _source ) );
		}
		catch ( VetoException e ) {
			/* Cannot happen here */
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public DATA receive() throws IOException {
		return _connectionReceiver.receive();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean subscribeObserver( ConnectionRequestObserver aObserver ) {
		return _observable.subscribeObserver( aObserver );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean unsubscribeObserver( ConnectionRequestObserver aObserver ) {
		return _observable.unsubscribeObserver( aObserver );
	}

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

	@SuppressWarnings("unchecked")
	private CON toSource() {
		try {
			final Field theField = getClass().getField( "_source" );
			if ( theField.getGenericType() instanceof Class theClass ) {
				return theClass.isAssignableFrom( getClass() ) ? (CON) this : null;
			}

		}
		catch ( Exception ignore ) { /* ignore */ }
		return null;
	}

	// /////////////////////////////////////////////////////////////////////////
	// INNER CLASSES:
	// /////////////////////////////////////////////////////////////////////////

	private class ConnectionObservable extends AbstractObservable, ConnectionEvent> {

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

		private final ExecutionStrategy _executionStrategy;

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

		/**
		 * Instantiates a new connection observable.
		 */
		public ConnectionObservable() {
			_executionStrategy = ExecutionStrategy.SEQUENTIAL;
		}

		/**
		 * Instantiates a new connection observable.
		 *
		 * @param aExecutorService the executor service
		 * @param aExecutionStrategy the execution strategy
		 */
		public ConnectionObservable( ExecutorService aExecutorService, ExecutionStrategy aExecutionStrategy ) {
			super( aExecutorService );
			_executionStrategy = ( aExecutionStrategy != null ) ? aExecutionStrategy : ExecutionStrategy.SEQUENTIAL;
		}

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

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void clear() {
			super.clear();
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public boolean isEmpty() {
			return super.isEmpty();
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int size() {
			return super.size();
		}

		// /////////////////////////////////////////////////////////////////////
		// HOOKS:
		// /////////////////////////////////////////////////////////////////////

		/**
		 * Same as {@link #fireEvent(ConnectionStatusEvent, ExecutionStrategy)}
		 * with a predefined {@link ExecutionStrategy}.
		 *
		 * @param aEvent the event to be fired.
		 * 
		 * @return Returns true, if dispatching the event was successful.
		 * 
		 * @throws VetoException Thrown in case there was a veto.
		 * 
		 * @see #fireEvent(ConnectionStatusEvent, ExecutionStrategy)
		 */
		protected boolean fireEvent( ConnectionEvent aEvent ) throws VetoException {
			return super.fireEvent( aEvent, _executionStrategy );
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		@SuppressWarnings("unchecked")
		protected boolean fireEvent( ConnectionEvent aEvent, ConnectionRequestObserver aObserver, ExecutionStrategy aExecutionStrategy ) throws Exception {
			if ( aEvent instanceof OpenEvent ) {
				aObserver.onOpenEvent( (OpenEvent) aEvent );
			}
			else if ( aEvent instanceof CloseEvent ) {
				aObserver.onCloseEvent( (CloseEvent) aEvent );
			}
			else if ( aEvent instanceof OpenedEvent ) {
				aObserver.onOpendEvent( (OpenedEvent) aEvent );
			}
			else if ( aEvent instanceof ClosedEvent ) {
				aObserver.onClosedEvent( (ClosedEvent) aEvent );
			}
			aObserver.onEvent( (ConnectionEvent) aEvent );
			return true;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy