Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.refcodes.remoting.RemoteClientImpl Maven / Gradle / Ivy
Go to download
Artifact with proxy functionality for plain remote procedure calls
(RPC), a POJO alternative to RMI.
// /////////////////////////////////////////////////////////////////////////////
// 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/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.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
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.TimeoutIOException;
import org.refcodes.exception.VetoException;
import org.refcodes.generator.Generator;
import org.refcodes.generator.UniqueIdGenerator;
import org.refcodes.mixin.BusyAccessor;
import org.refcodes.mixin.DisposedAccessor;
import org.refcodes.mixin.Lockable;
import org.refcodes.remoting.RemoteClient.ProxyControl;
/**
* Abstract implementation of the {@link RemoteClient}'s base functionality.
*/
public class RemoteClientImpl extends AbstractRemote implements RemoteClient {
// /////////////////////////////////////////////////////////////////////////
// STATICS:
// /////////////////////////////////////////////////////////////////////////
private static Logger LOGGER = Logger.getLogger( RemoteClientImpl.class.getName() );
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
private InstanceHandler _instanceHandler = new InstanceHandler();
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
/**
* Instantiates a new remote client impl.
*/
public RemoteClientImpl() {
super();
}
/**
* Instantiates a new remote client impl.
*
* @param aExecutorService the executor service
*/
public RemoteClientImpl( ExecutorService aExecutorService ) {
super( aExecutorService );
}
// /////////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public void clear() {
ControlFlowUtility.throwIllegalStateException( isDestroyed() );
signOffAllProxies();
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void close() throws IOException {
if ( ENABLE_EXTENDED_DEBUG_LOGGING ) {
LOGGER.info( "CLOSE called on <" + getClass().getName() + ">." );
}
close( null );
}
/**
* {@inheritDoc}
*/
@Override
public boolean isBusy() {
ControlFlowUtility.throwIllegalStateException( isDestroyed() );
return _instanceHandler.isBusy();
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasProxy( Object proxy ) {
return getProxyDescriptor( proxy ) != null;
}
/**
* Gets the proxy.
*
* @param the generic type
* @param aType the type
*
* @return the proxy
*
* @throws AmbiguousProxyException the ambiguous proxy exception
* @throws NoSuchProxyException the no such proxy exception
*/
@SuppressWarnings("unchecked")
@Override
public T getProxy( Class aType ) throws AmbiguousProxyException, NoSuchProxyException {
Iterator e = proxies();
Object eProxy, theProxy = null;
while ( e.hasNext() ) {
eProxy = e.next();
if ( aType.isAssignableFrom( eProxy.getClass() ) ) {
if ( theProxy != null ) {
throw new AmbiguousProxyException( "More than one proxy matching the given class \"" + aType.getName() + "\" found; an ambigous state detected; only one proxy must match your type!" );
}
theProxy = eProxy;
}
}
if ( theProxy == null ) {
throw new NoSuchProxyException( "Not one proxy matching the given class \"" + aType.getName() + "\" found; exactly one proxy must match your type!" );
}
return (T) theProxy;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasProxy( Class> aType ) {
Iterator e = proxies();
Object eProxy;
while ( e.hasNext() ) {
eProxy = e.next();
if ( aType.isAssignableFrom( eProxy.getClass() ) ) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public Iterator proxies() {
ControlFlowUtility.throwIllegalStateException( isDestroyed() && !isOpened() );
ArrayList theProxyList = new ArrayList( _instanceHandler.size() );
synchronized ( _instanceHandler ) {
Iterator e = _instanceHandler.proxyDescriptors();
while ( e.hasNext() ) {
theProxyList.add( e.next().getProxy() );
}
}
return theProxyList.iterator();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isEmpty() {
ControlFlowUtility.throwIllegalStateException( isDestroyed() );
return _instanceHandler.isEmpty();
}
/**
* {@inheritDoc}
*/
@Override
public boolean signOffProxy( Object aProxy ) throws IOException {
ControlFlowUtility.throwIllegalStateException( isDestroyed() );
if ( aProxy == null ) {
return false;
}
ProxyDescriptor theProxyDescriptor = getProxyDescriptor( aProxy );
if ( theProxyDescriptor == null ) {
return false;
}
String theInstanceId = theProxyDescriptor.getInstanceId();
if ( !isClosed() ) {
if ( theProxyDescriptor.getInstanceId() == null ) {
return false;
}
if ( _instanceHandler.hasSignedOffInstanceId( theInstanceId ) ) {
return false;
}
if ( !_instanceHandler.hasProxyControl( theInstanceId ) ) {
return false;
}
}
ProxyControl theProxyControl = _instanceHandler.getProxyControl( theInstanceId );
// ---------------------------------------------------------------------
// Signal hook:
// ---------------------------------------------------------------------
onProxySignedOff( theProxyDescriptor.getProxy() );
// ---------------------------------------------------------------------
theProxyControl.lock();
waitForActiveSessions( theProxyControl );
synchronized ( _instanceHandler ) {
if ( _instanceHandler.hasSignedOffInstanceId( theInstanceId ) ) {
return false;
}
theProxyControl = _instanceHandler.getProxyControl( theInstanceId );
_instanceHandler.removeProxyDescriptor( theInstanceId );
// <--|
if ( _instanceHandler.hasMethodReplyDescriptor( theInstanceId ) ) {
theProxyControl.dispose();
throw new DuplicateInstanceIdRuntimeException( "The instance of the provided object in arguemnt is already in use and being calluted. Sorry - aborting operation !!!" );
}
}
SignOffProxyMessageImpl theSignOffProxyJob = new SignOffProxyMessageImpl();
theSignOffProxyJob.setInstanceId( theProxyDescriptor.getInstanceId() );
CancelMethodReplyMessageImpl theCancelMethodReplyJobImpl = new CancelMethodReplyMessageImpl();
theCancelMethodReplyJobImpl.setInstanceId( theInstanceId );
theCancelMethodReplyJobImpl.setHasReply( false );
_instanceHandler.addReplyDescriptor( theCancelMethodReplyJobImpl, theInstanceId );
try {
toReceiver( theSignOffProxyJob );
}
catch ( IOException aException ) {
theProxyControl.dispose();
theProxyControl.unlock();
_instanceHandler.removeMethodReplyDescriptor( theInstanceId );
if ( aException.getCause() instanceof IOException ) {
closeOnException();
}
throw aException;
}
RetryTimeout theRetryTimeout = new RetryTimeout( WAIT_FOR_REPLY_TIMEOUT, WAIT_FOR_REPLY_LOOPS );
while ( !theCancelMethodReplyJobImpl.hasReply() && theRetryTimeout.hasNextRetry() && isOpened() && !theProxyControl.isDisposed() ) {
// @formatter:off
if ( ENABLE_EXTENDED_DEBUG_LOGGING ) LOGGER.info( "Wait loop <" + theRetryTimeout.getRetryCount() + "> while waiting for method reply for <" + WAIT_FOR_REPLY_LOOPS + "> ms; connection status is " + getConnectionStatus() + "." );
// @formatter:on
theRetryTimeout.nextRetry( theCancelMethodReplyJobImpl );
}
theProxyControl.dispose();
_instanceHandler.removeMethodReplyDescriptor( theInstanceId );
if ( !theCancelMethodReplyJobImpl.hasReply() ) {
throw new TimeoutIOException( WAIT_FOR_REPLY_TIMEOUT, "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). Sorry - request aborted!" );
}
// evaluate the return value:
if ( theCancelMethodReplyJobImpl.isReturnValue() ) {
if ( theCancelMethodReplyJobImpl.getReturnValue() instanceof Boolean ) {
boolean returnValue = ((Boolean) theCancelMethodReplyJobImpl.getReturnValue()).booleanValue();
if ( returnValue ) theProxyControl.dispose();
return returnValue;
}
}
throw new InvalidMethodReplyRuntimeException( "Unexpected reply when publishing a class descripter." );
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
ControlFlowUtility.throwIllegalStateException( isDestroyed() );
return _instanceHandler.size();
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void destroy() {
if ( !isDestroyed() ) {
super.destroy();
closeQuietly();
_instanceHandler.clear();
_instanceHandler = null;
}
}
// /////////////////////////////////////////////////////////////////////////
// HELPER:
// /////////////////////////////////////////////////////////////////////////
/**
* A reply from a {@link RemoteServer} is pushed to the {@link RemoteClient}
* using this method.
*
* @param aMethodReply An object of type BlueprintMethodReply from which the
* result of a request can be retrieved.
*/
void pushMethodReply( Reply aMethodReply ) {
ControlFlowUtility.throwIllegalStateException( isDestroyed() );
ProxyControl theProxyControl = _instanceHandler.getProxyControl( aMethodReply.getInstanceId() );
if ( theProxyControl == null ) {
throw new IllegalArgumentException( "Unable to retrieve the the proxy control assosiated the instance TID <" + aMethodReply.getInstanceId() + "> of the given method reply." );
}
theProxyControl.pushMethodReply( aMethodReply );
}
// /////////////////////////////////////////////////////////////////////////
// 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
protected 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 );
}
// -----------------------------------------------------------------
// CANCEL METHOD REPLY JOB:
// -----------------------------------------------------------------
else if ( aJob instanceof CancelMethodReplyMessage ) {
CancelMethodReplyMessage replyRemotorJob = (CancelMethodReplyMessage) aJob;
if ( replyRemotorJob.getInstanceId() == null ) {
return;
}
String aInstanceId = replyRemotorJob.getInstanceId();
if ( !_instanceHandler.hasMethodReplyDescriptor( aInstanceId ) ) {
if ( isOpened() ) {
return;
}
throw new UnknownInstanceIdRuntimeException( "Unkwnown instance TID <" + aInstanceId + ">, expected a known instance TID." );
}
Object tmpReply = _instanceHandler.getMethodReplyDescriptor( aInstanceId );
if ( !(tmpReply instanceof CancelMethodReplyMessage) ) {
throw new InvalidMethodReplyRuntimeException( "Excpected a \"" + CancelMethodReplyMessage.class.getName() + "\"." );
}
CancelMethodReplyMessage waitingReplyRemotorJob = (CancelMethodReplyMessage) tmpReply;
waitingReplyRemotorJob.setReply( replyRemotorJob );
synchronized ( waitingReplyRemotorJob ) {
waitingReplyRemotorJob.notifyAll();
}
}
// -----------------------------------------------------------------
// METHOD REPLY JOB:
// -----------------------------------------------------------------
else if ( aJob instanceof MethodReplyMessage ) {
pushMethodReply( (Reply) aJob );
}
// -----------------------------------------------------------------
// PULISH SUBJECT JOB:
// -----------------------------------------------------------------
else if ( aJob instanceof PublishSubjectMessage ) {
try {
boolean returnValue = publishClassDescriptor( (InstanceDescriptor) aJob );
PublishSubjectReplyMessageImpl thePublishSubjectReplyJob = new PublishSubjectReplyMessageImpl();
thePublishSubjectReplyJob.setInstanceId( aJob.getInstanceId() );
thePublishSubjectReplyJob.setException( null );
thePublishSubjectReplyJob.setReturnValue( returnValue );
thePublishSubjectReplyJob.setHasReply( true );
toReceiver( thePublishSubjectReplyJob );
}
catch ( VetoException aException ) {
PublishSubjectReplyMessageImpl theMethodReplyJob = new PublishSubjectReplyMessageImpl();
theMethodReplyJob.setInstanceId( aJob.getInstanceId() );
theMethodReplyJob.setException( aException );
theMethodReplyJob.setReturnValue( null );
theMethodReplyJob.setHasReply( false );
toReceiver( theMethodReplyJob );
}
}
// -----------------------------------------------------------------
// SIGN-OFF SUBJECT JOB:
// -----------------------------------------------------------------
else if ( aJob instanceof SignOffSubjectMessage ) {
InstanceId theInstanceDescriptor = aJob;
boolean isSignOff = signoffInstanceDescriptor( theInstanceDescriptor );
PublishSubjectReplyMessageImpl theMethodReplyJob = new PublishSubjectReplyMessageImpl();
theMethodReplyJob.setInstanceId( aJob.getInstanceId() );
theMethodReplyJob.setReturnValue( isSignOff );
theMethodReplyJob.setException( null );
theMethodReplyJob.setHasReply( true );
toReceiver( theMethodReplyJob );
}
}
catch ( IOException aException ) {
closeOnException();
throw new DigestException( "Digesting the job caued a cause exception to be thrown.", aException );
}
}
/**
* {@inheritDoc}
*/
@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 + ">; connection status is " + getConnectionStatus() + "." );
}
if ( !isClosed() ) {
signOffAllProxies();
_instanceHandler.lock();
RetryTimeout theRetryTimeout = new RetryTimeout( IoTimeout.NORM.getTimeInMs(), RetryLoopCount.NORM_NUM_RETRY_LOOPS.getValue() );
while ( (isBusy()) && theRetryTimeout.hasNextRetry() ) {
if ( ENABLE_EXTENDED_DEBUG_LOGGING ) LOGGER.info( "Wait loop <" + theRetryTimeout.getRetryCount() + "> while being BUSY for <" + SleepLoopTime.NORM.getTimeInMs() + "> ms." );
theRetryTimeout.nextRetry();
}
super.close( aJob );
if ( isBusy() ) {
LOGGER.log( Level.WARNING, "Still being BUSY even after reaching the timeout of <" + IoTimeout.NORM.getTimeInMs() + "> ms, closing connection nonetheless." );
}
try {
super.close();
}
catch ( IOException e ) {
LOGGER.log( Level.WARNING, "Unable to close malfunctioning connection.", e );
}
onClosed();
}
}
// /////////////////////////////////////////////////////////////////////////
// SIGNALS:
// /////////////////////////////////////////////////////////////////////////
/**
* Hook when a proxy is about to be published.
*
* @param aType The type to be stored by the property.
*
* @throws VetoException Thrown to signal that an operation is being vetoed
* by a third party observing the invocation of the given operation.
*/
protected void onPublishProxy( Class> aType ) throws VetoException {}
/**
* Hook when a proxy has been published.
*
* @param aProxy The proxy to being published.
*/
protected void onProxyPublished( Object aProxy ) {}
/**
* Hook when a proxy has been signed-off.
*
* @param aProxy The proxy which has been signed-off.
*/
protected void onProxySignedOff( Object aProxy ) {}
// /////////////////////////////////////////////////////////////////////////
// HELPER:
// /////////////////////////////////////////////////////////////////////////
/**
* A request from a proxy control only is pushed to the remotor using this
* method.
*
* @param aMethodRequestDescriptor An object of type BlueprintMethodRequest
* from which the request can be retrieved.
*
* @throws IOException the open exception
*/
void pushMethodRequest( MethodRequest aMethodRequestDescriptor ) throws IOException {
MethodRequestMessageImpl theMethodRequestJob = new MethodRequestMessageImpl();
theMethodRequestJob.setMethodRequestDescriptor( aMethodRequestDescriptor );
toReceiver( theMethodRequestJob );
}
/**
* Only cleanup, no close!.
*/
private void clearOnException() {
ProxyDescriptor eProxyDescriptor;
ProxyControl eProxyControl;
synchronized ( _instanceHandler ) {
Iterator e = _instanceHandler.proxyDescriptors();
while ( e.hasNext() ) {
eProxyDescriptor = e.next();
eProxyControl = _instanceHandler.getProxyControl( eProxyDescriptor.getInstanceId() );
if ( eProxyControl != null ) {
eProxyControl.dispose();
}
onProxySignedOff( eProxyDescriptor.getProxy() );
}
}
_instanceHandler.clear();
}
/**
* Only cleanup, no close!.
*/
private void signOffAllProxies() {
boolean hasException = false;
synchronized ( _instanceHandler ) {
ProxyControl eProxyControl;
Iterator e = _instanceHandler.proxyControls();
while ( e.hasNext() ) {
eProxyControl = e.next();
try {
signOffProxy( eProxyControl.getProxy() );
}
catch ( IOException aException ) {
hasException = true;
}
}
if ( hasException == true ) {
clearOnException();
}
_instanceHandler.clear();
}
}
/**
* Closes the connection and disposes all proxy controls. This method is
* called whenever an exception indicated some not reparable damage on the
* connection so that the system is finished up and events are fired for all
* proxies being removed.
*/
private void closeOnException() {
clearOnException();
try {
super.close();
}
catch ( IOException e ) {
LOGGER.log( Level.WARNING, "Unable to close malfunctioning connection.", e );
}
onClosed();
}
/**
* This method is used to publish a subject which resides in a
* {@link RemoteServer}.
*
* @param aClassDescriptor The {@link InstanceDescriptor} describing the
* subject.
*
* @return True in case the according proxy has been published successfully.
*
* @throws VetoException the veto exception
*/
private boolean publishClassDescriptor( InstanceDescriptor aClassDescriptor ) throws VetoException {
assert (aClassDescriptor != null);
assert (aClassDescriptor.getInstanceId() != null);
if ( _instanceHandler.hasProxyControl( aClassDescriptor.getInstanceId() ) ) {
throw new DuplicateInstanceIdRuntimeException( "The instance TID <" + aClassDescriptor.getInstanceId() + "> of the given class descriptor is already in use." );
}
String aInstanceId = aClassDescriptor.getInstanceId();
onPublishProxy( aClassDescriptor.getType() );
ProxyControl theProxyControl = new ProxyControlImpl( aClassDescriptor );
ProxyDescriptor proxyDescriptor = new ProxyDescriptorImpl( aClassDescriptor, theProxyControl.getProxy() );
_instanceHandler.addProxyControl( theProxyControl, aInstanceId );
_instanceHandler.addProxyDescriptor( proxyDescriptor, aInstanceId );
onProxyPublished( proxyDescriptor.getProxy() );
return true;
}
/**
* Signs off an instance previously published using the
* publishClassDescriptor() method. This method is to be used by services
* only - the toolkit uses the corresponding signoffInstanceDescriptor().
*
* @param aInstanceDescriptor An object of type GenericInstanceDescriptor
* containing the information needed to sign-off an instance.
*
* @return True is returned if the instance could be signed-off
*/
private boolean signoffInstanceDescriptor( 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 TID of argument ." );
}
String aInstanceId = aInstanceDescriptor.getInstanceId();
if ( _instanceHandler.hasSignedOffInstanceId( aInstanceId ) ) {
return true;
}
if ( !_instanceHandler.hasProxyControl( aInstanceId ) ) {
throw new UnknownInstanceIdRuntimeException( "Expected a known instance TID in argument ." );
}
ProxyDescriptor theProxyDescriptor = _instanceHandler.getProxyDescriptor( aInstanceId );
ProxyControlImpl theProxyControl = (ProxyControlImpl) _instanceHandler.getProxyControl( aInstanceId );
onProxySignedOff( theProxyDescriptor.getProxy() );
theProxyControl.lock();
waitForActiveSessions( theProxyControl );
_instanceHandler.removeProxyDescriptor( aInstanceId );
theProxyControl.dispose();
return true;
}
/**
* Wait for active sessions.
*
* @param aProxyControl the proxy control
*/
private void waitForActiveSessions( ProxyControl aProxyControl ) {
RetryTimeout theRetryTimeout = new RetryTimeout( WAIT_FOR_ACTIVE_SESSIONS_TIMEOUT_IN_MS, WAIT_FOR_ACTIVE_SESSIONS_LOOPS );
while ( (aProxyControl.isBusy()) && theRetryTimeout.hasNextRetry() && isOpened() ) {
if ( ENABLE_EXTENDED_DEBUG_LOGGING ) LOGGER.info( "Wait loop <" + theRetryTimeout.getRetryCount() + "> while proxy \"" + aProxyControl.getProxy() + "\" (<" + aProxyControl.getClass() + ">) having ACTIVE SESSIONS for <" + WAIT_FOR_REPLY_LOOPS + "> ms." );
theRetryTimeout.nextRetry( aProxyControl );
}
}
/**
* Retrieves the {@link ProxyDescriptor} describing a proxy.
*
* @param aProxy The proxy for which to get the descriptor.
*
* @return The according descriptor or null if none such descriptor was
* found.
*/
private ProxyDescriptor getProxyDescriptor( Object aProxy ) {
ControlFlowUtility.throwIllegalStateException( isDestroyed() && !isOpened() );
ProxyDescriptor eProxyDescriptor;
synchronized ( _instanceHandler ) {
Iterator e = _instanceHandler.proxyDescriptors();
while ( e.hasNext() ) {
eProxyDescriptor = e.next();
if ( eProxyDescriptor.getProxy() == aProxy ) return eProxyDescriptor;
}
}
return null;
}
// /////////////////////////////////////////////////////////////////////////
// INNER CLASSES:
// /////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////
// PROXY CONTROL:
// /////////////////////////////////////////////////////////////////////////
/**
* Implementation of the {@link ProxyControl} interface providing
* functionality to manage a proxy being published from a subject by a
* {@link RemoteServer}.
*/
private class ProxyControlImpl implements ProxyControl {
private final static boolean IS_THROW_UNKNOWN_INSTANCE_ID_EXCETIONS_ENABLED = false;
private InstanceDescriptor _classDescriptor;
private int _hasActiveSessionsCount = 0;
private boolean _isDisposeable = false;
private boolean _isLocked = false;
private boolean _isProxyDisposed = false;
private Object _proxy;
private Generator _sessionIdGenerator = new UniqueIdGenerator( SESSION_ID_LENGTH );
private Map _sessionIds2MethodReply = new HashMap();
/**
* Creates a new ProxyControl object.
*
* @param aClassDescriptor Description is currently not available!
*/
ProxyControlImpl( InstanceDescriptor aClassDescriptor ) {
// |--> put the GenericProxy interface into the proxy's definition:
int length = aClassDescriptor.getType().getInterfaces().length;
Class>[] interfaceArray = aClassDescriptor.getType().getInterfaces();
Class>[] proxyInterfaceArray = new Class[length + 2];
for ( int i = 0; i < length; i++ ) proxyInterfaceArray[i] = interfaceArray[i];
proxyInterfaceArray[length] = DisposedAccessor.class;
proxyInterfaceArray[length + 1] = Disposedable.class;
if ( Disposedable.class.isAssignableFrom( aClassDescriptor.getType() ) ) _isDisposeable = true;
// <--|
_proxy = Proxy.newProxyInstance( this.getClass().getClassLoader(), proxyInterfaceArray, this );
_classDescriptor = aClassDescriptor;
}
/**
* Dispose.
*/
@Override
public void dispose() {
synchronized ( getProxy() ) {
_isProxyDisposed = true;
}
}
/**
* Equals.
*
* @param obj the obj
*
* @return true, if successful
*/
@Override
public boolean equals( Object obj ) {
return super.equals( obj );
}
/**
* Gets the class descriptor.
*
* @return the class descriptor
*/
@Override
public InstanceDescriptor getClassDescriptor() {
return _classDescriptor;
}
/**
* Gets the instance id.
*
* @return the instance id
*/
@Override
public String getInstanceId() {
return _classDescriptor.getInstanceId();
}
/**
* Gets the proxy.
*
* @param the generic type
*
* @return the proxy
*/
@SuppressWarnings("unchecked")
@Override
public
P getProxy() {
return (P) _proxy;
}
/**
* Checks if is busy.
*
* @return true, if is busy
*/
@Override
public boolean isBusy() {
return (_hasActiveSessionsCount > 0);
}
/**
* Hash code.
*
* @return the int
*/
@Override
public int hashCode() {
return super.hashCode();
}
/**
* Invoke.
*
* @param proxy the proxy
* @param method the method
* @param arguments the arguments
*
* @return the object
*
* @throws Throwable the throwable
*/
@Override
public Object invoke( Object proxy, Method method, Object[] arguments ) throws Throwable {
_hasActiveSessionsCount++;
if ( (method.getName().equals( "equals" )) && (arguments != null) && (arguments.length == 1) ) {
_hasActiveSessionsCount--;
return getProxy() == arguments[0];
}
// |--> test the GenericProxy interface method:
if ( (method.getName().equals( "isProxyDisposed" )) && (arguments == null) ) {
_hasActiveSessionsCount--;
// return new Boolean(isProxyDisposed());
return isProxyUnusable();
}
// |--> test the GenericDisposeablePublic interface method:
if ( (method.getName().equals( "isDisposed" )) && (arguments == null) ) {
if ( _isDisposeable ) {
if ( isProxyUnusable() ) {
_hasActiveSessionsCount--;
return true;
}
}
else {
_hasActiveSessionsCount--;
return isProxyUnusable();
}
}
// <--|
if ( (method.getName().equals( "hashCode" )) && (arguments == null) ) {
_hasActiveSessionsCount--;
return super.hashCode();
}
// if ((method.getName().equals("equals")) && (arguments != null)
// && (arguments.length == 1))
// return new Boolean(equals(arguments[0]));
if ( isProxyUnusable() ) {
// |--> !!!
// if ((method.getName().equals("equals")) && (arguments !=
// null) && (arguments.length == 1)) {
// _hasActiveSessionsCount --;
// return new Boolean(super.equals(arguments[0])); / ?
// return new Boolean(getProxy() == arguments[0]); / !
// }
// <--| !!!
// |--> !!!
if ( (method.getName().equals( "toString" )) && (arguments == null) ) {
_hasActiveSessionsCount--;
return super.toString();
}
// <--| !!!
// |--> !!!
if ( (method.getName().equals( "isDisposed" )) && (arguments == null) ) {
_hasActiveSessionsCount--;
return true;
}
// <--| !!!
throw new ProxyDisposedRuntimeException( "The proxy object <" + getClassDescriptor().getType().getName() + "> is disposed!" );
}
String theSessionId = null;
synchronized ( _sessionIdGenerator ) {
if ( _sessionIdGenerator.hasNext() ) {
theSessionId = _sessionIdGenerator.next();
}
else {
_hasActiveSessionsCount--;
throw new IllegalStateException( "The TID generator is unable to create more unique IDs" );
}
}
if ( theSessionId == null ) {
_hasActiveSessionsCount--;
throw new IllegalStateException( "The TID generator is unable to create more unique IDs" );
}
Reply methodReply = null;
synchronized ( _sessionIds2MethodReply ) {
if ( _sessionIds2MethodReply.containsKey( theSessionId ) ) {
_hasActiveSessionsCount--;
throw new DuplicateSessionIdRuntimeException( "The session id generator seems to generatoe duplicate id objects. Sorry - aborting operation!" );
}
String instanceId = getInstanceId();
methodReply = new ReplyDescriptor( instanceId, theSessionId );
_sessionIds2MethodReply.put( theSessionId, methodReply );
}
MethodRequest methodRequest = new MethodRequestDescriptorImpl( method, arguments, getInstanceId(), theSessionId );
pushMethodRequest( methodRequest );
// -----------------------------------------------------------------
// Pushed request - waiting for reply:
// -----------------------------------------------------------------
long timeout = WAIT_FOR_REPLY_TIMEOUT;
while ( !methodReply.hasReply() && (timeout >= 0) && !isProxyUnusable() && isOpened() ) {
synchronized ( methodReply ) {
try {
if ( !methodReply.hasReply() && (timeout >= 0) && (!isProxyUnusable()) ) {
methodReply.wait( WAIT_FOR_REPLY_LOOPS );
}
}
catch ( InterruptedException ie ) {}
}
timeout -= WAIT_FOR_REPLY_LOOPS;
}
_sessionIds2MethodReply.remove( theSessionId );
_hasActiveSessionsCount--;
if ( !methodReply.hasReply() ) {
if ( isProxyUnusable() ) {
throw new ProxyDisposedRuntimeException( "The proxy object <" + getClassDescriptor().getType().getName() + "> is disposed!" );
}
else if ( timeout < 0 ) {
throw new TimeoutIOException( WAIT_FOR_REPLY_TIMEOUT, "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). Sorry - request aborted!" );
}
else {
throw new IOException( "The proxy object <" + getClassDescriptor().getType().getName() + "> is did not recieve the expected remote reply - unkown cause!" );
}
}
// -----------------------------------------------------------------
if ( methodReply.isException() )
throw methodReply.getException();
else {
if ( (method.getName().equals( "equals" )) && (arguments != null) && (arguments.length == 1) && (methodReply.getReturnValue() instanceof Boolean) ) {
boolean returnValue = ((Boolean) methodReply.getReturnValue()).booleanValue();
return super.equals( arguments[0] ) | returnValue;
}
return methodReply.getReturnValue();
}
}
/**
* Checks if is disposed.
*
* @return true, if is disposed
*/
@Override
public boolean isDisposed() {
return (isProxyUnusable());
}
/**
* Push method reply.
*
* @param aMethodReply the method reply
*/
@Override
public void pushMethodReply( Reply aMethodReply ) {
if ( aMethodReply == null ) {
return;
}
if ( !getInstanceId().equals( aMethodReply.getInstanceId() ) ) {
if ( !isProxyUnusable() ) {
throw new UnknownInstanceIdRuntimeException( "The instance id of the argument ( object) is not the same as the expected instance id !!! Sorry - aborting operation!" );
}
else {
return;
}
}
if ( !_sessionIds2MethodReply.containsKey( aMethodReply.getSessionId() ) ) {
if ( !isProxyUnusable() ) {
if ( IS_THROW_UNKNOWN_INSTANCE_ID_EXCETIONS_ENABLED ) {
throw new UnknownInstanceIdRuntimeException( "The session id of the argument ( object) is not the same as the expected session id !!! Sorry - aborting operation!" );
}
else {
return;
}
}
else {
return;
}
}
Reply waitingMethodReply = _sessionIds2MethodReply.remove( aMethodReply.getSessionId() );
if ( waitingMethodReply == null ) {
if ( !isProxyUnusable() ) {
throw new IllegalStateException( "No prepared method reply object found." );
}
else {
return;
}
}
waitingMethodReply.setReply( aMethodReply );
synchronized ( waitingMethodReply ) {
waitingMethodReply.notifyAll();
}
}
/**
* Lock.
*/
@Override
public void lock() {
synchronized ( getProxy() ) {
_isLocked = true;
}
}
/**
* Unlock.
*/
@Override
public void unlock() {
synchronized ( getProxy() ) {
_isLocked = false;
}
}
/**
* Checks if is locked.
*
* @return true, if is locked
*/
@Override
public boolean isLocked() {
return _isLocked;
}
/**
* Checks if is proxy unusable.
*
* @return Description is currently not available!
*/
private boolean isProxyUnusable() {
return ((_isProxyDisposed) || (_isLocked) || (RemoteClientImpl.this.isClosed()) || (RemoteClientImpl.this.isEmpty()));
}
}
// /////////////////////////////////////////////////////////////////////////
// INSTANCE HANDLER:
// /////////////////////////////////////////////////////////////////////////
/**
* Helper class for providing synchronized access to vital data structures.
*/
private class InstanceHandler implements Lockable, BusyAccessor {
// /////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////
private HashMap _instanceIdsToProxyControl = new HashMap();
private HashMap _instanceIdsToProxyDescriptor = new HashMap();
private HashMap _instanceIdsToMethodReplyDescriptor = new HashMap();
private Set _signedOffInstanceIds = Collections.newSetFromMap( new WeakHashMap() );
// /////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////
/**
* Adds a {@link ProxyControl} to this instance associated to the given
* instance TID.
*
* @param aProxyControl A {@link ProxyControl} to be added.
* @param aInstanceId The instance TID to which the {@link ProxyControl}
* is to be associated.
*
* @return True if the operation has been performed successfully.
*/
boolean addProxyControl( ProxyControl aProxyControl, String aInstanceId ) {
synchronized ( this ) {
if ( PERFORM_CONSISTENCY_CHECKS ) {
if ( hasProxyControl( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is already in use by a proxy control; an unused instance TID must be provided." );
}
}
else if ( hasProxyControl( aInstanceId ) ) {
return false;
}
_instanceIdsToProxyControl.put( aInstanceId, aProxyControl );
}
return true;
}
/**
* Adds a {@link ProxyDescriptor} to this instance associated to the
* given instance TID.
*
* @param aPreoxyDescriptor A {@link ProxyDescriptor} to be added.
* @param aInstanceId The instance TID to which the
* {@link ProxyDescriptor} is to be associated.
*
* @return True if the operation has been performed successfully.
*/
boolean addProxyDescriptor( ProxyDescriptor aPreoxyDescriptor, String aInstanceId ) {
synchronized ( this ) {
if ( PERFORM_CONSISTENCY_CHECKS ) {
if ( hasProxyDescriptor( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is already in use by a proxy descriptor; an unused instance TID must be provided." );
}
}
else if ( hasProxyDescriptor( aInstanceId ) ) {
return false;
}
_instanceIdsToProxyDescriptor.put( aInstanceId, aPreoxyDescriptor );
}
return true;
}
/**
* Adds a reply descriptor to this instance associated to the given
* instance TID.
*
* @param aMethodReplyDescriptor An object of type
* BlueprintMethodReplyDescriptor to be added.
* @param aInstanceId The aInstanceId 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 TID <" + aInstanceId + "> is already in use by a method reply descriptor; an unused instance TID must be provided." );
}
}
else if ( hasMethodReplyDescriptor( aInstanceId ) ) {
return false;
}
_instanceIdsToMethodReplyDescriptor.put( aInstanceId, aMethodReplyDescriptor );
return true;
}
/**
* Clears the {@link InstanceHandler} instances.
*/
synchronized void clear() {
_instanceIdsToProxyControl.clear();
_instanceIdsToProxyDescriptor.clear();
_instanceIdsToMethodReplyDescriptor.clear();
_signedOffInstanceIds.clear();
}
/**
* Returns the proxy control associated to the given instance TID.
*
* @param aInstanceId The instance TID which's proxy control is to be
* returned.
*
* @return An object of type BlueprintMethodProxyControl which is
* associated to the given instance TID
*/
ProxyControl getProxyControl( String aInstanceId ) {
synchronized ( this ) {
if ( PERFORM_CONSISTENCY_CHECKS ) {
if ( !hasProxyControl( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is unknown by any proxy control; a valid instance TID must be provided." );
}
}
else if ( !hasProxyControl( aInstanceId ) ) {
return null;
}
}
return _instanceIdsToProxyControl.get( aInstanceId );
}
/**
* Returns the proxy descriptor associated to the given instance TID.
*
* @param aInstanceId The instance TID which's proxy descriptor is to be
* returned.
*
* @return An object of type BlueprintMethodProxyDescriptor which is
* associated to the given instance TID
*/
ProxyDescriptor getProxyDescriptor( String aInstanceId ) {
if ( PERFORM_CONSISTENCY_CHECKS ) {
if ( !hasProxyDescriptor( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is unknown by any proxy descriptor; a valid instance TID must be provided." );
}
}
else if ( !hasProxyDescriptor( aInstanceId ) ) {
return null;
}
return _instanceIdsToProxyDescriptor.get( aInstanceId );
}
/**
* Returns the reply descriptor associated to the given instance TID.
*
* @param aInstanceId The instance TID which's reply descriptor is to be
* returned.
*
* @return An object of type BlueprintMethodReplyDescriptor which is
* associated to the given instance TID
*/
Reply getMethodReplyDescriptor( String aInstanceId ) {
if ( PERFORM_CONSISTENCY_CHECKS ) {
if ( !hasMethodReplyDescriptor( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is unknown by any method reply descriptor; a valid instance TID must be provided." );
}
}
else if ( !hasMethodReplyDescriptor( aInstanceId ) ) {
return null;
}
return _instanceIdsToMethodReplyDescriptor.get( aInstanceId );
}
/**
* Tests if the given instance TID is in the list of the waiting proxy
* controls.
*
* @param aInstanceId The instance TID for which the existence of a
* proxy control is to be tested.
*
* @return True if there is a proxy control associated to the instance
* id.
*/
boolean hasProxyControl( String aInstanceId ) {
return _instanceIdsToProxyControl.containsKey( aInstanceId );
}
/**
* Tests if the given instance TID is in the list of the waiting proxy
* descriptors.
*
* @param aInstanceId The instance TID for which the existence of a
* proxy descriptor is to be tested.
*
* @return True if there is a proxy descriptor associated to the
* instance TID
*/
boolean hasProxyDescriptor( String aInstanceId ) {
return _instanceIdsToProxyDescriptor.containsKey( aInstanceId );
}
/**
* Tests if the given instance id is in the list of the waiting reply
* descriptors.
*
* @param aInstanceId The instance TID for which the existence of a
* reply descriptor is to be tested.
*
* @return True if there is a reply descriptor associated to the
* instance TID
*/
boolean hasMethodReplyDescriptor( String aInstanceId ) {
return _instanceIdsToMethodReplyDescriptor.containsKey( aInstanceId );
}
/**
* Tests if the provided instance TID has already been used and is not
* in use any more.
*
* @param aInstanceId The TID which is to be tested.
*
* @return Description is currently not available!
*/
boolean hasSignedOffInstanceId( String aInstanceId ) {
return (_signedOffInstanceIds.contains( aInstanceId ));
}
/**
* Returns an {@link Iterator} containing the currently managed
* {@link ProxyControl} instances.
*
* @return An {@link Iterator} containing the currently managed
* {@link ProxyControl} instances.
*/
synchronized Iterator proxyControls() {
return new ArrayList( _instanceIdsToProxyControl.values() ).iterator();
}
/**
* Returns an {@link Iterator} containing the currently managed
* {@link ProxyDescriptor} instances.
*
* @return An {@link Iterator} containing the currently managed
* {@link ProxyDescriptor} instances.
*/
Iterator proxyDescriptors() {
return _instanceIdsToProxyDescriptor.values().iterator();
}
/**
* Removes the proxy descriptor from this instance which has been
* associated to the given instance TID.
*
* @param aInstanceId The aInstanceId which's proxy descriptor is to be
* removed.
*
* @return The proxy descriptor which has been removed.
*/
ProxyDescriptor removeProxyDescriptor( String aInstanceId ) {
synchronized ( this ) {
if ( PERFORM_CONSISTENCY_CHECKS ) {
if ( !hasProxyDescriptor( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is unknown by any proy descriptor; a valid instance TID must be provided." );
}
if ( !hasProxyControl( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is unknown by any proxy control; a valid instance TID must be provided." );
}
if ( hasSignedOffInstanceId( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is already invalidated; an unused instance TID must be provided." );
}
}
else {
if ( !hasProxyDescriptor( aInstanceId ) ) {
return null;
}
if ( !hasProxyControl( aInstanceId ) ) {
return null;
}
if ( hasSignedOffInstanceId( aInstanceId ) ) {
return null;
}
}
addSignedOffInstanceId( aInstanceId );
removeProxyControl( aInstanceId );
return _instanceIdsToProxyDescriptor.remove( aInstanceId );
}
}
/**
* Removes the reply descriptor from this instance which has been
* associated to the given instance TID.
*
* @param aInstanceId The aInstanceId which's reply descriptor is to be
* removed.
*
* @return The reply descriptor which has been removed.
*/
Reply removeMethodReplyDescriptor( String aInstanceId ) {
if ( PERFORM_CONSISTENCY_CHECKS ) {
if ( !hasMethodReplyDescriptor( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is unknown by any method reply descriptor; a valid instance TID must be provided." );
}
}
else if ( !hasMethodReplyDescriptor( aInstanceId ) ) {
return null;
}
return _instanceIdsToMethodReplyDescriptor.remove( aInstanceId );
}
/**
* Returns the number if {@link ProxyDescriptor} instances being
* currently managed.
*
* @return The number if {@link ProxyDescriptor} instances being
* currently managed.
*/
int size() {
return _instanceIdsToProxyDescriptor.size();
}
/**
* Adds an instance TID to the list of signed-off instance IDs.
*
* @param aInstanceId The instance TID to be added.
*
* @return True if added, false if already added before.
*/
private boolean addSignedOffInstanceId( String aInstanceId ) {
synchronized ( this ) {
return (_signedOffInstanceIds.add( aInstanceId ));
}
}
/**
* Removes the {@link ProxyControl} identified by the given instance
* TID.
*
* @param aInstanceId The instance TID which's {@link ProxyControl} is
* to be removed.
*
* @return The {@link ProxyControl} which has been removed.
*/
private ProxyControl removeProxyControl( String aInstanceId ) {
synchronized ( this ) {
if ( PERFORM_CONSISTENCY_CHECKS ) {
if ( !hasProxyControl( aInstanceId ) ) {
throw new IllegalArgumentException( "The instance TID <" + aInstanceId + "> is unknown by any proxy control; a valid instance TID must be provided." );
}
}
else if ( !hasProxyControl( aInstanceId ) ) return null;
return _instanceIdsToProxyControl.remove( aInstanceId );
}
}
/**
* Determines whether the currently managed number of
* {@link ProxyDescriptor} instances is zero.
*
* @return True in case the currently managed number of
* {@link ProxyDescriptor} instances is zero.
*/
boolean isEmpty() {
return _instanceIdsToProxyDescriptor.isEmpty();
}
/**
* Checks if is busy.
*
* @return true, if is busy
*/
@Override
synchronized public boolean isBusy() {
if ( !_instanceIdsToMethodReplyDescriptor.isEmpty() ) {
return false;
}
Iterator e = proxyControls();
ProxyControl eProxyControl;
while ( e.hasNext() ) {
eProxyControl = e.next();
if ( eProxyControl.isBusy() ) return true;
}
return false;
}
/**
* Lock.
*/
@Override
public synchronized void lock() {
Iterator e = proxyControls();
while ( e.hasNext() ) {
e.next().lock();
}
}
/**
* Unlock.
*/
@Override
public synchronized void unlock() {
Iterator e = proxyControls();
while ( e.hasNext() ) {
e.next().unlock();
}
}
/**
* Checks if is locked.
*
* @return true, if is locked
*/
@Override
public synchronized boolean isLocked() {
Iterator e = proxyControls();
while ( e.hasNext() ) {
if ( !e.next().isLocked() ) {
return false;
}
}
return true;
}
}
}