Beispiel #1
0
        /// <summary>
        /// Initializes a new instance of the BrokerResponseEnumerator class
        /// </summary>
        /// <param name="responseService">Client the broker's response service</param>
        /// <param name="callbackManager">Manages all callbacks for the session</param>
        /// <param name="typedMessageConverter">Typed message convertor</param>
        /// <param name="newResponsesTimeout">Timeout waiting for responses</param>
        /// <param name="action">Action of the response messages</param>
        /// <param name="clientId">client id</param>
        internal BrokerResponseEnumerator(
            SessionBase session,
            IResponseService responseServiceClient,
            CallbackManager callbackManager,
            TimeSpan newResponsesTimeout,
            BrokerClientBase brokerClient,
            string clientId,
            string action,
            string replyAction)
        {
            Type responseType = typeof(TMessage);

            this.newResponsesTimeout     = newResponsesTimeout;
            this.callbackManager         = callbackManager;
            this.callbackManagerId       = this.callbackManager.Register(this);
            this.endOfResponsesConverter = TypedMessageConverter.Create(typeof(EndOfResponses), Constant.EndOfMessageAction);
            this.brokerClient            = brokerClient;
            this.clientId = clientId;
            this.session  = session;

            // If caller specified a responseType or both actions, make sure the type or the actions are valid before starting the enum by getting
            //  the deserialization objects now. Else the deseralization objects are retrieved dynamically based on response messages
            if ((typeof(TMessage) != typeof(object)) || (!string.IsNullOrEmpty(action) && !string.IsNullOrEmpty(replyAction)))
            {
                string errorMessage;

                // If replyAction isnt known, GetResponseMessageInfo will return it. This is used to track which response deserialization is currently cached in the enumerator
                if (!this.brokerClient.GetResponseMessageInfo(ref action, ref replyAction, ref responseType, out this.typedMessageConverter, out this.faultCollection, out errorMessage))
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            this.action                     = action;
            this.replyAction                = replyAction;
            this.responseType               = responseType;
            this.responseServiceClient      = responseServiceClient;
            this.responseServiceAsyncClient = responseServiceClient as IResponseServiceAsync;
            this.flushResponsesTimer        = new Timer(this.FlushResponseTimerCallback, null, Timeout.Infinite, Timeout.Infinite);

            if (this.session.Info is SessionInfo && (this.session.Info as SessionInfo).UseAzureQueue == true)
            {
                this.GetMoreResponses(true, Constant.GetResponse_All);
            }
            else
            {
                this.GetMoreResponses(true, ResponseWindowSize * 2);
            }
        }
        /// <summary>
        /// Receives response messages from broker's response service
        /// </summary>
        /// <param name="message">Response message</param>
        public void SendResponse(Message message)
        {
            try
            {
                int currentResponseCount = 0;

                // Reset the heartbeat since operation just succeeded
                this.session.ResetHeartbeat();

                // Reset timeout timer since a response was received
                if (this.responseHandlerTimeoutTimer != null)
                {
                    this.responseHandlerTimeoutTimer.Change(this.responseHanderTimeout, 0);
                }

                // Bug #15946: handling client side exception thrown from WebResponseHandler(when talking to rest service)
                if (string.Equals(message.Headers.Action, Constant.WebAPI_ClientSideException, StringComparison.Ordinal))
                {
                    Exception e = message.Properties[Constant.WebAPI_ClientSideException] as Exception;
                    this.InvokeCallback(new BrokerResponse <TMessage>(e, message.Headers.RelatesTo));
                    return;
                }

                // TODO: Consider whether user callback should get an EOM message
                if (!Utility.IsEOM(message))
                {
                    // If the handler is closed, timed out, closed or broker heartbeat signaled, dont forward any more requests to the user.
                    // NOTE: If some responses already slipped through its OK because we prefer that over adding a lock here
                    if (this.shuttingDown)
                    {
                        return;
                    }

                    // Create a BrokerResponse object wrapper for the message object. A copy of the message must be created
                    MessageBuffer messageBuffer = null;
                    try
                    {
                        messageBuffer = message.CreateBufferedCopy(Constant.MaxBufferSize);
                    }
                    catch (Exception e)
                    {
                        Utility.LogError("AsyncResponseCallback.SendResponse received exception - {0}", e);
                        this.InvokeCallback(new BrokerResponse <TMessage>(e, message.Headers.RelatesTo));
                        return;
                    }

                    BrokerResponse <TMessage> brokerResponse = this.CreateResponse(BrokerClientBase.GetActionFromResponseMessage(message),
                                                                                   !message.IsFault ? message.Headers.Action : String.Empty,
                                                                                   messageBuffer,
                                                                                   message.Headers.RelatesTo == null ? SoaHelper.GetMessageId(message) : message.Headers.RelatesTo);

                    BrokerResponse <TMessage> lastResponse = null;

                    if (this.ignoreIsLastResponseProperty)
                    {
                        lastResponse = brokerResponse;
                    }
                    else
                    {
                        // Atomically swap out the last response.
                        lastResponse = Interlocked.Exchange <BrokerResponse <TMessage> >(ref this.lastBrokerResponse, brokerResponse);
                    }

                    // If there was a previous last response
                    if (lastResponse != null)
                    {
                        // Send it to the callback
                        this.InvokeCallback(lastResponse);

                        // Increment response count
                        currentResponseCount = Interlocked.Increment(ref this.currentResponseCount);
                    }

                    // If the caller wants all messages at once, do so.
                    // Else we always request ResponseWindowSize * 2 messages. If we get to
                    // the end of a window, request another
                    if (this.responseWindowSize != Constant.GetResponse_All && currentResponseCount != 0 &&
                        (currentResponseCount + 1) % this.responseWindowSize == 0)
                    {
                        // Call async version and block on completion in order to workaround System.Net.Socket bug #750028
                        try
                        {
                            SessionBase.TraceSource.TraceInformation("GetResponse : currentResponseCount {0} : clientId {1}", currentResponseCount, this.clientId);
                            this.responseService.GetResponses(
                                this.action,
                                this.callbackManagerId,
                                GetResponsePosition.Current,
                                this.responseWindowSize,
                                this.clientId);
                        }
                        catch (FaultException <SessionFault> e)
                        {
                            throw Utility.TranslateFaultException(e);
                        }
                    }
                }

                // If this is a client purged fault, return that exception in a BrokerResponse
                else
                {
                    TypedMessageConverter endOfResponsesConverter = TypedMessageConverter.Create(typeof(EndOfResponses), Constant.EndOfMessageAction);
                    EndOfResponses        endOfResponses          = (EndOfResponses)endOfResponsesConverter.FromMessage(message);

                    switch (endOfResponses.Reason)
                    {
                    case EndOfResponsesReason.ClientPurged:
                        this.InvokeCallback(new BrokerResponse <TMessage>(SessionBase.ClientPurgedException, new UniqueId(Guid.Empty)));
                        break;

                    case EndOfResponsesReason.ClientTimeout:
                        this.InvokeCallback(new BrokerResponse <TMessage>(SessionBase.ClientTimeoutException, new UniqueId(Guid.Empty)));
                        break;

                    default:
                        // Save how many responses are expected (minus 1 for the last response)
                        System.Diagnostics.Debug.Assert(endOfResponses.Count > 0, "Response count should also be positive number");
                        this.expectedResponseCount = endOfResponses.Count - 1;
                        break;
                    }
                }

                if (!this.ignoreIsLastResponseProperty)
                {
                    // Check to see if we receive the EOM message and all the responses.
                    // If so pass last response to callback marked as such. Also make
                    // sure this isnt called again with a lock.
                    if (this.expectedResponseCount == this.currentResponseCount)
                    {
                        lock (this.lastResponseLock)
                        {
                            if (this.expectedResponseCount == this.currentResponseCount)
                            {
                                // Mark the response as the last one
                                this.lastBrokerResponse.isLastResponse = true;

                                // Send last response to callback. Note since currentResponseCount
                                // is incremented after responses are sent to the callback, the
                                // callback will not be processing any other responses at this time.
                                this.InvokeCallback(this.lastBrokerResponse);

                                this.expectedResponseCount = -1;
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                // Log and eat unhandled user exceptions from their calback
                Utility.LogError("Unhandled exception processing response - {0}", e);
            }
        }
        /// <summary>
        /// Initializes a new instance of the AsyncResponseCallback class
        /// </summary>
        /// <param name="session">indicating the session instance</param>
        /// <param name="responseService">Broker's response service</param>
        /// <param name="callbackManager">Reference to internal callback manager</param>
        /// <param name="brokerClient">indicating the broker client instance</param>
        /// <param name="clientId">indicating the client id</param>
        /// <param name="action">Action of responses to retrieve</param>
        /// <param name="replyAction">indicating the reply action</param>
        /// <param name="callback">User's callback delegate</param>
        /// <param name="callbackState">indicating the callback state</param>
        /// <param name="state">indicating the async state</param>
        /// <param name="ignoreIsLastResponseProperty">indicating if ignore the LastResponse property</param>
        public AsyncResponseCallback(
            SessionBase session,
            IResponseService responseService,
            CallbackManager callbackManager,
            BrokerClientBase brokerClient,
            string clientId,
            string action,
            string replyAction,
            BrokerResponseHandler <TMessage> callback,
            BrokerResponseStateHandler <TMessage> callbackState,
            object state,
            bool ignoreIsLastResponseProperty)
        {
            Type responseType = typeof(TMessage);

            // If session is communicating to the Web service, we cannot get all responses
            // at one time by indicating responseWindowSize to -1.
            // This is because broker would not delete responses immediately if it is web
            // service connecting, it only delete responses when the next GetResponse come
            // in. If session API asks for ALL responses, it might lead to hang because of
            // throttling.

            if (brokerClient.TransportScheme == TransportScheme.WebAPI)
            {
                this.responseWindowSize = BrokerResponseEnumerator <TMessage> .ResponseWindowSize;
            }
            else if (brokerClient.TransportScheme == TransportScheme.Http)
            {
                if (session.Info is SessionInfo && (session.Info as SessionInfo).UseAzureQueue == true)
                {
                    this.responseWindowSize = Constant.GetResponse_All;
                }
                else
                {
                    this.responseWindowSize = BrokerResponseEnumerator <TMessage> .ResponseWindowSize;
                }
            }
            else
            {
                this.responseWindowSize = Constant.GetResponse_All;
            }

            this.responseService = responseService;
            this.brokerClient    = brokerClient;
            this.clientId        = clientId;
            this.callback        = callback;
            this.callbackState   = callbackState;
            this.callbackManager = callbackManager;
            this.state           = state;
            this.session         = session;
            this.ignoreIsLastResponseProperty = ignoreIsLastResponseProperty;

            // If caller specified a responseType or both actions, make sure the type or the actions are valid before starting the enum by getting
            //  the deserialization objects now. Else the deseralization objects are retrieved dynamically based on response messages
            if ((typeof(TMessage) != typeof(Object)) || (!String.IsNullOrEmpty(action) && !String.IsNullOrEmpty(replyAction)))
            {
                string errorMessage;

                // If replyAction isnt known, GetResponseMessageInfo will return it. This is used to track which response deserialization is currently cached in the enumerator
                if (!this.brokerClient.GetResponseMessageInfo(ref action, ref replyAction, ref responseType, out this.typedMessageConverter, out this.faultCollection, out errorMessage))
                {
                    throw new InvalidOperationException(errorMessage);
                }
            }

            this.action       = action;
            this.replyAction  = replyAction;
            this.responseType = responseType;
        }
Beispiel #4
0
        /// <summary>
        /// Receives new response messages from broker
        /// </summary>
        /// <param name="response">Response message</param>
        void IResponseServiceCallback.SendResponse(Message response)
        {
            MessageBuffer  messageBuffer       = null;
            EndOfResponses endOfResponses      = null;
            Exception      clientSideException = null;
            bool           isEOM = false;

            // Reset the heartbeat since operation just succeeded
            this.session.ResetHeartbeat();

            try
            {
                // Check whether this is an EOM message
                isEOM = Utility.IsEOM(response);

                if (!isEOM)
                {
                    // If not EOM, create a copy of the message's buffer so it can be deserialized
                    // A copy is needed because WCF will dispose this message when the callback returns
                    // Alternatively we could deserialize the entire message but we want
                    //  callback to be quick and we may wind up deserializing messages the user
                    //  never looks at (i.e. just checks user data or exits enum early)
                    try
                    {
                        messageBuffer = response.CreateBufferedCopy(Constant.MaxBufferSize);
                    }
                    catch (Exception e)
                    {
                        SessionBase.TraceSource.TraceEvent(TraceEventType.Error, 0, "[Session:{0}][BrokerResponseEnumerator] Failed to create message buffer from the response message: {1}", this.session.Id, e);
                        clientSideException = e;
                    }
                }
                else
                {
                    // If EOM, deserialize EndOfResponses message
                    endOfResponses = (EndOfResponses)this.endOfResponsesConverter.FromMessage(response);
                }

                string messageId = String.Empty;
                try
                {
                    messageId = SoaHelper.GetMessageId(response).ToString();
                }
                catch
                {
                    // Swallow possible ObjectDisposedException
                }

                SessionBase.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "[Session:{0}][BrokerResponseEnumerator] Response received from broker. IsEOM = {1}, MessageId = {2}", this.session.Id, isEOM, messageId);

                lock (this.responsesLock)
                {
                    // If the enumerator is disposing or disposed, return
                    if (this.isDisposeCalled)
                    {
                        return;
                    }

                    // If this is not an EOM message
                    if (!isEOM)
                    {
                        string actionFromResponse = BrokerClientBase.GetActionFromResponseMessage(response);

                        // Save the response in the current window
                        MessageWindowItem messageItem = new MessageWindowItem(
                            messageBuffer,
                            actionFromResponse,
                            !response.IsFault ? response.Headers.Action : String.Empty,
                            response.Headers.RelatesTo == null ? SoaHelper.GetMessageId(response) : response.Headers.RelatesTo);

                        // Bug #15946: handling client side exception thrown from WebResponseHandler(when talking to rest service)
                        // To propagate the client side exception to BrokerResponseEnumerator api, store the exception in MessageWindowItem
                        if (string.Equals(response.Headers.Action, Constant.WebAPI_ClientSideException, StringComparison.Ordinal))
                        {
                            clientSideException = response.Properties[Constant.WebAPI_ClientSideException] as Exception;
                        }
                        messageItem.CarriedException = clientSideException;

                        this.currentReceiveWindow.Enqueue(messageItem);

                        // Increment the current number of responses
                        this.currentResponseCount++;

                        // Lower number of outstanding requests
                        if (Interlocked.Decrement(ref this.outstandingRequestCount) == 0)
                        {
                            try
                            {
                                this.GetMoreResponses(false, ResponseWindowSize);
                            }
                            catch (Exception e)
                            {
                                SessionBase.TraceSource.TraceEvent(TraceEventType.Error,
                                                                   0, "[Session:{0}][BrokerResponseEnumerator] Failed to get more responses in SendResponse context: {1}", this.session.Id, e);

                                // Send broker down signal since connection to broker might be bad
                                ((IResponseServiceCallback)this).SendBrokerDownSignal(false);
                            }
                        }

                        // If the current window is full or there are no more responses
                        if (this.currentReceiveWindow.Count == ResponseWindowSize ||
                            this.currentResponseCount == this.totalResponseCount)
                        {
                            // Disable the flush timer
                            this.flushResponsesTimer.Change(Timeout.Infinite, Timeout.Infinite);

                            // Immediately flush the receive window
                            this.FlushCurrentReceiveWindow();
                        }

                        // Otherwise reset the flushtimer to the full interval
                        else
                        {
                            this.flushResponsesTimer.Change(this.flushResponsesTimerInterval, Timeout.Infinite);
                        }
                    }
                    else
                    {
                        // If we already received an EOM, ignore any others. We may get multiple EOM since
                        // more responses are requested until we get EOM. This can lead to overrequesting.
                        if (this.totalResponseCount != -1)
                        {
                            return;
                        }

                        // Save total response count
                        this.totalResponseCount   = endOfResponses.Count;
                        this.endOfResponsesReason = endOfResponses.Reason;

                        // If there are no more responses
                        if (this.currentResponseCount == this.totalResponseCount)
                        {
                            // Make the current receive window available to enumerator if it has responses
                            if (this.currentReceiveWindow.Count != 0)
                            {
                                this.responsesWindows.Enqueue(this.currentReceiveWindow);
                                this.currentReceiveWindow = new Queue <MessageWindowItem>();
                            }

                            // Notify enumerator that a new window is available. Notify even if
                            // its empty since it allows MoveNext to exit and return there are no
                            // more items
                            this.newResponseWindowOrEOM.Set();
                        }
                    }
                }
            }
            catch (Exception e)
            {
                // If we get an exception here log and dispose the enum
                this.Dispose();

                SessionBase.TraceSource.TraceEvent(TraceEventType.Error, 0, "[Session:{0}][BrokerResponseEnumerator] Unhandled exception in response enumerator callback :{1}", this.session.Id, e);
            }
        }