0

I am have a windows service which will subscribe to a queue and process messages continuously. Every thing is working except except for the reconnect logic. I am using IBM.XMS.dll version 8 in my code.

I am having issues in re-establishing the subscription when IBM Websphere MQ server fail-over happens. During fail-over, I get the following exception:

IBM.XMS.IllegalStateException: XMSCC0026
XMSCC0026.explanation
XMSCC0026.useraction
   at IBM.XMS.Client.Impl.State.CheckNotClosed(String message)
   at IBM.XMS.Client.Impl.XmsMessageProducerImpl.Send_(Boolean inIdentifiedContext, XmsDestinationImpl dest, IMessage message, DeliveryMode deliveryMode, Int32 priority, Int64 timeToLive, Boolean explicitDlvModePriorityAndTimeToLive)
   at IBM.XMS.Client.Impl.XmsMessageProducerImpl.Send(IMessage message)

Stack Trace:    at IBM.XMS.Client.Impl.State.CheckNotClosed(String message)
   at IBM.XMS.Client.Impl.XmsMessageProducerImpl.Send_(Boolean inIdentifiedContext, XmsDestinationImpl dest, IMessage message, DeliveryMode deliveryMode, Int32 priority, Int64 timeToLive, Boolean explicitDlvModePriorityAndTimeToLive)
   at IBM.XMS.Client.Impl.XmsMessageProducerImpl.Send(IMessage message)

I need some help in re-establishing the subscription in onException event handler:

private void ReconnectToXMS_MQ()
    {
        if (!_cancellationToken.IsCancellationRequested)
        {
            LoggingService.LogDebug($"[XMSQueue] Reconnecting for queue: {ConfigQueueName} and Address: {Address}");

            if(MsgQueueType == QueueType.Publisher)
            {
                Dispose(true);
                InitializeXMSMQ();
                InitialiseOutbound(ConfigQueueName, Pattern, false);
            }
            else if(MsgQueueType == QueueType.Subscriber)
            {
                //Need help here
            }
            else if(MsgQueueType == QueueType.Receiver)
            {
                Dispose(true);
                InitializeXMSMQ();
                InitialiseInbound(ConfigQueueName, Pattern, false);
            }
        }
    }

Following is the full code snippet of my XMS implementation.

internal class XMSQueue : MessageQueueBase
{
    #region " Local Methods/Properties "

    private IConnectionFactory _connectionfactory;
    private IConnection _connection;
    private IDestination _destination;
    private ISession _session;
    private IMessageConsumer _consumer;
    private IMessageProducer _producer;
    private CancellationToken _cancellationTOke;

    private string ConfigQueueName { get; set; }

    private XMSProperties xmsConfiguration { get; set; }

    private MessageFormat MsgFormat { get; set; }

    private QueueType MsgQueueType { get; set; }

    #endregion

    #region " MessageQueueBase OVerrides "

    public override string Name { get; set; }


    #region " Receive/Subscribe "

    public override void InitialiseInbound(string name, MessagePattern pattern, bool isTemporary)
    {
        try
        {
            ConfigQueueName = name;
            Initialise(Direction.Inbound, name, pattern, isTemporary);

            InitializeXMSMQ();

            //Set Destination
            _destination = CreateDestinationInbound();

            //Create Consumer
            _consumer = _session.CreateConsumer(_destination);

        }
        catch (XMSException xmsError)
        {
            LogXMSExceptionDetails(xmsError);
            throw xmsError;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    public override void Subscribe<TReceiveMessageType>(Action<TReceiveMessageType> onMessageReceived, CancellationToken cancellationToken, MessageFormat messageFormat = MessageFormat.XMS_IMessage)
    {
        try
        {
            MsgQueueType = QueueType.Subscriber;
            _cancellationTOke = cancellationToken;
            cancellationToken.Register(() =>
            {
                _consumer.MessageListener = null;
            });

            MsgFormat = messageFormat;
            // Create and register the listener
            MessageListener messageListener = new MessageListener((msg) =>
            {
                TReceiveMessageType message;
                message = ProcessInboundMessage<TReceiveMessageType>(messageFormat, msg);
                onMessageReceived(message);

            });
            _consumer.MessageListener = messageListener;

            // Start the connection
            _connection.Start();
        }
        catch (XMSException xmsError)
        {
            LogXMSExceptionDetails(xmsError);

            ReconnectToXMS_MQ();
        }
        catch (Exception ex)
        {
            ReconnectToXMS_MQ();
        }
    }

    public override void Receive<TReceiveMessageType>(Action<TReceiveMessageType> onMessageReceived, bool processAsync, int maximumWaitMilliseconds = 0, MessageFormat messageFormat = MessageFormat.XMS_IMessage)
    {
        try
        {
            MsgQueueType = QueueType.Receiver;
            MsgFormat = messageFormat;
            // Start the connection
            _connection.Start();

            IMessage inbound = null;
            TReceiveMessageType message;

            // Receive the message                                    
            inbound = _consumer.Receive();
            if (inbound != null)
            {
                message = ProcessInboundMessage<TReceiveMessageType>(messageFormat, inbound);

                if (processAsync)
                {
                    Task.Factory.StartNew(() => onMessageReceived(message));
                }
                else
                {
                    onMessageReceived(message);
                }
            }
            else
            {
                throw new Exception("Message received was null.");
            }
        }
        catch (XMSException xmsError)
        {
            LogXMSExceptionDetails(xmsError);
            throw xmsError;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    #endregion

    #region " Send/Publish "

    public override void InitialiseOutbound(string name, MessagePattern pattern, bool isTemporary)
    {
        try
        {
            ConfigQueueName = name; //Save the config queue name for later use in reconnection logic
            Initialise(Direction.Outbound, name, pattern, isTemporary);

            InitializeXMSMQ();

            //Set Destination
            _destination = CreateDestinationOutbound();

            //Create Producer
            _producer = _session.CreateProducer(_destination);

        }
        catch (XMSException xmsError)
        {
            LogXMSExceptionDetails(xmsError);
            throw xmsError;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    public override bool Send<T>(T message, MessageFormat messageFormat)
    {
        try
        {
            MsgQueueType = QueueType.Publisher;
            MsgFormat = messageFormat;
            //Start the connection
            _connection.Start();

            //Create Message
            IMessage outbound = null;
            if (messageFormat == MessageFormat.String)
            {
                outbound = _session.CreateTextMessage(message.ToString());
            }
            else if (messageFormat == MessageFormat.ByteArray)
            {
                outbound = _session.CreateObjectMessage();
                byte[] byteText = message as byte[];
                ((IObjectMessage)outbound).SetObject(byteText);
            }
            else if (messageFormat == MessageFormat.XMS_IMessage)
            {
                outbound = message as IMessage;
            }
            else
            {
                throw new NotSupportedException("UnRecognized/UnSUpported message format. Please use String, ByteArray or XMS_IMessage message formats");
            }

            _producer.Send(outbound);
            return true;
        }
        catch (XMSException xmsError)
        {
            LogXMSExceptionDetails(xmsError);
            return false;
        }
        catch (Exception ex)
        {
            return false;
        }
    }

    #endregion

    protected override void Dispose(bool disposing)
    {
        try
        {
            if (_consumer != null)
            {
                _consumer.Close();
                _consumer.Dispose();
            }

            //Reset Producer
            if (_producer != null)
            {
                _producer.Close();
                _producer.Dispose();
            }

            //Reset Destination
            if (_destination != null)
                _destination.Dispose();

            //Reset Session
            if (_session != null)
            {
                _session.Close();
                _session.Dispose();
            }

            //Reset Connection
            if (_connection != null)
            {
                _connection.Close();
                _consumer.Dispose();
            }
        }
        catch (Exception)
        {
            //ignore any exceptions at this point
        }
    }

    #endregion

    #region " Local Methods "

    #region " Initialize and Reconnect "

    private void InitializeXMSMQ()
    {
        xmsConfiguration = new XMSProperties(Properties);
        xmsConfiguration.ConnectionType = RequirePropertyValue(ConfigurationKeys.ConnectionType);

        //SetConnectionFactory Connection Factory
        SetConnectionFactory();

        //Set Connection
        _connection = _connectionfactory.CreateConnection(null, null); //We do not use UserID and Password to connect.
        _connection.ExceptionListener = new ExceptionListener(OnConnectionException);

        //Set Session
        _session = _connection.CreateSession(false, AcknowledgeMode.AutoAcknowledge);
    }

    private void ReconnectToXMS_MQ()
    {
        if (!_cancellationTOke.IsCancellationRequested)
        {
            if(MsgQueueType == QueueType.Publisher)
            {
                Dispose(true);
                InitializeXMSMQ();
                InitialiseOutbound(ConfigQueueName, Pattern, false);
            }
            else if(MsgQueueType == QueueType.Subscriber)
            {
                //Need help here
            }
            else if(MsgQueueType == QueueType.Receiver)
            {
                Dispose(true);
                InitializeXMSMQ();
                InitialiseInbound(ConfigQueueName, Pattern, false);
            }
        }
    }

    private void OnConnectionException(Exception ex)
    {
        ReconnectToXMS_MQ();
    }

    private static void LogXMSExceptionDetails(XMSException xmsError)
    {
        if (xmsError.LinkedException != null)
        {
            LogError($"[XMSQueue] Linked Exception: {xmsError.LinkedException.Message} ");
            if (xmsError.LinkedException.InnerException != null)
                LogError($"[XMSQueue] Linked Inner Exception: {xmsError.LinkedException.InnerException} ");
        }
    }

    #endregion

    #region " XMS Connection Factory "
    private void SetConnectionFactory()
    {
        _connectionfactory = CreateConnectionFactory();
    }

    private IConnectionFactory CreateConnectionFactory()
    {
        IConnectionFactory iConnFact;
        switch (xmsConfiguration.ConnectionType.ToUpper())
        {
            // WPM connection factory
            case "WPM":
                iConnFact = CreateConnectionFactoryWPM();
                break;
            // RTT connection factory
            case "RTT":
                iConnFact = CreateConnectionFactoryRTT();
                break;
            // WMQ connection factory
            case "WMQ":
                iConnFact = CreateConnectionFactoryWMQ();
                break;
            default:
                iConnFact = null;
                break;
        }
        return iConnFact;
    }

    /// <summary>
    /// Create a WPM connection factory and set relevant properties.
    /// </summary>
    /// <returns>A connection factory</returns>
    private static IConnectionFactory CreateConnectionFactoryWPM()
    {
        // Create the connection factories factory
        XMSFactoryFactory factoryFactory = XMSFactoryFactory.GetInstance(XMSC.CT_WPM);

        // Use the connection factories factory to create a connection factory
        IConnectionFactory cf = factoryFactory.CreateConnectionFactory();


        // WPM multicast is currently disabled
        // cf.SetIntProperty(XMSC.WPM_MULTICAST, Options.MulticastModeWPM.ValueAsNumber);

        return (cf);
    }

    /// <summary>
    /// Create a RTT connection factory and set relevant properties.
    /// </summary>
    /// <returns>A connection factory</returns>
    private static IConnectionFactory CreateConnectionFactoryRTT()
    {
        // Create the connection factories factory
        XMSFactoryFactory factoryFactory = XMSFactoryFactory.GetInstance(XMSC.CT_RTT);

        // Use the connection factories factory to create a connection factory
        IConnectionFactory cf = factoryFactory.CreateConnectionFactory();

        return (cf);
    }

    /// <summary>
    /// Create a WMQ connection factory and set relevant properties.
    /// </summary>
    /// <returns>A connection factory</returns>
    private IConnectionFactory CreateConnectionFactoryWMQ()
    {
        // Create the connection factories factory
        XMSFactoryFactory factoryFactory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ);

        // Use the connection factories factory to create a connection factory
        IConnectionFactory cf = factoryFactory.CreateConnectionFactory();

        // Set the properties
        cf.SetStringProperty(XMSC.WMQ_HOST_NAME, xmsConfiguration.WMQProperties.Hostname);
        cf.SetIntProperty(XMSC.WMQ_PORT, xmsConfiguration.WMQProperties.Port);
        cf.SetStringProperty(XMSC.WMQ_CHANNEL, xmsConfiguration.WMQProperties.Channel);
        cf.SetIntProperty(XMSC.WMQ_CONNECTION_MODE, xmsConfiguration.WMQProperties.ConnectionMode);
        if (string.IsNullOrEmpty(xmsConfiguration.WMQProperties.QueueManager))
        {
            cf.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, "");
        }
        else
        {
            cf.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, xmsConfiguration.WMQProperties.QueueManager);
        }

        if (xmsConfiguration.WMQProperties.BrokerVersion != -1) //-1 => Not set in configuration
            cf.SetIntProperty(XMSC.WMQ_BROKER_VERSION, xmsConfiguration.WMQProperties.BrokerVersion);

        cf.SetStringProperty(XMSC.WMQ_CONNECTION_NAME_LIST, xmsConfiguration.WMQProperties.Hostname);
        if (!string.IsNullOrWhiteSpace(xmsConfiguration.WMQProperties.ConnectionList))
        {
            cf.SetStringProperty(XMSC.WMQ_CONNECTION_NAME_LIST, xmsConfiguration.WMQProperties.ConnectionList);
            cf.SetIntProperty(XMSC.WMQ_CLIENT_RECONNECT_TIMEOUT, xmsConfiguration.WMQProperties.ReconnectTimeout);
            cf.SetIntProperty(XMSC.WMQ_CLIENT_RECONNECT_OPTIONS, xmsConfiguration.WMQProperties.ReconnectOptions);
        }

        if (!string.IsNullOrWhiteSpace(xmsConfiguration.WMQProperties.SSLCertRepository))
        {
            cf.SetStringProperty(XMSC.WMQ_SSL_KEY_REPOSITORY, xmsConfiguration.WMQProperties.SSLCertRepository);
            cf.SetStringProperty(XMSC.WMQ_SSL_CIPHER_SPEC, xmsConfiguration.WMQProperties.SSLCipherSpec);
        }

        cf.SetStringProperty(XMSC.WMQ_PROVIDER_VERSION, XMSC.WMQ_PROVIDER_VERSION_DEFAULT);
        cf.SetBooleanProperty(XMSC.WMQ_SYNCPOINT_ALL_GETS, true);
        return (cf);
    }

    #endregion

    #region " Create IDestination "

    protected IDestination CreateDestinationOutbound()
    {
        IDestination iDest;

        switch (xmsConfiguration.ConnectionType.ToUpper())
        {
            // WPM destination
            case Literals.WPM:
            case Literals.WMQ:
                if (Pattern == MessagePattern.FireAndForget)
                {
                    iDest = (Address.StartsWith("queue://")) ?
                                _session.CreateQueue(Address) : // Create a Queue
                                _session.CreateTopic(Address);  // FireAndForget is defaulted to Topic for Outbound unless Address is defined with queue
                }
                else if (Pattern == MessagePattern.PublishSubscribe)
                {
                    iDest = (Address.StartsWith("queue://")) ?
                                _session.CreateQueue(Address) : // Create a Queue
                                _session.CreateTopic(Address);  // PublishSubscribe is defaulted to Topic for Outbound unless Address is defined with queue
                }
                else
                {
                    iDest = (Address.StartsWith("queue://")) ?
                                _session.CreateQueue(Address) : // Create a queue
                                _session.CreateTopic(Address);  // Otherwise, default to creating a topic
                }

                iDest.SetIntProperty(XMSC.DELIVERY_MODE, xmsConfiguration.WMQProperties.DeliveryMode);
                iDest.SetIntProperty(XMSC.WMQ_TARGET_CLIENT, xmsConfiguration.WMQProperties.TargetClient);
                break;

            // RTT destination
            case Literals.RTT:
                iDest = _session.CreateTopic(Address);  // Create a topic
                break;
            default:
                iDest = null;
                break;
        }
        return (iDest);
    }

    protected IDestination CreateDestinationInbound()
    {
        IDestination iDest;

        switch (xmsConfiguration.ConnectionType.ToUpper())
        {
            // WPM destination
            case Literals.WPM:
            case Literals.WMQ:
                if (Pattern == MessagePattern.FireAndForget)
                {
                    iDest = (Address.StartsWith("topic://")) ?
                                _session.CreateTopic(Address) : // Create a Topic
                                _session.CreateQueue(Address);  // FireAndForget is defaulted to Queues for Inbound unless Address is defined with topic
                }
                else if (Pattern == MessagePattern.PublishSubscribe)
                {
                    iDest = (Address.StartsWith("topic://")) ?
                                _session.CreateTopic(Address) : // Create a Topic
                                _session.CreateQueue(Address);  // PublishSubscribe is defaulted to Queue for Inbound unless Address is defined with topic
                }
                else
                {
                    iDest = (Address.StartsWith("topic://")) ?
                                _session.CreateTopic(Address) : // Create a Topic
                                _session.CreateQueue(Address);  // Otherwise, default to creating a Queue
                }

                iDest.SetIntProperty(XMSC.DELIVERY_MODE, xmsConfiguration.WMQProperties.DeliveryMode);
                iDest.SetIntProperty(XMSC.WMQ_TARGET_CLIENT, xmsConfiguration.WMQProperties.TargetClient);
                break;

            // RTT destination
            case Literals.RTT:
                iDest = _session.CreateQueue(Address);  // Create a Queue
                break;
            default:
                iDest = null;
                break;
        }
        return (iDest);
    }

    #endregion

    private static TReceiveMessageType ProcessInboundMessage<TReceiveMessageType>(MessageFormat messageFormat, IMessage inbound)
    {
        TReceiveMessageType message;
        if (messageFormat == MessageFormat.String)
        {
            ITextMessage txtMessage = (ITextMessage)inbound;
            message = (TReceiveMessageType)(object)txtMessage.Text;
        }
        else if (messageFormat == MessageFormat.ByteArray)
        {
            IObjectMessage inboundBytes = (IObjectMessage)inbound;
            byte[] body = inboundBytes.GetObject();

            message = (TReceiveMessageType)(object)body;
        }
        else if (messageFormat == MessageFormat.WMQ_MQMessage)
        {
            throw new NotSupportedException("MessageFormat.WMQ_MQMessage is not supported in IBM.XMS implementation. Please use String, ByteArray, Stream, Object (byte[]) or XMS_IMessage message formats");
        }
        else if (messageFormat == MessageFormat.XMS_IMessage)
        {
            message = (TReceiveMessageType)inbound;
        }
        else if (messageFormat == MessageFormat.Object)
        {
            IObjectMessage inboundObject = (IObjectMessage)inbound;
            byte[] body = inboundObject.GetObject();

            message = (TReceiveMessageType)(object)body;
        }
        else if (messageFormat == MessageFormat.Stream)
        {
            IObjectMessage inboundObject = (IObjectMessage)inbound;
            byte[] body = inboundObject.GetObject();

            MemoryStream streamBody = new MemoryStream(body);
            message = (TReceiveMessageType)(object)streamBody;
        }
        else
        {
            throw new NotSupportedException("UnRecognized message format. Please use String, ByteArray, Stream, Object (byte[]) or XMS_IMessage message formats");
        }

        return message;
    }

    #endregion        
}
PushCode
  • 1,419
  • 3
  • 15
  • 31
  • What issue or error are you getting? – JoshMc Jun 29 '17 at 04:51
  • If you are using client auto reconnect feature then you don't need to dispose and create a new resources,automatically the application should try connecting to the second connection in the connectionnamelist – subbaraoc Jun 29 '17 at 07:49
  • @JoshMc I added exception details to the post. – PushCode Jun 29 '17 at 11:57
  • @subbaraoc You mean that I do not require `ReconnectToXMS_MQ()` at all as I am using connection list? – PushCode Jun 29 '17 at 11:58
  • 2
    Yes,as you are setting "XMSC.WMQ_CLIENT_RECONNECT_OPTIONS" property,the application will re-connect to the second entry in the connectionNameList when the connection to the first entry is lost. Eg:cf.SetStringProperty(XMSC.WMQ_CONNECTION_NAME_LIST,"IP1(2828),IP2(2727)"); if connection to the IP1(2828) is lost due to some reason, the application will try to connect to IP2(2727). That is how client auto reconnect works. Following is the infocenter link which talks more about it https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_8.0.0/com.ibm.mq.con.doc/q017800_.htm – subbaraoc Jun 29 '17 at 12:31
  • @subbaraoc Awesome. Thank you. Will try that out and see if that helps me. – PushCode Jun 29 '17 at 12:35

0 Answers0