private void TryReceiveMessage()
        {
            // NOTE this method _should not_ throw an exception!
            ServiceBrokerMessage message     = null;
            SqlTransaction       transaction = null;

            try
            {
                transaction = BeginTransaction();
                logger.Debug("RECEIVE FROM {0}", ServiceBrokerQueue);
                message = ServiceBrokerWrapper.WaitAndReceive(transaction, ServiceBrokerQueue, waitTimeout);
            }
            catch (Exception e)
            {
                logger.Error(e, "Exception caught trying to receive from queue {0}. Rolling back and backing off before reconnecting.", ServiceBrokerQueue);
                TryRollbackTransaction(transaction);
                TryDisposeTransactionAndConnection(transaction);
                workerThreadPool.Release(1);

                Thread.Sleep(1000); // back-off because we will return and loop immediately
                return;
            }

            // No message? That's okay
            if (message == null)
            {
                try
                {
                    transaction.Commit();
                }
                finally
                {
                    TryDisposeTransactionAndConnection(transaction);
                    workerThreadPool.Release(1);
                }
                return;
            }

            // start a
            Task.Run(() =>
            {
                // TryHandleHessage _should not_ throw an exception. It will also commit and cleanup the transactions
                try
                {
                    TryHandleMessage(transaction, message);
                }
                finally
                {
                    workerThreadPool.Release(1); // release this semaphore back to the pool regardless of what happened
                }
            });
        }
        private static ServiceBrokerMessage ReceiveInternal(IDbTransaction transaction, string queueName, Guid?conversationHandle, bool wait, int?waitTimeout)
        {
            EnsureSqlTransaction(transaction);
            var cmd = transaction.Connection.CreateCommand() as SqlCommand;

            var query = new StringBuilder();

            if (wait)
            {
                query.Append("WAITFOR(");
            }
            query.Append("RECEIVE TOP(1) ");

            query.Append("conversation_handle");
            query.Append(", service_name, service_contract_name, message_type_name");
            query.Append(", message_body");
            query.Append(" FROM ");
            query.Append(queueName);

            if (conversationHandle.HasValue && conversationHandle.Value != Guid.Empty)
            {
                query.Append(" WHERE conversation_handle = @ch");
                var param = cmd.Parameters.Add("@ch", SqlDbType.UniqueIdentifier);
                param.Value = conversationHandle.Value;
            }

            if (wait)
            {
                query.Append("), ");
                if (waitTimeout.HasValue && waitTimeout.Value > 0)
                {
                    query.Append(" TIMEOUT @to");
                    var param = cmd.Parameters.Add("@to", SqlDbType.Int);
                    param.Value        = waitTimeout.Value;
                    cmd.CommandTimeout = 0;
                }
            }

            cmd.CommandText = query.ToString();
            cmd.Transaction = transaction as SqlTransaction;

            using (var dataReader = cmd.ExecuteReader())
            {
                if (dataReader.Read())
                {
                    return(ServiceBrokerMessage.Load(dataReader));
                }
            }

            return(null);
        }
        private void TryHandleMessageInner(SqlTransaction transaction, ServiceBrokerMessage message)
        {
            // Only handle transport messages
            if (message.MessageTypeName != ServiceBrokerMessageType &&
                !message.IsDialogTimerMessage())
            {
                logger.Debug("Ignoring message of type {0} from queue {1}", message.MessageTypeName, ServiceBrokerQueue);
                return; // ignore
            }
            IncomingTransportMessage transportMessage = null;

            try
            {
                if (message.IsDialogTimerMessage())
                {
                    transportMessage = TryLoadDialogTimerTimeoutMessage(transaction, message);
                }
                else
                {
                    transportMessage = TryDeserializeTransportMessage(message);
                }

                if (!transportMessage.Headers.ContainsKey(StandardHeaders.OriginatingAddress))
                {
                    transportMessage.Headers[StandardHeaders.OriginatingAddress] = ServiceBrokerWrapper.TryGetConversationFarService(transaction, message.ConversationHandle);
                }

                Exception lastException = null;
                if (faultManager.MaxRetriesExceeded(transportMessage, out lastException))
                {
                    logger.Debug("MaxRetriesExceeded: Moving message {0} from queue {1} to poison message table.", message.ConversationHandle.ToString(), ServiceBrokerQueue);
                    MoveToPoisonMessage(transaction, message, transportMessage, lastException, "MaxRetriesExceeded", MaxRetries);
                    return; // return without error to commit transaction
                }
                logger.Debug("Notifying observers of new TransportMessage for message {0} from queue {1}.", message.ConversationHandle, ServiceBrokerQueue);
                if (this.MessageAvailable != null)
                {
                    this.MessageAvailable(this, new MessageAvailable(transportMessage));
                }
            }
            catch (CannotDeserializeMessageException e)
            {
                // If we can't deserialize the transport message or inner message, there is no reason to retry.
                // note: error is already logged so we don't need to log here
                logger.Debug("DeserializeError: Moving message {0} from queue {1} to poison message table.", message.ConversationHandle.ToString(), ServiceBrokerQueue);
                MoveToPoisonMessage(transaction, message, transportMessage, e, "DeserializeError", 0);
                return; // return without error to commit transaction
            }
        }
 private IncomingTransportMessage TryDeserializeTransportMessage(ServiceBrokerMessage message)
 {
     using (var stream = new MemoryStream(message.Body))
     {
         try
         {
             return(FastXmlTransportMessageSerializer.Deserialize(message.ConversationHandle.ToString(), stream));
         }
         catch (Exception e)
         {
             logger.Error(e, "Cannot deserialize transport message for message {0} from queue {1}.", message.ConversationHandle, ServiceBrokerQueue);
             throw new CannotDeserializeMessageException(e);
         }
     }
 }
 public static ServiceBrokerMessage Load(SqlDataReader reader)
 {
     var message = new ServiceBrokerMessage();
     message.ConversationHandle = reader.GetGuid(0);
     message.ServiceName = reader.GetString(1);
     message.ServiceContractName = reader.GetString(2);
     message.MessageTypeName = reader.GetString(3);
     if (!reader.IsDBNull(4))
     {
         message.Body = reader.GetSqlBytes(4).Buffer;
     }
     else
     {
         message.Body = new byte[0];
     }
     return message;
 }
        public static ServiceBrokerMessage Load(SqlDataReader reader)
        {
            var message = new ServiceBrokerMessage();

            message.ConversationHandle  = reader.GetGuid(0);
            message.ServiceName         = reader.GetString(1);
            message.ServiceContractName = reader.GetString(2);
            message.MessageTypeName     = reader.GetString(3);
            if (!reader.IsDBNull(4))
            {
                message.Body = reader.GetSqlBytes(4).Buffer;
            }
            else
            {
                message.Body = new byte[0];
            }
            return(message);
        }
        private void TryHandleMessage(SqlTransaction transaction, ServiceBrokerMessage message)
        {
            logger.Debug("Received message {0} from queue {1}", message.ConversationHandle, ServiceBrokerQueue);
            // NOTE this method _should not_ throw an exception!
            // It is responsible for committing or rolling back the SqlTransaction
            // that was passed in.
            // We can keep track of the openTransaction on the current thread in case we need it.
            openTransaction = transaction;
            try
            {
                // this may throw an exception if we need to rollback
                TryHandleMessageInner(transaction, message);
                // End the conversation and commit
                ServiceBrokerWrapper.EndConversation(transaction, message.ConversationHandle);
                transaction.Commit();
                faultManager.ClearFailuresForMessage(message.ConversationHandle.ToString());
                logger.Debug("Committed message {0} from queue {1}", message.ConversationHandle, ServiceBrokerQueue);
            }
            catch (Exception e)
            {
                TryRollbackTransaction(transaction);
                faultManager.IncrementFailuresForMessage(message.ConversationHandle.ToString(), e);
                logger.Error(e, "Exception caught handling message {0} from queue {1}. Rolling back transaction to retry.", message.ConversationHandle, ServiceBrokerQueue);
#if DEBUG
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debugger.Break();
                }
#endif
            }
            finally
            {
                openTransaction = null;
                TryDisposeTransactionAndConnection(transaction); // shoud not throw exception
            }
        }
 private IncomingTransportMessage TryLoadDialogTimerTimeoutMessage(SqlTransaction transaction, ServiceBrokerMessage message)
 {
     var messageBuffer = PopTimeoutMessage(transaction, message.ConversationHandle);
     if (messageBuffer == null || messageBuffer.Length == 0)
     {
         var errorMessage = string.Format("Cannot deserialize transport message. DialogTimer TimeoutMessage is missing for for message {0} from queue {1}.", message.ConversationHandle, ServiceBrokerQueue);
         logger.Error(errorMessage);
         throw new CannotDeserializeMessageException(errorMessage);
     }
     using (var stream = new MemoryStream(messageBuffer))
     {
         try
         {
             return FastXmlTransportMessageSerializer.Deserialize(message.ConversationHandle.ToString(), stream);
         }
         catch (Exception e)
         {
             logger.Error(e, "Cannot deserialize transport message for message {0} from queue {1}.", message.ConversationHandle, ServiceBrokerQueue);
             throw new CannotDeserializeMessageException(e);
         }
     }
 }
        private void TryHandleMessageInner(SqlTransaction transaction, ServiceBrokerMessage message)
        {
            // Only handle transport messages
            if (message.MessageTypeName != ServiceBrokerMessageType &&
                !message.IsDialogTimerMessage())
            {
                logger.Debug("Ignoring message of type {0} from queue {1}", message.MessageTypeName, ServiceBrokerQueue);
                return; // ignore
            }
            IncomingTransportMessage transportMessage = null;
            try
            {
                if (message.IsDialogTimerMessage())
                {
                    transportMessage = TryLoadDialogTimerTimeoutMessage(transaction, message);
                }
                else
                {
                    transportMessage = TryDeserializeTransportMessage(message);
                }

                if (!transportMessage.Headers.ContainsKey(StandardHeaders.OriginatingAddress))
                {
                    transportMessage.Headers[StandardHeaders.OriginatingAddress] = ServiceBrokerWrapper.TryGetConversationFarService(transaction, message.ConversationHandle);
                }

                Exception lastException = null;
                if (faultManager.MaxRetriesExceeded(transportMessage, out lastException))
                {
                    logger.Debug("MaxRetriesExceeded: Moving message {0} from queue {1} to poison message table.", message.ConversationHandle.ToString(), ServiceBrokerQueue);
                    MoveToPoisonMessage(transaction, message, transportMessage, lastException, "MaxRetriesExceeded", MaxRetries);
                    return; // return without error to commit transaction
                }
                logger.Debug("Notifying observers of new TransportMessage for message {0} from queue {1}.", message.ConversationHandle, ServiceBrokerQueue);
                if (this.MessageAvailable != null)
                {
                    this.MessageAvailable(this, new MessageAvailable(transportMessage));
                }
            }
            catch (CannotDeserializeMessageException e)
            {
                // If we can't deserialize the transport message or inner message, there is no reason to retry.
                // note: error is already logged so we don't need to log here
                logger.Debug("DeserializeError: Moving message {0} from queue {1} to poison message table.", message.ConversationHandle.ToString(), ServiceBrokerQueue);
                MoveToPoisonMessage(transaction, message, transportMessage, e, "DeserializeError", 0);
                return; // return without error to commit transaction
            }
        }
 private void TryHandleMessage(SqlTransaction transaction, ServiceBrokerMessage message)
 {
     logger.Debug("Received message {0} from queue {1}", message.ConversationHandle, ServiceBrokerQueue);
     // NOTE this method _should not_ throw an exception!
     // It is responsible for committing or rolling back the SqlTransaction
     // that was passed in.
     // We can keep track of the openTransaction on the current thread in case we need it.
     openTransaction = transaction;
     try
     {
         // this may throw an exception if we need to rollback
         TryHandleMessageInner(transaction, message);
         // End the conversation and commit
         ServiceBrokerWrapper.EndConversation(transaction, message.ConversationHandle);
         transaction.Commit();
         faultManager.ClearFailuresForMessage(message.ConversationHandle.ToString());
         logger.Debug("Committed message {0} from queue {1}", message.ConversationHandle, ServiceBrokerQueue);
     }
     catch (Exception e)
     {
         TryRollbackTransaction(transaction);
         faultManager.IncrementFailuresForMessage(message.ConversationHandle.ToString(), e);
         logger.Error(e, "Exception caught handling message {0} from queue {1}. Rolling back transaction to retry.", message.ConversationHandle, ServiceBrokerQueue);
     #if DEBUG
         if (System.Diagnostics.Debugger.IsAttached)
         {
             System.Diagnostics.Debugger.Break();
         }
     #endif
     }
     finally
     {
         openTransaction = null;
         TryDisposeTransactionAndConnection(transaction); // shoud not throw exception
     }
 }
 private IncomingTransportMessage TryDeserializeTransportMessage(ServiceBrokerMessage message)
 {
     using (var stream = new MemoryStream(message.Body))
     {
         try
         {
             return FastXmlTransportMessageSerializer.Deserialize(message.ConversationHandle.ToString(), stream);
         }
         catch (Exception e)
         {
             logger.Error(e, "Cannot deserialize transport message for message {0} from queue {1}.", message.ConversationHandle, ServiceBrokerQueue);
             throw new CannotDeserializeMessageException(e);
         }
     }
 }
 private void MoveToPoisonMessage(SqlTransaction transaction, ServiceBrokerMessage serviceBrokerMessage, IncomingTransportMessage transportMessage, Exception exception, string errorCode, int retries)
 {
     var origin_service_name = "";
     if (transportMessage != null && transportMessage.Headers.ContainsKey(StandardHeaders.OriginatingAddress))
     {
         origin_service_name = transportMessage.Headers[StandardHeaders.OriginatingAddress];
     }
     else if (transportMessage != null && transportMessage.Headers.ContainsKey(StandardHeaders.ReplyToAddress))
     {
         origin_service_name = transportMessage.Headers[StandardHeaders.ReplyToAddress];
     }
     else
     {
         origin_service_name = ServiceBrokerWrapper.TryGetConversationFarService(transaction, serviceBrokerMessage.ConversationHandle);
     }
     try
     {
         // write to the message to the PosionMessage table
         using (var command = transaction.Connection.CreateCommand())
         {
             command.CommandText = "spInsertPoisonMessage";
             command.CommandType = CommandType.StoredProcedure;
             command.Parameters.AddWithValue("@conversation_handle", serviceBrokerMessage.ConversationHandle);
             command.Parameters.AddWithValue("@origin_service_name", origin_service_name);
             command.Parameters.AddWithValue("@service_name", this.ServiceBrokerService);
             command.Parameters.AddWithValue("@queue_name", this.ServiceBrokerQueue);
             command.Parameters.AddWithValue("@message_body", serviceBrokerMessage.Body);
             command.Parameters.AddWithValue("@retries", retries);
             command.Parameters.AddWithValue("@errorCode", errorCode);
             if (exception != null)
                 command.Parameters.AddWithValue("@errorMessage", FormatErrorMessage(exception));
             else
                 command.Parameters.AddWithValue("@errorMessage", DBNull.Value);
             command.Transaction = transaction;
             command.ExecuteNonQuery();
         }
     }
     catch (Exception e)
     {
         logger.Fatal(e, "Failed to write PoisonMessage for message {0} from queue {1}", serviceBrokerMessage.ConversationHandle, ServiceBrokerQueue);
         // suppress -- don't let this exception take down the process
     }
     if (PoisonMessageDetected != null)
     {
         PoisonMessageDetected(this, new PoisonMessageDetectedEventArgs()
         {
             MessageId = serviceBrokerMessage.ConversationHandle.ToString(),
             OriginServiceName = origin_service_name,
             ServiceName = this.ServiceBrokerService,
             QueueName = this.ServiceBrokerQueue,
             MessageBody = serviceBrokerMessage.Body,
             Retries = retries,
             ErrorCode = errorCode,
             Exception = exception,
         });
     }
 }
        private void MoveToPoisonMessage(SqlTransaction transaction, ServiceBrokerMessage serviceBrokerMessage, IncomingTransportMessage transportMessage, Exception exception, string errorCode, int retries)
        {
            var origin_service_name = "";

            if (transportMessage != null && transportMessage.Headers.ContainsKey(StandardHeaders.OriginatingAddress))
            {
                origin_service_name = transportMessage.Headers[StandardHeaders.OriginatingAddress];
            }
            else if (transportMessage != null && transportMessage.Headers.ContainsKey(StandardHeaders.ReplyToAddress))
            {
                origin_service_name = transportMessage.Headers[StandardHeaders.ReplyToAddress];
            }
            else
            {
                origin_service_name = ServiceBrokerWrapper.TryGetConversationFarService(transaction, serviceBrokerMessage.ConversationHandle);
            }
            try
            {
                // write to the message to the PosionMessage table
                using (var command = transaction.Connection.CreateCommand())
                {
                    command.CommandText = "spInsertPoisonMessage";
                    command.CommandType = CommandType.StoredProcedure;
                    command.Parameters.AddWithValue("@conversation_handle", serviceBrokerMessage.ConversationHandle);
                    command.Parameters.AddWithValue("@origin_service_name", origin_service_name);
                    command.Parameters.AddWithValue("@service_name", this.ServiceBrokerService);
                    command.Parameters.AddWithValue("@queue_name", this.ServiceBrokerQueue);
                    command.Parameters.AddWithValue("@message_body", serviceBrokerMessage.Body);
                    command.Parameters.AddWithValue("@retries", retries);
                    command.Parameters.AddWithValue("@errorCode", errorCode);
                    if (exception != null)
                    {
                        command.Parameters.AddWithValue("@errorMessage", FormatErrorMessage(exception));
                    }
                    else
                    {
                        command.Parameters.AddWithValue("@errorMessage", DBNull.Value);
                    }
                    command.Transaction = transaction;
                    command.ExecuteNonQuery();
                }
            }
            catch (Exception e)
            {
                logger.Fatal(e, "Failed to write PoisonMessage for message {0} from queue {1}", serviceBrokerMessage.ConversationHandle, ServiceBrokerQueue);
                // suppress -- don't let this exception take down the process
            }
            if (PoisonMessageDetected != null)
            {
                PoisonMessageDetected(this, new PoisonMessageDetectedEventArgs()
                {
                    MessageId         = serviceBrokerMessage.ConversationHandle.ToString(),
                    OriginServiceName = origin_service_name,
                    ServiceName       = this.ServiceBrokerService,
                    QueueName         = this.ServiceBrokerQueue,
                    MessageBody       = serviceBrokerMessage.Body,
                    Retries           = retries,
                    ErrorCode         = errorCode,
                    Exception         = exception,
                });
            }
        }
        private IncomingTransportMessage TryLoadDialogTimerTimeoutMessage(SqlTransaction transaction, ServiceBrokerMessage message)
        {
            var messageBuffer = PopTimeoutMessage(transaction, message.ConversationHandle);

            if (messageBuffer == null || messageBuffer.Length == 0)
            {
                var errorMessage = string.Format("Cannot deserialize transport message. DialogTimer TimeoutMessage is missing for for message {0} from queue {1}.", message.ConversationHandle, ServiceBrokerQueue);
                logger.Error(errorMessage);
                throw new CannotDeserializeMessageException(errorMessage);
            }
            using (var stream = new MemoryStream(messageBuffer))
            {
                try
                {
                    return(FastXmlTransportMessageSerializer.Deserialize(message.ConversationHandle.ToString(), stream));
                }
                catch (Exception e)
                {
                    logger.Error(e, "Cannot deserialize transport message for message {0} from queue {1}.", message.ConversationHandle, ServiceBrokerQueue);
                    throw new CannotDeserializeMessageException(e);
                }
            }
        }