Use the Kaazing JMS Client API for .NET

In this procedure, you will learn how to use the signed Kaazing Gateway JMS client libraries for .NET and the supported methods.

Note: For this how-to, you can use any JMS-compliant message broker. We will use the publicly available Kaazing WebSocket Gateway and Apache ActiveMQ broker at the URL wss://demos.kaazing.com/jms. If you are using a local Kaazing WebSocket Gateway, the Gateway is configured to connect to the server on tcp://localhost:61616. You can configure the connect URL in the file GATEWAY_HOME/conf/gateway-config.xml. See About Integrating Kaazing Gateway and JMS-Compliant Message Brokers for more information.

The Kaazing Microsoft .NET JMS API supports the following deployment scenarios:

Before You Begin

This procedure is part of Checklist: How to Build Microsoft .NET JMS Clients.

Note: Learn about supported browsers, operating systems, and platform versions in the Release Notes.

Taking a Look at the Microsoft .NET Client Tutorial App

  1. Download or clone the Kaazing .NET Tutorials project from https://github.com/kaazing/dotnet.client.tutorials.
  2. Navigate to the JMS app for Windows Desktop at dotnet.client.tutorials\jms\WindowsDesktop.
  3. Double-click JmsDemo.sln. The solution opens in Visual Studio.
  4. In Solution Explorer, right-click JmsDemo and click Build.
  5. To run the app, click the Start arrow. The desktop app appears in a new window.

    Figure: Windows JMS Desktop Tutorial App
  6. In the app, click Connect to connect to the publicly available Kaazing WebSocket Gateway and jms service at URL wss://demos.kaazing.com/jms.
  7. Click Subscribe to subscribe to a destination and then click Send to send a message to the JMS broker via Kaazing WebSocket Gateway and receive the message as part of your subscription.

Supported Data Types

You can send and receive JMS messages using one of the following data types:

To Use the Kaazing JMS .NET API

To demonstrate the Kaazing JMS .NET API, let's create a simple .NET desktop application that uses the Kaazing WebSocket Gateway and its jms service to send and receives messages to a JMS broker over WebSocket. This is the same .NET JMS desktop application that is available on Github as part of the Kaazing .NET tutorials here https://github.com/kaazing/dotnet.client.tutorials.

  1. Install dependencies.
    1. Install .NET Framework 4.6.2. To see if you have this version installed, see How to: Determine Which .NET Framework Versions Are Installed.
    2. Install a .NET Integrated Development Environment (IDE). This procedure assumes that you are using Microsoft Visual Studio or the free Visual Studio Community.

      Note: You can develop .NET Framework applications in any of the .NET programming languages. Microsoft Visual C# is used in the code examples in this document.

    3. Download the Kaazing Microsoft .NET SDK NuGet package file (.nupkg) from kaazing.com/download.
  2. Install the Kaazing Microsoft .NET SDK into your project.
    1. Open Visual Studio.
    2. Create a new project. Click File, click New, and then click Project.
    3. Click the Installed navigation heading, expand Templates, expand Visual C#, and click Windows Desktop.
    4. Click Windows Forms Application.
    5. At the top of the dialog, select .NET Framework 4.6.2.
    6. In Name, enter JmsDemo and click OK. Visual Studio created the new JmsDemo project.
    7. Install the Kaazing Microsoft .NET SDK. Click TOOLS, click NuGet Package Manager, and then click Package Manager Settings.
    8. In the navigation, click Package Sources.
    9. Click the plus icon to add a new source.
    10. In Name, enter Kaazing.
    11. In Source, click the browse button, ....
    12. Locate the folder containing the .nupkg file for the Kaazing Microsoft .NET SDK and click Select.
    13. Click OK.
    14. Right-click the JMSDemo project, and click Manage NuGet Packages.
    15. Click Online, click Kaazing, and then click Install.
    16. Click Close.
    17. In your project, expand the References element to see the Kaazing .NET SDK libraries.
  3. Create the UI for the application. In JmsDemo, rename the Form1.cs form to StompDemoForm.cs and let Visual Studio rename all related references.
    1. Expand StompDemoForm.cs and click StompDemoFormDesigner.cs
    2. Replace the existing code with the code below. You can also obtain the code on Github at https://github.com/kaazing/dotnet.client.tutorials/blob/develop/jms/WindowsDesktop/StompDemoForm.Designer.cs. This code will define the User Interface for the application:
      namespace JmsDemo
      {
        partial class StompDemoForm
        {
          /// <summary>
          /// Required designer variable.
          /// </summary>
          private System.ComponentModel.IContainer components = null;
      
          /// <summary>
          /// Clean up any resources being used.
          /// </summary>
          /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
          protected override void Dispose(bool disposing)
          {
            if (disposing && (components != null))
            {
              components.Dispose();
            }
            base.Dispose(disposing);
          }
      
          #region Windows Form Designer generated code
      
          /// <summary>
          /// Required method for Designer support - do not modify
          /// the contents of this method with the code editor.
          /// </summary>
          private void InitializeComponent()
          {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(StompDemoForm));
            this.LocationLabel = new System.Windows.Forms.Label();
            this.LocationText = new System.Windows.Forms.TextBox();
            this.Output = new System.Windows.Forms.TextBox();
            this.ClearLogButton = new System.Windows.Forms.Button();
            this.ConnectButton = new System.Windows.Forms.Button();
            this.DisconnectButton = new System.Windows.Forms.Button();
            this.MessageLabel = new System.Windows.Forms.Label();
            this.MessageText = new System.Windows.Forms.TextBox();
            this.DestinationText = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.SubscribeButton = new System.Windows.Forms.Button();
            this.SendButton = new System.Windows.Forms.Button();
            this.UnsubscribeButton = new System.Windows.Forms.Button();
            this.Title = new System.Windows.Forms.Label();
            this.Subtitle = new System.Windows.Forms.Label();
            this.label3 = new System.Windows.Forms.Label();
            this.label4 = new System.Windows.Forms.Label();
            this.BinaryCheckBox = new System.Windows.Forms.CheckBox();
            // 
            // LocationLabel
            // 
            this.LocationLabel.AutoSize = true;
            this.LocationLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.LocationLabel.Location = new System.Drawing.Point(16, 88);
            this.LocationLabel.Name = "LocationLabel";
            this.LocationLabel.Size = new System.Drawing.Size(75, 17);
            this.LocationLabel.TabIndex = 0;
            this.LocationLabel.Text = "Location:";
            // 
            // LocationText
            // 
            this.LocationText.Location = new System.Drawing.Point(104, 88);
            this.LocationText.Name = "LocationText";
            this.LocationText.Size = new System.Drawing.Size(197, 20);
            this.LocationText.TabIndex = 1;
            this.LocationText.TextChanged += new System.EventHandler(this.LocationText_TextChanged);
            // 
            // Output
            // 
            this.Output.Location = new System.Drawing.Point(19, 173);
            this.Output.Multiline = true;
            this.Output.Name = "Output";
            this.Output.Size = new System.Drawing.Size(625, 338);
            this.Output.TabIndex = 6;
            // 
            // ClearLogButton
            // 
            this.ClearLogButton.Location = new System.Drawing.Point(545, 517);
            this.ClearLogButton.Name = "ClearLogButton";
            this.ClearLogButton.Size = new System.Drawing.Size(99, 33);
            this.ClearLogButton.TabIndex = 7;
            this.ClearLogButton.Text = "Clear Log";
            this.ClearLogButton.UseVisualStyleBackColor = true;
            this.ClearLogButton.Click += new System.EventHandler(this.ClearLogButton_Click);
            // 
            // ConnectButton
            // 
            this.ConnectButton.Location = new System.Drawing.Point(101, 139);
            this.ConnectButton.Name = "ConnectButton";
            this.ConnectButton.Size = new System.Drawing.Size(85, 28);
            this.ConnectButton.TabIndex = 8;
            this.ConnectButton.Text = "Connect";
            this.ConnectButton.UseVisualStyleBackColor = true;
            this.ConnectButton.Click += new System.EventHandler(this.ConnectButton_Click);
            // 
            // DisconnectButton
            // 
            this.DisconnectButton.Enabled = false;
            this.DisconnectButton.Location = new System.Drawing.Point(192, 139);
            this.DisconnectButton.Name = "DisconnectButton";
            this.DisconnectButton.Size = new System.Drawing.Size(85, 28);
            this.DisconnectButton.TabIndex = 9;
            this.DisconnectButton.Text = "Disconnect";
            this.DisconnectButton.UseVisualStyleBackColor = true;
            this.DisconnectButton.Click += new System.EventHandler(this.CloseButton_Click);
            // 
            // MessageLabel
            // 
            this.MessageLabel.AutoSize = true;
            this.MessageLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.MessageLabel.Location = new System.Drawing.Point(16, 116);
            this.MessageLabel.Name = "MessageLabel";
            this.MessageLabel.Size = new System.Drawing.Size(77, 17);
            this.MessageLabel.TabIndex = 10;
            this.MessageLabel.Text = "Message:";
            // 
            // MessageText
            // 
            this.MessageText.Location = new System.Drawing.Point(104, 113);
            this.MessageText.Name = "MessageText";
            this.MessageText.Size = new System.Drawing.Size(454, 20);
            this.MessageText.TabIndex = 11;
            this.MessageText.Text = "Hello, Message";
            // 
            // DestinationText
            // 
            this.DestinationText.Location = new System.Drawing.Point(447, 88);
            this.DestinationText.Name = "DestinationText";
            this.DestinationText.Size = new System.Drawing.Size(197, 20);
            this.DestinationText.TabIndex = 13;
            this.DestinationText.Text = "/topic/destination";
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.label1.Location = new System.Drawing.Point(338, 88);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(103, 17);
            this.label1.TabIndex = 12;
            this.label1.Text = "Subscription:";
            // 
            // SubscribeButton
            // 
            this.SubscribeButton.Enabled = false;
            this.SubscribeButton.Location = new System.Drawing.Point(353, 139);
            this.SubscribeButton.Name = "SubscribeButton";
            this.SubscribeButton.Size = new System.Drawing.Size(85, 28);
            this.SubscribeButton.TabIndex = 14;
            this.SubscribeButton.Text = "Subscribe";
            this.SubscribeButton.UseVisualStyleBackColor = true;
            this.SubscribeButton.Click += new System.EventHandler(this.SubscribeButton_Click);
            // 
            // SendButton
            // 
            this.SendButton.Enabled = false;
            this.SendButton.Location = new System.Drawing.Point(444, 139);
            this.SendButton.Name = "SendButton";
            this.SendButton.Size = new System.Drawing.Size(85, 28);
            this.SendButton.TabIndex = 15;
            this.SendButton.Text = "Send";
            this.SendButton.UseVisualStyleBackColor = true;
            this.SendButton.Click += new System.EventHandler(this.SendButton_Click);
            // 
            // UnsubscribeButton
            // 
            this.UnsubscribeButton.Enabled = false;
            this.UnsubscribeButton.Location = new System.Drawing.Point(535, 139);
            this.UnsubscribeButton.Name = "UnsubscribeButton";
            this.UnsubscribeButton.Size = new System.Drawing.Size(85, 28);
            this.UnsubscribeButton.TabIndex = 16;
            this.UnsubscribeButton.Text = "Unsubscribe";
            this.UnsubscribeButton.UseVisualStyleBackColor = true;
            this.UnsubscribeButton.Click += new System.EventHandler(this.UnsubscribeButton_Click);
            // 
            // Title
            // 
            this.Title.AutoSize = true;
            this.Title.Font = new System.Drawing.Font("Microsoft Sans Serif", 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.Title.Location = new System.Drawing.Point(46, 8);
            this.Title.Name = "Title";
            this.Title.Size = new System.Drawing.Size(367, 26);
            this.Title.TabIndex = 18;
            this.Title.Text = "Kaazing .Net Framework JMS Demo";
            // 
            // Subtitle
            // 
            this.Subtitle.AutoSize = true;
            this.Subtitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.Subtitle.Location = new System.Drawing.Point(14, 35);
            this.Subtitle.Name = "Subtitle";
            this.Subtitle.Size = new System.Drawing.Size(639, 17);
            this.Subtitle.TabIndex = 19;
            this.Subtitle.Text = "This is a demo of a JMS .Net Framework client application that communicates with " +
              "a message broker";
            // 
            // label3
            // 
            this.label3.AutoSize = true;
            this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.label3.Location = new System.Drawing.Point(14, 52);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(635, 17);
            this.label3.TabIndex = 24;
            this.label3.Text = "via WebSocket to subscribe to destinations, send and receive messages, and proces" +
              "s transactions.";
            // 
            // label4
            // 
            this.label4.AutoSize = true;
            this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.label4.Location = new System.Drawing.Point(564, 114);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(59, 17);
            this.label4.TabIndex = 25;
            this.label4.Text = "Binary:";
            // 
            // BinaryCheckBox
            // 
            this.BinaryCheckBox.AutoSize = true;
            this.BinaryCheckBox.Location = new System.Drawing.Point(629, 116);
            this.BinaryCheckBox.Name = "BinaryCheckBox";
            this.BinaryCheckBox.Size = new System.Drawing.Size(15, 14);
            this.BinaryCheckBox.TabIndex = 26;
            this.BinaryCheckBox.UseVisualStyleBackColor = true;
            // 
            // StompDemoForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.SystemColors.Window;
            this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
            this.ClientSize = new System.Drawing.Size(666, 562);
            this.Controls.Add(this.BinaryCheckBox);
            this.Controls.Add(this.label4);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.Subtitle);
            this.Controls.Add(this.Title);
            this.Controls.Add(this.UnsubscribeButton);
            this.Controls.Add(this.SendButton);
            this.Controls.Add(this.SubscribeButton);
            this.Controls.Add(this.DestinationText);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.MessageText);
            this.Controls.Add(this.MessageLabel);
            this.Controls.Add(this.DisconnectButton);
            this.Controls.Add(this.ConnectButton);
            this.Controls.Add(this.ClearLogButton);
            this.Controls.Add(this.Output);
            this.Controls.Add(this.LocationText);
            this.Controls.Add(this.LocationLabel);
            this.Name = "StompDemoForm";
            this.Text = "JMS .Net Client Demo";
            this.Load += new System.EventHandler(this.StompDemoForm_Load);
            this.ResumeLayout(false);
            this.PerformLayout();
      
          }
      
          #endregion
      
          private System.Windows.Forms.Label LocationLabel;
          private System.Windows.Forms.TextBox LocationText;
          private System.Windows.Forms.TextBox Output;
          private System.Windows.Forms.Button ClearLogButton;
          private System.Windows.Forms.Button ConnectButton;
          private System.Windows.Forms.Button DisconnectButton;
          private System.Windows.Forms.Label MessageLabel;
          private System.Windows.Forms.TextBox MessageText;
          private System.Windows.Forms.TextBox DestinationText;
          private System.Windows.Forms.Label label1;
          private System.Windows.Forms.Button SubscribeButton;
          private System.Windows.Forms.Button SendButton;
          private System.Windows.Forms.Button UnsubscribeButton;
          private System.Windows.Forms.Label Title;
          private System.Windows.Forms.Label Subtitle;
          private System.Windows.Forms.Label label3;
          private System.Windows.Forms.Label label4;
          private System.Windows.Forms.CheckBox BinaryCheckBox;
        }
      }
      
  4. Create the main code for our project in StompDemoForm.cs. Right-click StompDemoForm.cs and click View Code.
    Note: The entire code for StompDemoForm.cs can be found on Github at https://github.com/kaazing/dotnet.client.tutorials/blob/develop/jms/WindowsDesktop/StompDemoForm.cs.
    1. In the StompDemoForm.cs code window, replace the default import statements with the following:
      using System;
      using System.Collections;
      using System.Collections.Generic;
      using System.ComponentModel;
      using System.Drawing;
      using System.Linq;
      using System.Text;
      using System.Resources;
      using System.Windows.Forms;
      using Kaazing.JMS;
      using Kaazing.JMS.Stomp;
      using Kaazing.Security;
      using System.Threading;
      using Kaazing.HTML5;
      using System.Threading.Tasks;
      
    2. In the StompDemoForm class, add the interfaces for connections, sessions, messages, and the dictionary for the key/value pairs that will be sent and received.
      namespace JmsDemo
      {
          /// <summary>
          /// Top level JMS Client Demo
          /// </summary>
          public partial class StompDemoForm : Form
          {
              private IConnection connection = null;
              private ISession session = null;
              private IMessageConsumer consumer = null;
              private IDictionary<String, List<IMessageConsumer>> consumers = null;
      
              private delegate void InvokeDelegate();
              private delegate void InvokeDelegate1();
              
              /// The code for the subsequent steps goes here.
              
          }
      }
      
    3. Next, add the StompDemoForm() function just below the /// The code for the subsequent steps goes here comment. It will set up the form, and set the default URL for the application to connect to. We will use the publicly available Kaazing WebSocket Gateway at wss://demos.kaazing.com/jms:
      /// <summary>
      /// JMS Demo Form constructor
      /// </summary>
      public StompDemoForm()
      {
        InitializeComponent();
      
        String defaultLocation = "wss://demos.kaazing.com/jms";
        LocationText.Text = defaultLocation;
      
        ResourceManager resourceManager = new ResourceManager("JmsDemo.StompDemoForm", GetType().Assembly);
          
      }
      
      private void StompDemoForm_Load(object sender, EventArgs e)
      {
      }
      
      private void HandleLog(String message)
      {
        this.BeginInvoke((InvokeDelegate)(() =>
        {
          Log("LOG: " + message);
        }));
      }
      
    4. Add functions to respond when the user clicks the Connect and Disconnect buttons. The ConnectButton_Click() function will respond to the Connect button by calling the second function, JMS_Connect(). JMS_Connect() creates a connection instance using the StompConnectionFactory factory, calls methods to update the UI (we will add these later), and connects to the URL specified earlier. Note the use of a try...catch statement for connecting and responding to exceptions. The CloseButton_Click() function responds to when a user clicks the Disconnect button by closing the connection and then calls DisconnectedHandler() to update the UI.
      private void ConnectButton_Click(object sender, EventArgs e)
      {
        try
        {
          // try to establish the JMS connection
          this.JMS_Connect();                
        }
        catch (System.IO.FileLoadException)
        {
          MessageBox.Show("You need to upgrade your .NET Framework version. Check the Release Notes.");
        }
      }
      
      /*
      * "Delegate, invoked by the "click handler", which actually establishes the JMS connection.
      */
      private void JMS_Connect()
      {
        // Immediately disable the connect button
        ConnectButton.Enabled = false;
        LocationText.Enabled = false;
        Log("CONNECT:" + LocationText.Text);
      
        try
        {
          StompConnectionFactory connectionFactory = new StompConnectionFactory(new Uri(LocationText.Text));
          connection = connectionFactory.CreateConnection("", "");
          Log("CONNECTED");
          connection.ExceptionListener = new ExceptionHandler(this);
          consumers = new Dictionary<String, List<IMessageConsumer>>();
          session = connection.CreateSession(false, SessionConstants.AUTO_ACKNOWLEDGE);
      
          connection.Start();
      
          // Enable User Interface for Connected application
          SubscribeButton.Enabled = true;
          SendButton.Enabled = true;
          UnsubscribeButton.Enabled = true;
      
          DisconnectButton.Enabled = true;
        }
        catch (Exception exc)
        {
          if (connection != null)
          {
            connection.Close();
          }
      
          Log("CONNECTION FAILED: " + exc.Message);
          LocationText.Enabled = true;
          ConnectButton.Enabled = true;
              
        }
      }
      
      private void CloseButton_Click(object sender, EventArgs e)
      {
        Log("CLOSE");
      
        if (connection != null)
        {
          try
          {
            connection.Close();
          }
          catch (Exception exc)
          {
            Log("EXCEPTION: " + exc.Message);
          }
          finally
          {
            connection = null;
          }
        }
      
        DisconnectedHandler();
      }
      
      private void DisconnectedHandler()
      {
        this.BeginInvoke((InvokeDelegate)(() =>
        {
          Log("DISCONNECTED");
          LocationText.Enabled = true;
          ConnectButton.Enabled = true;
      
          SendButton.Enabled = false;
          SubscribeButton.Enabled = false;
          UnsubscribeButton.Enabled = false;
      
          DisconnectButton.Enabled = false;
        }));
      }
      
    5. Add a function to respond to when the user click the Subscribe or Unsubscribe buttons. The SubscribeButton_Click function creates the destination supplied by the user, creates a consumer of that destination, and add a MessageListener to listen for incoming messages. Then the function calls MessageHandler(), which we will create next. An IMessageConsumer object is used to get a list of consumers for the destination and add a consumer to it.
      private void SubscribeButton_Click(object sender, EventArgs e)
      {
          Log("SUBSCRIBE:" + DestinationText.Text);
      
          IDestination destination;
          if (DestinationText.Text.StartsWith("/topic/"))
          {
              destination = session.CreateTopic(DestinationText.Text);
          }
          else if (DestinationText.Text.StartsWith("/queue/"))
          {
              destination = session.CreateQueue(DestinationText.Text);
          }
          else
          {
              Log("Destination must start with /topic/ or /queue/");
              return;
          }
      
          consumer = session.CreateConsumer(destination);
          consumer.MessageListener = new MessageHandler(this);
      
          List<IMessageConsumer> consumerList = null;
          try
          {
              consumerList = consumers[DestinationText.Text];
          }
          catch (KeyNotFoundException)
          {
              consumerList = new List<IMessageConsumer>();
          }
          consumerList.Add(consumer);
      
          try
          {
              consumers.Add(DestinationText.Text, consumerList);
          }
          catch (ArgumentException)
          {
      
              List<IMessageConsumer> oldValue = consumers[DestinationText.Text];
              consumers.Remove(DestinationText.Text);
              consumers.Add(DestinationText.Text, consumerList);
          }
      
      }
      
      private void UnsubscribeButton_Click(object sender, EventArgs e)
      {
          Log("UNSUBSCRIBE:" + DestinationText.Text);
          List<IMessageConsumer> consumerList = consumers[DestinationText.Text];
          int consumerlistSize = consumerList.Count;
      
          if (consumerlistSize > 0)
          {
              IMessageConsumer consumer = (IMessageConsumer)consumerList[consumerlistSize - 1];
              consumerList.RemoveAt(consumerlistSize - 1);
              if (consumer != null)
              {
                  consumer.Close();
              }
              else
              {
                  Log("ERROR: Destination not found: " + DestinationText.Text);
              }
      
          }
          else
          {
              Log("ERROR: Destination not found: " + DestinationText.Text);
          }
      }
      
    6. Add a function to handle incoming messages. The function will determine if the message is text or binary or a mapMessage (a set of name-value pairs) and display the message, message headers, and any properties in the log console.
      class MessageHandler : IMessageListener
      {
          StompDemoForm form;
      
          internal MessageHandler(StompDemoForm form)
          {
              this.form = form;
          }
      
          public void OnMessage(IMessage message)
          {
              form.BeginInvoke((InvokeDelegate)(() =>
              {
                  if (message is ITextMessage)
                  {
                      ITextMessage textMessage = (ITextMessage)message;
                      form.Log("RECEIVED ITextMessage: " + textMessage.Text);
                  }
                  else if (message is IBytesMessage)
                  {
                      IBytesMessage msg = (IBytesMessage)message;
                      byte[] actual = new byte[(int)msg.BodyLength];
                      msg.ReadBytes(actual);
                      form.Log("RECEIVED IBytesMessage: " + BitConverter.ToString(actual));
      
                  }
                  else if (message is IMapMessage)
                  {
                      IMapMessage mapMessage = (IMapMessage)message;
                      IEnumerator<String> mapNames = mapMessage.MapNames;
                      while (mapNames.MoveNext())
                      {
                          String name = mapNames.Current;
                          Object obj = mapMessage.GetObject(name);
                          if (obj == null)
                          {
                              form.Log(name + ": null");
                          }
                          else if (obj.GetType().IsArray)
                          {
                              form.Log(name + ": " + BitConverter.ToString(obj as byte[]) + " (byte[])");
                          }
                          else
                          {
                              String type = obj.GetType().ToString();
                              form.Log(name + ": " + obj.ToString() + " ("+type+")");
                          }
                      }
                      form.Log("RECEIVED IMapMessage:");
                  }
                  else
                  {
                      form.Log("UNKNOWN MESSAGE TYPE");
                  }
              }));
          }
      }
      
    7. Add an exception listener to handle any exceptions and write them to the console log.
      class ExceptionHandler : IExceptionListener
      {
          StompDemoForm form;
      
          internal ExceptionHandler(StompDemoForm form)
          {
              this.form = form;
          }
      
          public void OnException(JMSException exc)
          {
              form.BeginInvoke((InvokeDelegate)(() =>
              {
                  form.Log(exc.Message);
              }));
          }
      }
      
    8. Add the SendButton_Click() function to respond when a user clicks the Send button. The function checks to see if the message is text or binary (user checked the binary option), adds any properties, updates the log console, and sends the message using IMessageProducer.
      private void SendButton_Click(object sender, EventArgs e)
      {
          // Create a destination for the producer
          IDestination destination;
          if (DestinationText.Text.StartsWith("/topic/"))
          {
              destination = session.CreateTopic(DestinationText.Text);
          }
          else if (DestinationText.Text.StartsWith("/queue/"))
          {
              destination = session.CreateQueue(DestinationText.Text);
          }
          else
          {
              Log("Destination must start with /topic/ or /queue/");
              return;
          }
      
          // Create the message to send
          IMessage message;
          if (BinaryCheckBox.Checked)
          {
              Log("SEND IBytesMessage:" + BitConverter.ToString(Encoding.UTF8.GetBytes(MessageText.Text)) + ": " + DestinationText.Text);
              message = session.CreateBytesMessage();
              ((IBytesMessage)message).WriteUTF(MessageText.Text);              
          }
          else
          {
              Log("SEND ITextMessage: " + MessageText.Text + ": " + DestinationText.Text);
              message = session.CreateTextMessage(MessageText.Text);
          }
      
          // Create the producer, send, and close
          IMessageProducer producer = session.CreateProducer(destination);
          producer.Send(message);
          producer.Close();
      }
      
    9. Add functions to define the console log limitations for the UI, the Clear Log button, and whether the Connect button is enabled.
      /*
       * Console output 
       */
      private const int LOG_LIMIT = 50;
      private Queue<string> logLines = new Queue<string>();
      private void Log(string arg)
      {
          logLines.Enqueue(arg);
          if (logLines.Count > LOG_LIMIT)
          {
              logLines.Dequeue();
          }
          string[] o = logLines.ToArray<string>();
      
          o = o.Reverse<string>().ToArray<string>();
      
          Output.Text = string.Join("\r\n", o);
      }
      
      private void ClearLogButton_Click(object sender, EventArgs e)
      {
          logLines.Clear();
          Output.Text = "";
      }
      
      private void LocationText_TextChanged(object sender, EventArgs e)
      {
          if (LocationText.Text.Length == 0)
          {
              ConnectButton.Enabled = false;
          }
          else
          {
              ConnectButton.Enabled = true;
          }
      }
      
    10. Ensure the closing braces for the program are there.
          }
      }
      
  5. Build and test your WebSocket .NET application.
    1. From the Build menu, click Build. The Visual Studio output console records the successful build.
    2. Click Start. The JMS .NET desktop application displays.
    3. Ensure that the Location field contains wss://demos.kaazing.com/jms. Click Connect. The log console displays CONNECTED.
    4. Click Subscribe and then Send. The text message is sent to the JMS broker via the jms service on the Gateway returns the message as part of the subscription.

Durable Subscribers

Note: Currently, the Gateway does not support durable subscribers with Apache ActiveMQ. You may use durable subscribers with TIBCO EMS or Informatica UM. For more information, see Durable Subscribers.

If your JMS client needs to receive all of the messages published on a topic, including the ones published while the subscriber is inactive because it is not being used or has lost connections (which is common when using mobile devices), create a durable ITopicSubscriber using the iSession.createDurableSubscriber() method.

To unsubscribe from a durable topic, use the ISession.Unsubscribe() method.

The JMS provider retains a separate record of each durable subscription and ensures that all messages from the topic's publishers are retained until they are acknowledged by each durable subscriber or they have expired. Whether messages have been acknowledged is tracked separately for each durable subscriber, and each durable subscriber is identified by the combination of its name and the clientID (if any) set on the Connection. Ensure your application confirms that the clientID (if used) is unique to the user or device, or, if it does not use clientID's, ensure that the durable name is unique to the user or device.

One method you can use to ensure that the durable name is unique to the user or device without using a client ID is to create a variable that is a concatenation of the user name and durable name and use that variable when creating the durable subscription.

Example

The following example creates a durable subscriber with message selector, sends one matching and one mismatching message to the subscription message selector, and verifies that only the matching is received.

String DURABLE_NAME = "durable_messageSelectorTest" + KZSystem.CurrentTimeMillis();
String CLIENT_ID = null;

/// Create the clientID variable
if (useClientID)
{
    CLIENT_ID = "client1";
}

StompConnectionFactory connectionFactory =
    new StompConnectionFactory(new Uri(GetStompProviderURL()));
IConnection connection =
    useClientID ? connectionFactory.CreateConnection(null, null, CLIENT_ID) :
        connectionFactory.CreateConnection(null, null);
ISession session = connection.CreateSession(false, SessionConstants.CLIENT_ACKNOWLEDGE);
connection.Start();

/// Create the durable subscriber with message selector
String TOPIC_NAME = "/topic/topic_offlineDeliveryTest" + KZSystem.CurrentTimeMillis();
const String MESSAGE_SELECTOR1 = "prop='val1'";
ITopic topic = session.CreateTopic(TOPIC_NAME);
ITopicSubscriber consumer =
    session.CreateDurableSubscriber(topic, DURABLE_NAME, MESSAGE_SELECTOR1, false);

IConnection sendConnection = connectionFactory.CreateConnection(null, null);
ISession sendSession = sendConnection.CreateSession(false, SessionConstants.AUTO_ACKNOWLEDGE);
IMessageProducer producer = sendSession.CreateProducer(topic);

/// Sleep at the point to ensure the SUBSCRIBE went through for the createDurableSubscriber call
/// Otherwise, if the send message goes through before SUBSCRIBE, the message is not received.
Thread.Sleep(2000);

/// Send one matching and one mismatching message to the subscription message selector
producer.Send(sendSession.CreateTextMessage("1-MISMATCHING-MSG"));
ITextMessage message = sendSession.CreateTextMessage("2-MATCHING-MSG");
message.SetStringProperty("prop", "val1");
producer.Send(message);

/// Verify that only the matching message2 is received
ITextMessage received = (ITextMessage)consumer.Receive(5000);
Assert.IsNotNull(received, "Message sent while subscriber still open should have been received");
Assert.AreEqual("2-MATCHING-MSG", received.Text);
received.Acknowledge();

/// Close consumer (UNSUBSCRIBE)
consumer.Close();
connection.Close();

Next Step

Secure Your Microsoft .NET Client

Notes