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

org.refcodes.remoting.impls.RemoteServerImpl Maven / Gradle / Ivy

Go to download

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

There is a newer version: 3.3.9
Show newest version
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// =============================================================================
// This code is copyright (c) by Siegfried Steiner, Munich, Germany and licensed
// 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/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.remoting.impls;

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 org.refcodes.collection.Properties;
import org.refcodes.component.CloseException;
import org.refcodes.component.DigestException;
import org.refcodes.component.OpenException;
import org.refcodes.controlflow.ControlFlowUtility;
import org.refcodes.controlflow.RetryTimeout;
import org.refcodes.controlflow.impls.RetryTimeoutImpl;
import org.refcodes.data.IoTimeout;
import org.refcodes.data.LoopSleepTime;
import org.refcodes.data.RetryLoopCount;
import org.refcodes.exception.ExceptionAccessor;
import org.refcodes.exception.ExceptionUtility;
import org.refcodes.exception.HiddenException;
import org.refcodes.exception.VetoException;
import org.refcodes.factory.RecyclableTypeFactory;
import org.refcodes.factory.impls.AbstractRecyclableTypeFactory;
import org.refcodes.generator.Generator;
import org.refcodes.generator.impls.UniqueIdGeneratorImpl;
import org.refcodes.io.SerializeUtility;
import org.refcodes.logger.RuntimeLogger;
import org.refcodes.logger.impls.RuntimeLoggerFactorySingleton;
import org.refcodes.mixin.BusyAccessor;
import org.refcodes.remoting.CloseConnectionMessage;
import org.refcodes.remoting.DuplicateInstanceIdRuntimeException;
import org.refcodes.remoting.InstanceDescriptor;
import org.refcodes.remoting.InstanceId;
import org.refcodes.remoting.InvalidMethodReplyRuntimeException;
import org.refcodes.remoting.InvalidMethodRequestRuntimeException;
import org.refcodes.remoting.Message;
import org.refcodes.remoting.MethodRequest;
import org.refcodes.remoting.MethodRequestMessage;
import org.refcodes.remoting.PublishSubjectReplyMessage;
import org.refcodes.remoting.RemoteClient;
import org.refcodes.remoting.RemoteServer;
import org.refcodes.remoting.Reply;
import org.refcodes.remoting.SignOffProxyMessage;
import org.refcodes.remoting.SubjectDescriptor;
import org.refcodes.remoting.SubjectInstance;
import org.refcodes.remoting.UnknownInstanceIdRuntimeException;

/**
 * Abstract implementation of the {@link RemoteServer}'s base functionality.
 */
public class RemoteServerImpl extends AbstractRemote implements RemoteServer {

	private static RuntimeLogger LOGGER = RuntimeLoggerFactorySingleton.createRuntimeLogger();

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

	private InstanceHandler _instanceHandler = new InstanceHandler();
	private CancelMethodReplyJobFactoryImpl _cancelMethodReplyJobFactory = new CancelMethodReplyJobFactoryImpl();
	private MethodReplyJobFactoryImpl _methodReplyJobFactory = new MethodReplyJobFactoryImpl();
	private SignOffJobFactoryImpl _signOffJobFactory = new SignOffJobFactoryImpl();
	private PublishProxyJobFactoryImpl _publishProxyJobFactory = new PublishProxyJobFactoryImpl();
	private PublishSubjectReplyJobFactoryImpl _publishSubjectReplyJobFactory = new PublishSubjectReplyJobFactoryImpl();
	private Generator _instanceIdGenerator = new UniqueIdGeneratorImpl( INSTANCE_ID_LENGTH );

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

	public RemoteServerImpl() {
		super( null );
	}

	public RemoteServerImpl( ExecutorService aExecutorService ) {
		super( aExecutorService );
	}

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

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

	@Override
	public synchronized void close() {
		if ( ENABLE_EXTENDED_DEBUG_LOGGING ) {
			LOGGER.info( "CLOSE called on <" + getClass().getName() + ">." );
		}
		close( null );
	}

	/**
	 * {@inheritDoc}
	 * 
	 * -------------------------------------------------------------------------
	 * TODO: Enhance the {@link #isBusy()} method, e.g. are there any threads
	 * active?
	 * -------------------------------------------------------------------------
	 */
	@Override
	public boolean isBusy() {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		return _instanceHandler.isBusy();
	}

	@Override
	public boolean hasSubject( Object obj ) {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		return _instanceHandler.hasSubject( obj );
	}

	@Override
	public Iterator subjects() {
		synchronized ( _instanceHandler ) {
			List thjeList = new ArrayList( _instanceHandler.getSubjects() );
			return thjeList.iterator();
		}
	}

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

	@Override
	public boolean publishSubject( Object aSubject ) throws OpenException {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() && !isOpened() );
		synchronized ( _instanceHandler ) {
			SubjectDescriptor eObjDescriptor;
			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." ); }

				SubjectInstance theSubjectInstanceDescriptor = new SubjectInstanceDescriptorImpl( aSubject, theInstanceId );
				InstanceDescriptor classDescriptor = new ClassDescriptorImpl( aSubject.getClass(), theInstanceId );

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

				PublishSubjectMessageImpl thePublishSubjectJob = _publishProxyJobFactory.toInstance();
				thePublishSubjectJob.setClassDescriptor( classDescriptor );
				PublishSubjectReplyMessageImpl theMethodReplyRemotingJob = _publishSubjectReplyJobFactory.toInstance();
				theMethodReplyRemotingJob.setInstanceId( theInstanceId );
				theMethodReplyRemotingJob.setHasReply( false );
				_instanceHandler.addReplyDescriptor( theMethodReplyRemotingJob, theInstanceId );

				try {
					toReceiver( thePublishSubjectJob );
				}
				catch ( OpenException aException ) {
					LOGGER.warn( ExceptionUtility.toMessage( aException ), aException );
					_instanceHandler.removeReplyDescriptor( theInstanceId );
					if ( aException.getCause() instanceof IOException ) {
						closeOnException();
					}
					if ( ENABLE_OBJECT_POOLING ) _publishSubjectReplyJobFactory.recycleInstance( theMethodReplyRemotingJob );
					throw aException;
				}

				RetryTimeout theRetryTimeout = new RetryTimeoutImpl( WAIT_FOR_REPLY_TIMEOUT, WAIT_FOR_REPLY_LOOPS );
				while ( (!theMethodReplyRemotingJob.hasReply()) && theRetryTimeout.hasNextRetry() && isOpened() ) {
					if ( ENABLE_EXTENDED_DEBUG_LOGGING ) 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.hasReply() ) {
					if ( ENABLE_OBJECT_POOLING ) _publishSubjectReplyJobFactory.recycleInstance( theMethodReplyRemotingJob );
					throw new IllegalStateException( "While processing the request a timeout of " + WAIT_FOR_REPLY_TIMEOUT + " ms has been overshot; propably lost the connection (you propably should close the connection)." );
				}

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

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

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

							onSubjectPublished( theSubjectInstanceDescriptor.getSubject() );
						}
						if ( ENABLE_OBJECT_POOLING ) _publishSubjectReplyJobFactory.recycleInstance( theMethodReplyRemotingJob );
						return theReturnValue;
					}
				}

				if ( ENABLE_OBJECT_POOLING ) _publishSubjectReplyJobFactory.recycleInstance( theMethodReplyRemotingJob );
				throw new InvalidMethodReplyRuntimeException( "Unexpected reply when publishing a class descripter. Sorry - operation aborted!" );
			}

			return false;
		}
	}

	@Override
	public boolean signOffSubject( Object aSubject ) throws OpenException, VetoException {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		String theSubjectId = toSubjectId( aSubject );
		if ( theSubjectId == null ) { return false; }
		return signoffInstanceDescriptor( new InstanceDescriptorImpl( theSubjectId ), -1 );
	}

	@Override
	public boolean signOffSubject( Object aSubject, int aTimeoutInMs ) throws OpenException {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		String theSubjectId = toSubjectId( aSubject );
		if ( theSubjectId == null ) { return false; }
		try {
			return signoffInstanceDescriptor( new InstanceDescriptorImpl( theSubjectId ), aTimeoutInMs );
		}
		catch ( VetoException aException ) {
			throw new HiddenException( aException );
		}
	}

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

	@Override
	public synchronized void destroy() {
		if ( !isDestroyed() ) {
			super.destroy();
			_instanceHandler.clear();
			_instanceHandler = null;
			_cancelMethodReplyJobFactory.clear();
			_cancelMethodReplyJobFactory = null;
			_methodReplyJobFactory.clear();
			_methodReplyJobFactory = null;
			_publishProxyJobFactory.clear();
			_publishProxyJobFactory = null;
			_publishSubjectReplyJobFactory.clear();
			_publishSubjectReplyJobFactory = null;
			_signOffJobFactory.clear();
			_signOffJobFactory = 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 OpenException 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 OpenException {
		toReceiver( aJob );
	}

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

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

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

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

	/**
	 * Takes care of closing the {@link RemoteServer} depending on the
	 * {@link CloseConnectionMessage} received from the {@link RemoteClient}
	 * counterpart. In case a {@link RemoteServer} manages a one-to-many
	 * connection to different {@link RemoteClient} instances, then the
	 * {@link CloseConnectionMessage} provides meta data on which
	 * {@link RemoteClient} connection to close.
	 * 
	 * By default just {@link #close()} is being invoked as if having a
	 * one-to-one connection.
	 * 
	 * @param aJob The {@link CloseConnectionMessage} providing additional
	 *        information on which {@link RemoteClient} connection to close.
	 */
	@Override
	protected synchronized void close( CloseConnectionMessage aJob ) {
		ControlFlowUtility.throwIllegalStateException( isDestroyed() );
		if ( ENABLE_EXTENDED_DEBUG_LOGGING ) {
			LOGGER.info( "CLOSE called on <" + getClass().getName() + "> with job <" + aJob + ">." );
		}
		if ( !isClosed() ) {
			super.close( aJob );
			RetryTimeout theRetryTimeout = new RetryTimeoutImpl( IoTimeout.NORM.getMilliseconds(), RetryLoopCount.NORM_NUM_RETRY_LOOPS.getNumber() );
			while ( (isBusy()) && theRetryTimeout.hasNextRetry() && isOpened() ) {
				if ( ENABLE_EXTENDED_DEBUG_LOGGING ) {
					LOGGER.info( "Wait loop <" + theRetryTimeout.getRetryCount() + "> while being BUSY for <" + LoopSleepTime.NORM.getMilliseconds() + "> ms." );
				}
				theRetryTimeout.nextRetry();
			}
			if ( isBusy() ) {
				LOGGER.warn( "Still being BUSY even after reaching the timeout of <" + IoTimeout.NORM.getMilliseconds() + "> ms, closing connection nonetheless." );
			}
			signOffAllSubjects();
			try {
				super.close();
			}
			catch ( CloseException e ) {
				LOGGER.warn( "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 OpenException Thrown in case opening or accessing an open line
	 *         (connection, junction, link) caused problems.
	 */
	private boolean signOffSubject( SubjectDescriptor aSubject ) throws OpenException {
		if ( isClosed() ) { return false; }

		String eId = null;
		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 InstanceDescriptorImpl( 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;
		Iterator e = subjects();

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

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

		Iterator ee = subjects();

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

			if ( eObj instanceof SubjectInstanceDescriptorImpl ) {
				if ( ((SubjectInstanceDescriptorImpl) eObj).getSubject() == aSubject ) eId = ((SubjectInstanceDescriptorImpl) 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;
		Iterator e = _instanceHandler.subjectDescriptors();

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

	/**
	 * Signs off all subjects.
	 */
	private void signOffAllSubjects() {
		Iterator e = _instanceHandler.subjectDescriptors();
		SubjectDescriptor eObjDescriptor;
		while ( e.hasNext() )
			try {
				eObjDescriptor = e.next();
				signOffSubject( eObjDescriptor );
			}
			catch ( OpenException oe ) {
				LOGGER.error( "Catched a  with message = " + oe.getMessage() );
				if ( oe.getCause() instanceof IOException ) clearOnException();
			}
		_instanceHandler.clear();
	}

	/**
	 */
	private void closeOnException() {
		clearOnException();
		SubjectDescriptor eObjectDescriptor;
		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 OpenException Thrown in case opening or accessing an open line
	 *            (connection, junction, link) caused problems.
	 */
	private Reply pushMethodRequest( MethodRequest aMethodRequestDescriptor ) throws OpenException {

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

		SubjectDescriptor objDescriptor;

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

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

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

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

			if ( !theDaemon.hasResult() ) {
				LOGGER.warn( "Some timeout has occurred - timeout is ignored..." );
			}

			Reply theMethodReplyDescriptor = new ReplyDescriptorImpl( theDaemon.getReturnValue(), theDaemon.getException(), aMethodRequestDescriptor );
			return theMethodReplyDescriptor;
		}
		catch ( IllegalArgumentException iae ) {
			throw new InvalidMethodRequestRuntimeException( iae );
		}
		catch ( NoSuchMethodException 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 ." );

		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 ) {
			RetryTimeout theRetryTimeout = new RetryTimeoutImpl( IoTimeout.MIN.getMilliseconds(), RetryLoopCount.NORM_NUM_RETRY_LOOPS.getNumber() );
			while ( !isClosed() && theRetryTimeout.hasNextRetry() && theSubjectDescriptor == null ) {
				theRetryTimeout.nextRetry();
				theSubjectDescriptor = _instanceHandler.getSubjectDescriptor( instanceId );
			}
			if ( theSubjectDescriptor == null && !isClosed() ) {
				LOGGER.warn( "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 aTimeoutInMs 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 VetoException Thrown to signal that an operation is being vetoed
	 *         by a third party observing the invocation of the given operation.
	 * 
	 * @throws OpenException Thrown in case opening or accessing an open line
	 *         (connection, junction, link) caused problems.
	 */
	private boolean signoffInstanceDescriptor( InstanceId aInstanceDescriptor, int aTimeoutInMs ) throws OpenException, VetoException {

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

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

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

		SignoffSubjectMessageImpl theSignoffSubjectJob = _signOffJobFactory.toInstance();
		theSignoffSubjectJob.setInstanceId( aInstanceDescriptor.getInstanceId() );
		theSignoffSubjectJob.setTimeoutInMs( aTimeoutInMs );
		PublishSubjectReplyMessageImpl thePublishSubjectReplyJob = _publishSubjectReplyJobFactory.toInstance();
		thePublishSubjectReplyJob.setInstanceId( theInstanceId );
		thePublishSubjectReplyJob.setHasReply( false );
		synchronized ( _instanceHandler ) {
			_instanceHandler.addReplyDescriptor( thePublishSubjectReplyJob, theInstanceId );
		}
		toReceiver( theSignoffSubjectJob );

		RetryTimeout theRetryTimeout = new RetryTimeoutImpl( WAIT_FOR_REPLY_TIMEOUT, WAIT_FOR_REPLY_LOOPS );
		while ( (!thePublishSubjectReplyJob.hasReply()) && theRetryTimeout.hasNextRetry() && isOpened() ) {
			if ( ENABLE_EXTENDED_DEBUG_LOGGING ) 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() ) {
			if ( ENABLE_OBJECT_POOLING ) _publishSubjectReplyJobFactory.recycleInstance( thePublishSubjectReplyJob );
			// @formatter:off
			// throw new OpenTimeoutException( 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
			// TODO: Throw actual OpenTimeoutException or similar!
			throw new IllegalStateException( "While processing the request a timeout of " + WAIT_FOR_REPLY_TIMEOUT + " ms has been overshot; propably lost the connection (you propably should close the connection)." );
		}

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

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

		if ( ENABLE_OBJECT_POOLING ) {
			_publishSubjectReplyJobFactory.recycleInstance( thePublishSubjectReplyJob );
			throw new InvalidMethodReplyRuntimeException( "Unexpected reply when publishing a subject." );
		}

		return false;
	}

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

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

		private Map _instanceIdsToSubjectDescriptors = new HashMap();
		private Map _instanceIdToMethodReplyDescriptor = new HashMap();
		private List _subjects = new ArrayList();
		private 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.
		 * @throws UnexpectedRuntimeException Description is currently not
		 *         available!
		 */
		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.
		 * 
		 * @throws UnexpectedRuntimeException Description is currently not
		 *         available!
		 */
		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();
		}

		/**
		 * 
		 * 
		 * @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.
		 * 
		 * @throws UnexpectedRuntimeException Description is currently not
		 *         available!
		 */
		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( (Object) 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 ) {
				List returnList = new ArrayList( _instanceIdsToSubjectDescriptors.values() );
				return returnList.iterator();
			}
		}

		/**
		 * Returns true if an object descriptor is associated to the provided
		 * instance id.
		 * 
		 * @param instanceId The instance id which's existence is to be tested.
		 * @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.
		 * @throws UnexpectedRuntimeException Description is currently not
		 *         available!
		 */
		SubjectDescriptor removeSubjectDescriptor( String aInstanceId ) {
			synchronized ( this ) {
				if ( _instanceIdsToSubjectDescriptors.containsKey( aInstanceId ) ) {
					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.
		 * @throws UnexpectedRuntimeException Description is currently not
		 *         available!
		 */
		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 );
		}

		/**
		 * 
		 * 
		 * @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();
			}
		}

		@Override
		synchronized public boolean isBusy() {
			return (!_instanceIdToMethodReplyDescriptor.isEmpty());
		}
	}

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

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

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

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

		private Object[] _argumentArray;
		private boolean _hasResult = false;
		private Method _method;
		private 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.
		 */
		MethodInvokationDaemon( Object aSubject, String aMethodName, Object[] anArgumentArray, Class[] aParameterArray ) throws IllegalArgumentException, 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] );
			}
		}

		@Override
		public void run() {
			try {
				Object returnValue = _method.invoke( _providedObject, _argumentArray );
				_returnValue = SerializeUtility.toSerializable( returnValue );
			}
			catch ( InvocationTargetException aInvocationTargetException ) {
				LOGGER.warn( ExceptionUtility.toMessage( aInvocationTargetException ), aInvocationTargetException );
				_returnThrowable = aInvocationTargetException.getTargetException();
			}
			catch ( IllegalAccessException aIllegalAccessException ) {
				LOGGER.warn( ExceptionUtility.toMessage( aIllegalAccessException ), aIllegalAccessException );
				throw new InvalidMethodRequestRuntimeException( aIllegalAccessException );
			}
			catch ( Throwable aThrowable ) {
				LOGGER.warn( ExceptionUtility.toMessage( aThrowable ), aThrowable );
				_returnThrowable = aThrowable;
			}
			finally {
				_hasResult = true;
				synchronized ( this ) {
					notifyAll();
				}
			}
		}

		/**
		 * 
		 * 
		 * @return Description is currently not available!
		 */
		Object getReturnValue() {

			return _returnValue;
		}

		/**
		 * @see ExceptionAccessor
		 */
		Throwable getException() {
			return _returnThrowable;
		}

		/**
		 * 
		 * 
		 * @return Description is currently not available!
		 */
		boolean hasResult() {

			return _hasResult;
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHOD REPLY JOB FACTORY:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Implementation of the {@link RecyclableTypeFactory} creating instances of
	 * the according type.
	 */
	protected static class MethodReplyJobFactoryImpl extends AbstractRecyclableTypeFactory {

		@Override
		protected MethodReplyMessageImpl newInstance() {
			return new MethodReplyMessageImpl();
		}

		@Override
		protected MethodReplyMessageImpl newInstance( Properties aProperties ) {
			return new MethodReplyMessageImpl();
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// PUBLISH PROXY JOB FACTORY:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Implementation of the {@link RecyclableTypeFactory} creating instances of
	 * the according type.
	 */
	protected static class PublishProxyJobFactoryImpl extends AbstractRecyclableTypeFactory {

		@Override
		protected PublishSubjectMessageImpl newInstance() {
			return new PublishSubjectMessageImpl();
		}

		@Override
		protected PublishSubjectMessageImpl newInstance( Properties aProperties ) {
			return new PublishSubjectMessageImpl();
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// SIGN-OFF JOB FACTORY:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Implementation of the {@link RecyclableTypeFactory} creating instances of
	 * the according type.
	 */
	protected static class SignOffJobFactoryImpl extends AbstractRecyclableTypeFactory {

		@Override
		protected SignoffSubjectMessageImpl newInstance() {
			return new SignoffSubjectMessageImpl();
		}

		@Override
		protected SignoffSubjectMessageImpl newInstance( Properties aProperties ) {
			return new SignoffSubjectMessageImpl();
		}
	}
}