private void CleanUpAfterInvoke(RabbitResourceHolder resourceHolder, R.IModel channelToUse, bool boundHere) { if (resourceHolder != null && boundHere) { // so the channel exposed (because exposeListenerChannel is false) will be closed resourceHolder.SynchronizedWithTransaction = false; } ConnectionFactoryUtils.ReleaseResources(resourceHolder); // NOSONAR - null check in method if (boundHere) { // unbind if we bound TransactionSynchronizationManager.UnbindResource(ConnectionFactory); if (!ExposeListenerChannel && IsChannelLocallyTransacted) { /* * 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, _logger); } } }
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 [" + 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 (AmqpException ex) { if (resourceHolder != null) { ConnectionFactoryUtils.ReleaseResources(resourceHolder); } throw new CannotCreateTransactionException("Could not create AMQP transaction", ex); } }
public void TestTemplateUsesPublisherConnectionUnlessInTx() { connectionFactory.Destroy(); template.UsePublisherConnection = true; template.ConvertAndSend("dummy", "foo"); Assert.Null(connectionFactory._connection.Target); Assert.NotNull(((CachingConnectionFactory)connectionFactory.PublisherConnectionFactory)._connection.Target); connectionFactory.Destroy(); Assert.Null(connectionFactory._connection.Target); Assert.Null(((CachingConnectionFactory)connectionFactory.PublisherConnectionFactory)._connection.Target); var channel = connectionFactory.CreateConnection().CreateChannel(true); Assert.NotNull(connectionFactory._connection.Target); var holder = new RabbitResourceHolder(channel, true); TransactionSynchronizationManager.BindResource(connectionFactory, holder); try { template.IsChannelTransacted = true; template.ConvertAndSend("dummy", "foo"); Assert.NotNull(connectionFactory._connection.Target); Assert.Null(((CachingConnectionFactory)connectionFactory.PublisherConnectionFactory)._connection.Target); } finally { TransactionSynchronizationManager.UnbindResource(connectionFactory); channel.Close(); } }
protected virtual void PrepareHolderForRollback(RabbitResourceHolder resourceHolder, Exception exception) { if (resourceHolder != null) { resourceHolder.RequeueOnRollback = AlwaysRequeueWithTxManagerRollback || ContainerUtils.ShouldRequeue(DefaultRequeueRejected, exception, _logger); } }
public void TestChannelCloseInTx() { connectionFactory.IsPublisherReturns = false; var channel = connectionFactory.CreateConnection().CreateChannel(true); var holder = new RabbitResourceHolder(channel, true); TransactionSynchronizationManager.BindResource(connectionFactory, holder); try { template.IsChannelTransacted = true; template.ConvertAndSend(ROUTE, "foo"); template.ConvertAndSend(Guid.NewGuid().ToString(), ROUTE, "xxx"); var n = 0; while (n++ < 100 && channel.IsOpen) { Thread.Sleep(100); } Assert.False(channel.IsOpen); try { template.ConvertAndSend(ROUTE, "bar"); throw new Exception("Expected exception"); } catch (RabbitUncategorizedException e) { if (e.InnerException is InvalidOperationException) { Assert.Contains("Channel closed during transaction", e.InnerException.Message); } else { throw new Exception("Expected InvalidOperationException"); } } catch (RabbitConnectException e) { Assert.IsType <R.Exceptions.AlreadyClosedException>(e.InnerException); } } finally { TransactionSynchronizationManager.UnbindResource(connectionFactory); channel.Close(); } }
/// <summary>Invokes the specified listener</summary> /// <param name="channel">The channel to operate on.</param> /// <param name="message">The received message.</param> /// <see cref="MessageListener"/> public virtual void InvokeListener(IModel channel, Message message) { var listener = this.MessageListener; if (listener is IChannelAwareMessageListener) { this.DoInvokeListener((IChannelAwareMessageListener)listener, channel, message); } else if (listener is IMessageListener || listener is Action <Message> ) { var bindChannel = this.ExposeListenerChannel && this.IsChannelLocallyTransacted(channel); if (bindChannel) { var resourceHolder = new RabbitResourceHolder(channel, false); resourceHolder.SynchronizedWithTransaction = true; TransactionSynchronizationManager.BindResource(this.ConnectionFactory, resourceHolder); } try { if (listener is IMessageListener) { this.DoInvokeListener((IMessageListener)listener, message); } else if (listener is Action <Message> ) { this.DoInvokeListener((Action <Message>)listener, message); } } finally { if (bindChannel) { // unbind if we bound TransactionSynchronizationManager.UnbindResource(this.ConnectionFactory); } } } else if (listener != null) { throw new ArgumentException("Only MessageListener and SessionAwareMessageListener supported: " + listener); } else { throw new InvalidOperationException("No message listener specified - see property MessageListener"); } }
/// <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); } }
protected virtual void ActualInvokeListener(R.IModel channel, object data) { var listener = MessageListener; if (listener is IChannelAwareMessageListener chanListener) { DoInvokeListener(chanListener, channel, data); } else if (listener != null) { var bindChannel = ExposeListenerChannel && IsChannelLocallyTransacted; if (bindChannel) { var resourceHolder = new RabbitResourceHolder(channel, false, _logger); resourceHolder.SynchronizedWithTransaction = true; TransactionSynchronizationManager.BindResource(ConnectionFactory, resourceHolder); } try { DoInvokeListener(listener, data); } finally { if (bindChannel) { // unbind if we bound TransactionSynchronizationManager.UnbindResource(ConnectionFactory); } } } else { throw new FatalListenerExecutionException("No message listener specified - see property 'messageListener'"); } }
/// <summary>Create the channel.</summary> /// <param name="holder">The rabbit resource holder.</param> /// <returns>The channel.</returns> protected IModel GetChannel(RabbitResourceHolder holder) { return(holder.Channel); }
/// <summary>Fetch an appropriate Connection from the given RabbitResourceHolder.</summary> /// <param name="holder">The holder.</param> /// <returns>The connection.</returns> protected IConnection GetConnection(RabbitResourceHolder holder) { return(holder.Connection); }
public RabbitTransactionObject(RabbitResourceHolder rabbitResourceHolder) { ResourceHolder = rabbitResourceHolder; }
protected virtual void DoInvokeListener(IChannelAwareMessageListener listener, R.IModel channel, object data) { Message message = null; RabbitResourceHolder resourceHolder = null; var channelToUse = channel; var boundHere = false; try { if (!ExposeListenerChannel) { // We need to expose a separate Channel. resourceHolder = GetTransactionalResourceHolder(); channelToUse = resourceHolder.GetChannel(); /* * If there is a real transaction, the resource will have been bound; otherwise * we need to bind it temporarily here. Any work done on this channel * will be committed in the finally block. */ if (IsChannelLocallyTransacted && !TransactionSynchronizationManager.IsActualTransactionActive()) { resourceHolder.SynchronizedWithTransaction = true; TransactionSynchronizationManager.BindResource(ConnectionFactory, resourceHolder); boundHere = true; } } else { // if locally transacted, bind the current channel to make it available to RabbitTemplate if (IsChannelLocallyTransacted) { var localResourceHolder = new RabbitResourceHolder(channelToUse, false); localResourceHolder.SynchronizedWithTransaction = true; TransactionSynchronizationManager.BindResource(ConnectionFactory, localResourceHolder); boundHere = true; } } // Actually invoke the message listener... try { if (data is List <Message> asList) { listener.OnMessageBatch(asList, channelToUse); } else { message = (Message)data; listener.OnMessage(message, channelToUse); } } catch (Exception e) { _logger?.LogError("Exception in OnMessage call", e); throw WrapToListenerExecutionFailedExceptionIfNeeded(e, data); } } finally { CleanUpAfterInvoke(resourceHolder, channelToUse, boundHere); } }
/// <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); } } } }