/// <summary> /// Removes the rule on the subscription identified by <paramref name="ruleName" />. /// </summary> /// /// <param name="ruleName">Name of the rule</param> /// <param name="timeout">The per-try timeout specified in the RetryOptions.</param> /// /// <returns>A task instance that represents the asynchronous remove rule operation.</returns> private async Task RemoveRuleInternalAsync( string ruleName, TimeSpan timeout) { // Create an AmqpRequest Message to remove rule var amqpRequestMessage = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.RemoveRuleOperation, timeout, null); amqpRequestMessage.Map[ManagementConstants.Properties.RuleName] = ruleName; AmqpResponseMessage response = await ManagementUtilities.ExecuteRequestResponseAsync( _connectionScope, _managementLink, amqpRequestMessage, timeout).ConfigureAwait(false); if (response.StatusCode != AmqpResponseStatusCode.OK) { throw response.ToMessagingContractException(); } }
/// <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(); } }
/// <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(); } } }
/// <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 } }
/// <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(); }
/// <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); } }
/// <summary> /// /// </summary> /// <param name="sequenceNumber"></param> /// <param name="retryPolicy"></param> /// <param name="receiveLinkName"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public override async Task CancelScheduledMessageAsync( long sequenceNumber, ServiceBusRetryPolicy retryPolicy, string receiveLinkName = null, CancellationToken cancellationToken = default) { var failedAttemptCount = 0; var stopWatch = Stopwatch.StartNew(); try { var tryTimeout = retryPolicy.CalculateTryTimeout(0); while (!cancellationToken.IsCancellationRequested) { try { var request = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.CancelScheduledMessageOperation, tryTimeout, null); if (receiveLinkName != null) { request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLinkName; } request.Map[ManagementConstants.Properties.SequenceNumbers] = new[] { sequenceNumber }; 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) { throw new Exception(); //throw response.ToMessagingContractException(); } return; } 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.CancelScheduledMessageError(EntityName, ex.Message); await Task.Delay(retryDelay.Value, cancellationToken).ConfigureAwait(false); tryTimeout = retryPolicy.CalculateTryTimeout(failedAttemptCount); stopWatch.Reset(); } else { throw ex; } } } // 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.CancelScheduledMessageError(EntityName, ex.Message); throw; } finally { stopWatch.Stop(); ServiceBusEventSource.Log.CancelScheduledMessageComplete(EntityName); } }
/// <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; } }