internal static string CheckConversationForErrorMessage(string connstr, Guid conversationHandle) { using (SqlConnection con = SsbHelper.GetConnection(connstr)) { con.Open(); return(CheckConversationForErrorMessage(con, conversationHandle)); } }
internal static ServiceInfo GetServiceInfo(string serviceName, TimeSpan timeout, string connstr) { using (SqlConnection con = SsbHelper.GetConnection(connstr)) { con.Open(); return(GetServiceInfo(serviceName, timeout, con)); } }
public static int EndAllConversationsWithCleanup(string connstr) { using (SqlConnection con = SsbHelper.GetConnection(connstr)) { con.Open(); return(EndAllConversationsWithCleanup(con)); } }
public static void EndConversationWithCleanup(string connstr, Guid conversationHandle) { using (SqlConnection con = SsbHelper.GetConnection(connstr)) { con.Open(); EndConversationWithCleanup(con, conversationHandle); } }
internal static void SetConversationTimer(Guid conversationHandle, TimeSpan timerTimeout, string connstr) { using (SqlConnection con = SsbHelper.GetConnection(connstr)) { con.Open(); SetConversationTimer(conversationHandle, timerTimeout, con); } }
internal static ConversationInfo GetConversationInfo(Guid conversationHandle, string constr) { using (SqlConnection con = SsbHelper.GetConnection(constr)) { con.Open(); return(GetConversationInfo(conversationHandle, con)); } }
internal void Send(byte[] buffer, TimeSpan timeout, string messageType) { ThrowIfDisposedOrNotOpen(); TimeoutHelper helper = new TimeoutHelper(timeout); //if the client hasn't explicitly begun a conversation, start one here. // CONSIDER (dbrowne) automatically ending the conversation in this case. if (conversation == null) { BeginNewConversation(); this.endConversationOnClose = true; } try { string SQL = string.Format(@"SEND ON CONVERSATION @Conversation MESSAGE TYPE [{0}](@MessageBody)", SsbHelper.ValidateIdentifier(messageType)); SqlCommand cmd = new SqlCommand(SQL, con); cmd.CommandTimeout = helper.RemainingTimeInMillisecondsOrZero(); SqlParameter pConversation = cmd.Parameters.Add("@Conversation", SqlDbType.UniqueIdentifier); pConversation.Value = this.conversation.ConversationHandle; SqlParameter pMessageBody = cmd.Parameters.Add("@MessageBody", SqlDbType.VarBinary); pMessageBody.Value = buffer; try { cmd.ExecuteNonQuery(); } catch (SqlException ex) { if (ex.Number == 8429) { string errorMessage = SsbHelper.CheckConversationForErrorMessage(con, this.conversation.ConversationHandle); if (errorMessage != null) { throw new Exception(errorMessage); } throw; } throw; } SsbInstrumentation.MessageSent(buffer.Length); } catch (Exception e) { if (!helper.IsTimeRemaining) { throw new TimeoutException("Timed out while sending a message. The timeout value passed in was " + timeout.TotalSeconds + " seconds"); } else { throw new CommunicationException(String.Format("An exception occurred while sending on conversation {0}.", this.conversation.ConversationHandle), e); } } }
protected override void OnOpen(TimeSpan timeout) { TimeoutHelper helper = new TimeoutHelper(timeout); if (this.con == null) { this.con = SsbHelper.GetConnection(this.connectionString); con.Open(); this.ownsConnection = true; } }
public Guid BeginNewConversation(Guid conversationGroupId, TimeSpan timeout) { ThrowIfDisposedOrNotOpen(); endConversationOnClose = false; //if a conversation is explicitly started, don't automatically close it. TimeoutHelper helper = new TimeoutHelper(timeout); try { string SQL = string.Format( @"BEGIN DIALOG CONVERSATION @ConversationHandle FROM SERVICE @Source TO SERVICE @Target ON CONTRACT [{0}] WITH ENCRYPTION = {1}", contract, useEncryption?"ON":"OFF"); if (conversationGroupId != Guid.Empty) { SQL += String.Format(", RELATED_CONVERSATION_GROUP = '{0}'", conversationGroupId); } SqlCommand cmd = new SqlCommand(SQL, con); cmd.CommandTimeout = helper.RemainingTimeInMillisecondsOrZero(); SqlParameter pconversationHandle = cmd.Parameters.Add("@ConversationHandle", SqlDbType.UniqueIdentifier); pconversationHandle.Direction = ParameterDirection.Output; SqlParameter pTarget = cmd.Parameters.Add("@Target", SqlDbType.VarChar); pTarget.Value = this.target; SqlParameter pSource = cmd.Parameters.Add("@Source", SqlDbType.VarChar); pSource.Value = this.source; cmd.ExecuteNonQuery(); this.conversation = SsbHelper.GetConversationInfo((Guid)pconversationHandle.Value, con); return(this.conversation.ConversationHandle); } catch (SqlException ex) { if (!helper.IsTimeRemaining) { throw new TimeoutException(String.Format("Timed out while beginning new conversation to service {0}. Timeout value was {1} seconds", this.target, timeout.TotalSeconds), ex); } else { throw new CommunicationException(String.Format("An exception occurred while beginning new conversation to service {0}", this.target), ex); } } finally { } }
internal SsbConversationGroupReceiver AcceptConversationGroup(TimeSpan timeout) { TimeoutHelper helper = new TimeoutHelper(timeout); try { //create a transaction to own the conversation. Open the SqlConnection while //the transaction is current, so all the work on the connection will be enlisted //in the transaction. SqlTransaction doesn't work this way. It requires every command //to be manually enlisted, yuck. //As an alternative you could simply issue a new SqlCommand("begin transaction",cn).ExecuteNonQuery() //to start a transaction. // TODO (dbrowne) Consider whether to allow service-level code to commit this transaction, or, alternatively, //commit the transaction after the last conversation message in this session is processed. //If service-level code is allowed to commit the transaction then SsbConversationGroupReciver _must_ //retrieve only a single message in each batch. Else it could fetch 2 messages and risk the service-level //commiting after the first message. CommittableTransaction tx = new CommittableTransaction(TimeSpan.MaxValue); Transaction currentTrans = Transaction.Current; Transaction.Current = tx; SqlConnection cn = SsbHelper.GetConnection(this.connstring); cn.Open(); Transaction.Current = currentTrans; tx.TransactionCompleted += new TransactionCompletedEventHandler(tx_TransactionCompleted); //wait for a new message, but if that message is in a conversation group that another SsbChannelListener is waiting on //then roll back the conversation group lock and get back in line. The waiting SsbChannelListener should pick it up. string sql = String.Format(@" retry: save transaction swiper_no_swiping declare @cg uniqueidentifier declare @rc int waitfor (get conversation group @cg from [{0}]), TIMEOUT @timeout if @cg is null begin return --timeout end exec @rc = sp_getapplock @Resource=@cg, @LockMode='Exclusive', @LockTimeout = 0 if @rc = -1 begin print 'skipping message for locked conversation_group' rollback transaction swiper_no_swiping goto retry end set @conversation_group_id = @cg ", this.serviceInfo.QueueName); SqlCommand cmd = new SqlCommand(sql, cn); cmd.CommandTimeout = 0; cn.InfoMessage += new SqlInfoMessageEventHandler(cn_InfoMessage); SqlParameter pTimeout = cmd.Parameters.Add("@timeout", SqlDbType.Int); pTimeout.Value = TimeoutHelper.ToMilliseconds(timeout); SqlParameter pConversationGroupId = cmd.Parameters.Add("@conversation_group_id", SqlDbType.UniqueIdentifier); pConversationGroupId.Direction = ParameterDirection.Output; //Run the command, but abort if the another thread runs Close or Abort IAsyncResult result = cmd.BeginExecuteNonQuery(); int rc = WaitHandle.WaitAny(new WaitHandle[] { result.AsyncWaitHandle, cancelEvent }); if (rc == 1) //cancel event { cmd.Cancel(); TraceHelper.TraceEvent(System.Diagnostics.TraceEventType.Verbose, "Canceling Service Broker wait on SsbChannelListener shutdown", "AcceptConversationGroup"); cn.Close(); return(null); } if (rc != 0) { throw new InvalidOperationException("Unexpected state"); } cmd.EndExecuteNonQuery(result); if (pConversationGroupId.Value == null || pConversationGroupId.Value.GetType() != typeof(Guid)) { throw new TimeoutException(String.Format("Timed out while waiting for Conversation Group")); } Guid conversationGroupId = (Guid)pConversationGroupId.Value; TraceHelper.TraceEvent(System.Diagnostics.TraceEventType.Verbose, string.Format("Accepted conversation group {0}", conversationGroupId), "AcceptConversationGroup"); SsbConversationGroupReceiver cg = new SsbConversationGroupReceiver(cn, tx, conversationGroupId, this.serviceInfo, this); return(cg); } catch (SqlException ex) { if (!helper.IsTimeRemaining) { throw new TimeoutException(String.Format("Timed out while waiting for Conversation Group. Timeout value was {0} seconds", timeout.TotalSeconds), ex); } else { throw new ProtocolException("An error occurred while waiting for a Conversation Group", ex); } } }
internal SsbConversationGroupReceiver AcceptConversationGroup(Guid conversationGroupId, TimeSpan timeout) { TimeoutHelper helper = new TimeoutHelper(timeout); TransactionOptions to = new TransactionOptions(); to.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted; to.Timeout = TimeSpan.MaxValue; CommittableTransaction tx = new CommittableTransaction(to); Transaction currentTrans = Transaction.Current; Transaction.Current = tx; SqlConnection cn = SsbHelper.GetConnection(this.connstring); cn.Open(); Transaction.Current = currentTrans; //first lock the the Conversation Group. This will prevent other SsbConversationGroupReceivers //from poaching the messages. string sql = @" declare @cg uniqueidentifier declare @rc int set @cg = @conversation_group_id exec @rc = sp_getapplock @Resource=@cg, @LockMode='Exclusive' if @rc not in (0,1) begin raiserror('Failed to lock conversation group. sp_getapplock failure code %d.',16,1,@rc); end "; SqlCommand cmd = new SqlCommand(sql, cn); SqlParameter pConversationGroupId = cmd.Parameters.Add("@conversation_group_id", SqlDbType.UniqueIdentifier); pConversationGroupId.Value = conversationGroupId; cmd.CommandTimeout = helper.RemainingTimeInMillisecondsOrZero(); //Run the command, but abort if the another thread runs Close or Abort IAsyncResult result = cmd.BeginExecuteNonQuery(); int rc = WaitHandle.WaitAny(new WaitHandle[] { result.AsyncWaitHandle, cancelEvent }); if (rc == 1) //cancel event { cmd.Cancel(); TraceHelper.TraceEvent(System.Diagnostics.TraceEventType.Verbose, "Canceling Service Broker wait on SsbChannelListener shutdown", "AcceptConversationGroup"); cn.Close(); return(null); } if (rc != 0) { throw new InvalidOperationException("Unexpected state"); } cmd.EndExecuteNonQuery(result); SsbConversationGroupReceiver cg = new SsbConversationGroupReceiver(cn, tx, conversationGroupId, this.serviceInfo, this); cg.Open(); //now wait for a message on the conversation. Do it here because WCF expects the wait to be here instead of in a subsequent receive. //And also there is a different timeout to accepting a channel and waiting for additional messages. //But we need to pass in the cancelEvent to abort the listen, if this ChannelListener is closed or aborted if (!cg.WaitForFirstMessage(helper.RemainingTime(), cancelEvent)) { if (this.State != CommunicationState.Opened)//this ChannelListener was closed or aborted, so this is expected. { //for some reason the transaction tx is in an uncommitable state here. Not sure why. //however no other work has occured on this connection, so we can just shut it down. cn.Close(); return(null); } throw new TimeoutException(String.Format("Timed out while waiting for Conversation Group")); } return(cg); }
protected override void OnOpen(TimeSpan timeout) { this.serviceInfo = SsbHelper.GetServiceInfo(this.listenUri.Service, timeout, this.connstring); }
public void SetConversationTimer(Guid conversationHandle, TimeSpan timerTimeout) { SsbHelper.SetConversationTimer(conversationHandle, timerTimeout, this.con); }
public void OpenConversation(Guid conversationHandle, TimeSpan timeout) { ThrowIfDisposedOrNotOpen(); this.conversation = SsbHelper.GetConversationInfo(conversationHandle, con); }