Kaazing WebSocket Gateway |
|
How to Use the Microsoft Silverlight AmqpClient Client Library |
|
Technologies used: AMQP 0-9 and Microsoft Silverlight |
This document contains the following sections:
- Overview of AMQP 0-9
- Overview of Microsoft Silverlight
- WebSocket and AMQP
- Taking a Look at the AMQP Demo
- Setting Up an AMQP Broker
- Configuring Kaazing Gateway to Connect to an AMQP Broker
- Setting Up Your Silverlight Development Environment
- Using the Silverlight AmqpClient Library
- Other Coding Styles
- Summary
Overview of AMQP 0-9
Advanced Message Queuing Protocol (AMQP) is an open standard for messaging middleware that was originally designed by the financial services industry to provide an interoperable protocol for managing the flow of enterprise messages. To guarantee messaging interoperability, AMQP defines both a wire-level protocol and a model--the AMQP Model--of messaging capabilities.
The AMQP Model defines three main components:
- Exchange--clients publish messages to an exchange
- Queue--clients read messages from a queue
- Binding--a mapping from an exchange to a queue
An AMQP client connects to an AMQP broker and opens a channel. Once the channel is established, the client can send messages to an exchange and receive messages from a queue. To learn more about AMQP functionality, take a look at Kaazing's Real-Time Interactive Guide to AMQP, an interactive guide that takes you step-by-step through the main features of AMQP version 0-9.
For more information about AMQP, visit http://www.amqp.org.
Overview of Microsoft Silverlight
Microsoft Silverlight (Silverlight) is a browser plugin that enables rich Internet applications. It runs in browsers on the Microsoft Windows and Mac operating systems. Silverlight version 2.0 provided support for .NET languages and development tools.
For more information about Silverlight, visit http://www.microsoft.com/silverlight.
WebSocket and AMQP
WebSocket enables direct communication from the browser to an AMQP broker. Kaazing Gateway radically simplifies Web application design by providing the AmqpClient client libraries for the JavaScript, Adobe Flex, and Silverlight client technologies. Web developers can code directly against the back-end AMQP broker without the need for custom Servlets or server-side programming.
Using the AmqpClient client libraries, you can take advantage of the AMQP features, making the browser a first-class citizen in AMQP systems (similar to C, Java, Python, and other clients). This means that you can now run AMQP clients directly in a browser.
The AmqpClient libraries use Kaazing's ByteSocket client library, because AMQP messages use a binary format. Since the implementation is layered on top of ByteSocket, which uses WebSocket--and thus the entire stack of Kaazing HTML 5 Communications client libraries--applications developed using the AmqpClient libraries are provided with guaranteed persistence, reliability, and message-receipt acknowledgment all the way to the browser.
Taking a Look at the AMQP Demo
Before you start, take a look at a demonstration that was built with the Silverlight version of the AmqpClient client library. To see this demo in action, perform the following steps:
- Start the Kaazing WebSocket Gateway demo bundle as described in Getting Started.
- In a browser, navigate to http://localhost:8000/demo/demo.html#amqp-silverlight.
From this page, you can navigate to additional AMQP demos for different client technologies (JavaScript and Adobe Flex).
The demo requires a connection to an AMQP broker. The next section describes how to set up a local AMQP broker.
Setting Up an AMQP Broker
There is a wide variety of AMQP brokers available that implement different AMQP versions. For example, OpenAMQ, Apache Qpid, Red Hat Enterprise MRG, RabbitMQ, ØMQ, and Zyre. If you do not have an AMQP broker installed yet, you can use Apache Qpid AMQP broker, which supports AMQP version 0-9. To set up the Apache Qpid broker on your system, perform the following steps:
- Download the Apache Qpid (the broker or the full release) from http://qpid.apache.org.
- Unpack the distribution into a directory of your choice (for example, C:\qpid). This directory contains the AMQP broker and its components and is referred to as AMQP_HOME in the Kaazing documentation.
- Follow the steps in the Apache Qpid Getting Started to start the broker.
Note: You may have to run a script to create the example SSL stores before you can start the broker. This script creates the Qpid keystore and truststore files that must be present in the AMQP_HOME/etc directory before you can start the broker. - Start Kaazing Gateway as described in Getting Started.
- Open a browser and navigate to http://localhost:8000/demo/demo.html#amqp and log in to the demo to verify that it is working.
Configuring Kaazing Gateway to Connect to an AMQP Broker
Note: If you have the Kaazing WebSocket Gateway demo bundle running on localhost and if you have an AMQP broker running on localhost at the default AMQP port 5672, you do not have to configure anything to see the AMQP demos and the interactive AMQP guide.
The following is an example of the default configuration element for the AMQP service in the Kaazing WebSocket Gateway demo bundle, as specified in the configuration file KAAZING_HOME/conf/gateway-config.xml:
<!-- Proxy service to AMQP server -->
<service>
<accept>ws://localhost:8000/amqp</accept>
<accept>wss://localhost:9000/amqp</accept>
<type>proxy</type>
<properties>
<connect>tcp://localhost:5672</connect>
</properties>
<cross-site-constraint>
<allow-origin>http://localhost:8000</allow-origin>
</cross-site-constraint>
<cross-site-constraint>
<allow-origin>https://localhost:9000</allow-origin>
</cross-site-constraint>
</service>
In this case, the service is configured to accept WebSocket AMQP requests from the browser at ws://localhost:8000/amqp and securely at wss://localhost:9000/amqp and proxy those requests to a locally installed AMQP broker (localhost) at port 5672.
To configure Kaazing Gateway to accept WebSocket requests at another URL or to connect to a different AMQP broker, you can edit KAAZING_HOME/conf/gateway-config.xml, update the values for the accept elements, change the connect property, and restart Kaazing Gateway. For example, the following configuration configures Kaazing Gateway to accept WebSocket AMQP requests at ws://www.example.com:80/amqp and proxy those requests to an AMQP broker (amqp.example.com) on port 5672.
<!-- Proxy service to AMQP server -->
<service>
<accept>ws://www.example.com:80/amqp</accept>
<type>proxy</type>
<properties>
<connect>tcp://amqp.example.com:5672</connect>
</properties>
</service>
Setting Up Your Silverlight Development Environment
To develop a Silverlight 2 application, you must install the following software:
- A .NET Integrated Development Environment (IDE) such as Visual Studio 2008 or the free Visual Web Developer 2008 Express
- Silverlight 2 Tools for Visual Studio 2008 Add-on
- The Silverlight 2 browser plugin
To use the Kaazing Silverlight client library in your Silverlight application, you must add a reference to the Kaazing library located in the .DLL file kaazing-enterprise-gateway-client-release-version.dll to your Silverlight project. This .DLL file can be found in the Kaazing Gateway demo bundle in the directory KAAZING_HOME/lib/client/silverlight.
Note: You can develop Silverlight 2 applications in any of the .NET programming languages. Microsoft Visual C# is used in this how-to.
Silverlight applications primarily consist of Extensible Application Markup Language (XAML) files and their corresponding code-behind classes .XAML.CS (in C#). The .XAML files typically contain the User Interface (UI) elements and the .XAML.CS files contain the page's code-behind classes.
A typical Silverlight application is deployed as a compressed .XAP file. Adding the reference to the Silverlight client library .DLL file in your Silverlight application packages the .DLL file in your finished application. To deploy your Silverlight application .XAP file, you can host it on the Kaazing Gateway. For example, you can place it in the web directory KAAZING_HOME/web.
To access the Kaazing Silverlight client library API documentation in Visual Studio (after you have added a reference to the client library to your Silverlight project), select Object Browser from the View menu and select kaazing-enterprise-gateway-client-release-version.
Using the Silverlight AmqpClient Library
This section contains the following subsections:
- Create the AmqpClient Object
- Connect to an AMQP Broker
- Create Channels
- Declare an Exchange
- Declare a Queue
- Bind an Exchange to a Queue
- Publish Messages
- Consume Messages
- Use Transactions
- Control Message Flow
- Handle Exceptions
- Add the Silverlight Object Tag
Create the AmqpClient Object
First, create an AmqpClient client object. Before you create the client object, add the following import statements in your application page's .XAML.CS file:
using com.kaazing.gateway.client.html5;
using com.kaazing.gateway.client.protocol.amqp;
Next, to use the features of the Kaazing Silverlight client library , you must register the id of its object element with the library as shown in the following example. (This is specified in the object tag in the HTML file that contains the Silverlight application. See the section Add the Silverlight Object Tag for details.)
Html5Utils.Init("MyApplicationId");
Next, declare a variable and create an instance of the AmqpClient object as shown in the following example.
AmqpClient client = new AmqpClient();
Now that you have created an instance of the AmqpClient object, you can use the AMQP protocol commands. To handle open and close events, add two event handlers--one for OpenEvent and one for closeEvent, as shown in the following example.
client.OpenEvent += new AmqpEventHandler(OpenHandler);
client.CloseEvent += new AmqpEventHandler(CloseHandler);
Connect to an AMQP Broker
Next, you must to connect and log in to an AMQP broker. The client generally manages all of its communication on a single connection to an AMQP broker. You establish a connection to an AMQP broker by passing in the broker address, a user name and password, the AMQP version you want to use, and, optionally, a virtual host name (the name of a collection of exchanges and queues hosted on independent server domains). These parameters are passed in when you call the connect method as shown in the following example.
client.Connect(url, virtualHost, username, password, amqpVersion);
Note: Version 0-9-1 is used if you do not specify an AMQP version number. If your AMQP broker does not support version 0-9-1, you must specify another version (for example, 0-9-0). Refer your AMQP broker documentation and to Release Notes for information about certified AMQP versions.
Create Channels
Once a connection to an AMQP broker has been established, the client must create a channel to communicate to the broker. A channel is a bi-directional connection between an AMQP client and an AMQP broker. AMQP is multi-channeled, which means that channels are multiplexed over a single network socket connection. Channels are light-weight and cheap, and therefore used in AMQP's exception handling mechanism--channels are closed when an exception occurs. The following example shows how you can create two channels (one for publishing to an exchange and one for consuming from a queue):
publishChannel = client.OpenChannel();
consumeChannel = client.OpenChannel();
Once you have created the channels, you can add event handlers for various channel events as shown in the following example.
publishChannel.OpenEvent += new
AmqpEventHandler(PublishOpenHandler);
publishChannel.CloseChannelEvent += new
AmqpEventHandler(PublishCloseChannelHandler);
publishChannel.DeclareExchangeEvent += new
AmqpEventHandler(DeclareExchangeHandler);
publishChannel.SelectTransactionEvent += new
AmqpEventHandler(SelectTransactionHandler);
publishChannel.CommitTransactionEvent += new
AmqpEventHandler(CommitTransactionHandler);
publishChannel.RollbackTransactionEvent += new
AmqpEventHandler(RollbackTransactionHandler);
consumeChannel.OpenEvent += new
AmqpEventHandler(ConsumeOpenHandler);
consumeChannel.CloseChannelEvent += new
AmqpEventHandler(ConsumeCloseChannelHandler);
consumeChannel.ConsumeEvent += new
AmqpEventHandler(ConsumeHandler);
consumeChannel.BindQueueEvent += new
AmqpEventHandler(BindQueueHandler);
consumeChannel.DeclareQueueEvent += new
AmqpEventHandler(DeclareQueueHandler);
consumeChannel.FlowEvent += new
AmqpEventHandler(FlowHandler);
consumeChannel.MessageEvent += new
AmqpEventHandler(MessageHandler);
In this example, each of the event handlers has an associated function that processes the AMQP event. The following is an example of a PublishOpenHandler function:
private void PublishOpenHandler(object sender, AmqpEventArgs event)
{
Frame frame = (Frame)event.frame;
Log("Channel Created");
}
Declare an Exchange
AMQP messages are published to exchanges. Messages contain a routing key that contains the information about the message's destination. The exchange accepts messages and their routing keys and delivers them to a message queue. You can think of an exchange as an electronic mailman that delivers the messages to a mailbox (the queue) based on the address on the message's envelope (the routing key). Exchanges do not store messages.
AMQP defines different exchange types. Some of these exchange types (Direct, Fanout, and Topic) must be supported by all AMQP brokers while others (Headers and System) are optional. AMQP brokers can also support custom exchange types. The following are the different types of exchanges:
- Direct--Messages are sent only to a queue that is bound with a binding key that matches the message's routing key.
- Fanout--Messages are sent to every queue that is bound to the exchange.
- Topic--Messages are sent to a queue based on categorical binding keys and wildcards.
- Headers--Messages are sent to a queue based on their header property values.
- System--Messages are sent to system services.
Exchanges can be durable, meaning that the exchange survives broker shut-down and must be deleted manually or non-durable (temporary) meaning that the exchange lasts only until the broker is shut down. Finally, to check if an exchange exists on the AMQP broker (without actually creating it), you can create a passive exchange. The following example shows how you can create a direct exchange on the publish channel:
private void DeclareExchange()
{
publishChannel.DeclareExchange(txtExchange, "direct", passive, durable, noWait, null);
}
Note: In this example, the arguments passive, durable, and noWait represent boolean values. Note also that no custom parameters are passed in.
After the exchange is created successfully, a DeclareExchangeEvent event is raised, which calls the previously registered event handler DeclareExchangeHandler.Declare a Queue
AMQP messages are consumed from queues. You can think of a queue as a mailbox; messages addressed to a particular address (the routing key) are placed in the mailbox for the consumer to pick up. If multiple consumers are bound to a single queue, only one of the consumers receives the message (the one that picked up the mail).
To check if a queue exists on the AMQP broker (without creating it), you can create a passive queue. Additionally, queues can be marked exclusive, which means that they are tied to a specific connection. If a queue is marked exclusive, it is deleted when the connection on which it was created is closed.
Queues can be durable, meaning that the queue survives broker shut-down and must be deleted manually or non-durable (temporary) meaning that the queue lasts only until the broker is shut down. Queues can also be marked auto delete, which means that the queue is automatically deleted when it is no longer in use. The following example shows how you can create a queue on the consume channel:
consumeChannel.DeclareQueue("myqueuename", passive, durable, exclusive, autoDelete,
noWait, null);
Note: In this example, the arguments passive, durable, exclusive, autoDelete, and noWait represent boolean values. Note also that no custom parameters are passed in.
After the queue is created successfully, a DeclareQueueEvent event is raised, which calls the previously registered event handler DeclareQueueHandler.
Bind an Exchange to a Queue
Once you have created an exchange and a queue in AMQP, you must bind--or map--one to the other so that messages published to a specific exchange are delivered to a particular queue. You bind a queue to an exchange with a routing key as shown in the following example.
publishChannel.BindQueue("myqueuename", txtExchange, txtRoutingKey, noWait, null);
After the exchange is bound to the queue successfully, a BindQueueEvent event is raised, which calls the previously registered event handler BindQueueHandler.
Publish Messages
Messages are published to exchanges. The established binding rules (routing keys) then determine to which queue a message is delivered. Messages have content that consists of two parts:
- Content Header--A set of properties that describes the message
- Content Body--A blob of binary data
Additionally, messages can be marked mandatory to send a notification to the publisher in case a message cannot be delivered to a queue. You can also mark a message immediate so that it is returned to the sender if the message cannot be routed to a queue consumer immediately. The following example shows how the content body of a message is added to a buffer (AMQP uses a binary message format) and published to an exchange using the publish channel:
private void PublishBasicExchange(object sender, RoutedEventArgs e)
{
ByteBuffer buffer = new ByteBuffer();
buffer.PutString(txtMessage, System.Text.Encoding.UTF8);
buffer.Flip();
Dictionary<string, object> headersArg = new Dictionary<string, object>();
publishChannel.PublishBasic(buffer, txtExchange, headersArg, txtRoutingKey, mandatory, immediate);
}
Note: The arguments mandatory and immediate use boolean values. Note also that no custom parameters are passed in.
Consume Messages
Once messages are published, they can be consumed from a queue. A variety of options can be applied to messages in a queue. For example, publishers can choose to require acknowledgement (ack) of messages so that messages can be redelivered in the case of a delivery failure. If the queue is set to exclusive, it is scoped to just the current connection and deleted when the connection on which it was established is closed. Additionally, you can use the no local setting to notify the broker not to send messages to the connection on which the messages were published. The following example shows how you can consume messages from a queue on the consume channel:
consumeChannel.ConsumeBasic("myqueuename", txtRoutingKey, noLocal, noAck,
exclusive, noWait, null);
Note: In this example, the arguments noLocal, noAck, noWait, and exclusive represent boolean values.
After the consumeBasic method is successful, a ConsumeEvent event is raised, which calls the previously registered event handler ConsumeHandler. The AMQP broker can then start delivering messages to the client and these messages raise the MessageEvent event, which calls the previously registered event handler MessageHandler. The following example shows how the MessageHandler function retrieves information from the AmqpEventArgs object.
private void MessageHandler(object sender, AmqpEventArgs args)
{
Frame f = (Frame)args.frame;
AmqpBuffer buf = f.body;
string message = buf.GetString(System.Text.Encoding.UTF8);
Log("Message :-" + message);
}
Use Transactions
AMQP supports transactional messaging, through server local transactions. In a transaction the server only publishes a set of messages as one unit when the client commits the transaction. Transactions only apply to message publishing and not to the consumption of the messages.
Note: Once you commit or roll back a transaction on a channel, a new transaction is started automatically. For this reason you must commit all future messages you want to publish on that channel or create a new, non-transactional channel to publish messages on.
The following transaction-related methods can be used to work select (start), commit, and rollback a transaction:
publishChannel.SelectTx();
publishChannel.CommitTx();
publishChannel.RollbackTx();
After the transaction is successfully selected, committed, or rolled back, the corresponding events (SelectTransactionEvent, CommitTransactionEvent, and RollbackTransactionEvent) are raised. These events call the previously registered event handlers. Each of the event handlers has an associated function that processes the event.
Control Message Flow
You can use flow control in AMQP to temporarily--or permanently--halt the flow of messages on a channel from a queue to a consumer. If you turn the message flow off, no messages are sent to the consumer. The following example shows how you can turn the flow of messages on a channel off and back on:
consumeChannel.FlowChannel(false);
consumeChannel.FlowChannel(true);
After the flow on a channel is halted or resumed successfully, a FlowEvent event is raised, which calls the previously registered event handler FlowHandler.
Handle Exceptions
Channels are light-weight and cheap, and therefore used in AMQP's exception handling mechanism--channels are closed when an exception occurs. When the CloseChannelEvent event is raised, the previously registered PublishCloseChannelHandler event handler calls the associated PublishCloseChannelHandler function that processes the AMQP event. The following example shows how that function can be used to log a message about why the channel was closed:
private void PublishCloseChannelHandler(object sender, AmqpEventArgs args)
{
Frame f = (Frame)args.frame;
AmqpBuffer buf = f.body;
string message = buf.GetString(System.Text.Encoding.UTF8);
Log("Channel closed: " + message);
}
Add the Silverlight Object Tag
To ensure the Silverlight application works properly, you must add an object tag that points to the location of your XAP file to the HTML file that includes your Silverlight application. At runtime, Silverlight downloads the XAP file that is referenced and run it in the browser. The following is an example object tag:
<object data="data:application/x-silverlight,"
type="application/x-silverlight-2"
width="100%" height="100%"
id="MyApplicationId">
<param name="source" value="MySilverlightApplication.xap"/>
<param name="EnableHtmlAccess" value="true"/>
<!--
Optional parameters
-->
</object>
The following attributes are required if you want to use Kaazing Silverlight client library in your Silverlight application.
- source--The path to the Silverlight application XAP file.
- EnableHtmlAccess--Enables the Kaazing Silverlight client library to communicate with the HTML page that contains it.
- id--An id that is required for communication between the Silverlight application and the HTML page that contains it. The value of this id attribute is used as the value for the Html5Utils.Init method as shown in the Create the AmqpClient Object section.
Other Coding Styles
In this how-to, you have used an event programming style. You can also use a continuation-passing programming style. The following example shows how you can declare an exchange using the continuation-passing programming style:
publishChannel.DeclareExchange(txtExchange, "direct", passive, durable, noWait, null, declareExchangeHandlerContinuation, errorHandlerContinuation);
You can also combine the two programming styles.
Summary
As you have seen in this how-to, you can build Web applications that can communicate directly from the browser with an AMQP broker using the Silverlight AmqpClient client library. Thus, with the help of WebSocket the browser now enjoys the first-class citizenry of AMQP communications that has, until recently, been enjoyed only by desktop applications.