예제 #1
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);
            }
        }
예제 #2
0
        /// <summary>
        ///   <para>Advances the enumerator to the next element of the collection.</para>
        /// </summary>
        /// <returns>
        ///   <para>
        ///     <see cref="System.Boolean" /> object that specifies whether the enumerator successfully advanced to the next element.
        /// True indicates that the enumerator successfully advanced to the next element.
        /// False indicates that the enumerator passed the end of the collection.</para>
        /// </returns>
        /// <exception cref="System.InvalidOperationException">
        ///   <para>The collection was modified after the enumerator was created. </para>
        /// </exception>
        /// <remarks>
        ///   <para>After you create the enumerator or call the
        ///
        /// <see cref="Reset" /> method, the enumerator is positioned before the first element of the collection, and the first call to the
        ///
        /// <see cref="MoveNext" /> method moves the enumerator over the first element of the collection.</para>
        ///   <para>If
        ///
        /// <see cref="MoveNext" /> passes the end of the collection, the enumerator is positioned after the last element in the collection, and
        /// <see cref="MoveNext" /> returns
        /// False. When the enumerator is at this position, subsequent calls to
        /// <see cref="MoveNext" /> also returns
        /// False until you call
        /// <see cref="Reset" />.</para>
        ///   <para>An enumerator remains valid as long as the collection remains unchanged. If you make changes to
        /// the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call to
        /// <see cref="MoveNext" /> or
        /// <see cref="Reset" /> results in an
        /// <see cref="System.InvalidOperationException" />.</para>
        /// </remarks>
        /// <seealso cref="Reset" />
        /// <seealso cref="System.Collections.IEnumerator.MoveNext" />
        public bool MoveNext()
        {
            // If the corresponding BrokerClient is closed or disposed, return no more items
            if (this.close)
            {
                if (!this.session.IsBrokerAvailable)
                {
                    throw SessionBase.GetHeartbeatException(this.isBrokerNodeDown);
                }
                else
                {
                    return(false);
                }
            }

            ThrowIfNeed(this.endOfResponsesReason);

            bool flag1, flag2, flag3;

            lock (this.responsesLock)
            {
                flag1 = this.currentEnumWindow == null || this.currentEnumWindow.Count == 0;
                flag2 = this.responsesWindows.Count == 0;
                flag3 = this.totalResponseCount != this.currentResponseCount;
            }

            // If there is no current window for enumeration or the current one is empty
            if (flag1)
            {
                // If there are no other windows ready for enumeration
                if (flag2)
                {
                    // If all responses have not been returned
                    if (flag3)
                    {
                        // Wait for more response windows to be ready for enumeration
                        int ret = WaitHandle.WaitAny(new WaitHandle[] { this.newResponseWindowOrEOM, this.signalBrokerDown }, this.newResponsesTimeout, false);

                        // If the timeout expires,
                        if (ret == WaitHandle.WaitTimeout)
                        {
                            // Check to see if the receive window has messages
                            if (this.currentReceiveWindow.Count == 0)
                            {
                                // If not return that there are no more messages
                                // RICHCI: 6/7/9 : Changed from returning false to throwing timeout exception
                                //  so user can distinguish between timeout and no more responses
                                throw new TimeoutException(String.Format(SR.TimeoutGetResponse, this.newResponsesTimeout.TotalMilliseconds));
                            }
                            else
                            {
                                // If there are response messages, move the partially filled receive window to
                                // the "ready to read" response windows queue
                                lock (this.responsesLock)
                                {
                                    this.responsesWindows.Enqueue(this.currentReceiveWindow);
                                    this.currentReceiveWindow = new Queue <MessageWindowItem>();
                                }

                                // Fall through to pulling a new window for the current enum window
                            }
                        }

                        // If the broker down event is signaled, throw exception
                        if (ret == 1)
                        {
                            var faultException = this.exceptionCauseBrokerDown as FaultException <SessionFault>;

                            if (faultException != null)
                            {
                                throw Utility.TranslateFaultException(faultException);
                            }
                            else
                            {
                                throw SessionBase.GetHeartbeatException(this.isBrokerNodeDown);
                            }
                        }

                        ThrowIfNeed(this.endOfResponsesReason);

                        // If the BrokerClient is disposed or closed, return there are no more responses
                        if (this.close)
                        {
                            return(false);
                        }

                        // If all we were waiting for was EOM, return there are no more responses
                        if (this.responsesWindows.Count == 0)
                        {
                            return(false);
                        }

                        // Else fall through to pulling a new window for the current enum window
                    }
                    else
                    {
                        // If there are no responses pending, return that there are no more responses
                        return(false);
                    }
                }

                int responseWindowCount = 0;

                // Get a new current enum window
                lock (this.responsesLock)
                {
                    this.currentEnumWindow = this.responsesWindows.Dequeue();
                    responseWindowCount    = this.responsesWindows.Count;
                }

                // If this was the last window
                if (responseWindowCount == 0)
                {
                    // Reset the event so subsequent calls to Move wait
                    this.newResponseWindowOrEOM.Reset();

                    // Do not ask for more responses. The enumerator would ask for more
                    // responses only when outstanding responses is 0.
                }
            }

            // Dequeue the current message
            MessageWindowItem messageWindowItem = this.currentEnumWindow.Dequeue();

            // Bug #15946: handling client side exception thrown from WebResponseHandler(when talking to rest service)
            if (messageWindowItem.CarriedException != null)
            {
                throw messageWindowItem.CarriedException;
            }

            // Create a BrokerResponse object from Message
            this.currentResponse = this.CreateResponse(messageWindowItem.ActionFromResponse, messageWindowItem.ReplyAction, messageWindowItem.MessageBuffer, messageWindowItem.RelatesTo);

            // Return true since Current will have a new element
            return(true);
        }