private async Task SinglePhaseCommitAsync(SinglePhaseEnlistment singlePhaseEnlistment)
        {
            try
            {
                FaultTolerantAmqpObject <Controller> faultTolerantController = _connectionScope.TransactionController;
                Controller controller = await faultTolerantController.GetOrCreateAsync(_timeout)
                                        .ConfigureAwait(false);

                await controller.DischargeAsync(AmqpTransactionId, fail : false).ConfigureAwait(false);

                singlePhaseEnlistment.Committed();
                ServiceBusEventSource.Log.TransactionDischarged(
                    _transactionId,
                    AmqpTransactionId,
                    false);
                await CloseAsync().ConfigureAwait(false);
            }
            catch (Exception e)
            {
                Exception exception = AmqpExceptionHelper.TranslateException(e, null);
                ServiceBusEventSource.Log.TransactionDischargeException(
                    _transactionId,
                    AmqpTransactionId,
                    exception);
                singlePhaseEnlistment.InDoubt(exception);
            }
        }
        public static Exception ToMessagingContractException(this AmqpMessage responseMessage, AmqpResponseStatusCode statusCode)
        {
            AmqpSymbol errorCondition    = AmqpExceptionHelper.GetResponseErrorCondition(responseMessage, statusCode);
            var        statusDescription = responseMessage.ApplicationProperties.Map[ManagementConstants.Response.StatusDescription] as string ?? errorCondition.Value;

            return(AmqpExceptionHelper.ToMessagingContractException(errorCondition.Value, statusDescription));
        }
示例#3
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="sequenceNumbers"></param>
        /// <param name="timeout"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        internal async Task CancelScheduledMessageInternalAsync(
            long[] sequenceNumbers,
            TimeSpan timeout,
            CancellationToken cancellationToken = default)
        {
            var sendLink = default(SendingAmqpLink);

            try
            {
                var request = AmqpRequestMessage.CreateRequest(
                    ManagementConstants.Operations.CancelScheduledMessageOperation,
                    timeout,
                    null);

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

                request.Map[ManagementConstants.Properties.SequenceNumbers] = sequenceNumbers;

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

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                if (amqpResponseMessage.StatusCode != AmqpResponseStatusCode.OK)
                {
                    throw amqpResponseMessage.ToMessagingContractException();
                }
            }
            catch (Exception exception)
            {
                ExceptionDispatchInfo.Capture(AmqpExceptionHelper.TranslateException(
                                                  exception,
                                                  sendLink?.GetTrackingId(),
                                                  null,
                                                  HasLinkCommunicationError(sendLink)))
                .Throw();

                throw; // will never be reached
            }
        }
示例#4
0
        private async Task RollbackAsync(SinglePhaseEnlistment singlePhaseEnlistment)
        {
            try
            {
                Controller controller = await GetController(_timeout).ConfigureAwait(false);

                await controller.DischargeAsync(AmqpTransactionId, fail : true).ConfigureAwait(false);

                singlePhaseEnlistment.Aborted();
                ServiceBusEventSource.Log.TransactionDischarged(_transactionId, AmqpTransactionId, true);
            }
            catch (Exception e)
            {
                Exception exception = AmqpExceptionHelper.TranslateException(e, null);
                ServiceBusEventSource.Log.TransactionDischargeException(
                    _transactionId,
                    AmqpTransactionId,
                    exception);
                singlePhaseEnlistment.Aborted(exception);
            }
        }
        private async Task RollbackAsync(SinglePhaseEnlistment singlePhaseEnlistment)
        {
            try
            {
                FaultTolerantAmqpObject <Controller> faultTolerantController = _connectionScope.TransactionController;
                Controller controller = await faultTolerantController.GetOrCreateAsync(_timeout)
                                        .ConfigureAwait(false);

                await controller.DischargeAsync(AmqpTransactionId, fail : true).ConfigureAwait(false);

                singlePhaseEnlistment.Aborted();
                MessagingEventSource.Log.AmqpTransactionDischarged(_transactionId, AmqpTransactionId, true);
            }
            catch (Exception e)
            {
                Exception exception = AmqpExceptionHelper.TranslateException(e, null);
                MessagingEventSource.Log.AmqpTransactionDischargeException(
                    _transactionId,
                    AmqpTransactionId,
                    exception);
                singlePhaseEnlistment.Aborted(exception);
            }
        }
        /// <summary>
        ///   Creates an AMQP link for use with publishing operations.
        /// </summary>
        /// <param name="entityPath"></param>
        /// <param name="viaEntityPath">The entity path to route the message through. Useful when using transactions.</param>
        /// <param name="connection">The active and opened AMQP connection to use for this link.</param>
        /// <param name="timeout">The timeout to apply when creating the link.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>A link for use for operations related to receiving events.</returns>
        ///
        protected virtual async Task <SendingAmqpLink> CreateSendingLinkAsync(
            string entityPath,
            string viaEntityPath,
            AmqpConnection connection,
            TimeSpan timeout,
            CancellationToken cancellationToken)
        {
            Argument.AssertNotDisposed(IsDisposed, nameof(AmqpConnectionScope));
            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            var session   = default(AmqpSession);
            var stopWatch = Stopwatch.StartNew();

            try
            {
                string[] audience;
                Uri      destinationEndpoint = null;

                // if there is a via entityPath, include that in the audience

                if (!string.IsNullOrEmpty(viaEntityPath))
                {
                    destinationEndpoint = new Uri(ServiceEndpoint, viaEntityPath);
                    var finalDestinationEndpoint = new Uri(ServiceEndpoint, entityPath);
                    audience = new string[] { finalDestinationEndpoint.AbsoluteUri, destinationEndpoint.AbsoluteUri };
                }
                else
                {
                    destinationEndpoint = new Uri(ServiceEndpoint, entityPath);
                    audience            = new string[] { destinationEndpoint.AbsoluteUri };
                }

                // Perform the initial authorization for the link.

                var authClaims = new[] { ServiceBusClaim.Send };

                DateTime authExpirationUtc = await RequestAuthorizationUsingCbsAsync(
                    connection,
                    TokenProvider,
                    destinationEndpoint,
                    audience,
                    authClaims,
                    timeout.CalculateRemaining(stopWatch.Elapsed))
                                             .ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                // Create and open the AMQP session associated with the link.

                var sessionSettings = new AmqpSessionSettings {
                    Properties = new Fields()
                };
                session = connection.CreateSession(sessionSettings);

                await OpenAmqpObjectAsync(session, timeout).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                // Create and open the link.

                var linkSettings = new AmqpLinkSettings
                {
                    Role = false,
                    InitialDeliveryCount = 0,
                    Source = new Source {
                        Address = Guid.NewGuid().ToString()
                    },
                    Target = new Target {
                        Address = destinationEndpoint.AbsolutePath
                    }
                };

                if (!string.IsNullOrEmpty(viaEntityPath))
                {
                    linkSettings.AddProperty(AmqpClientConstants.TransferDestinationAddress, entityPath);
                }

                linkSettings.AddProperty(AmqpProperty.Timeout, (uint)timeout.CalculateRemaining(stopWatch.Elapsed).TotalMilliseconds);

                var link = new SendingAmqpLink(linkSettings);
                linkSettings.LinkName = $"{ Id };{ connection.Identifier }:{ session.Identifier }:{ link.Identifier }";
                link.AttachTo(session);

                stopWatch.Stop();

                // Configure refresh for authorization of the link.

                var refreshTimer = default(Timer);

                TimerCallback refreshHandler = CreateAuthorizationRefreshHandler
                                               (
                    entityPath,
                    connection,
                    link,
                    TokenProvider,
                    destinationEndpoint,
                    audience,
                    authClaims,
                    AuthorizationRefreshTimeout,
                    () => refreshTimer
                                               );

                refreshTimer = new Timer(refreshHandler, null, CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan);

                // Track the link before returning it, so that it can be managed with the scope.

                BeginTrackingLinkAsActive(entityPath, link, refreshTimer);
                return(link);
            }
            catch (Exception exception)
            {
                // Aborting the session will perform any necessary cleanup of
                // the associated link as well.

                session?.Abort();
                throw AmqpExceptionHelper.TranslateException(
                          exception,
                          null,
                          session.GetInnerException(),
                          connection.IsClosing());
            }
        }
        /// <summary>
        ///   Creates an AMQP link for use with receiving operations.
        /// </summary>
        /// <param name="entityPath"></param>
        ///
        /// <param name="connection">The active and opened AMQP connection to use for this link.</param>
        /// <param name="endpoint">The fully qualified endpoint to open the link for.</param>
        /// <param name="prefetchCount">Controls the number of events received and queued locally without regard to whether an operation was requested.</param>
        /// <param name="receiveMode">The <see cref="ReceiveMode"/> used to specify how messages are received. Defaults to PeekLock mode.</param>
        /// <param name="sessionId"></param>
        /// <param name="isSessionReceiver"></param>
        /// <param name="timeout">The timeout to apply when creating the link.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>A link for use for operations related to receiving events.</returns>
        ///
        protected virtual async Task <ReceivingAmqpLink> CreateReceivingLinkAsync(
            string entityPath,
            AmqpConnection connection,
            Uri endpoint,
            TimeSpan timeout,
            uint prefetchCount,
            ReceiveMode receiveMode,
            string sessionId,
            bool isSessionReceiver,
            CancellationToken cancellationToken)
        {
            Argument.AssertNotDisposed(IsDisposed, nameof(AmqpConnectionScope));
            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            var session   = default(AmqpSession);
            var stopWatch = Stopwatch.StartNew();

            try
            {
                // Perform the initial authorization for the link.

                string[] authClaims        = new string[] { ServiceBusClaim.Send };
                var      audience          = new[] { endpoint.AbsoluteUri };
                DateTime authExpirationUtc = await RequestAuthorizationUsingCbsAsync(
                    connection,
                    TokenProvider,
                    endpoint,
                    audience,
                    authClaims,
                    timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                // Create and open the AMQP session associated with the link.

                var sessionSettings = new AmqpSessionSettings {
                    Properties = new Fields()
                };
                session = connection.CreateSession(sessionSettings);

                await OpenAmqpObjectAsync(session, timeout).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                var filters = new FilterSet();

                // even if supplied sessionId is null, we need to add the Session filter if it is a session receiver
                if (isSessionReceiver)
                {
                    filters.Add(AmqpClientConstants.SessionFilterName, sessionId);
                }

                var linkSettings = new AmqpLinkSettings
                {
                    Role            = true,
                    TotalLinkCredit = prefetchCount,
                    AutoSendFlow    = prefetchCount > 0,
                    SettleType      = (receiveMode == ReceiveMode.PeekLock) ? SettleMode.SettleOnDispose : SettleMode.SettleOnSend,
                    Source          = new Source {
                        Address = endpoint.AbsolutePath, FilterSet = filters
                    },
                    Target = new Target {
                        Address = Guid.NewGuid().ToString()
                    }
                };

                var link = new ReceivingAmqpLink(linkSettings);
                linkSettings.LinkName = $"{connection.Settings.ContainerId};{connection.Identifier}:{session.Identifier}:{link.Identifier}:{linkSettings.Source.ToString()}";

                link.AttachTo(session);

                stopWatch.Stop();

                // Configure refresh for authorization of the link.

                var refreshTimer = default(Timer);

                TimerCallback refreshHandler = CreateAuthorizationRefreshHandler
                                               (
                    entityPath,
                    connection,
                    link,
                    TokenProvider,
                    endpoint,
                    audience,
                    authClaims,
                    AuthorizationRefreshTimeout,
                    () => (ActiveLinks.ContainsKey(link) ? refreshTimer : null)
                                               );

                refreshTimer = new Timer(refreshHandler, null, CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan);

                // Track the link before returning it, so that it can be managed with the scope.

                BeginTrackingLinkAsActive(entityPath, link, refreshTimer);
                return(link);
            }
            catch (Exception exception)
            {
                // Aborting the session will perform any necessary cleanup of
                // the associated link as well.

                session?.Abort();
                throw AmqpExceptionHelper.TranslateException(
                          exception,
                          null,
                          session.GetInnerException(),
                          connection.IsClosing());
            }
        }
        /// <summary>
        ///   Creates an AMQP link for use with management operations.
        /// </summary>
        /// <param name="entityPath"></param>
        ///
        /// <param name="connection">The active and opened AMQP connection to use for this link.</param>
        /// <param name="timeout">The timeout to apply when creating the link.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>A link for use with management operations.</returns>
        ///
        protected virtual async Task <RequestResponseAmqpLink> CreateManagementLinkAsync(
            string entityPath,
            AmqpConnection connection,
            TimeSpan timeout,
            CancellationToken cancellationToken)
        {
            Argument.AssertNotDisposed(IsDisposed, nameof(AmqpConnectionScope));
            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            var session   = default(AmqpSession);
            var stopWatch = Stopwatch.StartNew();

            try
            {
                // Create and open the AMQP session associated with the link.

                var sessionSettings = new AmqpSessionSettings {
                    Properties = new Fields()
                };
                session = connection.CreateSession(sessionSettings);

                await OpenAmqpObjectAsync(session, timeout).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                // Create and open the link.

                var linkSettings = new AmqpLinkSettings();
                linkSettings.AddProperty(AmqpProperty.Timeout, (uint)timeout.CalculateRemaining(stopWatch.Elapsed).TotalMilliseconds);
                linkSettings.AddProperty(AmqpClientConstants.EntityTypeName, AmqpClientConstants.EntityTypeManagement);
                entityPath += '/' + AmqpClientConstants.ManagementAddress;

                // Perform the initial authorization for the link.

                string[] claims            = { ServiceBusClaim.Manage, ServiceBusClaim.Listen, ServiceBusClaim.Send };
                var      endpoint          = new Uri(ServiceEndpoint, entityPath);
                var      audience          = new[] { endpoint.AbsoluteUri };
                DateTime authExpirationUtc = await RequestAuthorizationUsingCbsAsync(
                    connection,
                    TokenProvider,
                    ServiceEndpoint,
                    audience,
                    claims,
                    timeout.CalculateRemaining(stopWatch.Elapsed))
                                             .ConfigureAwait(false);

                var link = new RequestResponseAmqpLink(
                    AmqpClientConstants.EntityTypeManagement,
                    session,
                    entityPath,
                    linkSettings.Properties);
                linkSettings.LinkName = $"{connection.Settings.ContainerId};{connection.Identifier}:{session.Identifier}:{link.Identifier}";
                stopWatch.Stop();

                // Track the link before returning it, so that it can be managed with the scope.
                var refreshTimer = default(Timer);

                TimerCallback refreshHandler = CreateAuthorizationRefreshHandler
                                               (
                    entityPath,
                    connection,
                    link,
                    TokenProvider,
                    ServiceEndpoint,
                    audience,
                    claims,
                    AuthorizationRefreshTimeout,
                    () => (ActiveLinks.ContainsKey(link) ? refreshTimer : null)
                                               );

                refreshTimer = new Timer(refreshHandler, null, CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan);

                // Track the link before returning it, so that it can be managed with the scope.

                BeginTrackingLinkAsActive(entityPath, link, refreshTimer);
                return(link);
            }
            catch (Exception exception)
            {
                // Aborting the session will perform any necessary cleanup of
                // the associated link as well.

                session?.Abort();
                throw AmqpExceptionHelper.TranslateException(
                          exception,
                          null,
                          session.GetInnerException(),
                          connection.IsClosing());
            }
        }
示例#9
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
            }
        }
示例#10
0
        /// <summary>
        ///    Sends a set of messages to the associated Queue/Topic using a batched approach.
        /// </summary>
        ///
        /// <param name="messageFactory"></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(
            Func <AmqpMessage> messageFactory,
            TimeSpan timeout,
            CancellationToken cancellationToken)
        {
            var stopWatch = ValueStopwatch.StartNew();
            var link      = default(SendingAmqpLink);

            try
            {
                using (AmqpMessage batchMessage = messageFactory())
                {
                    string messageHash = batchMessage.GetHashCode().ToString(CultureInfo.InvariantCulture);

                    ArraySegment <byte> transactionId      = AmqpConstants.NullBinary;
                    Transaction         ambientTransaction = Transaction.Current;
                    if (ambientTransaction != null)
                    {
                        transactionId = await AmqpTransactionManager.Instance.EnlistAsync(
                            ambientTransaction,
                            _connectionScope,
                            timeout).ConfigureAwait(false);
                    }

                    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 > MaxMessageSize)
                    {
                        throw new ServiceBusException(string.Format(CultureInfo.InvariantCulture, Resources.MessageSizeExceeded, messageHash, batchMessage.SerializedMessageSize, MaxMessageSize, _entityPath), ServiceBusException.FailureReason.MessageSizeExceeded);
                    }

                    // Attempt to send the message batch.

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

                    cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                    if (outcome.DescriptorCode != Accepted.Code)
                    {
                        throw (outcome as Rejected)?.Error.ToMessagingContractException();
                    }

                    cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();
                }
            }
            catch (Exception exception)
            {
                ExceptionDispatchInfo.Capture(AmqpExceptionHelper.TranslateException(
                                                  exception,
                                                  link?.GetTrackingId(),
                                                  null,
                                                  HasLinkCommunicationError(link)))
                .Throw();

                throw; // will never be reached
            }
        }