protected override void DoBegin(object transaction, ITransactionDefinition definition) { if (definition.IsolationLevel != AbstractTransactionDefinition.ISOLATION_DEFAULT) { throw new InvalidIsolationLevelException("AMQP does not support an isolation level concept"); } var txObject = (RabbitTransactionObject)transaction; RabbitResourceHolder resourceHolder = null; try { resourceHolder = ConnectionFactoryUtils.GetTransactionalResourceHolder(ConnectionFactory, true); _logger?.LogDebug("Created AMQP transaction on channel [{channel}]", resourceHolder.GetChannel()); txObject.ResourceHolder = resourceHolder; txObject.ResourceHolder.SynchronizedWithTransaction = true; var timeout = DetermineTimeout(definition); if (timeout != AbstractTransactionDefinition.TIMEOUT_DEFAULT) { txObject.ResourceHolder.SetTimeoutInSeconds(timeout); } TransactionSynchronizationManager.BindResource(ConnectionFactory, txObject.ResourceHolder); } catch (RabbitException ex) { if (resourceHolder != null) { ConnectionFactoryUtils.ReleaseResources(resourceHolder); } throw new CannotCreateTransactionException("Could not create AMQP transaction", ex); } }
/// <summary> /// Initializes this container. Creates a Connection, starts the Connection /// (if the property <see cref="AutoStartup"/> hasn't been turned off), and calls /// <see cref="DoInitialize"/>. /// </summary> /// <exception cref="EMSException">If startup failed</exception> public virtual void Initialize() { try { lock (this.lifecycleMonitor) { this.active = true; System.Threading.Monitor.PulseAll(this.lifecycleMonitor); } if (this.autoStartup) { DoStart(); } DoInitialize(); } catch (Exception) { lock (this.sharedConnectionMonitor) { ConnectionFactoryUtils.ReleaseConnection(sharedConnection, ConnectionFactory, autoStartup); } throw; } }
private IMessage Handle(Delivery delivery) { if (delivery == null && Shutdown != null) { throw new ShutdownSignalException(Shutdown); } if (delivery == null) { return(null); } var body = delivery.Body; var messageProperties = MessageHeadersConverter.ToMessageHeaders(delivery.Properties, delivery.Envelope, EncodingUtils.Utf8); var accesor = RabbitHeaderAccessor.GetMutableAccessor(messageProperties); accesor.ConsumerTag = delivery.ConsumerTag; accesor.ConsumerQueue = delivery.Queue; var message = Message.Create(body, accesor.MessageHeaders); Logger?.LogDebug("Received message: {message}", message); if (messageProperties.DeliveryTag() != null) { DeliveryTags.Add(messageProperties.DeliveryTag().Value); } if (Transactional && !LocallyTransacted) { ConnectionFactoryUtils.RegisterDeliveryTag(ConnectionFactory, Channel, delivery.Envelope.DeliveryTag); } return(message); }
public void Stop() { if (AbortStarted == 0) { AbortStarted = DateTimeOffset.Now.ToUnixTimeMilliseconds(); } if (!Cancelled) { try { RabbitUtils.CloseMessageConsumer(Channel, GetConsumerTags(), Transactional); } catch (Exception e) { Logger?.LogDebug(e, "Error closing consumer: {consumer}", ToString()); } } Logger?.LogDebug("Closing Rabbit Channel : {channel}", Channel); RabbitUtils.SetPhysicalCloseRequired(Channel, true); ConnectionFactoryUtils.ReleaseResources(ResourceHolder); DeliveryTags.Clear(); _ = Consumers.TakeWhile((kvp) => Consumers.Count > 0); _ = Queue.TakeWhile((d) => Queue.Count > 0); }
/// <summary> /// Refreshes the shared connection that this container holds. /// </summary> /// <remarks> /// Called on startup and also after an infrastructure exception /// that occurred during invoker setup and/or execution. /// </remarks> /// <exception cref="NMSException">If thrown by NMS API methods</exception> protected void RefreshSharedConnection() { lock (sharedConnectionMonitor) { ConnectionFactoryUtils.ReleaseConnection(sharedConnection, ConnectionFactory, sharedConnectionStarted); sharedConnection = CreateSharedConnection(); if (sharedConnectionStarted) { sharedConnection.Start(); } } }
/// <summary>Perform a commit or message acknowledgement, as appropriate</summary> /// <param name="locallyTransacted">if set to <c>true</c> [locally transacted].</param> /// <returns>True if committed, else false.</returns> public bool CommitIfNecessary(bool locallyTransacted) { if (this.deliveryTags == null || this.deliveryTags.Count < 1) { return(false); } try { var ackRequired = !this.acknowledgeMode.IsAutoAck() && !this.acknowledgeMode.IsManual(); if (ackRequired) { if (this.transactional && !locallyTransacted) { // Not locally transacted but it is transacted so it // could be synchronized with an external transaction foreach (var deliveryTag in this.deliveryTags) { ConnectionFactoryUtils.RegisterDeliveryTag(this.connectionFactory, this.channel, deliveryTag); } } else { if (this.deliveryTags != null && this.deliveryTags.Count > 0) { var copiedTags = new List <long>(this.deliveryTags); if (copiedTags.Count > 0) { var deliveryTag = copiedTags[copiedTags.Count - 1]; this.channel.BasicAck((ulong)deliveryTag, true); } } } } if (locallyTransacted) { // For manual acks we still need to commit RabbitUtils.CommitIfNecessary(this.channel); } } finally { this.deliveryTags.Clear(); } return(true); }
private Apache.NMS.IMessage SendAndReceive(IMessage requestMessage) { IConnection connection = CreateConnection(); ISession session = null; IMessageProducer messageProducer = null; IMessageConsumer messageConsumer = null; IDestination replyTo = null; try { session = CreateSession(connection); Apache.NMS.IMessage jmsRequest = this.messageConverter.ToMessage(requestMessage, session); messageProducer = session.CreateProducer(this.GetRequestDestination(session)); messageProducer.DeliveryMode = deliveryMode; messageProducer.Priority = priority; messageProducer.TimeToLive = timeToLive; replyTo = GetReplyDestination(session); jmsRequest.NMSReplyTo = replyTo; connection.Start(); messageProducer.Send(jmsRequest); if (replyTo is ITemporaryQueue || replyTo is ITemporaryTopic) { messageConsumer = session.CreateConsumer(replyTo); } else { String messageId = jmsRequest.NMSMessageId.Replace("'", "''"); String messageSelector = "NMSCorrelationID = '" + messageId + "'"; messageConsumer = session.CreateConsumer(replyTo, messageSelector); } return((TimeSpan.Compare(receiveTimeout, TimeSpan.FromMilliseconds(0)) == 1) ? messageConsumer.Receive(receiveTimeout) : messageConsumer.Receive()); } finally { NmsUtils.CloseMessageProducer(messageProducer); NmsUtils.CloseMessageConsumer(messageConsumer); this.DeleteDestinationIfTemporary(session, replyTo); NmsUtils.CloseSession(session); ConnectionFactoryUtils.ReleaseConnection(connection, this.connectionFactory, true); } }
public void Start() { Logger?.LogDebug("Starting consumer {consumer}", ToString()); try { ResourceHolder = ConnectionFactoryUtils.GetTransactionalResourceHolder(ConnectionFactory, Transactional); Channel = ResourceHolder.GetChannel(); // ClosingRecoveryListener.AddRecoveryListenerIfNecessary(Channel); } catch (RabbitAuthenticationException e) { throw new FatalListenerStartupException("Authentication failure", e); } DeliveryTags.Clear(); ActiveObjectCounter.Add(this); PassiveDeclarations(); SetQosAndCreateConsumers(); }
public void TestReceiveBlockingGlobalTx() { template.ConvertAndSend(ROUTE, "blockGTXNoTO"); var resourceHolder = ConnectionFactoryUtils .GetTransactionalResourceHolder(template.ConnectionFactory, true); TransactionSynchronizationManager.SetActualTransactionActive(true); ConnectionFactoryUtils.BindResourceToTransaction(resourceHolder, template.ConnectionFactory, true); template.ReceiveTimeout = -1; template.IsChannelTransacted = true; var o = template.ReceiveAndConvert <string>(ROUTE); resourceHolder.CommitAll(); resourceHolder.CloseAll(); Assert.Same(resourceHolder, TransactionSynchronizationManager.UnbindResource(template.ConnectionFactory)); Assert.NotNull(o); Assert.Equal("blockGTXNoTO", o); template.ReceiveTimeout = 0; Assert.Null(template.Receive(ROUTE)); }
private void ExecuteListenerInTransaction(Message message, ulong deliveryTag) { if (IsRabbitTxManager) { ConsumerChannelRegistry.RegisterConsumerChannel(Model, ConnectionFactory); } if (TransactionTemplate == null) { TransactionTemplate = new TransactionTemplate(TransactionManager, TransactionAttribute); } TransactionTemplate.Execute <object>(s => { var resourceHolder = ConnectionFactoryUtils.BindResourceToTransaction(new RabbitResourceHolder(Model, false), ConnectionFactory, true); if (resourceHolder != null) { resourceHolder.AddDeliveryTag(Model, deliveryTag); } // unbound in ResourceHolderSynchronization.beforeCompletion() try { CallExecuteListener(message, deliveryTag); } catch (Exception e1) { _container.PrepareHolderForRollback(resourceHolder, e1); throw; } // catch (Throwable e2) // { // //NOSONAR ok to catch Throwable here because we re-throw it below // throw new WrappedTransactionException(e2); // } return(null); }); }
/// <summary> /// Stop the shared connection, call <see cref="DoShutdown"/>, and close this container. /// </summary> public virtual void Shutdown() { logger.Debug("Shutting down message listener container"); bool wasRunning = false; lock (this.lifecycleMonitor) { wasRunning = this.running; this.running = false; this.active = false; System.Threading.Monitor.PulseAll(this.lifecycleMonitor); } if (wasRunning && SharedConnectionEnabled) { try { StopSharedConnection(); } catch (Exception ex) { logger.Debug("Could not stop NMS Connection on shutdown", ex); } } // Shut down the invokers try { DoShutdown(); } finally { lock (this.sharedConnectionMonitor) { ConnectionFactoryUtils.ReleaseConnection(this.sharedConnection, ConnectionFactory, false); } } }
/// <summary>Do begin.</summary> /// <param name="transaction">The transaction.</param> /// <param name="definition">The definition.</param> protected override void DoBegin(object transaction, ITransactionDefinition definition) { // https://jira.springsource.org/browse/SPRNET-1444 (SPRNET 2.0) has default TransactionIsolationLevel as IsolationLevel.Unspecified, letting this work. Will not work for SPRNET <= 1.3.2. if (definition.TransactionIsolationLevel != IsolationLevel.Unspecified) { throw new InvalidIsolationLevelException("AMQP does not support an isolation level concept"); } var transactionObject = (RabbitTransactionObject)transaction; RabbitResourceHolder resourceHolder = null; try { resourceHolder = ConnectionFactoryUtils.GetTransactionalResourceHolder(this.ConnectionFactory, true); Logger.Debug(m => m("Created AMQP transaction on channel [{0}]", resourceHolder.Channel)); // resourceHolder.DeclareTransactional(); transactionObject.ResourceHolder = resourceHolder; transactionObject.ResourceHolder.SynchronizedWithTransaction = true; var timeout = this.DetermineTimeout(definition); if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT) { transactionObject.ResourceHolder.TimeoutInSeconds = timeout; } TransactionSynchronizationManager.BindResource(this.ConnectionFactory, transactionObject.ResourceHolder); } catch (AmqpException ex) { if (resourceHolder != null) { ConnectionFactoryUtils.ReleaseResources(resourceHolder); } throw new CannotCreateTransactionException("Could not create AMQP transaction", ex); } }
public void SessionCallbackWithinSynchronizedTransaction() { SingleConnectionFactory scf = new SingleConnectionFactory(mockConnectionFactory); NmsTemplate template = CreateTemplate(); template.ConnectionFactory = scf; mockConnection.Start(); LastCall.On(mockConnection).Repeat.Times(2); // We're gonna call getTransacted 3 times, i.e. 2 more times. Expect.Call(mockSession.Transacted).Return(UseTransactedSession).Repeat.Twice(); if (UseTransactedTemplate) { mockSession.Commit(); LastCall.On(mockSession).Repeat.Once(); } mockSession.Close(); LastCall.On(mockSession).Repeat.Once(); mockConnection.Stop(); LastCall.On(mockConnection).Repeat.Once(); mockConnection.Close(); LastCall.On(mockConnection).Repeat.Once(); mocks.ReplayAll(); TransactionSynchronizationManager.InitSynchronization(); try { template.Execute(session => { bool b = session.Transacted; return(null); }); template.Execute(session => { bool b = session.Transacted; return(null); }); Assert.AreSame(mockSession, ConnectionFactoryUtils.GetTransactionalSession(scf, null, false)); Assert.AreSame(mockSession, ConnectionFactoryUtils.GetTransactionalSession(scf, scf.CreateConnection(), false)); //In Java this test was doing 'double-duty' and testing TransactionAwareConnectionFactoryProxy, which has //not been implemented in .NET template.Execute(session => { bool b = session.Transacted; return(null); }); IList synchs = TransactionSynchronizationManager.Synchronizations; Assert.AreEqual(1, synchs.Count); ITransactionSynchronization synch = (ITransactionSynchronization)synchs[0]; synch.BeforeCommit(false); synch.BeforeCompletion(); synch.AfterCommit(); synch.AfterCompletion(TransactionSynchronizationStatus.Unknown); } finally { TransactionSynchronizationManager.ClearSynchronization(); //Assert.IsTrue(TransactionSynchronizationManager.ResourceDictionary.Count == 0); //Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive); scf.Dispose(); } Assert.IsTrue(TransactionSynchronizationManager.ResourceDictionary.Count == 0); mocks.VerifyAll(); }
/// <summary>The start.</summary> public void Start() { Logger.Debug(m => m("Starting consumer {0}", this)); this.channel = ConnectionFactoryUtils.GetTransactionalResourceHolder(this.connectionFactory, this.transactional).Channel; this.consumer = new InternalConsumer(this.channel, this); this.deliveryTags.Clear(); this.activeObjectCounter.Add(this); var passiveDeclareTries = 3; // mirrored queue might be being moved while (passiveDeclareTries > 0) { passiveDeclareTries--; try { if (!this.acknowledgeMode.IsAutoAck()) { // Set basicQos before calling basicConsume (otherwise if we are not acking the broker // will send blocks of 100 messages) // The Java client includes a convenience method BasicQos(ushort prefetchCount), which sets 0 as the prefetchSize and false as global this.channel.BasicQos(0, (ushort)this.prefetchCount, false); } foreach (var t in this.queues) { this.channel.QueueDeclarePassive(t); } passiveDeclareTries = 0; } catch (Exception e) { if (passiveDeclareTries > 0 && this.channel.IsOpen) { Logger.Warn(m => m("Reconnect failed; retries left=" + (passiveDeclareTries - 1)), e); try { Thread.Sleep(5000); } catch (ThreadInterruptedException e1) { Thread.CurrentThread.Interrupt(); } } else { this.activeObjectCounter.Release(this); throw new FatalListenerStartupException("Cannot prepare queue for listener. Either the queue doesn't exist or the broker will not allow us to use it.", e); } } } try { foreach (var t in this.queues) { this.channel.BasicConsume(t, this.acknowledgeMode.IsAutoAck(), this.consumer); Logger.Debug(m => m("Started on queue '{0}': {1}", t, this)); } } catch (Exception e) { throw RabbitUtils.ConvertRabbitAccessException(e); } }
/// <summary> /// Get a transactional resource holder. /// </summary> /// <returns> /// The rabbit resource holder. /// </returns> protected RabbitResourceHolder GetTransactionalResourceHolder() { return(ConnectionFactoryUtils.GetTransactionalResourceHolder(this.connectionFactory, this.ChannelTransacted)); }
/// <summary> /// Execute Run. /// </summary> public void Run() { var aborted = false; try { try { this.consumer.Start(); this.start.Signal(); } catch (FatalListenerStartupException ex) { throw; } catch (Exception t) { if (this.start.CurrentCount > 0) { this.start.Signal(); } this.outer.HandleStartupFailure(t); throw; } if (this.outer.transactionManager != null) { // Register the consumer's channel so it will be used by the transaction manager // if it's an instance of RabbitTransactionManager. ConnectionFactoryUtils.RegisterConsumerChannel(this.consumer.Channel); } // Always better to stop receiving as soon as possible if // transactional var continuable = false; while (this.outer.IsActive || continuable) { try { // Will come back false when the queue is drained continuable = this.outer.ReceiveAndExecute(this.consumer) && !this.outer.ChannelTransacted; } catch (ListenerExecutionFailedException ex) { Logger.Trace(m => m("Error on ReceiveAndExecute"), ex); // Continue to process, otherwise re-throw } } } catch (ThreadInterruptedException e) { Logger.Debug(m => m("Consumer thread interrupted, processing stopped.")); Thread.CurrentThread.Interrupt(); aborted = true; } catch (FatalListenerStartupException ex) { Logger.Error(m => m("Consumer received fatal exception on startup", ex)); this.startupException = ex; // Fatal, but no point re-throwing, so just abort. aborted = true; } catch (FatalListenerExecutionException ex) { Logger.Error(m => m("Consumer received fatal exception during processing"), ex); // Fatal, but no point re-throwing, so just abort. aborted = true; } catch (Exception t) { Logger.Warn(m => m("Consumer raised exception, processing can restart if the connection factory supports it. " + "Exception summary: " + t)); } finally { if (this.outer.transactionManager != null) { ConnectionFactoryUtils.UnRegisterConsumerChannel(); } } // In all cases count down to allow container to progress beyond startup if (this.start.CurrentCount > 0) { this.start.Signal(); } if (!this.outer.IsActive || aborted) { Logger.Debug(m => m("Cancelling {0}", this.consumer)); try { this.consumer.Stop(); } catch (AmqpException e) { Logger.Info(m => m("Could not cancel message consumer"), e); } if (aborted) { Logger.Info(m => m("Stopping container from aborted consumer")); this.outer.Stop(); } } else { Logger.Info(m => m("Restarting {0}", this.consumer)); this.outer.Restart(this.consumer); } }
/// <summary>Invoke the specified listener as Spring SessionAwareMessageListener, /// exposing a new Rabbit Channel (potentially with its own transaction) /// to the listener if demanded.</summary> /// <param name="listener">The Spring ISessionAwareMessageListener to invoke.</param> /// <param name="channel">The channel to operate on.</param> /// <param name="message">The received message.</param> /// <see cref="IChannelAwareMessageListener"/><see cref="ExposeListenerChannel"/> protected virtual void DoInvokeListener(IChannelAwareMessageListener listener, IModel channel, Message message) { RabbitResourceHolder resourceHolder = null; var channelToUse = channel; var boundHere = false; try { if (!this.ExposeListenerChannel) { // We need to expose a separate Channel. resourceHolder = this.GetTransactionalResourceHolder(); channelToUse = resourceHolder.Channel; if (this.IsChannelLocallyTransacted(channelToUse) && !TransactionSynchronizationManager.ActualTransactionActive) { resourceHolder.SynchronizedWithTransaction = true; TransactionSynchronizationManager.BindResource( this.ConnectionFactory, resourceHolder); boundHere = true; } } else { // if locally transacted, bind the current channel to make it available to RabbitTemplate if (this.IsChannelLocallyTransacted(channel)) { var localResourceHolder = new RabbitResourceHolder(channelToUse, false); localResourceHolder.SynchronizedWithTransaction = true; TransactionSynchronizationManager.BindResource(this.ConnectionFactory, localResourceHolder); boundHere = true; } } // Actually invoke the message listener try { listener.OnMessage(message, channelToUse); } catch (Exception e) { throw this.WrapToListenerExecutionFailedExceptionIfNeeded(e); } } finally { if (resourceHolder != null && boundHere) { // so the channel exposed (because exposeListenerChannel is false) will be closed resourceHolder.SynchronizedWithTransaction = false; } ConnectionFactoryUtils.ReleaseResources(resourceHolder); if (boundHere) { // unbind if we bound TransactionSynchronizationManager.UnbindResource(this.ConnectionFactory); if (!this.ExposeListenerChannel && this.IsChannelLocallyTransacted(channelToUse)) { /* * commit the temporary channel we exposed; the consumer's channel * will be committed later. Note that when exposing a different channel * when there's no transaction manager, the exposed channel is committed * on each message, and not based on txSize. */ RabbitUtils.CommitIfNecessary(channelToUse); } } } }