예제 #1
0
        public static void EndUpdate(this IUpdateable updateable, System.Threading.CancellationToken token)
        {
            if (Common.IDisposedExtensions.IsNullOrDisposed(updateable))
            {
                throw new System.ArgumentNullException();                                                          //return default(System.Threading.CancellationToken);
            }
            //Ensure a token
            if (object.ReferenceEquals(token, null))
            {
                return;
            }

            //That came from out cancellation source
            if (token.Equals(updateable.UpdateTokenSource.Token).Equals(false))
            {
                throw InvalidStateException;
            }

            // check for manually removed state or a call without an update..
            //if(m_Update.Wait(1, token)) { would check that the event was manually cleared... }

            // acknowledge cancellation
            if (token.IsCancellationRequested)
            {
                throw new System.OperationCanceledException(token);
            }

            //Allow threads to modify
            updateable.ManualResetEvent.Set(); //To unblocked
        }
예제 #2
0
        /// <summary>Send an HTTP request as an asynchronous operation.</summary>
        /// <returns>Returns <see cref="T:System.Threading.Tasks.Task`1" />.The task object representing the asynchronous operation.</returns>
        /// <param name="request">The HTTP request message to send.</param>
        /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="request" /> was null.</exception>
        public override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            if (request.RequestUri == null && BaseAddress == null)
            {
                throw new InvalidOperationException("The request URI must either be set or BaseAddress must be set");
            }

            if (disposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
            hasKickStarted = true;
            PrepareMessage(request);

            //Get Request Options
            RequestOptions requestOptions    = GetRequestOptions(request);
            var            messageProperties = GetMessagingProperties(requestOptions);

            //Determine if message expects a response
            TimeSpan requestTimeout = GetRequestTimeout(requestOptions);
            //TODO: expectingResponse has a slightly different meaning in publisher confirms
            //where timespan may be longer than zero but MessageExpectsReply is false
            //in which case the timeout only applies to how long to wait for the publisher confirmation.
            bool expectingResponse = requestTimeout != TimeSpan.Zero && GetExpectsReply(request);

            //Declare messaging resources
            ExpectedResponse   arrival = null;
            AmqpModelContainer model   = null;
            bool   modelClosed         = false;
            string correlationId       = null;

            //Get channel pool and decide on RPC strategy
            var          pool        = connectionMgr.GetConnectedPool();
            IRPCStrategy rpcStrategy = pool.IsDirectReplyToCapable && !Settings.DisableDirectReplies ? directStrategy : callbackStrategy;

            try
            {
                #region Ensure CallbackQueue is started (If Using CallbackQueue Strategy)

                rpcStrategy.StartStrategy(pool, expectingResponse);

                #endregion

                #region Populate BasicProperties
                //Fill BasicProperties

                BasicProperties basicProperties = new BasicProperties();

                //Set message delivery mode -- Make message persistent if either:
                // 1. Properties.Persistent is true
                // 2. messagingConfig.PersistentMessages is true and Properties.Persistent is null
                // 3. messagingConfig.PersistentMessages is true and Properties.Persistent is true
                if (messageProperties.Persistent == true || (messagingConfig.PersistentMessages && messageProperties.Persistent != false))
                {
                    basicProperties.Persistent = true;
                }

                //Set Exchange Headers
                var exchangeHeaders = messageProperties.Headers ?? messageMapper.GetHeaders(request);
                if (exchangeHeaders != null)
                {
                    basicProperties.Headers = exchangeHeaders;
                }

                if (expectingResponse)
                {
                    //Set CorrelationId
                    correlationId = correlationIdGen.GetNextId();
                    basicProperties.CorrelationId = correlationId;

                    //Set Expiration if messageProperties doesn't override Client.Timeout, RequestOptions and MessageMapper.
                    if (!messageProperties.Expiration.HasValue && requestTimeout != System.Threading.Timeout.InfiniteTimeSpan &&
                        (messagingConfig.MessageExpires == null || messagingConfig.MessageExpires(request)))
                    {
                        if (requestTimeout.TotalMilliseconds > Int32.MaxValue)
                        {
                            basicProperties.Expiration = Int32.MaxValue.ToString();
                        }
                        else
                        {
                            basicProperties.Expiration = ((int)requestTimeout.TotalMilliseconds).ToString();
                        }
                    }
                }
                else if (!messageProperties.Expiration.HasValue && (messagingConfig.MessageExpires == null || messagingConfig.MessageExpires(request)))
                {
                    //Request has a zero timeout and the message mapper indicates it should expire and messageproperties expiration is not set:
                    //Set the expiration to zero which means RabbitMQ will only transmit if there is a consumer ready to receive it.
                    //If there is no ready consumer, RabbitMQ drops the message. See https://www.rabbitmq.com/ttl.html

                    basicProperties.Expiration = "0";
                }

                //Set expiration if set in message properties
                if (messageProperties.Expiration.HasValue)
                {
                    if (messageProperties.Expiration != System.Threading.Timeout.InfiniteTimeSpan)
                    {
                        var expiration = messageProperties.Expiration.Value.Duration();
                        if (expiration.TotalMilliseconds > Int32.MaxValue)
                        {
                            basicProperties.Expiration = Int32.MaxValue.ToString();
                        }
                        else
                        {
                            basicProperties.Expiration = ((int)expiration.TotalMilliseconds).ToString();
                        }
                    }
                    else
                    {
                        //Infinite Timespan indicates that message should never expire
                        basicProperties.ClearExpiration();
                    }
                }

                #endregion

                #region Get Ready to Send Message

                model = rpcStrategy.GetModel(pool, false);

                var serviceName =
                    (requestOptions == null || requestOptions.ServiceName == null)
                    ? (messageMapper.GetServiceName(request) ?? String.Empty).Trim()
                    : requestOptions.ServiceName.Trim();

                RedeclareExchangesAndQueues(model, serviceName);

                //TODO: Check if cancellation token was set before operation even began
                var taskSource = new TaskCompletionSource <HttpResponseMessage>();

                var exchangeKind = ExchangeKind.Direct;
                //TODO: Get ExchangeKind from CLient.Settings.ExchangeKind
                //TODO: Pull exchangeName from a concurrent dictionary that has a key of serviceName, exchangeKind
                //exchangeKind could be an index into arrays that have concurrentDictionaries.
                var exchangeName = AmqpUtils.GetExchangeName(messagingConfig, serviceName, exchangeKind);

                #endregion

                #region Start waiting for response
                //Start waiting for response if a request timeout is set.
                if (expectingResponse)
                {
                    //TODO: Better to just check if cancellationHasbeen requested instead of checking if it's None
                    if (!cancellationToken.Equals(System.Threading.CancellationToken.None))
                    {
                        //TODO: Have cancellationtokens cancel event trigger callbackHandle
                        //In fact turn this whole thing into an extension
                    }

                    arrival = rpcStrategy.PrepareForResponse(correlationId, basicProperties, model, request, requestTimeout, cancellationToken, taskSource);
                }

                #endregion

                #region Send Message
                //TODO: Implement routing to a different exchangeKind via substituting exchangeName
                //Send message
                model.Channel.BasicPublish(exchangeName,
                                           messageProperties.RoutingKey ?? messageMapper.GetRoutingKey(request, exchangeKind) ?? AmqpUtils.GetWorkQueueRoutingKey(),
                                           basicProperties,
                                           request.ToHttpRequestPacket().Serialize());

                //Close channel
                if (!expectingResponse || rpcStrategy.ReturnModelAfterSending)
                {
                    CloseAmqpModel(model);
                    modelClosed = true;
                }

                #endregion

                #region Cleanup if not expecting response
                //Exit with OK result if no request timeout was set.
                if (!expectingResponse)
                {
                    //TODO: Investigate adding a publisher confirm for zero timeout messages so we know that RabbitMQ did pick up the message before replying OK.
                    //Might add extra complexity to this class.

                    //Zero timespan means the client isn't interested in a response
                    taskSource.SetResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)
                    {
                        Content = _emptyByteArrayContent
                    });

                    rpcStrategy.CleanupMessagingResources(correlationId, arrival);
                }

                #endregion

                return(taskSource.Task);
            }
            catch (Exception ex)
            {
                //TODO: Log this

                if (model != null && !modelClosed)
                {
                    if (expectingResponse && model.Flags == ChannelFlags.RPC || model.Flags == ChannelFlags.RPCWithPublisherConfirms)
                    {
                        //Model might still be in use in waiting thread and so unsafe to be recycled
                        model.Discard = true;
                    }

                    CloseAmqpModel(model);
                }

                rpcStrategy.CleanupMessagingResources(correlationId, arrival);

                if (ex is HttpRequestException)
                {
                    throw;
                }
                else
                {
                    throw GetWrappedException("An error occurred while sending the request.", ex);
                }
            }
        }
예제 #3
0
 public static bool UnderModification(this IUpdateable updateable, System.Threading.CancellationToken token)
 {
     return(object.ReferenceEquals(token, null).Equals(false) &&
            Common.IDisposedExtensions.IsNullOrDisposed(updateable).Equals(false) &&
            token.Equals(updateable.UpdateTokenSource.Token));
 }