/// <summary>
        ///   Initializes a new instance of the <see cref="AmqpMessageBatch"/> class.
        /// </summary>
        ///
        /// <param name="options">The set of options to apply to the batch.</param>
        ///
        public AmqpMessageBatch(CreateMessageBatchOptions options)
        {
            Argument.AssertNotNull(options, nameof(options));
            Argument.AssertNotNull(options.MaxSizeInBytes, nameof(options.MaxSizeInBytes));

            Options        = options;
            MaxSizeInBytes = options.MaxSizeInBytes.Value;

            // Initialize the size by reserving space for the batch envelope.

            using AmqpMessage envelope = AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(Enumerable.Empty <ServiceBusMessage>());
            _reservedSize = envelope.SerializedMessageSize;
            _sizeBytes    = _reservedSize;
        }
        /// <summary>
        ///   Sends a set of messages to the associated Queue/Topic using a batched approach.
        /// </summary>
        ///
        /// <param name="messageBatch">The set of messages to send.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>A task to be resolved on when the operation has completed.</returns>
        ///
        public override async Task SendBatchAsync(
            ServiceBusMessageBatch messageBatch,
            CancellationToken cancellationToken)
        {
            AmqpMessage messageFactory() => AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(messageBatch.AsEnumerable <ServiceBusMessage>());

            await _retryPolicy.RunOperation(async (timeout) =>
                                            await SendBatchInternalAsync(
                                                messageFactory,
                                                timeout,
                                                cancellationToken).ConfigureAwait(false),
                                            _connectionScope,
                                            cancellationToken).ConfigureAwait(false);
        }
Beispiel #3
0
        /// <summary>
        ///   Sends a message to the associated Service Bus entity.
        /// </summary>
        ///
        /// <param name="message">A message to send.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        public override async Task SendAsync(
            ServiceBusMessage message,
            CancellationToken cancellationToken)
        {
            Argument.AssertNotNull(message, nameof(message));
            Argument.AssertNotClosed(_closed, nameof(AmqpSender));
            AmqpMessage messageFactory() => AmqpMessageConverter.SBMessageToAmqpMessage(message);

            await _retryPolicy.RunOperation(async (timeout) =>
                                            await SendBatchInternalAsync(
                                                messageFactory,
                                                timeout,
                                                cancellationToken).ConfigureAwait(false),
                                            _connectionScope,
                                            cancellationToken).ConfigureAwait(false);
        }
        /// <summary>
        ///   Attempts to add a message to the batch, ensuring that the size
        ///   of the batch does not exceed its maximum.
        /// </summary>
        ///
        /// <param name="message">The message to attempt to add to the batch.</param>
        ///
        /// <returns><c>true</c> if the message was added; otherwise, <c>false</c>.</returns>
        ///
        public override bool TryAddMessage(ServiceBusMessage message)
        {
            Argument.AssertNotNull(message, nameof(message));
            Argument.AssertNotDisposed(_disposed, nameof(ServiceBusMessageBatch));

            AmqpMessage amqpMessage = null;

            try
            {
                if (BatchMessages.Count == 0)
                {
                    // Initialize the size by reserving space for the batch envelope taking into account the properties from the first
                    // message which will be used to populate properties on the batch envelope.
                    amqpMessage = AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(message, forceBatch: true);
                }
                else
                {
                    amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message);
                }

                // Calculate the size for the message, based on the AMQP message size and accounting for a
                // bit of reserved overhead size.

                var size = _sizeBytes
                           + amqpMessage.SerializedMessageSize
                           + (amqpMessage.SerializedMessageSize <= MaximumBytesSmallMessage
                        ? OverheadBytesSmallMessage
                        : OverheadBytesLargeMessage);

                if (size > MaxSizeInBytes)
                {
                    return(false);
                }

                _sizeBytes = size;
                BatchMessages.Add(message);

                return(true);
            }
            finally
            {
                amqpMessage?.Dispose();
            }
        }
        /// <summary>
        ///    Sends a set of messages to the associated Queue/Topic using a batched approach.
        /// </summary>
        ///
        /// <param name="messageBatch"></param>
        /// <param name="timeout"></param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        internal virtual async Task SendBatchInternalAsync(
            ServiceBusMessageBatch messageBatch,
            TimeSpan timeout,
            CancellationToken cancellationToken)
        {
            var stopWatch = Stopwatch.StartNew();

            AmqpMessage messageFactory() => AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(messageBatch.AsEnumerable <ServiceBusMessage>());

            using (AmqpMessage batchMessage = messageFactory())
            {
                //ServiceBusEventSource.Log.SendStart(Entityname, messageHash);

                string messageHash = batchMessage.GetHashCode().ToString();

                SendingAmqpLink link = await _sendLink.GetOrCreateAsync(UseMinimum(_connectionScope.SessionTimeout, timeout)).ConfigureAwait(false);

                // Validate that the message is not too large to send.  This is done after the link is created to ensure
                // that the maximum message size is known, as it is dictated by the service using the link.

                if (batchMessage.SerializedMessageSize > MaximumMessageSize)
                {
                    throw new ServiceBusException(string.Format(Resources1.MessageSizeExceeded, messageHash, batchMessage.SerializedMessageSize, MaximumMessageSize, _entityName), ServiceBusException.FailureReason.MessageSizeExceeded);
                }

                // Attempt to send the message batch.

                var deliveryTag = new ArraySegment <byte>(BitConverter.GetBytes(Interlocked.Increment(ref _deliveryCount)));
                var outcome     = await link.SendMessageAsync(batchMessage, deliveryTag, AmqpConstants.NullBinary, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                if (outcome.DescriptorCode != Accepted.Code)
                {
                    throw AmqpError.CreateExceptionForError((outcome as Rejected)?.Error, _entityName);
                }

                //ServiceBusEventSource.Log.SendStop(Entityname, messageHash);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();
                stopWatch.Stop();
            }
        }
        /// <summary>
        /// Get all rules associated with the subscription.
        /// </summary>
        ///
        /// <param name="timeout">The per-try timeout specified in the RetryOptions.</param>
        ///
        /// <returns>Returns a list of rules description</returns>
        private async Task <IList <RuleProperties> > GetRulesInternalAsync(TimeSpan timeout)
        {
            var amqpRequestMessage = AmqpRequestMessage.CreateRequest(
                ManagementConstants.Operations.EnumerateRulesOperation,
                timeout,
                null);

            amqpRequestMessage.Map[ManagementConstants.Properties.Top]  = int.MaxValue;
            amqpRequestMessage.Map[ManagementConstants.Properties.Skip] = 0;

            var response = await ManagementUtilities.ExecuteRequestResponseAsync(
                _connectionScope,
                _managementLink,
                amqpRequestMessage,
                null,
                timeout).ConfigureAwait(false);

            var ruleDescriptions = new List <RuleProperties>();

            if (response.StatusCode == AmqpResponseStatusCode.OK)
            {
                var ruleList = response.GetListValue <AmqpMap>(ManagementConstants.Properties.Rules);
                foreach (var entry in ruleList)
                {
                    var amqpRule        = (AmqpRuleDescriptionCodec)entry[ManagementConstants.Properties.RuleDescription];
                    var ruleDescription = AmqpMessageConverter.GetRuleDescription(amqpRule);
                    ruleDescriptions.Add(ruleDescription);
                }
            }
            else if (response.StatusCode == AmqpResponseStatusCode.NoContent)
            {
                // Do nothing. Return empty list;
            }
            else
            {
                throw response.ToMessagingContractException();
            }

            return(ruleDescriptions);
        }
        /// <summary>
        /// Adds a rule to the current subscription to filter the messages reaching from topic to the subscription.
        /// </summary>
        ///
        /// <param name="description">The rule description that provides the rule to add.</param>
        /// <param name="timeout">The per-try timeout specified in the RetryOptions.</param>
        ///
        /// <returns>A task instance that represents the asynchronous add rule operation.</returns>
        private async Task AddRuleInternalAsync(
            RuleProperties description,
            TimeSpan timeout)
        {
            // Create an AmqpRequest Message to add rule
            var amqpRequestMessage = AmqpRequestMessage.CreateRequest(
                ManagementConstants.Operations.AddRuleOperation,
                timeout,
                null);

            amqpRequestMessage.Map[ManagementConstants.Properties.RuleName]        = description.Name;
            amqpRequestMessage.Map[ManagementConstants.Properties.RuleDescription] = AmqpMessageConverter.GetRuleDescriptionMap(description);

            AmqpResponseMessage response = await ManagementUtilities.ExecuteRequestResponseAsync(
                _connectionScope,
                _managementLink,
                amqpRequestMessage,
                timeout).ConfigureAwait(false);

            if (response.StatusCode != AmqpResponseStatusCode.OK)
            {
                throw response.ToMessagingContractException();
            }
        }
Beispiel #8
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="message"></param>
        /// <param name="timeout"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        internal async Task <long> ScheduleMessageInternalAsync(
            ServiceBusMessage message,
            TimeSpan timeout,
            CancellationToken cancellationToken = default)
        {
            var stopWatch = Stopwatch.StartNew();

            using (AmqpMessage amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message))
            {
                var request = AmqpRequestMessage.CreateRequest(
                    ManagementConstants.Operations.ScheduleMessageOperation,
                    timeout,
                    null);

                if (_sendLink.TryGetOpenedObject(out SendingAmqpLink sendLink))
                {
                    request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = sendLink.Name;
                }

                ArraySegment <byte>[] payload = amqpMessage.GetPayload();
                var buffer = new BufferListStream(payload);
                ArraySegment <byte> value = buffer.ReadBytes((int)buffer.Length);

                var entry = new AmqpMap();
                {
                    entry[ManagementConstants.Properties.Message]   = value;
                    entry[ManagementConstants.Properties.MessageId] = message.MessageId;

                    if (!string.IsNullOrWhiteSpace(message.SessionId))
                    {
                        entry[ManagementConstants.Properties.SessionId] = message.SessionId;
                    }

                    if (!string.IsNullOrWhiteSpace(message.PartitionKey))
                    {
                        entry[ManagementConstants.Properties.PartitionKey] = message.PartitionKey;
                    }

                    if (!string.IsNullOrWhiteSpace(message.ViaPartitionKey))
                    {
                        entry[ManagementConstants.Properties.ViaPartitionKey] = message.ViaPartitionKey;
                    }
                }

                request.Map[ManagementConstants.Properties.Messages] = new List <AmqpMap> {
                    entry
                };

                RequestResponseAmqpLink mgmtLink = await _managementLink.GetOrCreateAsync(
                    UseMinimum(_connectionScope.SessionTimeout,
                               timeout.CalculateRemaining(stopWatch.Elapsed)))
                                                   .ConfigureAwait(false);

                using AmqpMessage response = await mgmtLink.RequestAsync(
                          request.AmqpMessage,
                          timeout.CalculateRemaining(stopWatch.Elapsed))
                                             .ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();
                stopWatch.Stop();

                AmqpResponseMessage amqpResponseMessage = AmqpResponseMessage.CreateResponse(response);

                if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.OK)
                {
                    var sequenceNumbers = amqpResponseMessage.GetValue <long[]>(ManagementConstants.Properties.SequenceNumbers);
                    if (sequenceNumbers == null || sequenceNumbers.Length < 1)
                    {
                        throw new ServiceBusException(true, "Could not schedule message successfully.");
                    }

                    return(sequenceNumbers[0]);
                }
                else
                {
                    throw amqpResponseMessage.ToMessagingContractException();
                }
            }
        }
Beispiel #9
0
        /// <summary>
        ///   Receives a batch of <see cref="ServiceBusMessage" /> from the Service Bus entity partition.
        /// </summary>
        ///
        /// <param name="maximumMessageCount">The maximum number of messages to receive in this batch.</param>
        /// <param name="maximumWaitTime">The maximum amount of time to wait to build up the requested message count for the batch; if not specified, the per-try timeout specified by the retry policy will be used.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>The batch of <see cref="ServiceBusMessage" /> from the Service Bus entity partition this consumer is associated with.  If no events are present, an empty enumerable is returned.</returns>
        ///
        public override async Task <IEnumerable <ServiceBusMessage> > ReceiveAsync(
            int maximumMessageCount,
            TimeSpan?maximumWaitTime,
            CancellationToken cancellationToken)
        {
            Argument.AssertNotClosed(_closed, nameof(AmqpConsumer));
            Argument.AssertAtLeast(maximumMessageCount, 1, nameof(maximumMessageCount));

            var receivedMessageCount = 0;
            var failedAttemptCount   = 0;
            var tryTimeout           = _retryPolicy.CalculateTryTimeout(0);
            var waitTime             = (maximumWaitTime ?? tryTimeout);
            var link             = default(ReceivingAmqpLink);
            var retryDelay       = default(TimeSpan?);
            var amqpMessages     = default(IEnumerable <AmqpMessage>);
            var receivedMessages = default(List <ServiceBusMessage>);

            var stopWatch = Stopwatch.StartNew();

            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        ServiceBusEventSource.Log.MessageReceiveStart(EntityName);

                        link = await ReceiveLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, tryTimeout)).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        var messagesReceived = await Task.Factory.FromAsync
                                               (
                            (callback, state) => link.BeginReceiveRemoteMessages(maximumMessageCount, TimeSpan.FromMilliseconds(20), waitTime, callback, state),
                            (asyncResult) => link.EndReceiveMessages(asyncResult, out amqpMessages),
                            TaskCreationOptions.RunContinuationsAsynchronously
                                               ).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        // If event messages were received, then package them for consumption and
                        // return them.

                        if ((messagesReceived) && (amqpMessages != null))
                        {
                            receivedMessages ??= new List <ServiceBusMessage>();

                            foreach (AmqpMessage message in amqpMessages)
                            {
                                //link.DisposeDelivery(message, true, AmqpConstants.AcceptedOutcome);
                                receivedMessages.Add(AmqpMessageConverter.AmqpMessageToSBMessage(message));
                                message.Dispose();
                            }

                            receivedMessageCount = receivedMessages.Count;
                            return(receivedMessages);
                        }

                        // No events were available.

                        return(Enumerable.Empty <ServiceBusMessage>());
                    }
                    catch (ServiceBusException ex) when(ex.Reason == ServiceBusException.FailureReason.ServiceTimeout)
                    {
                        // Because the timeout specified with the request is intended to be the maximum
                        // amount of time to wait for events, a timeout isn't considered an error condition,
                        // rather a sign that no events were available in the requested period.

                        return(Enumerable.Empty <ServiceBusMessage>());
                    }
                    catch (Exception ex)
                    {
                        Exception activeEx = ex.TranslateServiceException(EntityName);

                        // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop.
                        // Otherwise, bubble the exception.

                        ++failedAttemptCount;
                        retryDelay = _retryPolicy.CalculateRetryDelay(activeEx, failedAttemptCount);

                        if ((retryDelay.HasValue) && (!ConnectionScope.IsDisposed) && (!cancellationToken.IsCancellationRequested))
                        {
                            ServiceBusEventSource.Log.MessageReceiveError(EntityName, activeEx.Message);
                            await Task.Delay(UseMinimum(retryDelay.Value, waitTime.CalculateRemaining(stopWatch.Elapsed)), cancellationToken).ConfigureAwait(false);

                            tryTimeout = _retryPolicy.CalculateTryTimeout(failedAttemptCount);
                        }
                        else if (ex is AmqpException)
                        {
                            throw activeEx;
                        }
                        else
                        {
                            throw;
                        }
                    }
                }

                // If no value has been returned nor exception thrown by this point,
                // then cancellation has been requested.

                throw new TaskCanceledException();
            }
            catch (Exception ex)
            {
                ServiceBusEventSource.Log.MessageReceiveError(EntityName, ex.Message);
                throw;
            }
            finally
            {
                stopWatch.Stop();
                ServiceBusEventSource.Log.MessageReceiveComplete(EntityName, receivedMessageCount);
            }
        }
Beispiel #10
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="message"></param>
        /// <param name="timeout"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        internal async Task <long> ScheduleMessageInternalAsync(
            ServiceBusMessage message,
            TimeSpan timeout,
            CancellationToken cancellationToken = default)
        {
            var sendLink = default(SendingAmqpLink);

            try
            {
                using (AmqpMessage amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message))
                {
                    var request = AmqpRequestMessage.CreateRequest(
                        ManagementConstants.Operations.ScheduleMessageOperation,
                        timeout,
                        null);

                    if (_sendLink.TryGetOpenedObject(out sendLink))
                    {
                        request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = sendLink.Name;
                    }

                    ArraySegment <byte>[] payload = amqpMessage.GetPayload();
                    var buffer = new BufferListStream(payload);
                    ArraySegment <byte> value = buffer.ReadBytes((int)buffer.Length);

                    var entry = new AmqpMap();
                    {
                        entry[ManagementConstants.Properties.Message]   = value;
                        entry[ManagementConstants.Properties.MessageId] = message.MessageId;

                        if (!string.IsNullOrWhiteSpace(message.SessionId))
                        {
                            entry[ManagementConstants.Properties.SessionId] = message.SessionId;
                        }

                        if (!string.IsNullOrWhiteSpace(message.PartitionKey))
                        {
                            entry[ManagementConstants.Properties.PartitionKey] = message.PartitionKey;
                        }

                        if (!string.IsNullOrWhiteSpace(message.ViaPartitionKey))
                        {
                            entry[ManagementConstants.Properties.ViaPartitionKey] = message.ViaPartitionKey;
                        }
                    }

                    request.Map[ManagementConstants.Properties.Messages] = new List <AmqpMap> {
                        entry
                    };


                    AmqpResponseMessage amqpResponseMessage = await ManagementUtilities.ExecuteRequestResponseAsync(
                        _connectionScope,
                        _managementLink,
                        request,
                        timeout).ConfigureAwait(false);

                    cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                    if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.OK)
                    {
                        var sequenceNumbers = amqpResponseMessage.GetValue <long[]>(ManagementConstants.Properties.SequenceNumbers);
                        if (sequenceNumbers == null || sequenceNumbers.Length < 1)
                        {
                            throw new ServiceBusException(true, "Could not schedule message successfully.");
                        }

                        return(sequenceNumbers[0]);
                    }
                    else
                    {
                        throw amqpResponseMessage.ToMessagingContractException();
                    }
                }
            }
            catch (Exception exception)
            {
                ExceptionDispatchInfo.Capture(AmqpExceptionHelper.TranslateException(
                                                  exception,
                                                  sendLink?.GetTrackingId(),
                                                  null,
                                                  HasLinkCommunicationError(sendLink)))
                .Throw();

                throw; // will never be reached
            }
        }
Beispiel #11
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="retryPolicy"></param>
        /// <param name="fromSequenceNumber"></param>
        /// <param name="messageCount"></param>
        /// <param name="sessionId"></param>
        /// <param name="receiveLinkName"></param>
        /// <param name="timeout"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        internal async Task <IEnumerable <ServiceBusMessage> > PeekInternal(
            ServiceBusRetryPolicy retryPolicy,
            long?fromSequenceNumber,
            int messageCount,
            string sessionId,
            string receiveLinkName,
            TimeSpan timeout,
            CancellationToken cancellationToken = default)
        {
            var stopWatch = new Stopwatch();

            stopWatch.Start();

            AmqpRequestMessage amqpRequestMessage = AmqpRequestMessage.CreateRequest(
                ManagementConstants.Operations.PeekMessageOperation,
                timeout,
                null);

            await AquireAccessTokenAsync(cancellationToken).ConfigureAwait(false);

            if (receiveLinkName != null)
            {
                // include associated link for service optimization
                amqpRequestMessage.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLinkName;
            }

            amqpRequestMessage.Map[ManagementConstants.Properties.FromSequenceNumber] = fromSequenceNumber ?? LastPeekedSequenceNumber + 1;
            amqpRequestMessage.Map[ManagementConstants.Properties.MessageCount]       = messageCount;

            if (!string.IsNullOrWhiteSpace(sessionId))
            {
                amqpRequestMessage.Map[ManagementConstants.Properties.SessionId] = sessionId;
            }

            RequestResponseAmqpLink link = await ManagementLink.GetOrCreateAsync(
                UseMinimum(ConnectionScope.SessionTimeout,
                           timeout.CalculateRemaining(stopWatch.Elapsed)))
                                           .ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            using AmqpMessage responseAmqpMessage = await link.RequestAsync(
                      amqpRequestMessage.AmqpMessage,
                      timeout.CalculateRemaining(stopWatch.Elapsed))
                                                    .ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            AmqpResponseMessage amqpResponseMessage = AmqpResponseMessage.CreateResponse(responseAmqpMessage);

            var messages = new List <ServiceBusMessage>();

            //AmqpError.ThrowIfErrorResponse(responseAmqpMessage, EntityName);
            if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.OK)
            {
                ServiceBusMessage     message     = null;
                IEnumerable <AmqpMap> messageList = amqpResponseMessage.GetListValue <AmqpMap>(ManagementConstants.Properties.Messages);
                foreach (AmqpMap entry in messageList)
                {
                    var payload     = (ArraySegment <byte>)entry[ManagementConstants.Properties.Message];
                    var amqpMessage = AmqpMessage.CreateAmqpStreamMessage(new BufferListStream(new[] { payload }), true);
                    message = AmqpMessageConverter.AmqpMessageToSBMessage(amqpMessage, true);
                    messages.Add(message);
                }

                if (message != null)
                {
                    LastPeekedSequenceNumber = message.SystemProperties.SequenceNumber;
                }
                return(messages);
            }

            if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.NoContent ||
                (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.NotFound && Equals(AmqpClientConstants.MessageNotFoundError, amqpResponseMessage.GetResponseErrorCondition())))
            {
                return(messages);
            }
            // TODO throw correct exception
            throw new Exception();
        }
Beispiel #12
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="message"></param>
        /// <param name="retryPolicy"></param>
        /// <param name="receiveLinkName"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async Task <long> ScheduleMessageAsync(
            ServiceBusMessage message,
            ServiceBusRetryPolicy retryPolicy,
            string receiveLinkName = null,
            CancellationToken cancellationToken = default)
        {
            var failedAttemptCount = 0;
            var stopWatch          = Stopwatch.StartNew();

            try
            {
                TimeSpan tryTimeout = retryPolicy.CalculateTryTimeout(0);
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        using (AmqpMessage amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message))
                        {
                            var request = AmqpRequestMessage.CreateRequest(
                                ManagementConstants.Operations.ScheduleMessageOperation,
                                tryTimeout,
                                null);

                            if (receiveLinkName != null)
                            {
                                request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLinkName;
                            }

                            ArraySegment <byte>[] payload = amqpMessage.GetPayload();
                            var buffer = new BufferListStream(payload);
                            ArraySegment <byte> value = buffer.ReadBytes((int)buffer.Length);

                            var entry = new AmqpMap();
                            {
                                entry[ManagementConstants.Properties.Message]   = value;
                                entry[ManagementConstants.Properties.MessageId] = message.MessageId;

                                if (!string.IsNullOrWhiteSpace(message.SessionId))
                                {
                                    entry[ManagementConstants.Properties.SessionId] = message.SessionId;
                                }

                                if (!string.IsNullOrWhiteSpace(message.PartitionKey))
                                {
                                    entry[ManagementConstants.Properties.PartitionKey] = message.PartitionKey;
                                }

                                if (!string.IsNullOrWhiteSpace(message.ViaPartitionKey))
                                {
                                    entry[ManagementConstants.Properties.ViaPartitionKey] = message.ViaPartitionKey;
                                }
                            }

                            request.Map[ManagementConstants.Properties.Messages] = new List <AmqpMap> {
                                entry
                            };


                            RequestResponseAmqpLink link = await ManagementLink.GetOrCreateAsync(
                                UseMinimum(ConnectionScope.SessionTimeout,
                                           tryTimeout.CalculateRemaining(stopWatch.Elapsed)))
                                                           .ConfigureAwait(false);

                            using AmqpMessage response = await link.RequestAsync(
                                      request.AmqpMessage,
                                      tryTimeout.CalculateRemaining(stopWatch.Elapsed))
                                                         .ConfigureAwait(false);

                            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();
                            stopWatch.Stop();

                            AmqpResponseMessage amqpResponseMessage = AmqpResponseMessage.CreateResponse(response);

                            if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.OK)
                            {
                                var sequenceNumbers = amqpResponseMessage.GetValue <long[]>(ManagementConstants.Properties.SequenceNumbers);
                                if (sequenceNumbers == null || sequenceNumbers.Length < 1)
                                {
                                    throw new ServiceBusException(true, "Could not schedule message successfully.");
                                }

                                return(sequenceNumbers[0]);
                            }
                            else
                            {
                                throw new Exception();

                                //throw response.ToMessagingContractException();
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop.
                        // Otherwise, mark the exception as active and break out of the loop.

                        ++failedAttemptCount;
                        TimeSpan?retryDelay = retryPolicy.CalculateRetryDelay(ex, failedAttemptCount);

                        if (retryDelay.HasValue && !ConnectionScope.IsDisposed && !cancellationToken.IsCancellationRequested)
                        {
                            ServiceBusEventSource.Log.ScheduleMessageError(EntityName, ex.Message);
                            await Task.Delay(retryDelay.Value, cancellationToken).ConfigureAwait(false);

                            tryTimeout = retryPolicy.CalculateTryTimeout(failedAttemptCount);
                            stopWatch.Reset();
                        }
                    }
                }
                // If no value has been returned nor exception thrown by this point,
                // then cancellation has been requested.

                throw new TaskCanceledException();
            }
            catch (Exception ex)
            {
                ServiceBusEventSource.Log.ScheduleMessageError(EntityName, ex.Message);
                throw;
            }
            finally
            {
                stopWatch.Stop();
                ServiceBusEventSource.Log.ScheduleMessageComplete(EntityName);
            }
        }
Beispiel #13
0
        /// <summary>
        /// Updates the disposition status of deferred messages.
        /// </summary>
        ///
        /// <param name="lockTokens">Message lock tokens to update disposition status.</param>
        /// <param name="timeout"></param>
        /// <param name="dispositionStatus"></param>
        /// <param name="isSessionReceiver"></param>
        /// <param name="sessionId"></param>
        /// <param name="receiveLinkName"></param>
        /// <param name="propertiesToModify"></param>
        /// <param name="deadLetterReason"></param>
        /// <param name="deadLetterDescription"></param>
        internal override async Task DisposeMessageRequestResponseAsync(
            Guid[] lockTokens,
            TimeSpan timeout,
            DispositionStatus dispositionStatus,
            bool isSessionReceiver,
            string sessionId       = null,
            string receiveLinkName = null,
            IDictionary <string, object> propertiesToModify = null,
            string deadLetterReason      = null,
            string deadLetterDescription = null)
        {
            try
            {
                // Create an AmqpRequest Message to update disposition
                var amqpRequestMessage = AmqpRequestMessage.CreateRequest(ManagementConstants.Operations.UpdateDispositionOperation, timeout, null);

                if (receiveLinkName != null)
                {
                    amqpRequestMessage.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLinkName;
                }
                amqpRequestMessage.Map[ManagementConstants.Properties.LockTokens]        = lockTokens;
                amqpRequestMessage.Map[ManagementConstants.Properties.DispositionStatus] = dispositionStatus.ToString().ToLowerInvariant();

                if (deadLetterReason != null)
                {
                    amqpRequestMessage.Map[ManagementConstants.Properties.DeadLetterReason] = deadLetterReason;
                }

                if (deadLetterDescription != null)
                {
                    amqpRequestMessage.Map[ManagementConstants.Properties.DeadLetterDescription] = deadLetterDescription;
                }

                if (propertiesToModify != null)
                {
                    var amqpPropertiesToModify = new AmqpMap();
                    foreach (var pair in propertiesToModify)
                    {
                        if (AmqpMessageConverter.TryGetAmqpObjectFromNetObject(pair.Value, MappingType.ApplicationProperty, out var amqpObject))
                        {
                            amqpPropertiesToModify[new MapKey(pair.Key)] = amqpObject;
                        }
                        else
                        {
                            throw new NotSupportedException(
                                      Resources.InvalidAmqpMessageProperty.FormatForUser(pair.Key.GetType()));
                        }
                    }

                    if (amqpPropertiesToModify.Count > 0)
                    {
                        amqpRequestMessage.Map[ManagementConstants.Properties.PropertiesToModify] = amqpPropertiesToModify;
                    }
                }

                if (!string.IsNullOrWhiteSpace(sessionId))
                {
                    amqpRequestMessage.Map[ManagementConstants.Properties.SessionId] = sessionId;
                }

                if (isSessionReceiver)
                {
                    // TODO -  ThrowIfSessionLockLost();
                }

                var amqpResponseMessage = await ExecuteRequestResponseAsync(amqpRequestMessage, timeout).ConfigureAwait(false);

                if (amqpResponseMessage.StatusCode != AmqpResponseStatusCode.OK)
                {
                    // throw amqpResponseMessage.ToMessagingContractException();
                }
            }
            catch (Exception)
            {
                // throw AmqpExceptionHelper.GetClientException(exception);
                throw;
            }
        }
        public static AmqpMessage BatchSBMessagesAsAmqpMessage(IEnumerable <SBMessage> sbMessages)
        {
            if (sbMessages == null)
            {
                throw Fx.Exception.ArgumentNull(nameof(sbMessages));
            }

            AmqpMessage amqpMessage;
            AmqpMessage firstAmqpMessage = null;
            SBMessage   firstMessage     = null;
            List <Data> dataList         = null;
            var         messageCount     = 0;

            foreach (var sbMessage in sbMessages)
            {
                messageCount++;

                amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(sbMessage);
                if (firstAmqpMessage == null)
                {
                    firstAmqpMessage = amqpMessage;
                    firstMessage     = sbMessage;
                    continue;
                }

                if (dataList == null)
                {
                    dataList = new List <Data> {
                        ToData(firstAmqpMessage)
                    };
                }

                dataList.Add(ToData(amqpMessage));
            }

            if (messageCount == 1 && firstAmqpMessage != null)
            {
                firstAmqpMessage.Batchable = true;
                return(firstAmqpMessage);
            }

            amqpMessage = AmqpMessage.Create(dataList);
            amqpMessage.MessageFormat = AmqpConstants.AmqpBatchedMessageFormat;

            if (firstMessage.MessageId != null)
            {
                amqpMessage.Properties.MessageId = firstMessage.MessageId;
            }
            if (firstMessage.SessionId != null)
            {
                amqpMessage.Properties.GroupId = firstMessage.SessionId;
            }

            if (firstMessage.PartitionKey != null)
            {
                amqpMessage.MessageAnnotations.Map[AmqpMessageConverter.PartitionKeyName] =
                    firstMessage.PartitionKey;
            }

            if (firstMessage.ViaPartitionKey != null)
            {
                amqpMessage.MessageAnnotations.Map[AmqpMessageConverter.ViaPartitionKeyName] =
                    firstMessage.ViaPartitionKey;
            }

            amqpMessage.Batchable = true;
            return(amqpMessage);
        }