/// <summary>
        /// Registers the broadcast sender that sends a message to several receivers.
        /// Returns Guid (it's like URI for an MBR object) assigned for this sender that can be used
        /// to delete this broadcast sender later.
        /// </summary>
        /// <param name="generalBroadcastSender">The broadcast sender which sends a message via "true" multicast channel.</param>
        public string Add(GeneralBroadcastSender generalBroadcastSender)
        {
            // just add it as it would be the usual receiver
            ReceiverInfo receiverInfo = new ReceiverInfo();

            receiverInfo.GeneralBroadcastSender = generalBroadcastSender;
            receiverInfo.Local = false;

            string uri = Guid.NewGuid().ToString("N") + "/" + generalBroadcastSender.Court;

            receiverInfo.MbrUri = uri;

            // register it
            using (WriterAutoLocker writer = new WriterAutoLocker(this._readerWriterLock))
            {
                this._cachedReceiversInfoArray = null;
                this._receivers[uri]           = receiverInfo;
            }

            // LOG:
            BinaryLogWriter binaryLogWriter = GenuineLoggingServices.BinaryLogWriter;

            if (binaryLogWriter != null && binaryLogWriter[LogCategory.BroadcastEngine] > 0)
            {
                binaryLogWriter.WriteBroadcastEngineEvent(LogCategory.BroadcastEngine, "Dispatcher.Add",
                                                          LogMessageType.BroadcastRecipientAdded, null, null, null, null,
                                                          GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                                                          null, null, false, this, null, true, receiverInfo,
                                                          generalBroadcastSender.Court, generalBroadcastSender.ITransportContext.HostIdentifier,
                                                          "The \"true\" multicast sender is added.");
            }

            return(uri);
        }
        /// <summary>
        /// Removes all recipients and broadcast senders.
        /// </summary>
        public void Clear()
        {
            using (WriterAutoLocker writer = new WriterAutoLocker(this._readerWriterLock))
            {
                this._cachedReceiversInfoArray = null;

                // collect all receivers to unregister the sponsor
                object[] receiverInfoItems = new object[this._receivers.Count];
                this._receivers.Values.CopyTo(receiverInfoItems, 0);
                GenuineThreadPool.QueueUserWorkItem(new WaitCallback(this.UnregisterSponsor), receiverInfoItems, true);

                this._receivers.Clear();
            }
        }
        /// <summary>
        /// Removes the receiver or the broadcast sender associated with the specified uri.
        /// Returns false if there is no such receiver found in the list of receivers.
        /// </summary>
        /// <param name="uri">The uri of the receiver.</param>
        public bool Remove(string uri)
        {
            if (uri == null || uri.Length <= 0)
            {
                return(false);
            }

            using (WriterAutoLocker writer = new WriterAutoLocker(this._readerWriterLock))
            {
                // check if it is in the list
                if (!this._receivers.ContainsKey(uri))
                {
                    return(false);
                }

                this._cachedReceiversInfoArray = null;

                ReceiverInfo receiverInfo = (ReceiverInfo)this._receivers[uri];
                this._receivers.Remove(uri);

                // LOG:
                BinaryLogWriter binaryLogWriter = GenuineLoggingServices.BinaryLogWriter;
                if (binaryLogWriter != null && binaryLogWriter[LogCategory.BroadcastEngine] > 0)
                {
                    binaryLogWriter.WriteBroadcastEngineEvent(LogCategory.BroadcastEngine, "Dispatcher.Remove",
                                                              LogMessageType.BroadcastRecipientRemoved, null, null, receiverInfo.DbgRemoteHost, null,
                                                              GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                                                              null, null, false, this, null, true, receiverInfo,
                                                              uri, null,
                                                              "The broadcast recipient or \"true\" broadcast sender is removed from the list of recipients.");
                }

                // Sponsor is being deleted in another thread; otherwise client disconnection
                // will cause dispatcher to be freezed for lengthy time out
                GenuineThreadPool.QueueUserWorkItem(new WaitCallback(this.DeleteSponsorFromTheObjectAndFireEvent), receiverInfo, false);
            }

            return(true);
        }
        /// <summary>
        /// Registers the receiver and associate the provided object with it.
        /// Returns false if the receiver has already been registered.
        /// WARNING: does not check whether the receiver supports the required interface (via Reflection)
        /// because this check requires client's dll.
        /// </summary>
        /// <param name="obj">The receiver being registered.</param>
        /// <param name="tag">The object associated with the receiver. This object is accessible when receiver is being unregistered or during filtering.</param>
        /// <param name="remoteGenuineUri">The uri of the remote host provided by any of Genuine Channels.</param>
        /// <param name="transportContext">The transport context of the remote host.</param>
        /// <returns>False if the receiver has been already registered.</returns>
        public bool Add(MarshalByRefObject obj, object tag, string remoteGenuineUri, ITransportContext transportContext)
        {
            if (obj == null)
            {
                throw new ArgumentNullException("obj");
            }

            BinaryLogWriter binaryLogWriter = GenuineLoggingServices.BinaryLogWriter;

            // check if it is in the list
            string uri = RemotingServices.GetObjectUri(obj);

            if (uri == null && !RemotingServices.IsObjectOutOfAppDomain(obj))
            {
                // it was not marshalled
                RemotingServices.Marshal(obj);
                uri = RemotingServices.GetObjectUri(obj);
            }

            using (ReaderAutoLocker reader = new ReaderAutoLocker(this._readerWriterLock))
            {
                if (this._receivers.ContainsKey(uri))
                {
                    return(false);
                }
            }

            // this check can not be performed because client's dll is required
//			// check on the interface
//			bool supportInterface = false;
//			foreach(Type interfaceType in obj.GetType().GetInterfaces())
//				if (interfaceType == this._interfaceToSupport)
//				{
//					supportInterface = true;
//					break;
//				}
//			if (! supportInterface)
//				throw GenuineExceptions.Get_Broadcast_ObjectDoesNotSupportDestinationInterface();

            // adds the object to the receiver list
            ReceiverInfo receiverInfo = new ReceiverInfo();

            receiverInfo.MbrObject = obj;
            receiverInfo.MbrUri    = uri;
            receiverInfo.Tag       = tag;

            if (binaryLogWriter != null)
            {
                try
                {
                    if (receiverInfo.MbrObject != null)
                    {
                        receiverInfo.DbgRemoteHost = GenuineUtility.FetchHostInformationFromMbr(receiverInfo.MbrObject);
                    }
                }
                catch (Exception ex)
                {
                    binaryLogWriter.WriteImplementationWarningEvent("Dispatcher.Add",
                                                                    LogMessageType.Error, ex, GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                                                                    "Can't get HostInformation from MbrObject.");
                }
            }

            ObjRef objRef = receiverInfo.MbrObject.CreateObjRef(typeof(MarshalByRefObject));

            receiverInfo.Local = objRef.IsFromThisAppDomain();

            // cache object's info to speed up sending thru Genuine Channels
            if (!receiverInfo.Local)
            {
                if (remoteGenuineUri != null)
                {
                    receiverInfo.IClientChannelSink = new GenuineTcpClientTransportSink(remoteGenuineUri, transportContext);
                }
                else
                {
                    // check whether the client sink has registered itself on this MBR
                    receiverInfo.IClientChannelSink = ChannelServices.GetChannelSinkProperties(obj)["GC_TS"] as IClientChannelSink;
                    if (receiverInfo.IClientChannelSink == null)
                    {
                        throw GenuineExceptions.Get_Broadcast_ClientSinkIsUnknown();
                    }
                }

                // object uri
                receiverInfo.SerializedObjRef = objRef;

//				// and shell's uri
//				string shellUri;
//				ITransportContext iTransportContext;
//				GenuineUtility.FetchChannelUriFromMbr(obj, out shellUri, out iTransportContext);
//				if (shellUri == null)
//					throw GenuineExceptions.Get_Send_NoSender(objRef.URI);
//
//				receiverInfo.ReceiverUri = shellUri;
            }

            // LOG:
            if (binaryLogWriter != null && binaryLogWriter[LogCategory.BroadcastEngine] > 0)
            {
                binaryLogWriter.WriteBroadcastEngineEvent(LogCategory.BroadcastEngine, "Dispatcher.Add",
                                                          LogMessageType.BroadcastRecipientAdded, null, null, receiverInfo.DbgRemoteHost, null,
                                                          GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                                                          null, null, false, this, null, true, receiverInfo,
                                                          null, null,
                                                          "The broadcast recipient is added.");
            }

            // register the sponsor to prevent unexpected reclaiming
            ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);

            if (lease != null)
            {
                lease.Register(this.GlobalSponsor);
            }

            // and register it
            using (WriterAutoLocker writer = new WriterAutoLocker(this._readerWriterLock))
            {
                this._cachedReceiversInfoArray = null;
                this._receivers[uri]           = receiverInfo;
            }
            return(true);
        }
        /// <summary>
        /// Removes the receiver or the broadcast sender associated with the specified uri.
        /// Returns false if there is no such receiver found in the list of receivers.
        /// </summary>
        /// <param name="uri">The uri of the receiver.</param>
        public bool Remove(string uri)
        {
            if (uri == null || uri.Length <= 0)
                return false;

            using (WriterAutoLocker writer = new WriterAutoLocker(this._readerWriterLock))
            {
                // check if it is in the list
                if (! this._receivers.ContainsKey(uri))
                    return false;

                this._cachedReceiversInfoArray = null;

                ReceiverInfo receiverInfo = (ReceiverInfo) this._receivers[uri];
                this._receivers.Remove(uri);

                // LOG:
                BinaryLogWriter binaryLogWriter = GenuineLoggingServices.BinaryLogWriter;
                if ( binaryLogWriter != null && binaryLogWriter[LogCategory.BroadcastEngine] > 0 )
                {
                    binaryLogWriter.WriteBroadcastEngineEvent(LogCategory.BroadcastEngine, "Dispatcher.Remove",
                        LogMessageType.BroadcastRecipientRemoved, null, null, receiverInfo.DbgRemoteHost, null,
                        GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                        null, null, false, this, null, true, receiverInfo,
                        uri, null,
                        "The broadcast recipient or \"true\" broadcast sender is removed from the list of recipients.");
                }

                // Sponsor is being deleted in another thread; otherwise client disconnection
                // will cause dispatcher to be freezed for lengthy time out
                GenuineThreadPool.QueueUserWorkItem(new WaitCallback(this.DeleteSponsorFromTheObjectAndFireEvent), receiverInfo, false);
            }

            return true;
        }
        /// <summary>
        /// Removes all recipients and broadcast senders.
        /// </summary>
        public void Clear()
        {
            using (WriterAutoLocker writer = new WriterAutoLocker(this._readerWriterLock))
            {
                this._cachedReceiversInfoArray = null;

                // collect all receivers to unregister the sponsor
                object[] receiverInfoItems = new object[this._receivers.Count];
                this._receivers.Values.CopyTo(receiverInfoItems, 0);
                GenuineThreadPool.QueueUserWorkItem(new WaitCallback(this.UnregisterSponsor), receiverInfoItems, true);

                this._receivers.Clear();
            }
        }
        /// <summary>
        /// Registers the broadcast sender that sends a message to several receivers.
        /// Returns Guid (it's like URI for an MBR object) assigned for this sender that can be used
        /// to delete this broadcast sender later.
        /// </summary>
        /// <param name="generalBroadcastSender">The broadcast sender which sends a message via "true" multicast channel.</param>
        public string Add(GeneralBroadcastSender generalBroadcastSender)
        {
            // just add it as it would be the usual receiver
            ReceiverInfo receiverInfo = new ReceiverInfo();
            receiverInfo.GeneralBroadcastSender = generalBroadcastSender;
            receiverInfo.Local = false;

            string uri = Guid.NewGuid().ToString("N") + "/" + generalBroadcastSender.Court;
            receiverInfo.MbrUri = uri;

            // register it
            using (WriterAutoLocker writer = new WriterAutoLocker(this._readerWriterLock))
            {
                this._cachedReceiversInfoArray = null;
                this._receivers[uri] = receiverInfo;
            }

            // LOG:
            BinaryLogWriter binaryLogWriter = GenuineLoggingServices.BinaryLogWriter;
            if ( binaryLogWriter != null && binaryLogWriter[LogCategory.BroadcastEngine] > 0 )
            {
                binaryLogWriter.WriteBroadcastEngineEvent(LogCategory.BroadcastEngine, "Dispatcher.Add",
                    LogMessageType.BroadcastRecipientAdded, null, null, null, null,
                    GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                    null, null, false, this, null, true, receiverInfo,
                    generalBroadcastSender.Court, generalBroadcastSender.ITransportContext.HostIdentifier,
                    "The \"true\" multicast sender is added.");
            }

            return uri;
        }
        /// <summary>
        /// Registers the receiver and associate the provided object with it.
        /// Returns false if the receiver has already been registered.
        /// WARNING: does not check whether the receiver supports the required interface (via Reflection) 
        /// because this check requires client's dll.
        /// </summary>
        /// <param name="obj">The receiver being registered.</param>
        /// <param name="tag">The object associated with the receiver. This object is accessible when receiver is being unregistered or during filtering.</param>
        /// <param name="remoteGenuineUri">The uri of the remote host provided by any of Genuine Channels.</param>
        /// <param name="transportContext">The transport context of the remote host.</param>
        /// <returns>False if the receiver has been already registered.</returns>
        public bool Add(MarshalByRefObject obj, object tag, string remoteGenuineUri, ITransportContext transportContext)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");

            BinaryLogWriter binaryLogWriter = GenuineLoggingServices.BinaryLogWriter;

            // check if it is in the list
            string uri = RemotingServices.GetObjectUri(obj);
            if (uri == null && ! RemotingServices.IsObjectOutOfAppDomain(obj))
            {
                // it was not marshalled
                RemotingServices.Marshal(obj);
                uri = RemotingServices.GetObjectUri(obj);
            }

            using (ReaderAutoLocker reader = new ReaderAutoLocker(this._readerWriterLock))
            {
                if (this._receivers.ContainsKey(uri))
                    return false;
            }

            // this check can not be performed because client's dll is required
            //			// check on the interface
            //			bool supportInterface = false;
            //			foreach(Type interfaceType in obj.GetType().GetInterfaces())
            //				if (interfaceType == this._interfaceToSupport)
            //				{
            //					supportInterface = true;
            //					break;
            //				}
            //			if (! supportInterface)
            //				throw GenuineExceptions.Get_Broadcast_ObjectDoesNotSupportDestinationInterface();

            // adds the object to the receiver list
            ReceiverInfo receiverInfo = new ReceiverInfo();
            receiverInfo.MbrObject = obj;
            receiverInfo.MbrUri = uri;
            receiverInfo.Tag = tag;

            if (binaryLogWriter != null)
            {
                try
                {
                    if (receiverInfo.MbrObject != null)
                        receiverInfo.DbgRemoteHost = GenuineUtility.FetchHostInformationFromMbr(receiverInfo.MbrObject);
                }
                catch(Exception ex)
                {
                    binaryLogWriter.WriteImplementationWarningEvent("Dispatcher.Add",
                        LogMessageType.Error, ex, GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                        "Can't get HostInformation from MbrObject.");
                }
            }

            ObjRef objRef = receiverInfo.MbrObject.CreateObjRef(typeof(MarshalByRefObject));
            receiverInfo.Local = objRef.IsFromThisAppDomain();

            // cache object's info to speed up sending thru Genuine Channels
            if (! receiverInfo.Local)
            {
                if (remoteGenuineUri != null)
                {
                    receiverInfo.IClientChannelSink = new GenuineTcpClientTransportSink(remoteGenuineUri, transportContext);
                }
                else
                {
                    // check whether the client sink has registered itself on this MBR
                    receiverInfo.IClientChannelSink = ChannelServices.GetChannelSinkProperties(obj)["GC_TS"] as IClientChannelSink;
                    if (receiverInfo.IClientChannelSink == null)
                        throw GenuineExceptions.Get_Broadcast_ClientSinkIsUnknown();
                }

                // object uri
                receiverInfo.SerializedObjRef = objRef;

            //				// and shell's uri
            //				string shellUri;
            //				ITransportContext iTransportContext;
            //				GenuineUtility.FetchChannelUriFromMbr(obj, out shellUri, out iTransportContext);
            //				if (shellUri == null)
            //					throw GenuineExceptions.Get_Send_NoSender(objRef.URI);
            //
            //				receiverInfo.ReceiverUri = shellUri;
            }

            // LOG:
            if ( binaryLogWriter != null && binaryLogWriter[LogCategory.BroadcastEngine] > 0 )
            {
                binaryLogWriter.WriteBroadcastEngineEvent(LogCategory.BroadcastEngine, "Dispatcher.Add",
                    LogMessageType.BroadcastRecipientAdded, null, null, receiverInfo.DbgRemoteHost, null,
                    GenuineUtility.CurrentThreadId, Thread.CurrentThread.Name,
                    null, null, false, this, null, true, receiverInfo,
                    null, null,
                    "The broadcast recipient is added.");
            }

            // register the sponsor to prevent unexpected reclaiming
            ILease lease = (ILease) RemotingServices.GetLifetimeService(obj);
            if (lease != null)
                lease.Register(this.GlobalSponsor);

            // and register it
            using (WriterAutoLocker writer = new WriterAutoLocker(this._readerWriterLock))
            {
                this._cachedReceiversInfoArray = null;
                this._receivers[uri] = receiverInfo;
            }
            return true;
        }