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

org.refcodes.remoting.RemoteServer Maven / Gradle / Ivy

Go to download

Artifact with proxy functionality for plain remote procedure calls (RPC), a POJO alternative to RMI.

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")
// 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.remoting;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.refcodes.component.DigestException;
import org.refcodes.controlflow.ControlFlowUtility;
import org.refcodes.controlflow.RetryTimeout;
import org.refcodes.data.IoTimeout;
import org.refcodes.data.RetryLoopCount;
import org.refcodes.data.SleepLoopTime;
import org.refcodes.exception.ExceptionAccessor;
import org.refcodes.exception.Trap;
import org.refcodes.exception.VetoException;
import org.refcodes.exception.VetoException.VetoRuntimeException;
import org.refcodes.generator.Generator;
import org.refcodes.generator.UniqueIdGenerator;
import org.refcodes.mixin.BusyAccessor;

/**
 * A {@link RemoteServer} promotes subjects to be operated on by
 * {@link RemoteClient} instances.
 */
public class RemoteServer extends AbstractRemote {

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

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

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

	private InstanceHandler _instanceHandler = new InstanceHandler();
	private Generator _instanceIdGenerator = new UniqueIdGenerator( INSTANCE_ID_LENGTH );

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

	/**
	 * Default constructor of the {@link RemoteServer} will use a default
	 * {@link ExecutorService}.
	 */
	public RemoteServer() {
		super( null );
	}

	/**
	 * Instantiates a new {@link RemoteServer} instance with the given
	 * {@link ExecutorService} to be used.
	 *
	 * @param aExecutorService The {@link ExecutorService} to be used.
	 */
	public RemoteServer( ExecutorService aExecutorService ) {
		super( aExecutorService );
	}

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void clear() {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		signOffAllSubjects();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void close() throws IOException {
		if ( IS_LOG_DEBUG_ENABLED ) {
			LOGGER.info( "CLOSE called on <" + getClass().getName() + ">." );
		}
		close( null );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isBusy() {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		return _instanceHandler.isBusy();
	}

	/**
	 * Returns true if the provided subject is contained inside this
	 * {@link RemoteServer}.
	 * 
	 * @param aSubject The subject to be tested if it is contained inside the
	 *        {@link RemoteServer}.
	 * 
	 * @return True if the given subject is contained inside the
	 *         {@link RemoteServer}.
	 */
	public boolean hasSubject( Object aSubject ) {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		return _instanceHandler.hasSubject( aSubject );
	}

	/**
	 * Returns an (immutable) iterator containing all the proxy objects
	 * previously being published. Use the {@link #signOffSubject(Object)}
	 * method in order to remove a published subject.
	 * 
	 * @return An iterator containing the published proxy objects.
	 */
	public Iterator subjects() {
		synchronized ( _instanceHandler ) {
			final List thjeList = new ArrayList<>( _instanceHandler.getSubjects() );
			return thjeList.iterator();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isEmpty() {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		return _instanceHandler.isEmpty();
	}

	/**
	 * Publishes an object to any {@link RemoteClient} connected to the
	 * {@link RemoteServer}.
	 * 
	 * @param aSubject A subject being published for inter-process communication
	 *        such as remote procedure calls or remote method invocations.
	 * 
	 * @return True is returned if the subject could be published, else false is
	 *         returned
	 * 
	 * @throws IOException Thrown in case opening or accessing an open line
	 *         (connection, junction, link) caused problems.
	 */
	public boolean publishSubject( Object aSubject ) throws IOException {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() && !isOpened() );
		synchronized ( _instanceHandler ) {
			SubjectDescriptor eObjDescriptor;
			final Iterator e = _instanceHandler.subjectDescriptors();
			while ( e.hasNext() ) {
				eObjDescriptor = e.next();
				if ( eObjDescriptor.getSubject() == aSubject ) {
					return false;
				}
			}
			if ( !_instanceHandler.hasSubject( aSubject ) ) {
				String theInstanceId = null;

				synchronized ( _instanceIdGenerator ) {
					if ( _instanceIdGenerator.hasNext() ) {
						theInstanceId = _instanceIdGenerator.next();
					}
					else {
						throw new IllegalStateException( "The instance ID generator in use is unable to produce more instance IDs." );
					}
				}
				if ( _instanceHandler.hasInstanceId( theInstanceId ) ) {
					throw new DuplicateInstanceIdRuntimeException( "The instance ID generator in use produces duplicate instance IDs." );
				}

				final SubjectInstanceDescriptor theSubjectInstanceDescriptor = new SubjectInstanceDescriptor( aSubject, theInstanceId );
				final ClassDescriptor classDescriptor = new ClassDescriptor( aSubject.getClass(), theInstanceId );

				if ( _instanceHandler.hasMethodReplyDescriptor( theInstanceId ) ) {
					throw new DuplicateInstanceIdRuntimeException( "The instance ID <" + theInstanceId + "> is already in use by the internal instance handler." );
				}

				final PublishSubjectMessage thePublishSubjectJob = new PublishSubjectMessage();
				thePublishSubjectJob.setClassDescriptor( classDescriptor );
				final PublishSubjectReplyMessage theMethodReplyRemotingJob = new PublishSubjectReplyMessage();
				theMethodReplyRemotingJob.setInstanceId( theInstanceId );
				theMethodReplyRemotingJob.setHasReply( false );
				_instanceHandler.addReplyDescriptor( theMethodReplyRemotingJob, theInstanceId );

				try {
					toReceiver( thePublishSubjectJob );
				}
				catch ( IOException aException ) {
					LOGGER.log( Level.WARNING, Trap.asMessage( aException ), aException );
					_instanceHandler.removeReplyDescriptor( theInstanceId );
					if ( aException.getCause() instanceof IOException ) {
						closeOnException();
					}
					throw aException;
				}

				final RetryTimeout theRetryTimeout = new RetryTimeout( WAIT_FOR_REPLY_TIMEOUT, WAIT_FOR_REPLY_LOOPS );
				while ( ( !theMethodReplyRemotingJob.hasReply() ) && theRetryTimeout.hasNextRetry() && isOpened() ) {
					if ( IS_LOG_DEBUG_ENABLED ) {
						LOGGER.info( "Wait loop <" + theRetryTimeout.getRetryCount() + "> while waiting for method reply for <" + WAIT_FOR_REPLY_LOOPS + "> ms." );
					}
					theRetryTimeout.nextRetry( theMethodReplyRemotingJob );
				}
				_instanceHandler.removeReplyDescriptor( theInstanceId );

				if ( theMethodReplyRemotingJob.isException() ) {
					throw new InvalidMethodReplyRuntimeException( "Unexpected reply when publishing a class descriptor. Sorry - operation aborted!" );
				}

				if ( theMethodReplyRemotingJob.isReturnValue() ) {
					if ( theMethodReplyRemotingJob.getReturnValue() instanceof Boolean ) {
						final boolean theReturnValue = ( (Boolean) theMethodReplyRemotingJob.getReturnValue() ).booleanValue();

						if ( theReturnValue ) {
							_instanceHandler.addSubjectDescriptor( theSubjectInstanceDescriptor, theInstanceId );

							onSubjectPublished( theSubjectInstanceDescriptor.getSubject() );
						}
						return theReturnValue;
					}
				}

				if ( !theMethodReplyRemotingJob.hasReply() ) {
					throw new IllegalStateException( "While processing the request for session <" + theMethodReplyRemotingJob.getInstanceId() + "> of instance <" + theMethodReplyRemotingJob.getInstanceId() + "> (" + theMethodReplyRemotingJob.getClass().getSimpleName() + ") a timeout of " + WAIT_FOR_REPLY_TIMEOUT + " ms has been overshot (propably lost the connection)." );
				}

				throw new InvalidMethodReplyRuntimeException( "Unexpected reply when publishing a class descriptor. Sorry - operation aborted!" );
			}

			return false;
		}
	}

	/**
	 * Tries to sign off the (previously published) subject, this can be vetoed
	 * in case the subject is still in use by a {@link RemoteClient}.
	 *
	 * @param aSubject Description is currently not available!
	 * 
	 * @return True if the removal of the subject has been successful. If the
	 *         subject has not been found then false is returned. If a
	 *         {@link RemoteClient} threw a {@link VetoException} then the
	 *         sign-off is aborted.
	 * 
	 * @throws VetoException the veto exception
	 * @throws IOException Thrown in case opening or accessing an open line
	 *         (connection, junction, link) caused problems.
	 */
	public boolean signOffSubject( Object aSubject ) throws IOException, VetoException {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		final String theSubjectId = toSubjectId( aSubject );
		if ( theSubjectId == null ) {
			return false;
		}
		return signoffInstanceDescriptor( new InstanceDescriptor( theSubjectId ), -1 );
	}

	/**
	 * Signs off the (previously published) subject, this be vetoed even in case
	 * the subject is still in use by a {@link RemoteClient}, but the veto will
	 * only delay the sign off by the given timeout.
	 * 
	 * @param aSubject The subject to be signed off.
	 * @param aTimeoutMillis The timeout to be granted in case the sign-off has
	 *        been vetoed, nevertheless the subject will be signed off after the
	 *        timeout elapsed.
	 * 
	 * @return True if the removal of the subject has been successful. If the
	 *         subject has not been found then false is returned. If a
	 *         {@link RemoteClient} threw a {@link VetoException} then the
	 *         sign-off is aborted.
	 * 
	 * @throws IOException Thrown in case opening or accessing an open line
	 *         (connection, junction, link) caused problems.
	 */
	public boolean signOffSubject( Object aSubject, int aTimeoutMillis ) throws IOException {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		final String theSubjectId = toSubjectId( aSubject );
		if ( theSubjectId == null ) {
			return false;
		}
		try {
			return signoffInstanceDescriptor( new InstanceDescriptor( theSubjectId ), aTimeoutMillis );
		}
		catch ( VetoException aException ) {
			throw new VetoRuntimeException( aException.getMessage(), aException );
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int size() {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		return _instanceHandler.size();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void destroy() {
		if ( !isDestroyed() ) {
			super.destroy();
			_instanceHandler.clear();
			_instanceHandler = null;
			_instanceIdGenerator = null;
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// DEBUG:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * For testing purposes, any job can be manually pushed into the job process
	 * to the receiver.
	 * 
	 * @param aJob The job to be pushed to the receiver.
	 * 
	 * @throws IOException Thrown in case the operation failed due to an I/O
	 *         malfunction such as the input- or output-connection (of an input-
	 *         and output-connection pair) is not available or being
	 *         disconnected.
	 */
	protected void doSendJob( Message aJob ) throws IOException {
		toReceiver( aJob );
	}

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void digest( Message aJob ) throws DigestException {
		try {
			if ( aJob == null ) {
				return;
			}
			// -----------------------------------------------------------------
			// CLOSE CONNECTION JOB:
			// -----------------------------------------------------------------
			if ( aJob instanceof CloseConnectionMessage ) {
				if ( IS_LOG_DEBUG_ENABLED ) {
					LOGGER.info( "Received a close connection job to <" + getClass().getName() + ">; closing connection." );
				}
				close( (CloseConnectionMessage) aJob );
			}
			// -----------------------------------------------------------------
			// PUBLISH SUBJECT REPLY JOB:
			// -----------------------------------------------------------------
			else if ( aJob instanceof PublishSubjectReplyMessage theReplyRemotingJob ) {
				if ( theReplyRemotingJob.getInstanceId() == null ) {
					return;
				}

				final String theInstanceId = theReplyRemotingJob.getInstanceId();
				if ( !_instanceHandler.hasMethodReplyDescriptor( theInstanceId ) ) {
					throw new UnknownInstanceIdRuntimeException( "Expected an instance ID <" + theInstanceId + "> which was not found in order to reply to a request..." );
				}

				final Object tmpReply = _instanceHandler.getMethodReplyDescriptor( theInstanceId );
				if ( !( tmpReply instanceof PublishSubjectReplyMessage ) ) {
					throw new InvalidMethodReplyRuntimeException( "Excpected a <" + PublishSubjectReplyMessage.class.toString() + "> to put the reply in." );
				}
				final PublishSubjectReplyMessage thePublishSubjectReplyJob = (PublishSubjectReplyMessage) tmpReply;
				thePublishSubjectReplyJob.setReply( theReplyRemotingJob );
				synchronized ( thePublishSubjectReplyJob ) {
					thePublishSubjectReplyJob.notifyAll();
				}
			}
			// -----------------------------------------------------------------
			// METHOD REQUEST JOB:
			// -----------------------------------------------------------------
			else if ( aJob instanceof MethodRequestMessage ) {
				final MethodRequest theMethodRequestDescriptor = (MethodRequest) aJob;
				final Reply theMethodReplyDescriptor = pushMethodRequest( theMethodRequestDescriptor );
				if ( theMethodReplyDescriptor == null ) {
					return;
				}
				final MethodReplyMessage theMethodReplyJob = new MethodReplyMessage();
				theMethodReplyJob.setMethodReplyDescriptor( theMethodReplyDescriptor );
				toReceiver( theMethodReplyJob );
			}
			// -----------------------------------------------------------------
			// SIGN-OFF PROXY JOB:
			// -----------------------------------------------------------------
			else if ( aJob instanceof SignOffProxyMessage ) {
				final InstanceId instanceDescriptor = aJob;
				final boolean theReturnValue = serviceSignoffInstanceDescriptor( instanceDescriptor );
				final CancelMethodReplyMessage theCancelReplyRemotingJob = new CancelMethodReplyMessage();
				theCancelReplyRemotingJob.setInstanceId( instanceDescriptor.getInstanceId() );
				theCancelReplyRemotingJob.setException( null );
				theCancelReplyRemotingJob.setReturnValue( theReturnValue );
				theCancelReplyRemotingJob.setHasReply( true );
				try {
					toReceiver( theCancelReplyRemotingJob );
				}
				catch ( IOException aException ) { /* ignore */ }
			}
		}
		catch ( IOException aException ) {
			throw new DigestException( aException );
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected synchronized void close( CloseConnectionMessage aJob ) {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		if ( IS_LOG_DEBUG_ENABLED ) {
			LOGGER.info( "CLOSE called on <" + getClass().getName() + "> with job <" + aJob + ">." );
		}
		if ( !isClosed() ) {
			super.close( aJob );
			final RetryTimeout theRetryTimeout = new RetryTimeout( IoTimeout.NORM.getTimeMillis(), RetryLoopCount.NORM_NUM_RETRY_LOOPS.getValue() );
			while ( ( isBusy() ) && theRetryTimeout.hasNextRetry() && isOpened() ) {
				if ( IS_LOG_DEBUG_ENABLED ) {
					LOGGER.info( "Wait loop <" + theRetryTimeout.getRetryCount() + "> while being BUSY for <" + SleepLoopTime.NORM.getTimeMillis() + "> ms." );
				}
				theRetryTimeout.nextRetry();
			}
			if ( isBusy() ) {
				LOGGER.log( Level.WARNING, "Still being BUSY even after reaching the timeout of <" + IoTimeout.NORM.getTimeMillis() + "> ms, closing connection nonetheless." );
			}
			signOffAllSubjects();
			try {
				super.close();
			}
			catch ( IOException e ) {
				LOGGER.log( Level.WARNING, "Unable to close malfunctioning connection.", e );
			}
			onClosed();
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// SIGNALS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Hook when a subject has been published.
	 * 
	 * @param aSubject The subject being published.
	 */
	protected void onSubjectPublished( Object aSubject ) {}

	/**
	 * Hook when a subject has been signed-off.
	 * 
	 * @param aSubject The subject which has been signed-off.
	 */
	protected void onSubjectSignedOff( Object aSubject ) {}

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

	/**
	 * Signs off the given subject identified by the provided
	 * {@link SubjectDescriptor}.
	 * 
	 * @param aSubject The {@link SubjectDescriptor} describing the subject to
	 *        be signed off.
	 * 
	 * @return True in case there was such a subject which was being removed.
	 * 
	 * @throws IOException Thrown in case opening or accessing an open line
	 *         (connection, junction, link) caused problems.
	 */
	private boolean signOffSubject( SubjectDescriptor aSubject ) throws IOException {
		if ( isClosed() ) {
			return false;
		}

		String eId = null;
		final Iterator e = _instanceHandler.instanceIds();

		while ( e.hasNext() ) {
			eId = e.next();
			if ( _instanceHandler.getSubjectDescriptor( eId ) == aSubject ) {
				break;
			}
		}

		if ( eId == null ) {
			return false;
		}

		boolean theResult;
		try {
			theResult = signoffInstanceDescriptor( new InstanceDescriptor( eId ), 0 );

		}
		catch ( VetoException ve ) {
			theResult = true;
		}
		return theResult;
	}

	/**
	 * Tries to determine the subject ID for the given subject.
	 * 
	 * @param aSubject The subject for which to get the subject ID.
	 * 
	 * @return The subject ID or null if there was none such subject found.
	 */
	private String toSubjectId( Object aSubject ) {
		String eId = null;
		boolean returnValue = false;
		final Iterator e = subjects();

		while ( e.hasNext() ) {
			final Object eObj = e.next();
			if ( eObj == aSubject ) {
				returnValue = true;
			}
		}

		if ( returnValue == false ) {
			return null;
		}

		final Iterator ee = subjects();

		while ( ee.hasNext() ) {
			final Object eObj = ee.next();

			if ( eObj instanceof SubjectInstanceDescriptor ) {
				if ( ( (SubjectInstanceDescriptor) eObj ).getSubject() == aSubject ) {
					eId = ( (SubjectInstanceDescriptor) eObj ).getInstanceId();
				}
			}
		}
		return eId;
	}

	/**
	 * Closes the connection and disposes all proxy controls. This method is
	 * called whenever a regular close failed because of an exception.
	 */
	private void clearOnException() {
		SubjectDescriptor eObjectDescriptor;
		final Iterator e = _instanceHandler.subjectDescriptors();

		while ( e.hasNext() ) {
			eObjectDescriptor = e.next();
			onSubjectSignedOff( eObjectDescriptor.getSubject() );
		}
		_instanceHandler.clear();
	}

	/**
	 * Signs off all subjects.
	 */
	private void signOffAllSubjects() {
		final Iterator e = _instanceHandler.subjectDescriptors();
		SubjectDescriptor eObjDescriptor;
		while ( e.hasNext() ) {
			try {
				eObjDescriptor = e.next();
				signOffSubject( eObjDescriptor );
			}
			catch ( IOException ioe ) {
				LOGGER.log( Level.SEVERE, "Cannot sign off all subjects as of: " + Trap.asMessage( ioe ), ioe );
				if ( ioe.getCause() instanceof IOException ) {
					clearOnException();
				}
			}
		}
		_instanceHandler.clear();
	}

	/**
	 * Close on exception.
	 */
	private void closeOnException() {
		clearOnException();
		SubjectDescriptor eObjectDescriptor;
		final Iterator e = _instanceHandler.subjectDescriptors();
		while ( e.hasNext() ) {
			eObjectDescriptor = e.next();
			onSubjectSignedOff( eObjectDescriptor.getSubject() );
		}
	}

	/**
	 * A request from a {@link RemoteClient} is pushed to the
	 * {@link RemoteServer} using this method.
	 * 
	 * @param aMethodRequestDescriptor An object of type BlueprintMethodRequest
	 *        from which the request can be retrieved.
	 * 
	 * @return Returns a BlueprintMethodReplyDescriptor object containing the
	 *         reply-descriptor of the pushed method
	 * 
	 * @exception IOException Thrown in case opening or accessing an open line
	 *            (connection, junction, link) caused problems.
	 */
	private Reply pushMethodRequest( MethodRequest aMethodRequestDescriptor ) throws IOException {

		if ( aMethodRequestDescriptor == null ) {
			return null;
		}

		final SubjectDescriptor objDescriptor;

		synchronized ( _instanceHandler ) {
			objDescriptor = _instanceHandler.getSubjectDescriptor( aMethodRequestDescriptor.getInstanceId() );
		}

		if ( objDescriptor == null ) {
			return null;
		}

		try {
			final MethodInvokationDaemon theDaemon = new MethodInvokationDaemon( objDescriptor.getSubject(), aMethodRequestDescriptor.getMethodName(), aMethodRequestDescriptor.getArgumentArray(), aMethodRequestDescriptor.getArgumentTypeArray() );
			getExecutorService().execute( theDaemon );

			final RetryTimeout theRetryTimeout = new RetryTimeout( WAIT_FOR_REPLY_TIMEOUT / 5 * 4, WAIT_FOR_REPLY_LOOPS );
			while ( ( !theDaemon.hasResult() ) && theRetryTimeout.hasNextRetry() && isOpened() ) {
				theRetryTimeout.nextRetry( theDaemon );
			}

			if ( !theDaemon.hasResult() ) {
				LOGGER.log( Level.WARNING, "Some timeout has occurred - timeout is ignored..." );
			}

			final Reply theMethodReplyDescriptor = new ReplyDescriptor( theDaemon.getReturnValue(), theDaemon.getException(), aMethodRequestDescriptor );
			return theMethodReplyDescriptor;
		}
		catch ( NoSuchMethodException | IllegalArgumentException nsme ) {
			throw new InvalidMethodRequestRuntimeException( nsme );
		}
	}

	/**
	 * Signs off an instance previously published using the
	 * {@link #publishSubject(Object)} method. The toolkit uses the
	 * corresponding {@link #signOffSubject(Object))}.
	 * 
	 * @param aInstanceDescriptor An object of type {@link InstanceId}
	 *        containing the information needed to sign-off an instance.
	 * 
	 * @return True is returned if the instanceDescriptor could be signed off,
	 *         else false is returned
	 */
	private boolean serviceSignoffInstanceDescriptor( InstanceId aInstanceDescriptor ) {

		if ( aInstanceDescriptor == null ) {
			throw new NullPointerException( "Expected an object of type  instead of a null value in argument ." );
		}

		if ( aInstanceDescriptor.getInstanceId() == null ) {
			throw new NullPointerException( "Expected an object of type  instead of a null value when retrieving the instance id of argument ." );
		}

		final String instanceId = aInstanceDescriptor.getInstanceId();

		if ( _instanceHandler.hasSignedOffInstanceId( instanceId ) ) {
			return true;
		}
		// ---------------------------------------------------------------------
		// Get the subject instance instead of testing for its existence in
		// order to avoid thread race conditions:
		// ---------------------------------------------------------------------
		SubjectDescriptor theSubjectDescriptor = _instanceHandler.getSubjectDescriptor( instanceId );
		// ---------------------------------------------------------------------
		if ( theSubjectDescriptor == null ) {
			final RetryTimeout theRetryTimeout = new RetryTimeout( IoTimeout.MIN.getTimeMillis(), RetryLoopCount.NORM_NUM_RETRY_LOOPS.getValue() );
			while ( !isClosed() && theRetryTimeout.hasNextRetry() && theSubjectDescriptor == null ) {
				theRetryTimeout.nextRetry();
				theSubjectDescriptor = _instanceHandler.getSubjectDescriptor( instanceId );
			}
			if ( theSubjectDescriptor == null && !isClosed() ) {
				LOGGER.log( Level.WARNING, "Expected known instance ID, but instance ID  <" + instanceId + "> is not known by the server (probably all instances have been signed off)." );
				// throw new UnknownInstanceIdRuntimeException( "Expected a
				// known instance ID in argument  (probably
				// all instances have been signed off), the unknown instance ID
				// is <" + instanceId + ">." );
			}
		}
		if ( theSubjectDescriptor != null ) {
			onSubjectSignedOff( theSubjectDescriptor.getSubject() );
		}
		return true;
	}

	/**
	 * Signs off an instance previously published using the
	 * {@link #publishSubject(Object)} method.
	 *
	 * @param aInstanceDescriptor An object of type {@link InstanceId}
	 *        containing the information needed to sign-off an instance.
	 * @param aTimeoutMillis If greater than 0 then the timeout that the subject
	 *        is being signed-off even when a {@link VetoException} is thrown by
	 *        the {@link RemoteClient}. In case the Timeout is 0 then the
	 *        subject will be signed off immediately. In case the timeout is -1,
	 *        then the {@link VetoException} (if any) is forwarded and the
	 *        sign-off operation is aborted.
	 * 
	 * @return True is returned if the {@link InstanceId} could be signed off,
	 *         else false is returned
	 * 
	 * @throws IOException Thrown in case opening or accessing an open line
	 *         (connection, junction, link) caused problems.
	 * @throws VetoException Thrown to signal that an operation is being vetoed
	 *         by a third party observing the invocation of the given operation.
	 */
	private boolean signoffInstanceDescriptor( InstanceId aInstanceDescriptor, int aTimeoutMillis ) throws IOException, VetoException {

		// synchronized(_instanceHandler.getMonitor()) {
		if ( aInstanceDescriptor == null ) {
			return false;
		}

		if ( aInstanceDescriptor.getInstanceId() == null ) {
			return false;
		}

		final String theInstanceId = aInstanceDescriptor.getInstanceId();
		if ( !_instanceHandler.hasInstanceId( theInstanceId ) ) {
			return false;
		}

		if ( !_instanceHandler.hasMethodReplyDescriptor( theInstanceId ) ) {
			return false;
		}
		if ( _instanceHandler.hasSignedOffInstanceId( theInstanceId ) ) {
			throw new DuplicateInstanceIdRuntimeException( "The instance <" + theInstanceId + "> of the provided instance descriptorin is already in use and being used up." );
		}

		final SignOffSubjectMessage theSignoffSubjectJob = new SignOffSubjectMessage();
		theSignoffSubjectJob.setInstanceId( aInstanceDescriptor.getInstanceId() );
		theSignoffSubjectJob.setReadTimeoutMillis( aTimeoutMillis );
		final PublishSubjectReplyMessage thePublishSubjectReplyJob = new PublishSubjectReplyMessage();
		thePublishSubjectReplyJob.setInstanceId( theInstanceId );
		thePublishSubjectReplyJob.setHasReply( false );
		synchronized ( _instanceHandler ) {
			_instanceHandler.addReplyDescriptor( thePublishSubjectReplyJob, theInstanceId );
		}
		toReceiver( theSignoffSubjectJob );

		final RetryTimeout theRetryTimeout = new RetryTimeout( WAIT_FOR_REPLY_TIMEOUT, WAIT_FOR_REPLY_LOOPS );
		while ( ( !thePublishSubjectReplyJob.hasReply() ) && theRetryTimeout.hasNextRetry() && isOpened() ) {
			if ( IS_LOG_DEBUG_ENABLED ) {
				LOGGER.info( "Wait loop <" + theRetryTimeout.getRetryCount() + "> while waiting for method reply for <" + WAIT_FOR_REPLY_LOOPS + "> ms." );
			}
			theRetryTimeout.nextRetry( thePublishSubjectReplyJob );
		}

		// ---------------------------------------------------------------------
		// Wait till the monitor is notified or timeout has run out of time:
		// ---------------------------------------------------------------------
		synchronized ( _instanceHandler ) {
			_instanceHandler.removeReplyDescriptor( theInstanceId );
		}
		// ---------------------------------------------------------------------

		if ( !thePublishSubjectReplyJob.hasReply() ) {
			// @formatter:off
			// throw new IOTimeoutException( WAIT_FOR_REPLY_TIMEOUT, "While processing the request a timeout of " + WAIT_FOR_REPLY_TIMEOUT + " ms has been overshot; probably lost the connection (you probably should close the connection)." );
			// @formatter:on
			throw new IllegalStateException( "While processing the request for session <" + thePublishSubjectReplyJob.getInstanceId() + "> of instance <" + thePublishSubjectReplyJob.getInstanceId() + "> (" + thePublishSubjectReplyJob.getClass().getSimpleName() + ") a timeout of " + WAIT_FOR_REPLY_TIMEOUT + " ms has been overshot (propably lost the connection)." );
		}

		if ( thePublishSubjectReplyJob.isException() ) {
			final Throwable theException = thePublishSubjectReplyJob.getException();
			if ( theException instanceof VetoException ) {
				if ( aTimeoutMillis == 0 ) {
					/* ignore, sign-off immediately */
				}
				else if ( aTimeoutMillis > 0 ) {
					synchronized ( this ) {
						try {
							Thread.sleep( aTimeoutMillis );
						}
						catch ( InterruptedException ignored ) {}
					}
				}
				else if ( aTimeoutMillis == -1 ) {
					throw (VetoException) theException;
				}
			}
			else {
				throw new InvalidMethodReplyRuntimeException( "Unexpected reply when publishing a subject." );
			}
		}

		if ( thePublishSubjectReplyJob.isReturnValue() ) {
			if ( thePublishSubjectReplyJob.getReturnValue() instanceof Boolean ) {
				final SubjectDescriptor theSubjectDescriptor = _instanceHandler.removeSubjectDescriptor( theInstanceId );
				onSubjectSignedOff( theSubjectDescriptor.getSubject() );
				return ( (Boolean) thePublishSubjectReplyJob.getReturnValue() ).booleanValue();
			}
		}
		return false;
	}

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

	/**
	 * Helper class for providing synchronized access to vital data structures.
	 */
	private static class InstanceHandler implements BusyAccessor {

		private final Map _instanceIdsToSubjectDescriptors = new HashMap<>();
		private final Map _instanceIdToMethodReplyDescriptor = new HashMap<>();
		private final List _subjects = new ArrayList<>();
		private final Set _signedOffInstanceIds = Collections.newSetFromMap( new WeakHashMap<>() );

		/**
		 * Adds a reply descriptor to this instance associated to the given
		 * instance id.
		 *
		 * @param aMethodReplyDescriptor An object of type
		 *        BlueprintMethodReplyDescriptor to be added.
		 * @param aInstanceId The instanceId to which the reply descriptor is to
		 *        be associated.
		 * 
		 * @return True if the operation has been performed successfully.
		 */
		boolean addReplyDescriptor( Reply aMethodReplyDescriptor, String aInstanceId ) {
			if ( PERFORM_CONSISTENCY_CHECKS ) {
				if ( hasMethodReplyDescriptor( aInstanceId ) ) {
					throw new IllegalArgumentException( "The instance ID <" + aInstanceId + "> is already in use by a method reply descriptor; an unused instance ID must be provided." );
				}
				if ( hasSignedOffInstanceId( aInstanceId ) ) {
					throw new IllegalArgumentException( "The instance ID <" + aInstanceId + "> is already invalidated; an unused instance ID must be provided." );
				}
			}
			else {
				if ( hasMethodReplyDescriptor( aInstanceId ) ) {
					return false;
				}
				if ( hasSignedOffInstanceId( aInstanceId ) ) {
					return false;
				}
			}

			_instanceIdToMethodReplyDescriptor.put( aInstanceId, aMethodReplyDescriptor );
			return true;
		}

		/**
		 * Adds an instance id to the list of signed off instance id's.
		 * 
		 * @param instanceId The instance id to be added
		 * 
		 * @return True if the operation has been performed successfully.
		 */
		boolean addSignedOffInstanceId( String instanceId ) {
			synchronized ( this ) {
				return ( _signedOffInstanceIds.add( instanceId ) );
			}
		}

		/**
		 * Clears the elements contained in this instance.
		 */
		void clear() {
			clearSignedOffInstanceIds();
			clearSubjectDescriptos();
			clearReplyDescriptors();
		}

		/**
		 * Returns the list of published subject instances.
		 * 
		 * @return The list of published subjects.
		 */
		List getSubjects() {
			return _subjects;
		}

		/**
		 * Returns the reply descriptor associated to the given instance id.
		 *
		 * @param aInstanceId The instance id which's reply descriptor is to be
		 *        returned.
		 * 
		 * @return An object of type BlueprintMethodReplyDescriptor which is
		 *         associated to the given instance id.
		 */
		Reply getMethodReplyDescriptor( String aInstanceId ) {
			if ( PERFORM_CONSISTENCY_CHECKS ) {
				if ( !hasMethodReplyDescriptor( aInstanceId ) ) {
					throw new IllegalArgumentException( "The instance ID <" + aInstanceId + "> is already in use by a method reply descriptor; an unused instance ID must be provided." );
				}
				if ( hasSignedOffInstanceId( aInstanceId ) ) {
					throw new IllegalArgumentException( "The instance ID <" + aInstanceId + "> is already invalidated; an unused instance ID must be provided." );
				}
			}
			else {
				if ( !hasMethodReplyDescriptor( aInstanceId ) ) {
					return null;
				}
				if ( hasSignedOffInstanceId( aInstanceId ) ) {
					return null;
				}
			}
			return _instanceIdToMethodReplyDescriptor.get( aInstanceId );
		}

		/**
		 * Returns true if the instance id is in use or was in use by this
		 * instance.
		 * 
		 * @param instanceId The instance id to be tested.
		 * 
		 * @return True if the given instance id is in use or has been in use.
		 */
		boolean hasInstanceId( String instanceId ) {
			return ( ( _instanceIdsToSubjectDescriptors.containsKey( instanceId ) ) || _signedOffInstanceIds.contains( instanceId ) );
		}

		/**
		 * Tests if the provided object is contained in any of the object
		 * descriptors managed by this instance.
		 * 
		 * @param obj The object which's existence is to be tested.
		 * 
		 * @return True if the object has been found.
		 */
		boolean hasSubject( Object obj ) {
			return _subjects.contains( obj );
		}

		/**
		 * Tests if the given isntance id is in the list of the waiting reply
		 * descriptors.
		 * 
		 * @param instanceId The instance id for which the existence of a reply
		 *        descriptor is to be tested.
		 * 
		 * @return True if there is a reply descriptor assosiated to the
		 *         instance id.
		 */
		boolean hasMethodReplyDescriptor( String instanceId ) {

			return _instanceIdToMethodReplyDescriptor.containsKey( instanceId );
		}

		/**
		 * Tests if the provided instance id has already been used and is not in
		 * use any more.
		 * 
		 * @param instanceId Thje id which is to be tested.
		 * 
		 * @return True is returned if the provided instance id has already been
		 *         used, else false is returned
		 */
		boolean hasSignedOffInstanceId( String instanceId ) {
			return ( _signedOffInstanceIds.contains( instanceId ) );
		}

		/**
		 * Returns an iterator containing all the instance id's currently in
		 * use.
		 * 
		 * @return An iterator containing all the instance id's currently in
		 *         use.
		 */
		Iterator instanceIds() {
			return _instanceIdsToSubjectDescriptors.keySet().iterator();
		}

		/**
		 * Checks if is empty.
		 *
		 * @return Description is currently not available!
		 */
		boolean isEmpty() {
			return _subjects.isEmpty();
		}

		/**
		 * Adds an object descriptor which is associated to the given instance
		 * id.
		 *
		 * @param theObjectDescriptor The object descriptor to be added.
		 * @param aInstanceId The instance id to which the object descriptor is
		 *        to be associated.
		 * 
		 * @return True if the operation has been performed successfully.
		 */
		boolean addSubjectDescriptor( SubjectDescriptor theObjectDescriptor, String aInstanceId ) {

			synchronized ( this ) {

				if ( !_instanceIdsToSubjectDescriptors.containsKey( aInstanceId ) ) {
					if ( PERFORM_CONSISTENCY_CHECKS ) {

						if ( hasInstanceId( aInstanceId ) ) {
							throw new IllegalArgumentException( "The instance ID <" + aInstanceId + "> is already in use by a object descriptor; an unused instance ID must be provided." );
						}
						if ( hasSubject( theObjectDescriptor.getSubject() ) ) {
							throw new IllegalArgumentException( "The subject to be related to instance ID <" + aInstanceId + "> has already been added." );
						}
					}
					else {
						if ( hasInstanceId( aInstanceId ) ) {
							return false;
						}

						if ( hasSubject( theObjectDescriptor.getSubject() ) ) {
							return false;
						}
					}
					_instanceIdsToSubjectDescriptors.put( aInstanceId, theObjectDescriptor );
					_subjects.add( theObjectDescriptor.getSubject() );
					return true;
				}
			}
			return false;
		}

		/**
		 * Returns an iterator of the objects descriptors.
		 * 
		 * @return An object of type java.util.Iterator containing the objects
		 *         found in the object descriptors.
		 */
		Iterator subjectDescriptors() {
			synchronized ( this ) {
				final List returnList = new ArrayList<>( _instanceIdsToSubjectDescriptors.values() );
				return returnList.iterator();
			}
		}

		/**
		 * Returns true if an object descriptor is associated to the provided
		 * instance id.
		 *
		 * @param aInstanceId the instance id
		 * 
		 * @return True if the instance id is used and an object descriptor
		 *         associated to it.
		 */
		// boolean hasSubject( String instanceId ) {
		// return (_instanceIdsToSubjectDescriptors.containsKey( instanceId ));
		// }

		/**
		 * Returns the object descriptor is associated to the provided instance
		 * id.
		 * 
		 * @param aInstanceId The instance id of the object descriptor.
		 * 
		 * @return The subject or null if there is none such object descriptor.
		 */
		SubjectDescriptor getSubjectDescriptor( String aInstanceId ) {
			return ( _instanceIdsToSubjectDescriptors.get( aInstanceId ) );
		}

		/**
		 * Removes an object descriptor identified by an instance id.
		 *
		 * @param aInstanceId The instance id to which the object descriptor is
		 *        associated which is to be removed.
		 * 
		 * @return An object of type BlueprintObjectDescriptor being the object
		 *         descriptor which has been removed.
		 */
		SubjectDescriptor removeSubjectDescriptor( String aInstanceId ) {
			synchronized ( this ) {
				if ( _instanceIdsToSubjectDescriptors.containsKey( aInstanceId ) ) {
					final SubjectDescriptor theObjectDescriptor = _instanceIdsToSubjectDescriptors.remove( aInstanceId );
					if ( PERFORM_CONSISTENCY_CHECKS ) {
						if ( !_subjects.contains( theObjectDescriptor.getSubject() ) ) {
							throw new IllegalStateException( "The subject described by the object descriptor associated to the instance ID <" + aInstanceId + "> is unknown; there is an illage state regarding the subject and the object descriptor data structures." );
						}
						if ( hasSignedOffInstanceId( aInstanceId ) ) {
							throw new IllegalArgumentException( "The instance ID <" + aInstanceId + "> is already invalidated; an unused instance ID must be provided." );
						}
					}
					else {

						if ( !_subjects.contains( theObjectDescriptor.getSubject() ) ) {
							return null;
						}
						if ( hasSignedOffInstanceId( aInstanceId ) ) {
							return null;
						}
					}
					_subjects.remove( theObjectDescriptor.getSubject() );
					addSignedOffInstanceId( aInstanceId );
					return theObjectDescriptor;
				}
			}

			return null;
		}

		/**
		 * Removes all object descriptors from this instance.
		 */
		private void clearSubjectDescriptos() {
			synchronized ( this ) {
				_instanceIdsToSubjectDescriptors.clear();
				_subjects.clear();
			}
		}

		/**
		 * Removes the reply descriptor from this instance which has been
		 * associated to the given instance id.
		 *
		 * @param aInstanceId The instanceId which's reply descriptor is to be
		 *        removed.
		 * 
		 * @return The reply descriptor which has been removed.
		 */
		Reply removeReplyDescriptor( String aInstanceId ) {
			if ( PERFORM_CONSISTENCY_CHECKS ) {
				if ( !hasMethodReplyDescriptor( aInstanceId ) ) {
					throw new IllegalArgumentException( "The instance ID <" + aInstanceId + "> is unknown by any method reply descriptor; a valid instance ID must be provided." );
				}
				if ( hasSignedOffInstanceId( aInstanceId ) ) {
					throw new IllegalArgumentException( "The instance ID <" + aInstanceId + "> is already invalidated; an unused instance ID must be provided." );
				}
			}
			else {
				if ( !hasMethodReplyDescriptor( aInstanceId ) ) {
					return null;
				}
				if ( hasSignedOffInstanceId( aInstanceId ) ) {
					return null;
				}
			}
			return _instanceIdToMethodReplyDescriptor.remove( aInstanceId );
		}

		/**
		 * Size.
		 *
		 * @return Description is currently not available!
		 */
		int size() {
			return _subjects.size();
		}

		/**
		 * Clears the list of reply descriptors.
		 */
		private void clearReplyDescriptors() {
			_instanceIdToMethodReplyDescriptor.clear();
		}

		/**
		 * Removes all instance id's from the list of signed-off instance IDs.
		 */
		private void clearSignedOffInstanceIds() {

			synchronized ( this ) {
				_signedOffInstanceIds.clear();
			}
		}

		/**
		 * Checks if is busy.
		 *
		 * @return true, if is busy
		 */
		@Override
		public synchronized boolean isBusy() {
			return ( !_instanceIdToMethodReplyDescriptor.isEmpty() );
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHOD INVOCATION DAEMON:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Daemon handling inter-process method invocation calls.
	 */
	private class MethodInvokationDaemon implements Runnable {

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

		private final Object[] _argumentArray;
		private boolean _hasResult = false;
		private final Method _method;
		private final Object _providedObject;
		private Throwable _returnThrowable = null;
		private Object _returnValue = null;

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

		/**
		 * Constructs the {@link MethodInvokationDaemon} with the given
		 * attributes identifying that method.
		 *
		 * @param aSubject The subject upon which to invoke the method.
		 * @param aMethodName The name of the method to be invoked.
		 * @param anArgumentArray The arguments (instances) to be passed to the
		 *        method to be invoked.
		 * @param aParameterArray The types of the parameters if the method to
		 *        be invoked.
		 * 
		 * @throws IllegalArgumentException the illegal argument exception
		 * @throws NoSuchMethodException the no such method exception
		 */
		MethodInvokationDaemon( Object aSubject, String aMethodName, Object[] anArgumentArray, Class[] aParameterArray ) throws NoSuchMethodException {
			_method = aSubject.getClass().getMethod( aMethodName, aParameterArray );
			_providedObject = aSubject;
			_argumentArray = anArgumentArray;

			if ( _argumentArray != null ) {
				for ( int i = 0; i < _argumentArray.length; i++ ) {
					_argumentArray[i] = SerializeUtility.toSerializable( _argumentArray[i] );
				}
			}
		}

		/**
		 * Run.
		 */
		@Override
		public void run() {
			try {
				final Object returnValue = _method.invoke( _providedObject, _argumentArray );
				_returnValue = SerializeUtility.toSerializable( returnValue );
			}
			catch ( InvocationTargetException aInvocationTargetException ) {
				LOGGER.log( Level.WARNING, Trap.asMessage( aInvocationTargetException ), aInvocationTargetException );
				_returnThrowable = aInvocationTargetException.getTargetException();
			}
			catch ( IllegalAccessException aIllegalAccessException ) {
				LOGGER.log( Level.WARNING, Trap.asMessage( aIllegalAccessException ), aIllegalAccessException );
				throw new InvalidMethodRequestRuntimeException( aIllegalAccessException );
			}
			catch ( Throwable aThrowable ) {
				LOGGER.log( Level.WARNING, Trap.asMessage( aThrowable ), aThrowable );
				_returnThrowable = aThrowable;
			}
			finally {
				_hasResult = true;
				synchronized ( this ) {
					notifyAll();
				}
			}
		}

		/**
		 * Gets the return value.
		 *
		 * @return Description is currently not available!
		 */
		Object getReturnValue() {

			return _returnValue;
		}

		/**
		 * Gets the exception.
		 *
		 * @return the exception
		 * 
		 * @see ExceptionAccessor
		 */
		Throwable getException() {
			return _returnThrowable;
		}

		/**
		 * Checks for result.
		 *
		 * @return Description is currently not available!
		 */
		boolean hasResult() {

			return _hasResult;
		}
	}
}