private async Task AcceptMessageEventAsync(MessageEventArgs msgEv, FlowBindings.FlowBindingElement binding)
        {
            try
            {
                Message rxMessage = SolaceNativeMsgAdapter.ConvertFromNativeMsg(msgEv.Message);

                bool continueDelivery = requestMgr.HandleIncoming(rxMessage);

                // If continueDelivery is false, the message was handled by the requestMgr so we drop it.
                if (continueDelivery && binding != null)
                {
                    await binding.DispatchMessageAsync(rxMessage).ConfigureAwait(false);
                }
                else if (continueDelivery && binding == null)
                {
                    // There is no callback on which to forward the message to, so dispatch
                    // the message to the default session callback.
                    await defaultAppMsgQueue.SendAsync(rxMessage).ConfigureAwait(false);
                }
            }
            finally
            {
                msgEv.Message.Dispose();
            }
        }
        private async Task <bool> SubscribeTopicAsyncInternal(Topic topic, BufferBlock <Message> messageQueue = null)
        {
            ITopic solTopic = ContextFactory.Instance.CreateTopic(topic.Name);

            FlowBindings.FlowBindingElement binding = null;
            if (messageQueue == null)
            {
                // Use default message block
                messageQueue = defaultAppMsgQueue;
            }
            bool newSubscription = flowBindings.AddBinding(topic, messageQueue, null, out binding);

            if (newSubscription && binding != null)
            {
                try
                {
                    TaskCompletionSource <SessionEventArgs> tcs = new TaskCompletionSource <SessionEventArgs>();
                    IDispatchTarget dTarget = session.CreateDispatchTarget(solTopic,
                                                                           async(sender, msgEv) => await AcceptMessageEventAsync(msgEv, binding).ConfigureAwait(false));
                    binding.TopicDispatchTarget = dTarget;
                    session.Subscribe(dTarget, SubscribeFlag.RequestConfirm, tcs);

                    // Check subscription result
                    var result = await tcs.Task.ConfigureAwait(false);

                    if (result.Event == SessionEvent.SubscriptionOk)
                    {
                        return(true);
                    }
                    else
                    {
                        logger.LogError("Subscription error to topic: {0} responseCode {1} errorInfo: {2}",
                                        topic.Name, result.ResponseCode, result.Info);
                        return(false);
                    }
                }
                catch (Exception e)
                {
                    binding.TopicDispatchTarget = null;
                    flowBindings.RemoveBinding(topic, out binding);
                    throw new MessagingException(e.Message, e);
                }
            }

            if (!newSubscription && binding != null)
            {
                // If existing subscription then ignore and return success
                return(true);
            }
            return(false);
        }
        /// <summary>
        /// Unsubscribes from the destination asynchronously. For Queue destinations
        /// the flow is unbound and destroyed. Caller will also receive a flow state
        /// changed event if flow events were configured during subscribe.
        /// </summary>
        /// <param name="destination">The destination to unsubscribe.</param>
        /// <returns>True if the subscription was removed succussfully, false otherwise.</returns>
        public async Task <bool> UnsubscribeAsync(Destination destination)
        {
            FlowBindings.FlowBindingElement bind = null;
            var removed = flowBindings.RemoveBinding(destination, out bind);

            if (removed && bind != null)
            {
                try
                {
                    // Unbind from queue if a flow is configured
                    if (bind.Flow != null)
                    {
                        bind.Flow.Stop();
                        // 100 ms delay to avoid race-condition if still receiving msgs
                        // and calling Flow.Ack which may cause deadlock and flow destroy will timeout.
                        await Task.Delay(100);

                        bind.Flow.Dispose();
                        bind.Flow = null;
                        return(true);
                    }
                    // Unsubscribe from topic if topic dispatcher is configured
                    if (bind.TopicDispatchTarget != null)
                    {
                        TaskCompletionSource <SessionEventArgs> tcs = new TaskCompletionSource <SessionEventArgs>();
                        session.Unsubscribe(bind.TopicDispatchTarget, SubscribeFlag.RequestConfirm, tcs);
                        // Check unsubscribe result
                        var result = await tcs.Task.ConfigureAwait(false);

                        // Solace API use Subscription OK events for both subscribe and unsubscribe
                        if (result.Event == SessionEvent.SubscriptionOk)
                        {
                            return(true);
                        }
                        else
                        {
                            logger.LogError("Unsubscribe error to topic: {0} responseCode {1} errorInfo: {2}",
                                            bind.Destination.Name, result.ResponseCode, result.Info);
                            return(false);
                        }
                    }
                }
                catch (Exception e)
                {
                    throw new MessagingException(e.Message, e);
                }
            }
            return(true);
        }
        private async Task <bool> SubscribeQueueAsyncInternal(Queue queue, BufferBlock <Message> messageQueue = null,
                                                              BufferBlock <FlowStateContext> flowEvtQueue     = null, bool flowStartState = false)
        {
            FlowBindings.FlowBindingElement binding = null;
            if (messageQueue == null)
            {
                // Use default message block
                messageQueue = defaultAppMsgQueue;
            }
            bool newSubscription = flowBindings.AddBinding(queue, messageQueue, flowEvtQueue, out binding);

            if (newSubscription && binding != null)
            {
                try
                {
                    // Configure flow properties
                    var fp = new FlowProperties
                    {
                        AckMode        = solaceOptions.ClientAck ? MessageAckMode.ClientAck : MessageAckMode.AutoAck,
                        BindBlocking   = false, // ensure we bind in non-blocking mode
                        FlowStartState = flowStartState
                    };

                    // Destination
                    IEndpoint solQueue = null;
                    if (queue.IsTemporary)
                    {
                        solQueue = session.CreateTemporaryQueue(queue.Name);
                    }
                    else
                    {
                        solQueue = ContextFactory.Instance.CreateQueue(queue.Name);
                    }

                    // Create the flow
                    TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>();
                    IFlow flow = session.CreateFlow(
                        fp,
                        solQueue,
                        null,
                        async(sender, msgEv) => { await AcceptMessageEventAsync(msgEv, binding).ConfigureAwait(false); },
                        async(sender, flowEv) =>
                    {
                        logger.LogDebug("FlowEvent: {0}, Info: {1}", flowEv.Event, flowEv.Info);
                        var flowStateCtx = new FlowStateContext()
                        {
                            Info = flowEv.Info, ResponseCode = flowEv.ResponseCode
                        };
                        switch (flowEv.Event)
                        {
                        case FlowEvent.UpNotice:
                            flowStateCtx.State = FlowState.Up;
                            tcs.TrySetResult(true);
                            break;

                        case FlowEvent.BindFailedError:
                            flowStateCtx.State = FlowState.BindFailedError;
                            logger.LogWarning(string.Format("Queue connection failure: {0}", flowEv.Event.ToString()));
                            tcs.TrySetResult(false);
                            break;

                        case FlowEvent.DownError:
                            flowStateCtx.State = FlowState.Down;
                            break;

                        case FlowEvent.FlowActive:
                            flowStateCtx.State = FlowState.FlowActive;
                            break;

                        case FlowEvent.FlowInactive:
                            flowStateCtx.State = FlowState.FlowInactive;
                            break;

                        default:
                            break;
                        }

                        // Notify caller of the flow event
                        await binding.DispatchFlowEventAsync(flowStateCtx).ConfigureAwait(false);
                    });
                    binding.Flow = flow;
                    return(await tcs.Task.ConfigureAwait(false));
                }
                catch (Exception e)
                {
                    binding.Flow = null;
                    flowBindings.RemoveBinding(queue, out binding);
                    throw new MessagingException(e.Message, e);
                }
            }

            if (!newSubscription && binding != null)
            {
                // If existing subscription then ignore and return success
                return(true);
            }
            return(false);
        }