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:
- .NET 4.0 Frameworks
- .NET 4.6 (4.6.2) Frameworks, including Windows Surface RT
- Windows 8 (8.1) desktop and Surface Pro applications
- Windows Phone 8.1 native apps
- Note: There is no support for Windows CE.
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
- Download or clone the Kaazing .NET Tutorials project from https://github.com/kaazing/dotnet.client.tutorials.
- Navigate to the JMS app for Windows Desktop at dotnet.client.tutorials\jms\WindowsDesktop.
- Double-click JmsDemo.sln. The solution opens in Visual Studio.
- In Solution Explorer, right-click JmsDemo and click Build.
To run the app, click the Start arrow. The desktop app appears in a new window.
Figure: Windows JMS Desktop Tutorial App - In the app, click Connect to connect to the publicly available Kaazing WebSocket Gateway and
jms
service at URLwss://demos.kaazing.com/jms
. - 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:
- String — A text WebSocket message (UTF-8).
- Binary (using the IBytesMessage class) — A compact byte array representation for sending, receiving and processing binary data.
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.
- Install dependencies.
- Install .NET Framework 4.6.2. To see if you have this version installed, see How to: Determine Which .NET Framework Versions Are Installed.
- 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.
- Download the Kaazing Microsoft .NET SDK NuGet package file (.nupkg) from kaazing.com/download.
- Install the Kaazing Microsoft .NET SDK into your project.
- Open Visual Studio.
- Create a new project. Click File, click New, and then click Project.
- Click the Installed navigation heading, expand Templates, expand Visual C#, and click Windows Desktop.
- Click Windows Forms Application.
- At the top of the dialog, select .NET Framework 4.6.2.
- In Name, enter JmsDemo and click OK. Visual Studio created the new JmsDemo project.
- Install the Kaazing Microsoft .NET SDK. Click TOOLS, click NuGet Package Manager, and then click Package Manager Settings.
- In the navigation, click Package Sources.
- Click the plus icon to add a new source.
- In Name, enter Kaazing.
- In Source, click the browse button, ....
- Locate the folder containing the .nupkg file for the Kaazing Microsoft .NET SDK and click Select.
- Click OK.
- Right-click the JMSDemo project, and click Manage NuGet Packages.
- Click Online, click Kaazing, and then click Install.
- Click Close.
- In your project, expand the References element to see the Kaazing .NET SDK libraries.
- Create the UI for the application. In JmsDemo, rename the Form1.cs form to StompDemoForm.cs and let Visual Studio rename all related references.
- Expand StompDemoForm.cs and click StompDemoFormDesigner.cs
- 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; } }
- 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.- 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;
- 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. } }
- 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 atwss://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); })); }
- 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 atry...catch
statement for connecting and responding to exceptions. TheCloseButton_Click()
function responds to when a user clicks the Disconnect button by closing the connection and then callsDisconnectedHandler()
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; })); }
- 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 callsMessageHandler()
, which we will create next. AnIMessageConsumer
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); } }
- 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"); } })); } }
- 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); })); } }
- 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 usingIMessageProducer
.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(); }
- 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; } }
- Ensure the closing braces for the program are there.
} }
- In the StompDemoForm.cs code window, replace the default import statements with the following:
- Build and test your WebSocket .NET application.
- From the Build menu, click Build. The Visual Studio output console records the successful build.
- Click Start. The JMS .NET desktop application displays.
- Ensure that the Location field contains
wss://demos.kaazing.com/jms
. Click Connect. The log console displays CONNECTED. - 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
- The Microsoft .NET 4.0 Framework has a maximum connection limit of two per domain, similar to the browser limitation. For any Microsoft .NET application that uses more than one WebSocket connection at a time, you must either ensure that any WebSocket connection is closed by using
WebSocket.Close()
before opening another WebSocket connection, or increase the connection limit on the application by updating themaxconnection
attribute in theapp.config
file. For more information, see (KG-1851) Two Connection Limit in Kaazing Gateway JMS Clients for Microsoft .NET in Release Notes. - Clients built using Kaazing Gateway 3.x libraries will work against Kaazing Gateway 4.x. If you wish to upgrade your 3.x client to the 4.x libraries, please note that the 3.x clients used a single Stomp JMS library and 4.x clients include and use separate WebSocket and Stomp JMS libraries. Update your client library file and code references to include both the WebSocket and Stomp JMS libraries, as described in the 4.x documentation.
- For more information on the Kaazing Gateway JMS client libraries for .NET, see Kaazing Gateway JMS Client Libraries: Supported APIs and the .NET Client API (Kaazing.JMS, Kaazing.JMS.Stomp, and Kaazing.JMS.Util) documentation.
- TemporaryTopic and TemporaryQueue objects are destroyed when the client loses its connection to the Gateway, or when the JMS-compliant message broker loses its connection to the Gateway. To address this, monitor the client's exception listener to handle recovery for your application. Once the connection is re-established, recreate TemporaryTopic and TemporaryQueue. ConnectionDroppedException and ConnectionInterruptedException are delivered to the connection's exception listener via onException, indicating that messages in flight might be lost, depending on message delivery options. ConnectionRestoredException is delivered to indicate that the connection through to the JMS-compliant message broker has been re-established. TemporaryTopic and TemporaryQueue should be recreated at that time to resume operations.
- You can verify that Kaazing Gateway has signed the relevant .NET DLL by selecting the DLL in the File Browser, then right-clicking and opening the Properties dialog. On the Digital Signatures tab, you can view the Name of Signer value and a timestamp of when the DLL was signed. The email address is "Not available." For more information, see an example C program that shows how to use the Microsoft mechanism to verify a signature (a DLL is one example of a Portable Executable, or PE, file). You can also learn more about preventing DLL pre-loading attacks.