Example #1
0
        /// <summary>
        /// EndOfMessage arrives
        /// </summary>
        /// <param name="msgCount">indicating the msg count</param>
        /// <param name="timeout">indicating the timeout</param>
        public void EndOfMessage(long msgCount, int batchId, int timeout)
        {
            lock (this.lockState)
            {
                if (this.state != BrokerClientState.ClientConnected)
                {
                    BrokerTracing.EtwTrace.LogBrokerClientRejectEOM(this.sharedData.BrokerInfo.SessionId, this.clientId, this.state.ToString());
                    switch (this.state)
                    {
                    case BrokerClientState.GetResponse:
                        ThrowHelper.ThrowSessionFault(SOAFaultCode.Broker_EOMReject_GetResponse, SR.EOMReject_GetResponse);
                        break;

                    case BrokerClientState.EndRequests:
                        ThrowHelper.ThrowSessionFault(SOAFaultCode.Broker_EOMReject_EndRequests, SR.EOMReject_EndRequests);
                        break;

                    default:
                        ThrowHelper.ThrowSessionFault(SOAFaultCode.Broker_EOMRejected, SR.EOMRejected, this.state.ToString());
                        break;
                    }
                }

                // Stop the timer
                this.timeoutManager.Stop();

                try
                {
                    this.Flush(msgCount, batchId, timeout, true);
                }
                catch (Exception e)
                {
                    BrokerTracing.TraceError("[BrokerClient] Failed to flush messages: {0}", e);

                    // If EndRequests throws exception from broker queue, the
                    // timer is closed and never reactivated without user action.
                    // Thus, the broker client would leak forever and the broker
                    // process would run away if user does not purge the client
                    // or close the session later.
                    this.timeoutManager.RegisterTimeout(this.sharedData.Config.Monitor.ClientIdleTimeout, this.TimeoutToDisconnected, this.state);
                    throw;
                }

                // State will change if flush succeeded
                // If flush failed, the state won't change
                BrokerTracing.TraceInfo("[BrokerClient] Client {0}: State: ClientConnected ==> EndOfMessage", this.clientId);
                this.state = BrokerClientState.EndRequests;
                this.endOfMessageCalled = true;
            }
        }
Example #2
0
        private void OnAllRequestsProcessed(object state)
        {
            lock (this.lockState)
            {
                BrokerTracing.EtwTrace.LogBrokerClientAllRequestDone(this.sharedData.BrokerInfo.SessionId, this.clientId);
                this.state = BrokerClientState.AllRequestDone;
            }

            // Instead of invoking AllRequestDone event handler directly, we fist make a copy of it and
            // then check for null. This ensures that we'll not fire NullReferenceException in case all
            // AllRequestDone subscribers are removed.
            EventHandler allRequestDoneCopy = this.AllRequestDone;

            if (allRequestDoneCopy != null)
            {
                allRequestDoneCopy(this, EventArgs.Empty);
            }

            bool flag = false;

            lock (this.connectedInstance)
            {
                if (this.connectedInstance.Count != 0)
                {
                    this.timeoutManager.RegisterTimeout(this.sharedData.Config.Monitor.ClientIdleTimeout, this.TimeoutToDisconnected, this.state);
                    flag = true;
                }
            }

            if (!flag && !this.singletonInstanceConnected)
            {
                // Bug 5193: If there was no channel connected to this client while all requests are done, close the client immediately
                BrokerTracing.EtwTrace.LogBrokerClientDisconnected(this.sharedData.BrokerInfo.SessionId, this.clientId, "all request done and there's no frontend connected");
                this.SyncDisconnect();
            }
            else if (!flag && this.singletonInstanceConnected)
            {
                // Bug 17241: If there's no connected instance and this.singletonInstanceConnceted
                // is true (this situation could happen when on azure, since it is the REST
                // instance connecting, singletonInstanceConnected is always set to true so that
                // the broker client would not dispose itself as the real client might be still
                // exist), we need to register timeout so that it could be timed out. Otherwise,
                // the client might be leaked forever so that the entire broker would runaway.
                this.timeoutManager.RegisterTimeout(this.sharedData.Config.Monitor.ClientIdleTimeout, this.TimeoutToDisconnected, this.state);
            }
        }
Example #3
0
        /// <summary>
        /// Sync disconnect the broker client
        /// </summary>
        private void SyncDisconnect()
        {
            bool flag = false;

            lock (this.lockState)
            {
                BrokerTracing.TraceInfo("[BrokerClient] Client {0}: SyncDisconnect the state is {1} and this.queue.ProcessingRequestsCount/ProcessedRequestsCount/FlushedRequestsCount are {2}/{3}/{4}.", this.clientId, this.state.ToString(), this.queue.ProcessingRequestsCount, this.queue.ProcessedRequestsCount, this.queue.FlushedRequestsCount);

                if (this.state == BrokerClientState.GetResponse ||
                    this.state == BrokerClientState.AllRequestDone ||
                    (this.state == BrokerClientState.ClientConnected &&
                     ((this.queue.ProcessingRequestsCount == 0 && this.queue.ProcessedRequestsCount >= this.queue.FlushedRequestsCount) ||
                      (this.v2ProxyClient && this.observer.AllRequestProcessed()))))  // check if the broker client is idle for v2 proxy client, in which case responses are directly sent back without persist in the queue.
                {
                    flag       = true;
                    this.state = BrokerClientState.Disconnected;

                    if (this.timeoutManager != null)
                    {
                        this.timeoutManager.Stop();
                    }
                }
            }

            if (flag)
            {
                BrokerTracing.TraceInfo("[BrokerClient] Client {0}: Disconnect client.", this.clientId);

                if (this.responsesClient != null)
                {
                    this.responsesClient.ClientDisconnect(false);
                }

                if (this.ClientDisconnected != null)
                {
                    this.ClientDisconnected(this, EventArgs.Empty);
                }
            }
            else if (!this.sharedData.BrokerInfo.Durable)
            {
                //update the flush counters for persisted and committed for interactive session when client disconnected without removal.
                this.queue.FlushCount();
            }
        }
Example #4
0
        /// <summary>
        /// Helper function that maps BrokerClientState to BrokerClientStatus
        /// </summary>
        /// <param name="state"></param>
        internal static BrokerClientStatus MapToBrokerClientStatus(BrokerClientState state)
        {
            switch (state)
            {
            case BrokerClientState.ClientConnected:
                return(BrokerClientStatus.Ready);

            case BrokerClientState.EndRequests:
                return(BrokerClientStatus.Processing);

            case BrokerClientState.AllRequestDone:
            case BrokerClientState.GetResponse:
                return(BrokerClientStatus.Finished);

            default:
                Debug.Fail("[BrokerClient] BrokerClient should not be in other states.");
                return(default(BrokerClientStatus));
            }
        }
Example #5
0
        /// <summary>
        /// Gets broker client status
        /// </summary>
        /// <param name="clientId">indicating the client id</param>
        /// <returns>returns the broker client status</returns>
        public BrokerClientStatus GetBrokerClientStatus(string clientId)
        {
            try
            {
                ParamCheckUtility.ThrowIfNull(clientId, "clientId");
                ParamCheckUtility.ThrowIfTooLong(clientId.Length, "clientId", Constant.MaxClientIdLength, SR.ClientIdTooLong);
                ParamCheckUtility.ThrowIfNotMatchRegex(ParamCheckUtility.ClientIdValid, clientId, "clientId", SR.InvalidClientId);

                BrokerTracing.TraceEvent(System.Diagnostics.TraceEventType.Information, 0, "[BrokerController] Get broker client status for Client {0}", clientId);

                this.ThrowIfDisposed();
                this.CheckAuth();

                BrokerClientState state = this.GetClient(clientId).State;
                return(BrokerClient.MapToBrokerClientStatus(state));
            }
            catch (Exception e)
            {
                BrokerTracing.TraceEvent(System.Diagnostics.TraceEventType.Error, 0, "[BrokerController] Get broker client status for Client {1} Failed: {0}", e, clientId);
                throw TranslateException(e);
            }
        }
Example #6
0
        /// <summary>
        /// Dispose the broker client
        /// </summary>
        /// <param name="disposing">indicating whether it is disposing</param>
        private void Dispose(bool disposing)
        {
            this.disposing = disposing;
            try
            {
                // Set state
                this.state = BrokerClientState.Disconnected;

                // Copy all the connected instances out of lock
                List <object> connectedList = null;
                lock (this.connectedInstance)
                {
                    connectedList = new List <object>(this.connectedInstance.Keys);
                }

                foreach (object instance in connectedList)
                {
                    try
                    {
                        BrokerTracing.TraceInfo("[BrokerClient] Try to close the connected instance: {0} ({1})", instance.ToString(), instance.GetHashCode());
                        if (instance is IDisposable)
                        {
                            // BrokerController
                            ((IDisposable)instance).Dispose();
                        }
                        else if (instance is IChannel)
                        {
                            Utility.AsyncCloseICommunicationObject((ICommunicationObject)instance);
                        }
                        else
                        {
                            Debug.Fail("[BrokerClient] Connected instance must be IDisposable or IChannel.");
                        }
                    }
                    catch (Exception e)
                    {
                        BrokerTracing.TraceWarning("[BrokerClient] Failed to close the connected instance: {0}", e);
                    }
                }

                if (disposing)
                {
                    if (this.queue != null)
                    {
                        this.queue.OnEvent -= this.Queue_OnEvent;
                        this.queue.OnPutResponsesSuccessEvent -= this.Queue_OnPutResponsesSuccessEvent;
                        this.queue.OnFatalExceptionEvent      -= this.Queue_OnFatalExceptionEvent;

                        //for durable session, the queue is closed unless there were flushed requests, dispose if EOM was called.
                        if (this.sharedData.BrokerInfo.Durable)
                        {
                            if (this.queue.FlushedRequestsCount == 0)
                            {
                                BrokerTracing.TraceInfo("[BrokerClient] durable session broker client {0} close the queue.", this.clientId);
                                //if not ever flushed, reduce the count of all requests in the queue
                                this.observer.ReduceUncommittedCounter(this.queue.AllRequestsCount);
                                this.queue.Close();
                            }
                            else if (this.endOfMessageCalled)
                            {
                                // Only dispose the broker queue if it is a durable session. Non-durable broker queue for
                                // interactive session should be kept/reused and closed when closing the broker domain.
                                //
                                // Note (related bug #14224): logic in BrokerClient.SyncDisconnect() ensures that BrokerClient
                                // instance will not be disposed if EndRequests is called (this.state = BrokerClientState.EndRequests).
                                // So below code snippet could only be reached on broker entry exiting.
                                this.observer.ReduceUncommittedCounter(this.queue.AllRequestsCount - this.queue.FlushedRequestsCount);
                                this.queue.Dispose();
                            }
                        }
                        else //for interactive session, close the queue if EOM is not called.
                        {
                            if (!this.endOfMessageCalled)
                            {
                                BrokerTracing.TraceInfo("[BrokerClient] interactive session broker client {0} close the queue.", this.clientId);
                                if (!this.v2ProxyClient)
                                {
                                    //reduce the count of all unflushed requests in the queue
                                    this.observer.ReduceUncommittedCounter(this.queue.AllRequestsCount - this.queue.FlushedRequestsCount);
                                }
                                this.queue.Close();
                            }
                        }

                        this.queue = null;

                        this.batchMessageIds.Clear();
                    }
                }

                if (this.responsesClient != null)
                {
                    this.responsesClient.Dispose();
                    this.responsesClient = null;
                }

                if (this.timeoutManager != null)
                {
                    this.timeoutManager.Dispose();
                    this.timeoutManager = null;
                }

                // do not dispose this.lockForDiscardRequests for this may block the threads in the write queue, which may lead to the hang when broker entry exits.
                // do not dispose this.batchIdChangedEvent for any wait for the event would hang
            }
            catch (Exception e)
            {
                BrokerTracing.TraceWarning("[BrokerClient] Exception thrown while disposing: {0}", e);
            }
        }
Example #7
0
        /// <summary>
        /// Initializes a new instance of the BrokerClient class
        /// </summary>
        /// <param name="clientId">indicating the client Id</param>
        /// <param name="userName">indicating the user name</param>
        /// <param name="queueFactory">indicating the queue factory</param>
        /// <param name="observer">indicating the observer</param>
        /// <param name="stateManager">indicating the state manager</param>
        /// <param name="monitor">indicating the monitor</param>
        /// <param name="sharedData">indicating the shared data</param>
        public BrokerClient(string clientId, string userName, BrokerQueueFactory queueFactory, BrokerObserver observer, BrokerStateManager stateManager, ServiceJobMonitorBase monitor, SharedData sharedData)
        {
            bool isNewCreated;

            // Set the "signletonInstanceConnected" property if
            // SessionStartInfo.AutoDisposeBrokerClient is set. This property is only possibly to
            // be set to true after if HTTP transport scheme is specified. And it is by design so.
            if (sharedData.StartInfo.AutoDisposeBrokerClient.HasValue)
            {
                this.singletonInstanceConnected = !sharedData.StartInfo.AutoDisposeBrokerClient.Value;
            }

            this.clientId               = clientId;
            this.v2ProxyClient          = clientId.StartsWith(FrontEndBase.DefaultClientPrefix);
            this.sharedData             = sharedData;
            this.observer               = observer;
            this.monitor                = monitor;
            this.stateManager           = stateManager;
            this.stateManager.OnFailed += new BrokerStateManager.SessionFailedEventHandler(this.StateManager_OnFailed);

            try
            {
                this.queue = queueFactory.GetPersistQueueByClient(clientId, userName, out isNewCreated);
            }
            catch (BrokerQueueException e)
            {
                // Catch the exception about the username not match and translte it to session fault
                if (e.ErrorCode == (int)BrokerQueueErrorCode.E_BQ_USER_NOT_MATCH)
                {
                    ThrowHelper.ThrowSessionFault(SOAFaultCode.AccessDenied_BrokerQueue, SR.AccessDenied_BrokerQueue, clientId, userName);
                }
                else
                {
                    throw;
                }
            }

            this.queue.OnEvent += new BrokerQueueEventDelegate(this.Queue_OnEvent);
            this.queue.OnPutResponsesSuccessEvent += new EventHandler <PutResponsesSuccessEventArgs>(this.Queue_OnPutResponsesSuccessEvent);
            this.queue.OnFatalExceptionEvent      += new EventHandler <ExceptionEventArgs>(this.Queue_OnFatalExceptionEvent);
            this.timeoutManager = new TimeoutManager("BrokerClient " + clientId);
            BrokerTracing.EtwTrace.LogBrokerClientCreated(this.sharedData.BrokerInfo.SessionId, clientId);

            if (this.queue.IsAllRequestsProcessed || monitor.ServiceJobState == ServiceJobState.Finished)
            {
                // If the queue has processed all the request or the service job is finished, the broker client can only get responses
                this.state = BrokerClientState.GetResponse;
                this.endOfMessageCalled = true;
                BrokerTracing.EtwTrace.LogBrokerClientStateTransition(this.sharedData.BrokerInfo.SessionId, this.clientId, "GetResponse");
                this.timeoutManager.RegisterTimeout(this.sharedData.Config.Monitor.ClientIdleTimeout, this.TimeoutToDisconnected, this.state);
            }
            else
            {
                if (!this.queue.EOMReceived)
                {
                    // If EndOfMessage is not received, the client is in the ClientConnected state and is ready to accept messages
                    this.state = BrokerClientState.ClientConnected;
                    BrokerTracing.EtwTrace.LogBrokerClientStateTransition(this.sharedData.BrokerInfo.SessionId, this.clientId, "ClientConnected");
                    this.timeoutManager.RegisterTimeout(this.sharedData.Config.Monitor.ClientIdleTimeout, this.TimeoutToDisconnected, this.state);
                }
                else
                {
                    // If EndOfMessage has been received, the client is in the EndOfMessage state and does not accept any more requests.
                    this.state = BrokerClientState.EndRequests;
                    this.endOfMessageCalled = true;
                    BrokerTracing.EtwTrace.LogBrokerClientStateTransition(this.sharedData.BrokerInfo.SessionId, this.clientId, "EndOfMessage");
                }
            }
        }