Added: qpid/java/trunk/doc/jms-client-0-10/src/docbkx/JMS-Client-0-10-Book.xml URL: http://svn.apache.org/viewvc/qpid/java/trunk/doc/jms-client-0-10/src/docbkx/JMS-Client-0-10-Book.xml?rev=1743759&view=auto ============================================================================== --- qpid/java/trunk/doc/jms-client-0-10/src/docbkx/JMS-Client-0-10-Book.xml (added) +++ qpid/java/trunk/doc/jms-client-0-10/src/docbkx/JMS-Client-0-10-Book.xml Fri May 13 21:45:40 2016 @@ -0,0 +1,6594 @@ +<?xml version='1.0' encoding='utf-8' ?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> + +<!-- + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +--> + +<book id="client-api-tutorial"> + <title>Programming in Apache Qpid</title> + <subtitle>Cross-Platform AMQP Messaging in Java JMS, .NET, C++, and Python</subtitle> + + <chapter> + <title>Introduction</title> + + <para>Apache Qpid is a reliable, asynchronous messaging system that + supports the AMQP messaging protocol in several common programming + languages. Qpid is supported on most common platforms. + </para> + + <itemizedlist> + <listitem> + <para> + On the Java platform, Qpid uses the + established <ulink url="http://java.sun.com/products/jms/">Java JMS + API</ulink>. + </para> + </listitem> + <listitem> + <para> + For Python, C++, and .NET, Qpid defines its own messaging API, the + <firstterm>Qpid Messaging API</firstterm>, which is + conceptually similar in each. + </para> + <para> + On the .NET platform, Qpid also provides a WCF binding. + </para> + </listitem> + <listitem> + <para> + Ruby will also use the Qpid Messaging API, which will soon + be implemented. (Ruby currently uses an API that is closely + tied to the AMQP version). + </para> + </listitem> + </itemizedlist> + + </chapter> + + <chapter> + <title>Using the Qpid Messaging API</title> + + <para>The Qpid Messaging API is quite simple, consisting of only a + handful of core classes. + </para> + + <itemizedlist> + + <listitem> + <para> + A <firstterm>message</firstterm> consists of a standard set + of fields (e.g. <literal>subject</literal>, + <literal>reply-to</literal>), an application-defined set of + properties, and message content (the main body of the + message). + </para> + </listitem> + + <listitem> + <para> + A <firstterm>connection</firstterm> represents a network + connection to a remote endpoint. + </para> + </listitem> + + <listitem> + <para> + A <firstterm>session</firstterm> provides a sequentially + ordered context for sending and receiving + <emphasis>messages</emphasis>. A session is obtained from a + connection. + </para> + </listitem> + + <listitem> + <para> + A <firstterm>sender</firstterm> sends messages to a target + using the <literal>sender.send</literal> method. A sender is + obtained from a session for a given target address. + </para> + </listitem> + + <listitem> + <para> + A <firstterm>receiver</firstterm> receives messages from a + source using the <literal>receiver.fetch</literal> method. + A receiver is obtained from a session for a given source + address. + </para> + </listitem> + + </itemizedlist> + + <para> + The following sections show how to use these classes in a + simple messaging program. + </para> + + <section> + <title>A Simple Messaging Program in C++</title> + + <para>The following C++ program shows how to create a connection, + create a session, send messages using a sender, and receive + messages using a receiver.</para> + + <example> + <title>"Hello world!" in C++</title> + <programlisting lang="c++"><![CDATA[ + #include <qpid/messaging/Connection.h> + #include <qpid/messaging/Message.h> + #include <qpid/messaging/Receiver.h> + #include <qpid/messaging/Sender.h> + #include <qpid/messaging/Session.h> + + #include <iostream>]]> + + using namespace qpid::messaging; + + int main(int argc, char** argv) { + std::string broker = argc > 1 ? argv[1] : "localhost:5672"; + std::string address = argc > 2 ? argv[2] : "amq.topic"; + std::string connectionOptions = argc > 3 ? argv[3] : ""; + + Connection connection(broker, connectionOptions); + try { + connection.open(); <co id="hello-cpp-open" linkends="callout-cpp-open"/> + Session session = connection.createSession(); <co id="hello-cpp-session" linkends="callout-cpp-session"/> + + Receiver receiver = session.createReceiver(address); <co id="hello-cpp-receiver" linkends="callout-cpp-receiver"/> + Sender sender = session.createSender(address); <co id="hello-cpp-sender" linkends="callout-cpp-sender"/> + + sender.send(Message("Hello world!")); + + Message message = receiver.fetch(Duration::SECOND * 1); <co id="hello-cpp-fetch" linkends="callout-cpp-fetch"/> + <![CDATA[std::cout << message.getContent() << std::endl;]]> + session.acknowledge(); <co id="hello-cpp-acknowledge" linkends="callout-cpp-acknowledge"/> + + connection.close(); <co id="hello-cpp-close" linkends="callout-cpp-close"/> + return 0; + } catch(const std::exception& error) { + <![CDATA[std::cerr << error.what() << std::endl;]]> + connection.close(); + return 1; + } + }</programlisting> + + <calloutlist> + <callout id="callout-cpp-open" arearefs="hello-cpp-open"> + <para>Establishes the connection with the messaging broker.</para> + </callout> + <callout id="callout-cpp-session" arearefs="hello-cpp-session"> + <para>Creates a session object on which messages will be sent and received.</para> + </callout> + <callout id="callout-cpp-receiver" arearefs="hello-cpp-receiver"> + <para>Creates a receiver that receives messages from the given address.</para> + </callout> + <callout id="callout-cpp-sender" arearefs="hello-cpp-sender"> + <para>Creates a sender that sends to the given address.</para> + </callout> + <callout id="callout-cpp-fetch" arearefs="hello-cpp-fetch"> + <para>Receives the next message. The duration is optional, if omitted, will wait indefinitely for the next message.</para> + </callout> + <callout id="callout-cpp-acknowledge" arearefs="hello-cpp-acknowledge"> + <para>Acknowledges receipt of all fetched messages on the + session. This informs the broker that the messages were + transferred and processed by the client successfully.</para> + </callout> + <callout id="callout-cpp-close" arearefs="hello-cpp-close"> + <para>Closes the connection, all sessions managed by the connection, and all senders and receivers managed by each session.</para> + </callout> + </calloutlist> + </example> + + + </section> + + <section> + <title>A Simple Messaging Program in Python</title> + + <para>The following Python program shows how to create a + connection, create a session, send messages using a sender, and + receive messages using a receiver.</para> + + <example> + <title>"Hello world!" in Python</title> + <programlisting lang="python"><![CDATA[ + import sys + from qpid.messaging import * + + broker = "localhost:5672" if len(sys.argv)<2 else sys.argv[1] + address = "amq.topic" if len(sys.argv)<3 else sys.argv[2]]]> + + connection = Connection(broker) + + try: + connection.open() <co id="hello-python-open" linkends="callout-python-open"/> + session = connection.session() <co id="hello-python-session" linkends="callout-python-session"/> + + sender = session.sender(address) <co id="hello-python-sender" linkends="callout-python-sender"/> + receiver = session.receiver(address) <co id="hello-python-receiver" linkends="callout-python-receiver"/> + + sender.send(Message("Hello world!")); + + message = receiver.fetch(timeout=1) <co id="hello-python-fetch" linkends="callout-python-fetch"/> + print message.content + session.acknowledge() <co id="hello-python-acknowledge" linkends="callout-python-acknowledge"/> + + except MessagingError,m: + print m + finally: + connection.close() <co id="hello-python-close" linkends="callout-python-close"/> + </programlisting> + + <calloutlist> + <callout id="callout-python-open" arearefs="hello-python-open"> + <para>Establishes the connection with the messaging broker.</para> + </callout> + <callout id="callout-python-session" arearefs="hello-python-session"> + <para>Creates a session object on which messages will be sent and received.</para> + </callout> + <callout id="callout-python-receiver" arearefs="hello-python-receiver"> + <para>Creates a receiver that receives messages from the given address.</para> + </callout> + <callout id="callout-python-sender" arearefs="hello-python-sender"> + <para>Creates a sender that sends to the given address.</para> + </callout> + <callout id="callout-python-fetch" arearefs="hello-python-fetch"> + <para>Receives the next message. The duration is optional, if omitted, will wait indefinitely for the next message.</para> + </callout> + <callout id="callout-python-acknowledge" arearefs="hello-python-acknowledge"> + <para>Acknowledges receipt of all fetched messages on + the session. This informs the broker that the messages were + transfered and processed by the client successfully.</para> + </callout> + <callout id="callout-python-close" arearefs="hello-python-close"> + <para>Closes the connection, all sessions managed by the connection, and all senders and receivers managed by each session.</para> + </callout> + </calloutlist> + + </example> + + </section> + + + + + <section> + <title>A Simple Messaging Program in .NET C#</title> + + <para>The following .NET C# + <footnote> + <para> + The .NET binding for the Qpid C++ Messaging API + applies to all .NET Framework managed code languages. C# was chosen + for illustration purposes only. + </para> + </footnote> + program shows how to create a connection, + create a session, send messages using a sender, and receive + messages using a receiver. + </para> + + <example> + <title>"Hello world!" in .NET C#</title> + <programlisting lang="c++"> + using System; + using Org.Apache.Qpid.Messaging; <co id="hello-csharp-using" linkends="callout-csharp-using"/> + + namespace Org.Apache.Qpid.Messaging { + class Program { + static void Main(string[] args) { + String broker = args.Length > 0 ? args[0] : "localhost:5672"; + String address = args.Length > 1 ? args[1] : "amq.topic"; + + Connection connection = null; + try { + connection = new Connection(broker); + connection.Open(); <co id="hello-csharp-open" linkends="callout-csharp-open"/> + Session session = connection.CreateSession(); <co id="hello-csharp-session" linkends="callout-csharp-session"/> + + Receiver receiver = session.CreateReceiver(address); <co id="hello-csharp-receiver" linkends="callout-csharp-receiver"/> + Sender sender = session.CreateSender(address); <co id="hello-csharp-sender" linkends="callout-csharp-sender"/> + + sender.Send(new Message("Hello world!")); + + Message message = new Message(); + message = receiver.Fetch(DurationConstants.SECOND * 1); <co id="hello-csharp-fetch" linkends="callout-csharp-fetch"/> + Console.WriteLine("{0}", message.GetContent()); + session.Acknowledge(); <co id="hello-csharp-acknowledge" linkends="callout-csharp-acknowledge"/> + + connection.Close(); <co id="hello-csharp-close" linkends="callout-csharp-close"/> + } catch (Exception e) { + Console.WriteLine("Exception {0}.", e); + if (null != connection) + connection.Close(); + } + } + } + } + + </programlisting> + + <calloutlist> + <callout id="callout-csharp-using" arearefs="hello-csharp-using"> + <para> Permits use of Org.Apache.Qpid.Messaging types and methods without explicit namespace qualification. Any .NET project must have a project reference to the assembly file <literal>Org.Apache.Qpid.Messaging.dll</literal> in order to obtain the definitions of the .NET Binding for Qpid Messaging namespace.</para> + </callout> + <callout id="callout-csharp-open" arearefs="hello-csharp-open"> + <para>Establishes the connection with the messaging broker.</para> + </callout> + <callout id="callout-csharp-session" arearefs="hello-csharp-session"> + <para>Creates a session object on which messages will be sent and received.</para> + </callout> + <callout id="callout-csharp-receiver" arearefs="hello-csharp-receiver"> + <para>Creates a receiver that receives messages from the given address.</para> + </callout> + <callout id="callout-csharp-sender" arearefs="hello-csharp-sender"> + <para>Creates a sender that sends to the given address.</para> + </callout> + <callout id="callout-csharp-fetch" arearefs="hello-csharp-fetch"> + <para>Receives the next message. The duration is optional, if omitted, will wait indefinitely for the next message.</para> + </callout> + <callout id="callout-csharp-acknowledge" arearefs="hello-csharp-acknowledge"> + <para>Acknowledges receipt of all fetched messages on the + session. This informs the broker that the messages were + transfered and processed by the client successfully.</para> + </callout> + <callout id="callout-csharp-close" arearefs="hello-csharp-close"> + <para>Closes the connection, all sessions managed by the connection, and all senders and receivers managed by each session.</para> + </callout> + </calloutlist> + </example> + + + </section> + + + + + + + <section id="section-addresses"> + <title>Addresses</title> + + <para>An <firstterm>address</firstterm> is the name of a message + target or message source. + + <footnote><para>In the programs we have just seen, we used + <literal>amq.topic</literal> as the default address if none is + passed in. This is the name of a standard exchange that always + exists on an AMQP 0-10 messaging broker.</para></footnote> + + The methods that create senders and receivers require an + address. The details of sending to a particular target or + receiving from a particular source are then handled by the + sender or receiver. A different target or source can be used + simply by using a different address. + </para> + + <para>An address resolves to a <firstterm>node</firstterm>. The + Qpid Messaging API recognises two kinds of nodes, + <firstterm>queues</firstterm> and <firstterm>topics</firstterm> + + <footnote><para>The terms <emphasis>queue</emphasis> and + <emphasis>topic</emphasis> here were chosen to align with + their meaning in JMS. These two addressing 'patterns', + queue and topic, are sometimes refered as point-to-point + and publish-subscribe. AMQP 0-10 has an exchange type + called a <emphasis>topic exchange</emphasis>. When the term + <emphasis>topic</emphasis> occurs alone, it refers to a + Messaging API topic, not the topic + exchange.</para></footnote>. + + A queue stores each message until it has been received and + acknowledged, and only one receiver can receive a given message + + <footnote><para>There are exceptions to this rule; for instance, + a receiver can use <literal>browse</literal> mode, which leaves + messages on the queue for other receivers to + read.</para></footnote>. + + A topic immediately delivers a message to all eligible + receivers; if there are no eligible receivers, it discards the + message. In the AMQP 0-10 implementation of the API, + + <footnote><para>The AMQP 0-10 implementation is the only one + that currently exists.</para></footnote> + + queues map to AMQP queues, and topics map to AMQP exchanges. + + <footnote><para>In AMQP 0-10, messages are sent to + exchanges, and read from queues. The Messaging API also + allows a sender to send messages to a queue; internally, + Qpid implements this by sending the message to the default + exchange, with the name of the queue as the routing key. The + Messaging API also allows a receiver to receive messages + from a topic; internally, Qpid implements this by setting up + a private subscription queue for the receiver and binding + the subscription queue to the exchange that corresponds to + the topic.</para></footnote> + </para> + + <para>In the rest of this tutorial, we present many examples + using two programs that take an address as a command line + parameter. <command>spout</command> sends messages to the + target address, <command>drain</command> receives messages from + the source address. The source code is available in C++, Python, and + .NET C# and can be found in the examples directory for each + language. These programs can use any address string as a source + or a destination, and have many command line options to + configure behavior—use the <command>-h</command> option + for documentation on these options. + + <footnote><para>Currently, the C++, Python, and .NET C# + implementations of <command>drain</command> and + <command>spout</command> have slightly different + options. This tutorial uses the C++ implementation. The + options will be reconciled in the near + future.</para></footnote> + + + The examples in this tutorial also use the + <command>qpid-config</command> utility to configure AMQP 0-10 + queues and exchanges on a Qpid broker. + </para> + + + <example> + <title>Queues</title> + + <para>Create a queue with <command>qpid-config</command>, send a message using + <command>spout</command>, and read it using <command>drain</command>:</para> + + <screen> + $ qpid-config add queue hello-world + $ ./spout hello-world + $ ./drain hello-world + + Message(properties={spout-id:c877e622-d57b-4df2-bf3e-6014c68da0ea:0}, content='') + </screen> + + <para>The queue stored the message sent by <command>spout</command> and delivered + it to <command>drain</command> when requested.</para> + + <para>Once the message has been delivered and and acknowledged + by <command>drain</command>, it is no longer available on the queue. If we run + <command>drain</command> one more time, no messages will be retrieved.</para> + + <screen> + $ ./drain hello-world + $ + </screen> + + </example> + + <example> + <title>Topics</title> + + <para>This example is similar to the previous example, but it + uses a topic instead of a queue.</para> + + <para>First, use <command>qpid-config</command> to remove the queue + and create an exchange with the same name:</para> + + <screen> + $ qpid-config del queue hello-world + $ qpid-config add exchange topic hello-world + </screen> + + <para>Now run <command>drain</command> and <command>spout</command> the same way we did in the previous example:</para> + + <screen> + $ ./spout hello-world + $ ./drain hello-world + $ + </screen> + + <para>Topics deliver messages immediately to any interested + receiver, and do not store messages. Because there were no + receivers at the time <command>spout</command> sent the + message, it was simply discarded. When we ran + <command>drain</command>, there were no messages to + receive.</para> + + <para>Now let's run <command>drain</command> first, using the + <literal>-t</literal> option to specify a timeout in seconds. + While <command>drain</command> is waiting for messages, + run <command>spout</command> in another window.</para> + + <para><emphasis>First Window:</emphasis></para> + + <screen> + $ ./drain -t 30 hello-word + </screen> + + + <para><emphasis>Second Window:</emphasis></para> + + <screen> + $ ./spout hello-word + </screen> + + <para>Once <command>spout</command> has sent a message, return + to the first window to see the output from + <command>drain</command>:</para> + + <screen> + Message(properties={spout-id:7da2d27d-93e6-4803-8a61-536d87b8d93f:0}, content='') + </screen> + + <para>You can run <command>drain</command> in several separate + windows; each creates a subscription for the exchange, and + each receives all messages sent to the exchange.</para> + + </example> + + <section> + <title>Address Strings</title> + + <para>So far, our examples have used address strings that + contain only the name of a node. An <firstterm>address + string</firstterm> can also contain a + <firstterm>subject</firstterm> and + <firstterm>options</firstterm>.</para> + + <para>The syntax for an address string is:</para> + + <programlisting><![CDATA[ + address_string ::= <address> [ / <subject> ] [ ; <options> ] + options ::= { <key> : <value>, ... } + ]]></programlisting> + + <para>Addresses, subjects, and keys are strings. Values can + be numbers, strings (with optional single or double quotes), + maps, or lists. A complete BNF for address strings appears in + <xref linkend="section-address-string-bnf"/>.</para> + + + <para>So far, the address strings in this tutorial have only + used simple names. The following sections show how to use + subjects and options.</para> + + </section> + + <section> + <title>Subjects</title> + + + <para>Every message has a property called + <firstterm>subject</firstterm>, which is analogous to the + subject on an email message. If no subject is specified, the + message's subject is null. For convenience, address strings + also allow a subject. If a sender's address contains a + subject, it is used as the default subject for the messages + it sends. + + If a receiver's address contains a subject, it is used to + select only messages that match the subject—the matching + algorithm depends on the message source. + </para> + + <para> + In AMQP 0-10, each exchange type has its own matching + algorithm. This is discussed in + <xref linkend="section-amqp0-10-mapping"/>. + </para> + + <note> + <para> + Currently, a receiver bound to a queue ignores subjects, + receiving messages from the queue without filtering. Support + for subject filtering on queues will be implemented soon. + </para> + </note> + + + <example> + <title>Using subjects</title> + + <para>In this example we show how subjects affect message + flow.</para> + + <para>First, let's use <command>qpid-config</command> to create a topic exchange.</para> + + <screen> + $ qpid-config add exchange topic news-service + </screen> + + <para>Now we use drain to receive messages from <literal>news-service</literal> that match the subject <literal>sports</literal>.</para> + <para><emphasis>First Window:</emphasis></para> + <screen> + $ ./drain -t 30 news-service/sports + </screen> + + <para>In a second window, let's send messages to <literal>news-service</literal> using two different subjects:</para> + + <para><emphasis>Second Window:</emphasis></para> + <screen> + $ ./spout news-service/sports + $ ./spout news-service/news + </screen> + + <para>Now look at the first window, the message with the + subject <literal>sports</literal> has been received, but not + the message with the subject <literal>news</literal>:</para> + + <screen> + Message(properties={qpid.subject:sports, spout-id:9441674e-a157-4780-a78e-f7ccea998291:0}, content='') + </screen> + + <para>If you run <command>drain</command> in multiple + windows using the same subject, all instances of + <command>drain</command> receive the messages for that + subject.</para> + </example> + + + <para>The AMQP exchange type we are using here, + <literal>amq.topic</literal>, can also do more sophisticated + matching. + + A sender's subject can contain multiple words separated by a + <quote>.</quote> delimiter. For instance, in a news + application, the sender might use subjects like + <literal>usa.news</literal>, <literal>usa.weather</literal>, + <literal>europe.news</literal>, or + <literal>europe.weather</literal>. + + The receiver's subject can include wildcard characters— + <quote>#</quote> matches one or more words in the message's + subject, <quote>*</quote> matches a single word. + + For instance, if the subject in the source address is + <literal>*.news</literal>, it matches messages with the + subject <literal>europe.news</literal> or + <literal>usa.news</literal>; if it is + <literal>europe.#</literal>, it matches messages with subjects + like <literal>europe.news</literal> or + <literal>europe.pseudo.news</literal>.</para> + + <example> + <title>Subjects with multi-word keys</title> + + <para>This example uses drain and spout to demonstrate the + use of subjects with two-word keys.</para> + + <para>Let's use <command>drain</command> with the subject + <literal>*.news</literal> to listen for messages in which + the second word of the key is + <literal>news</literal>.</para> + + <para><emphasis>First Window:</emphasis></para> + + <screen> + $ ./drain -t 30 news-service/*.news + </screen> + + <para>Now let's send messages using several different + two-word keys:</para> + + <para><emphasis>Second Window:</emphasis></para> + + <screen> + $ ./spout news-service/usa.news + $ ./spout news-service/usa.sports + $ ./spout news-service/europe.sports + $ ./spout news-service/europe.news + </screen> + + <para>In the first window, the messages with + <literal>news</literal> in the second word of the key have + been received:</para> + + <screen> + Message(properties={qpid.subject:usa.news, spout-id:73fc8058-5af6-407c-9166-b49a9076097a:0}, content='') + Message(properties={qpid.subject:europe.news, spout-id:f72815aa-7be4-4944-99fd-c64c9747a876:0}, content='') + </screen> + + + <para>Next, let's use <command>drain</command> with the + subject <literal>#.news</literal> to match any sequence of + words that ends with <literal>news</literal>.</para> + + <para><emphasis>First Window:</emphasis></para> + + <screen> + $ ./drain -t 30 news-service/#.news + </screen> + + <para>In the second window, let's send messages using a + variety of different multi-word keys:</para> + + <para><emphasis>Second Window:</emphasis></para> + + <screen> + $ ./spout news-service/news + $ ./spout news-service/sports + $ ./spout news-service/usa.news + $ ./spout news-service/usa.sports + $ ./spout news-service/usa.faux.news + $ ./spout news-service/usa.faux.sports + </screen> + + <para>In the first window, messages with + <literal>news</literal> in the last word of the key have been + received:</para> + + <screen> + Message(properties={qpid.subject:news, spout-id:cbd42b0f-c87b-4088-8206-26d7627c9640:0}, content='') + Message(properties={qpid.subject:usa.news, spout-id:234a78d7-daeb-4826-90e1-1c6540781eac:0}, content='') + Message(properties={qpid.subject:usa.faux.news, spout-id:6029430a-cfcb-4700-8e9b-cbe4a81fca5f:0}, content='') + </screen> + </example> + + </section> + + <section> + <title>Address String Options</title> + + <para> + The options in an address string can contain additional + information for the senders or receivers created for it, + including: + </para> + <itemizedlist> + <listitem> + <para> + Policies for assertions about the node to which an address + refers. + </para> + <para> + For instance, in the address string <literal>my-queue; + {assert: always, node:{ type: queue }}</literal>, the node + named <literal>my-queue</literal> must be a queue; if not, + the address does not resolve to a node, and an exception + is raised. + </para> + </listitem> + <listitem> + <para> + Policies for automatically creating or deleting the node to which an address refers. + </para> + <para> + For instance, in the address string <literal>xoxox ; {create: always}</literal>, + the queue <literal>xoxox</literal> is created, if it does + not exist, before the address is resolved. + </para> + </listitem> + <listitem> + <para> + Extension points that can be used for sender/receiver configuration. + </para> + <para> + For instance, if the address for a receiver is + <literal>my-queue; {mode: browse}</literal>, the receiver + works in <literal>browse</literal> mode, leaving messages + on the queue so other receivers can receive them. + </para> + </listitem> + <listitem> + <para> + Extension points providing more direct control over the underlying protocol. + </para> + <para> + For instance, the <literal>x-bindings</literal> property + allows greater control over the AMQP 0-10 binding process + when an address is resolved. + </para> + </listitem> + </itemizedlist> + + + <para> + Let's use some examples to show how these different kinds of + address string options affect the behavior of senders and + receives. + </para> + + <section> + <title>assert</title> + <para> + In this section, we use the <literal>assert</literal> option + to ensure that the address resolves to a node of the required + type. + </para> + + + <example> + <title>Assertions on Nodes</title> + + <para>Let's use <command>qpid-config</command> to create a + queue and a topic.</para> + + <screen> + $ qpid-config add queue my-queue + $ qpid-config add exchange topic my-topic + </screen> + + <para> + We can now use the address specified to drain to assert that it is + of a particular type: + </para> + + <screen> + $ ./drain 'my-queue; {assert: always, node:{ type: queue }}' + $ ./drain 'my-queue; {assert: always, node:{ type: topic }}' + 2010-04-20 17:30:46 warning Exception received from broker: not-found: not-found: Exchange not found: my-queue (../../src/qpid/broker/ExchangeRegistry.cpp:92) [caused by 2 \x07:\x01] + Exchange my-queue does not exist + </screen> + + <para> + The first attempt passed without error as my-queue is indeed a + queue. The second attempt however failed; my-queue is not a + topic. + </para> + + <para> + We can do the same thing for my-topic: + </para> + + <screen> + $ ./drain 'my-topic; {assert: always, node:{ type: topic }}' + $ ./drain 'my-topic; {assert: always, node:{ type: queue }}' + 2010-04-20 17:31:01 warning Exception received from broker: not-found: not-found: Queue not found: my-topic (../../src/qpid/broker/SessionAdapter.cpp:754) [caused by 1 \x08:\x01] + Queue my-topic does not exist + </screen> + </example> + + <para>Now let's use the <literal>create</literal> option to + create the queue <literal>xoxox</literal> if it does not already + exist:</para> + + </section> + + <section> + <title>create</title> + + <para>In previous examples, we created the queue before + listening for messages on it. Using <literal>create: + always</literal>, the queue is automatically created if it + does not exist.</para> + + <example> + <title>Creating a Queue Automatically</title> + + <para><emphasis>First Window:</emphasis></para> + <screen>$ ./drain -t 30 "xoxox ; {create: always}"</screen> + + + <para>Now we can send messages to this queue:</para> + + <para><emphasis>Second Window:</emphasis></para> + <screen>$ ./spout "xoxox ; {create: always}"</screen> + + <para>Returning to the first window, we see that <command>drain</command> has received this message:</para> + + <screen>Message(properties={spout-id:1a1a3842-1a8b-4f88-8940-b4096e615a7d:0}, content='')</screen> + </example> + <para>The details of the node thus created can be controlled by further options within the node. See <xref linkend="table-node-properties"/> for details.</para> + </section> + + <section> + <title>browse</title> + <para>Some options specify message transfer semantics; for + instance, they may state whether messages should be consumed or + read in browsing mode, or specify reliability + characteristics. The following example uses the + <literal>browse</literal> option to receive messages without + removing them from a queue.</para> + + <example> + <title>Browsing a Queue</title> + <para> + Let's use the browse mode to receive messages without + removing them from the queue. First we send three messages to the + queue: + </para> + <screen> + $ ./spout my-queue --content one + $ ./spout my-queue --content two + $ ./spout my-queue --content three + </screen> + + <para>Now we use drain to get those messages, using the browse option:</para> + <screen> + $ ./drain 'my-queue; {mode: browse}' + Message(properties={spout-id:fbb93f30-0e82-4b6d-8c1d-be60eb132530:0}, content='one') + Message(properties={spout-id:ab9e7c31-19b0-4455-8976-34abe83edc5f:0}, content='two') + Message(properties={spout-id:ea75d64d-ea37-47f9-96a9-d38e01c97925:0}, content='three') + </screen> + + <para>We can confirm the messages are still on the queue by repeating the drain:</para> + <screen> + $ ./drain 'my-queue; {mode: browse}' + Message(properties={spout-id:fbb93f30-0e82-4b6d-8c1d-be60eb132530:0}, content='one') + Message(properties={spout-id:ab9e7c31-19b0-4455-8976-34abe83edc5f:0}, content='two') + Message(properties={spout-id:ea75d64d-ea37-47f9-96a9-d38e01c97925:0}, content='three') + </screen> + </example> + </section> + + <section> + <title>x-bindings</title> + + <para>Greater control over the AMQP 0-10 binding process can + be achieved by including an <literal>x-bindings</literal> + option in an address string. + + For instance, the XML Exchange is an AMQP 0-10 custom exchange + provided by the Apache Qpid C++ broker. It allows messages to + be filtered using XQuery; queries can address either message + properties or XML content in the body of the message. The + xquery is specified in the arguments field of the AMQP 0-10 + command. When using the messaging API an xquery can be + specified in and address that resolves to an XML exchange by + using the x-bindings property.</para> + + + <para>An instance of the XML Exchange must be added before it + can be used:</para> + + <programlisting> + $ qpid-config add exchange xml xml + </programlisting> + + <para>When using the XML Exchange, a receiver provides an + XQuery as an x-binding argument. If the query contains a + context item (a path starting with <quote>.</quote>), then it + is applied to the content of the message, which must be + well-formed XML. For instance, <literal>./weather</literal> is + a valid XQuery, which matches any message in which the root + element is named <literal>weather</literal>. Here is an + address string that contains this query:</para> + + <programlisting><![CDATA[ + xml; { + link: { + x-bindings: [{exchange:xml, key:weather, arguments:{xquery:"./weather"} }] + } + } + ]]></programlisting> + + <para>When using longer queries with <command>drain</command>, + it is often useful to place the query in a file, and use + <command>cat</command> in the command line. We do this in the + following example.</para> + + <example> + <title>Using the XML Exchange</title> + + <para>This example uses an x-binding that contains queries, which filter based on the content of XML messages. Here is an XQuery that we will use in this example:</para> + + <programlisting> + <![CDATA[ + let $w := ./weather + return $w/station = 'Raleigh-Durham International Airport (KRDU)' + and $w/temperature_f > 50 + and $w/temperature_f - $w/dewpoint > 5 + and $w/wind_speed_mph > 7 + and $w/wind_speed_mph < 20 ]]> + </programlisting> + + <para>We can specify this query in an x-binding to listen to messages that meet the criteria specified by the query:</para> + + <para><emphasis>First Window:</emphasis></para> + + <screen> + $ ./drain -f "xml; {link:{x-bindings:[{key:'weather', + arguments:{xquery:\"$(cat rdu.xquery )\"}}]}}" + </screen> + + <para>In another window, let's create an XML message that meets the criteria in the query, and place it in the file <filename>rdu.xml</filename>:</para> + + <programlisting> + <![CDATA[ + <weather> + <station>Raleigh-Durham International Airport (KRDU)</station> + <wind_speed_mph>16</wind_speed_mph> + <temperature_f>70</temperature_f> + <dewpoint>35</dewpoint> + </weather> + ]]></programlisting> + + <para>Now let's use <command>spout</command> to send this message to the XML exchange:</para> + + <para><emphasis>Second Window:</emphasis></para> + <screen> + spout --content "$(cat rdu.xml)" xml/weather + </screen> + + <para>Returning to the first window, we see that the message has been received:</para> + + <screen><![CDATA[$ ./drain -f "xml; {link:{x-bindings:[{exchange:'xml', key:'weather', arguments:{xquery:\"$(cat rdu.xquery )\"}}]}}" + Message(properties={qpid.subject:weather, spout-id:31c431de-593f-4bec-a3dd-29717bd945d3:0}, + content='<weather> + <station>Raleigh-Durham International Airport (KRDU)</station> + <wind_speed_mph>16</wind_speed_mph> + <temperature_f>40</temperature_f> + <dewpoint>35</dewpoint> + </weather>') ]]> + </screen> + </example> + </section> + + <!-- + <para>When sending data using <command>cat</command> to provide arguments to <command>spout</command>, you can use <command>sed</command> to change the values that are sent:</para> + +<screen> +spout - -content "$(cat rdu.xml | sed -e 's/70/45/')" xml/weather +</screen> + --> + + <!-- + TODO: Add some reliability option examples + --> + + <section> + <title>Address String Options - Reference</title> + + <table pgwide="1"> + <title>Address String Options</title> + <tgroup cols="3"> + <thead> + <colspec colnum="1" colwidth="1*"/> + <colspec colnum="2" colwidth="3*"/> + <colspec colnum="3" colwidth="3*"/> + <row> + <entry>option</entry> + <entry>value</entry> + <entry>semantics</entry> + </row> + </thead> + <tbody> + <row> + <entry> + assert + </entry> + <entry> + one of: always, never, sender or receiver + </entry> + <entry> + Asserts that the properties specified in the node option + match whatever the address resolves to. If they do not, + resolution fails and an exception is raised. <!-- ### + Which exception --> + </entry> + </row> + + <row> + <entry> + create + </entry> + <entry> + one of: always, never, sender or receiver + </entry> + <entry> + Creates the node to which an address refers if it does + not exist. No error is raised if the node does + exist. The details of the node may be specified in the + node option. + </entry> + </row> + <row> + <entry> + delete + </entry> + <entry> + one of: always, never, sender or receiver + </entry> + <entry> + Delete the node when the sender or receiver is closed. + </entry> + </row> + <row> + <entry> + node + </entry> + <entry> + A nested map containing the entries shown in <xref linkend="table-node-properties"/>. + </entry> + <entry> + Specifies properties of the node to which the address + refers. These are used in conjunction with the assert or + create options. + </entry> + </row> + <row> + <entry> + link + </entry> + <entry> + A nested map containing the entries shown in <xref linkend="table-link-properties"/>. + </entry> + <entry> + Used to control the establishment of a conceptual link + from the client application to or from the target/source + address. + </entry> + </row> + <row> + <entry> + mode + </entry> + <entry> + one of: browse, consume + </entry> + <entry> + This option is only of relevance for source addresses + that resolve to a queue. If browse is specified the + messages delivered to the receiver are left on the queue + rather than being removed. If consume is specified the + normal behaviour applies; messages are removed from the + queue once the client acknowledges their receipt. + </entry> + </row> + </tbody> + </tgroup> + </table> + + + <table id="table-node-properties" pgwide="1"> + <title>Node Properties</title> + <tgroup cols="3"> + <thead> + <colspec colnum="1" colwidth="1*"/> + <colspec colnum="2" colwidth="3*"/> + <colspec colnum="3" colwidth="3*"/> + <row> + <entry>property</entry> + <entry>value</entry> + <entry>semantics</entry> + </row> + </thead> + <tbody> + <row> + <entry> + type + </entry> + <entry> + topic, queue + </entry> + <entry> + Indicates the type of the node. + </entry> + </row> + <row> + <entry> + durable + </entry> + <entry> + True, False + </entry> + <entry> + Indicates whether the node survives a loss of + volatile storage e.g. if the broker is restarted. + </entry> + </row> + <row> + <entry> + x-declare + </entry> + <entry> + A nested map whose values correspond to the valid fields + on an AMQP 0-10 queue-declare or exchange-declare + command. + </entry> + <entry> + These values are used to fine tune the creation or + assertion process. Note however that they are protocol + specific. + </entry> + </row> + <row> + <entry> + x-bindings + </entry> + <entry> + A nested list in which each binding is represented by + a map. The entries of the map for a binding contain + the fields that describe an AMQP 0-10 binding. Here is + the format for x-bindings: + + <programlisting><![CDATA[ + [ + { + exchange: <exchange>, + queue: <queue>, + key: <key>, + arguments: { + <key_1>: <value_1>, + ..., + <key_n>: <value_n> } + }, + ... + ] + ]]></programlisting> + </entry> + <entry> + In conjunction with the create option, each of these + bindings is established as the address is resolved. In + conjunction with the assert option, the existence of + each of these bindings is verified during + resolution. Again, these are protocol specific. + </entry> + </row> + </tbody> + </tgroup> + </table> + + <table id="table-link-properties" pgwide="1"> + <title>Link Properties</title> + <tgroup cols="3"> + <thead> + <colspec colnum="1" colwidth="1*"/> + <colspec colnum="2" colwidth="3*"/> + <colspec colnum="3" colwidth="3*"/> + <row> + <entry>option</entry> + <entry>value</entry> + <entry>semantics</entry> + </row> + </thead> + <tbody> + <row> + <entry> + reliability + </entry> + <entry> + one of: unreliable, at-least-once, at-most-once, exactly-once + </entry> + <entry> + Reliability indicates the level of reliability that + the sender or receiver. <literal>unreliable</literal> + and <literal>at-most-once</literal> are currently + treated as synonyms, and allow messages to be lost if + a broker crashes or the connection to a broker is + lost. <literal>at-least-once</literal> guarantees that + a message is not lost, but duplicates may be + received. <literal>exactly-once</literal> guarantees + that a message is not lost, and is delivered precisely + once. Currently only <literal>unreliable</literal> + and <literal>at-least-once</literal> are supported. + <footnote><para>If at-most-once is requested, + unreliable will be used and for durable messages on + durable queues there is the possibility that messages + will be redelivered; if exactly-once is requested, + at-least-once will be used and the application needs to + be able to deal with duplicates.</para></footnote> + </entry> + </row> + <row> + <entry> + durable + </entry> + <entry> + True, False + </entry> + <entry> + Indicates whether the link survives a loss of + volatile storage e.g. if the broker is restarted. + </entry> + </row> + <row> + <entry> + x-declare + </entry> + <entry> + A nested map whose values correspond to the valid fields + of an AMQP 0-10 queue-declare command. + </entry> + <entry> + These values can be used to customise the subscription + queue in the case of receiving from an exchange. Note + however that they are protocol specific. + </entry> + </row> + <row> + <entry> + x-subscribe + </entry> + <entry> + A nested map whose values correspond to the valid fields + of an AMQP 0-10 message-subscribe command. + </entry> + <entry> + These values can be used to customise the subscription. + </entry> + </row> + <row> + <entry> + x-bindings + </entry> + <entry> + A nested list each of whose entries is a map that may + contain fields (queue, exchange, key and arguments) + describing an AMQP 0-10 binding. + </entry> + <entry> + These bindings are established during resolution + independent of the create option. They are considered + logically part of the linking process rather than of + node creation. + </entry> + </row> + </tbody> + </tgroup> + </table> + + </section> + </section> + + <section id="section-address-string-bnf"> + <title>Address String Grammar</title> + + <para>This section provides a formal grammar for address strings.</para> + + <formalpara> + <title>Tokens</title> + <para>The following regular expressions define the tokens used + to parse address strings:</para></formalpara> + <programlisting><![CDATA[ + LBRACE: \\{ + RBRACE: \\} + LBRACK: \\[ + RBRACK: \\] + COLON: : + SEMI: ; + SLASH: / + COMMA: , + NUMBER: [+-]?[0-9]*\\.?[0-9]+ + ID: [a-zA-Z_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])? + STRING: "(?:[^\\\\"]|\\\\.)*"|\'(?:[^\\\\\']|\\\\.)*\' + ESC: \\\\[^ux]|\\\\x[0-9a-fA-F][0-9a-fA-F]|\\\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] + SYM: [.#*%@$^!+-] + WSPACE: [ \\n\\r\\t]+ + ]]></programlisting> + + <formalpara> + <title>Grammar</title> + <para>The formal grammar for addresses is given below:</para> + </formalpara> + + <programlisting><![CDATA[ + address := name [ SLASH subject ] [ ";" options ] + name := ( part | quoted )+ + subject := ( part | quoted | SLASH )* + quoted := STRING / ESC + part := LBRACE / RBRACE / COLON / COMMA / NUMBER / ID / SYM + options := map + map := "{" ( keyval ( "," keyval )* )? "}" + keyval "= ID ":" value + value := NUMBER / STRING / ID / map / list + list := "[" ( value ( "," value )* )? "]" + ]]></programlisting> + + + <formalpara> + <title>Address String Options</title> + <para>The address string options map supports the following parameters:</para> + </formalpara> + + <programlisting><![CDATA[ + <name> [ / <subject> ] ; { + create: always | sender | receiver | never, + delete: always | sender | receiver | never, + assert: always | sender | receiver | never, + mode: browse | consume, + node: { + type: queue | topic, + durable: True | False, + x-declare: { ... <declare-overrides> ... }, + x-bindings: [<binding_1>, ... <binding_n>] + }, + link: { + name: <link-name>, + durable: True | False, + reliability: unreliable | at-most-once | at-least-once | exactly-once, + x-declare: { ... <declare-overrides> ... }, + x-bindings: [<binding_1>, ... <binding_n>], + x-subscribe: { ... <subscribe-overrides> ... } + } + } + ]]></programlisting> + + + <itemizedlist> + <title>Create, Delete, and Assert Policies</title> + <para>The create, delete, and assert policies specify who should + perfom the associated action:</para> + <listitem><para><emphasis>always</emphasis>: the action is performed by any messaging client</para></listitem> + <listitem><para><emphasis>sender</emphasis>: the action is only performed by a sender</para></listitem> + <listitem><para><emphasis>receiver</emphasis>: the action is only performed by a receiver</para></listitem> + <listitem><para><emphasis>never</emphasis>: the action is never performed (this is the default)</para></listitem> + </itemizedlist> + + <itemizedlist> + <title>Node-Type</title> + <para>The node-type is one of:</para> + <listitem><para><emphasis>topic</emphasis>: in the AMQP 0-10 + mapping, a topic node defaults to the topic exchange, x-declare + may be used to specify other exchange types</para></listitem> + <listitem><para><emphasis>queue</emphasis>: this is the default node-type</para></listitem> + </itemizedlist> + </section> + + + </section> + + <section id="replay"> + <title>Sender Capacity and Replay</title> + + <para>The send method of a sender has an optional second parameter + that controls whether the send call is synchronous or not. A + synchronous send call will block until the broker has confirmed + receipt of the message. An asynchronous send call will return + before the broker confirms receipt of the message, allowing for + example further send calls to be made without waiting for a + roundtrip to the broker for each message. This is desirable where + increased throughput is important.</para> + + <para>The sender maintains a list of sent messages whose receipt + has yet to be confirmed by the broker. The maximum number of such + messages that it will hold is defined by the capacity of the + sender, which can be set by the application. If an application + tries to send with a sender whose capacity is already fully used + up, the send call will block waiting for capacity regardless of + the value of the sync flag.</para> + + <para>The sender can be queried for the available space (i.e. the + unused capacity), and for the current count of unsettled messages + (i.e. those held in the replay list pending confirmation by the + server). When the unsettled count is zero, all messages on that + sender have been successfully sent.</para> + + <para>If the connection fails and is transparently reconnected + (see <xref linkend="connection-options"/> for details on how to control + this feature), the unsettled messages for each sender over that + connection will be re-transmitted. This provides a transparent + level of reliability. This feature can be controlled through the + link's reliability as defined in the address (see + <xref linkend="table-link-properties"/>). At present only + at-least-once guarantees are offered. </para> + </section> + + <section id="prefetch"> + <title>Receiver Capacity (Prefetch)</title> + + <para>By default, a receiver requests the next message from the + server in response to each fetch call, resulting in messages being + sent to the receiver one at a time. As in the case of sending, it + is often desirable to avoid this roundtrip for each message. This + can be achieved by allowing the receiver + to <firstterm>prefetch</firstterm> messages in anticipation of + fetch calls being made. The receiver needs to be able to store + these prefetched messages, the number it can hold is controlled by + the receivers capacity.</para> + + </section> + + <section id="acknowledgements"> + <title>Acknowledging Received Messages</title> + + <para>Applications that receive messages should acknowledge their + receipt by calling the session's acknowledge method. As in the + case of sending messages, acknowledged transfer of messages to + receivers provides at-least-once reliability, which means that the + loss of the connection or a client crash does not result in lost + messages; durable messages are not lost even if the broker is + restarted. + + Some cases may not require this however and the reliability can be + controlled through a link property in the address options (see + <xref linkend="table-link-properties"/>).</para> + + <para>The acknowledge call acknowledges all messages received on + the session (i.e. all message that have been returned from a fetch + call on a receiver created on that session).</para> + + <para>The acknowledge call also support an optional parameter + controlling whether the call is synchronous or not. A synchronous + acknowledge will block until the server has confirmed that it has + received the acknowledgement. In the asynchronous case, when the + call returns there is not yet any guarantee that the server has + received and processed the acknowledgement. The session may be + queried for the number of unsettled acknowledgements; when that + count is zero all acknowledgements made for received messages have + been successful.</para> + + </section> + + + <section> + <title>Receiving Messages from Multiple Sources</title> + + <para>A receiver can only read from one source, but many + programs need to be able to read messages from many sources. In + the Qpid Messaging API, a program can ask a session for + the <quote>next receiver</quote>; that is, the receiver that is + responsible for the next available message. The following + examples show how this is done in C++, Python, and .NET C#. + </para> + + <para>Note that to use this pattern you must enable prefetching + for each receiver of interest so that the broker will send + messages before a fetch call is made. See + <xref linkend="prefetch"/> for more on this.</para> + + <example> + <title>Receiving Messages from Multiple Sources</title> + + <para>C++:</para> + + <programlisting><![CDATA[ + Receiver receiver1 = session.createReceiver(address1); + receiver1.setCapacity(10); + Receiver receiver2 = session.createReceiver(address2); + receiver2.setCapacity(10); + + Message message = session.nextReceiver().fetch(); + std::cout << message.getContent() << std::endl; + session.acknowledge(); // acknowledge message receipt + ]]> </programlisting> + + <para>Python:</para> + <programlisting><![CDATA[ + receiver1 = session.receiver(address1) + receiver1.capacity = 10 + receiver2 = session.receiver(address) + receiver2.capacity = 10 + message = session.next_receiver().fetch() + print message.content + session.acknowledge() + ]]> </programlisting> + + <para>.NET C#:</para> + <programlisting><![CDATA[ + Receiver receiver1 = session.CreateReceiver(address1); + receiver1.Capacity = 10; + Receiver receiver2 = session.CreateReceiver(address2); + receiver2.Capacity = 10; + + Message message = new Message(); + message = session.NextReceiver().Fetch(); + Console.WriteLine("{0}", message.GetContent()); + session.Acknowledge(); + ]]> </programlisting> + + </example> + </section> + + <section> + <title>Transactions</title> + + <para>Sometimes it is useful to be able to group messages + transfers - sent and/or received - on a session into atomic + grouping. This can be done be creating the session as + transactional. On a transactional session sent messages only + become available at the target address on commit. Likewise any + received and acknowledged messages are only discarded at their + source on commit + + <footnote><para>Note that this currently is only true for + messages received using a reliable mode + e.g. at-least-once. Messages sent by a broker to a receiver in + unreliable receiver will be discarded immediately regardless of + transctionality.</para></footnote> + + .</para> + + <example> + <title>Transactions</title> + <para>C++:</para> + <programlisting><![CDATA[ + Connection connection(broker); + Session session = connection.createTransactionalSession(); + ... + if (smellsOk()) + session.commit(); + else + session.rollback(); + ]]></programlisting> + <para> + .NET C#: + </para> + + <programlisting> + Connection connection = new Connection(broker); + Session session = connection.CreateTransactionalSession(); + ... + if (smellsOk()) + session.Commit(); + else + session.Rollback(); + </programlisting> + <!-- + <para>Python</para> + <programlisting><![CDATA[ + ### TODO + ]]></programlisting> + --> + </example> + + </section> + + <section id="connections"> + <title>Connections</title> + + <para> + Messaging connections are created by specifying a broker or a list of brokers, and + an optional set of connection options. The constructor prototypes for Connections + are: + </para> + + <programlisting><![CDATA[ + Connection connection(); + Connection connection(const string url); + Connection connection(const string url, const string& options); + Connection connection(const string url, const Variant::Map& options); + ]]></programlisting> + + <para> + Messaging connection URLs specify only the network host address(es). Connection + options are specified separately as an options string or map. This is different + from JMS Connection URLs that combine the network address and connection properties + in a single string. + </para> + + <section id="connection-url"> + <title>Connection URLs</title> + <para> + Connection URLs describe the broker or set of brokers to which the connection + is to attach. The format of the Connection URL is defined by AMQP 0.10 + Domain:connection.amqp-host-url. + </para> + + <programlisting><![CDATA[ + amqp_url = "amqp:" prot_addr_list + prot_addr_list = [prot_addr ","]* prot_addr + prot_addr = tcp_prot_addr | tls_prot_addr + + tcp_prot_addr = tcp_id tcp_addr + tcp_id = "tcp:" | "" + tcp_addr = [host [":" port] ] + host = <as per http://www.ietf.org/rfc/rfc3986.txt> + port = number ]]></programlisting> + + <para> + Examples of Messaging Connection URLs + </para> + + <programlisting><![CDATA[ + localhost + localhost:5672 + localhost:9999 + 192.168.1.2:5672 + mybroker.example.com:5672 + amqp:tcp:localhost:5672 + tcp:locahost:5672,localhost:5800 + ]]></programlisting> + + </section> + + <section id="connection-options"> + <title>Connection Options</title> + + <para> + Aspects of the connections behaviour can be controlled through + specifying connection options. For example, connections can be + configured to automatically reconnect if the connection to a + broker is lost. + </para> + + <example> + <title>Specifying Connection Options in C++, Python, and .NET</title> + + <para>In C++, these options can be set using <function>Connection::setOption()</function> or by passing in a set of options to the constructor. The options can be passed in as a map or in string form:</para> + + + <para>or</para> + + <programlisting><![CDATA[ + Connection connection("localhost:5672"); + connection.setOption("reconnect", true); + try { + connection.open(); + !!! SNIP !!! + ]]></programlisting> + + <para>In Python, these options can be set as attributes of the connection or using named arguments in + the <function>Connection</function> constructor:</para> + + <programlisting><![CDATA[ + connection = Connection("localhost:5672", reconnect=True) + try: + connection.open() + !!! SNIP !!! + ]]></programlisting> + + <para>or</para> + + <programlisting><![CDATA[ + connection = Connection("localhost:5672") + connection.reconnect = True + try: + connection.open() + !!! SNIP !!! + ]]></programlisting> + <para> + In .NET, these options can be set using <function>Connection.SetOption()</function> or by passing in a set of options to the constructor. The options can be passed in as a map or in string form: + </para> + + <programlisting> + Connection connection= new Connection("localhost:5672", "{reconnect: true}"); + try { + connection.Open(); + !!! SNIP !!! + </programlisting> + <para> + or + </para> + + <programlisting> + Connection connection = new Connection("localhost:5672"); + connection.SetOption("reconnect", true); + try { + connection.Open(); + !!! SNIP !!! + </programlisting> + + <para>See the reference documentation for details in each language.</para> + </example> + + <para>The following table lists the supported connection options.</para> + + <table pgwide="1"> + <title>Connection Options</title> + <tgroup cols="3"> + <thead> + <colspec colnum="1" colwidth="1*"/> + <colspec colnum="2" colwidth="1*"/> + <colspec colnum="3" colwidth="3*"/> + <row> + <entry>option name</entry> + <entry>value type</entry> + <entry>semantics</entry> + </row> + </thead> + <tbody> + + <row> + <entry> + <literal>username</literal> + </entry> + <entry> + string + </entry> + <entry> + The username to use when authenticating to the broker. + </entry> + </row> + <row> + <entry> + <literal>password</literal> + </entry> + <entry> + string + </entry> + <entry> + The password to use when authenticating to the broker. + </entry> + </row> + <row> + <entry> + <literal>sasl_mechanisms</literal> + </entry> + <entry> + string + </entry> + <entry> + The specific SASL mechanisms to use with the python + client when authenticating to the broker. The value + is a space separated list. + </entry> + </row> + + + <row> + <entry> + <literal>reconnect</literal> + </entry> + <entry> + boolean + </entry> + <entry> + Transparently reconnect if the connection is lost. + </entry> + </row> + <row> + <entry> + <literal>reconnect_timeout</literal> + </entry> + <entry> + integer + </entry> + <entry> + Total number of seconds to continue reconnection attempts before giving up and raising an exception. + </entry> + </row> + <row> + <entry> + <literal>reconnect_limit</literal> + </entry> + <entry> + integer + </entry> + <entry> + Maximum number of reconnection attempts before giving up and raising an exception. + </entry> + </row> + <row> + <entry> + <literal>reconnect_interval_min</literal> + </entry> + <entry> + integer representing time in seconds + </entry> + <entry> + Minimum number of seconds between reconnection attempts. The first reconnection attempt is made immediately; if that fails, the first reconnection delay is set to the value of <literal>reconnect_interval_min</literal>; if that attempt fails, the reconnect interval increases exponentially until a reconnection attempt succeeds or <literal>reconnect_interval_max</literal> is reached. + </entry> + </row> + <row> + <entry> + <literal>reconnect_interval_max</literal> + </entry> + <entry> + integer representing time in seconds + </entry> + <entry> + Maximum reconnect interval. + </entry> + </row> + <row> + <entry> + <literal>reconnect_interval</literal> + </entry> + <entry> + integer representing time in seconds + </entry> + <entry> + Sets both <literal>reconnection_interval_min</literal> and <literal>reconnection_interval_max</literal> to the same value. + </entry> + </row> + + <row> + <entry> + <literal>heartbeat</literal> + </entry> + <entry> + integer representing time in seconds + </entry> + <entry> + Requests that heartbeats be sent every N seconds. If two + successive heartbeats are missed the connection is + considered to be lost. + </entry> + </row> + <row> + <entry> + <literal>transport</literal> + </entry> + <entry> + string + </entry> + <entry> + Sets the underlying transport protocol used. The default option is 'tcp'. To enable ssl, set to 'ssl'. The C++ client additionally supports 'rdma'. + </entry> + </row> + <row> + <entry> + <literal>tcp-nodelay</literal> + </entry> + <entry> + boolean + </entry> + <entry> + Set tcp no-delay, i.e. disable Nagle algorithm. [C++ only] + </entry> + </row> + <row> + <entry> + <literal>protocol</literal> + </entry> + <entry> + string + </entry> + <entry> + Sets the application protocol used. The default option is 'amqp0-10'. To enable AMQP 1.0, set to 'amqp1.0'. + </entry> + </row> + </tbody> + </tgroup> + </table> + + </section> + </section> + + <section id="section-Maps"> + <title>Maps and Lists in Message Content</title> + + <para>Many messaging applications need to exchange data across + languages and platforms, using the native datatypes of each + programming language.</para> + + <para>The Qpid Messaging API supports <classname>map</classname> and <classname>list</classname> in message content. + + <footnote><para>Unlike JMS, there is not a specific message type for + map messages.</para></footnote> + + <footnote> + <para> + Note that the Qpid JMS client supports MapMessages whose values can be nested maps or lists. This is not standard JMS behaviour. + </para> + </footnote> + Specific language support for <classname>map</classname> and <classname>list</classname> objects are shown in the following table. + </para> + <table id="tabl-Programming_in_Apache_Qpid-Qpid_Maps_in_Message_Content"> + <title>Map and List Representation in Supported Languages</title> + <tgroup cols="3"> + <thead> + <row> + <entry>Language</entry> + <entry>map</entry> + <entry>list</entry> + </row> + </thead> + <tbody> + <row> + <entry>Python</entry> + <entry><classname>dict</classname></entry> + <entry><classname>list</classname></entry> + </row> + <row> + <entry>C++</entry> + <entry><classname>Variant::Map</classname></entry> + <entry><classname>Variant::List</classname></entry> + </row> + <row> + <entry>Java</entry> + <entry><classname>MapMessage</classname></entry> + <entry><classname> </classname></entry> + </row> + <row> + <entry>.NET</entry> + <entry><classname>Dictionary<string, object></classname></entry> + <entry><classname>Collection<object></classname></entry> + </row> + </tbody> + </tgroup> + </table> + <para> + In all languages, messages are encoded using AMQP's portable datatypes. + </para> + + <tip> + <para>Because of the differences in type systems among + languages, the simplest way to provide portable messages is to + rely on maps, lists, strings, 64 bit signed integers, and + doubles for messages that need to be exchanged across languages + and platforms.</para> + </tip> + + <section id="section-Python-Maps"> + <title>Qpid Maps and Lists in Python</title> + + <para>In Python, Qpid supports the <classname>dict</classname> and <classname>list</classname> types directly in message content. The following code shows how to send these structures in a message:</para> + + <example> + <title>Sending Qpid Maps and Lists in Python</title> + <programlisting><![CDATA[ + from qpid.messaging import * + # !!! SNIP !!! + + content = {'Id' : 987654321, 'name' : 'Widget', 'percent' : 0.99} + content['colours'] = ['red', 'green', 'white'] + content['dimensions'] = {'length' : 10.2, 'width' : 5.1,'depth' : 2.0}; + content['parts'] = [ [1,2,5], [8,2,5] ] + content['specs'] = {'colors' : content['colours'], + 'dimensions' : content['dimensions'], + 'parts' : content['parts'] } + message = Message(content=content) + sender.send(message) + ]]> </programlisting> + </example> + + + <para>The following table shows the datatypes that can be sent in a Python map message, + and the corresponding datatypes that will be received by clients in Java or C++.</para> + + + <table id="table-Python-Maps" > + <title>Python Datatypes in Maps</title> + <tgroup cols="3"> + <thead> + <row> + <entry>Python Datatype</entry> + <entry>→ C++</entry> + <entry>→ Java</entry> + </row> + </thead> + <tbody> + <row><entry>bool</entry><entry>bool</entry><entry>boolean</entry></row> + <row><entry>int</entry><entry>int64</entry><entry>long</entry></row> + <row><entry>long</entry><entry>int64</entry><entry>long</entry></row> + <row><entry>float</entry><entry>double</entry><entry>double</entry></row> + <row><entry>unicode</entry><entry>string</entry><entry>java.lang.String</entry></row> + <row><entry>uuid</entry><entry>qpid::types::Uuid</entry><entry>java.util.UUID</entry></row> + <row><entry>dict</entry><entry>Variant::Map</entry><entry>java.util.Map</entry></row> + <row><entry>list</entry><entry>Variant::List</entry><entry>java.util.List</entry></row> + </tbody> + </tgroup> + </table> + + </section> + + + + + <section id="section-cpp-Maps"> + <title>Qpid Maps and Lists in C++</title> + + + <para>In C++, Qpid defines the the + <classname>Variant::Map</classname> and + <classname>Variant::List</classname> types, which can be + encoded into message content. The following code shows how to + send these structures in a message:</para> + + <example> + <title>Sending Qpid Maps and Lists in C++</title> + <programlisting><![CDATA[ + using namespace qpid::types; + + // !!! SNIP !!! + + Message message; + Variant::Map content; + content["id"] = 987654321; + content["name"] = "Widget"; + content["percent"] = 0.99; + Variant::List colours; + colours.push_back(Variant("red")); + colours.push_back(Variant("green")); + colours.push_back(Variant("white")); + content["colours"] = colours; + + Variant::Map dimensions; + dimensions["length"] = 10.2; + dimensions["width"] = 5.1; + dimensions["depth"] = 2.0; + content["dimensions"]= dimensions; + + Variant::List part1; + part1.push_back(Variant(1)); + part1.push_back(Variant(2)); + part1.push_back(Variant(5)); + + Variant::List part2; + part2.push_back(Variant(8)); + part2.push_back(Variant(2)); + part2.push_back(Variant(5)); + + Variant::List parts; + parts.push_back(part1); + parts.push_back(part2); + content["parts"]= parts; + + Variant::Map specs; + specs["colours"] = colours; + specs["dimensions"] = dimensions; + specs["parts"] = parts; + content["specs"] = specs; + + encode(content, message); + sender.send(message, true); + ]]> </programlisting> + </example> + + <para>The following table shows the datatypes that can be sent + in a C++ map message, and the corresponding datatypes that + will be received by clients in Java and Python.</para> + + <table id="table-cpp-Maps"> + <title>C++ Datatypes in Maps</title> + <tgroup cols="3"> + <thead> + <row> + <entry>C++ Datatype</entry> + <entry>→ Python</entry> + <entry>→ Java</entry> + </row> + </thead> + <tbody> + <row><entry>bool</entry><entry>bool</entry><entry>boolean</entry></row> + <row><entry>uint16</entry><entry>int | long</entry><entry>short</entry></row> + <row><entry>uint32</entry><entry>int | long</entry><entry>int</entry></row> + <row><entry>uint64</entry><entry>int | long</entry><entry>long</entry></row> + <row><entry>int16</entry><entry>int | long</entry><entry>short</entry></row> + <row><entry>int32</entry><entry>int | long</entry><entry>int</entry></row> + <row><entry>int64</entry><entry>int | long</entry><entry>long</entry></row> + <row><entry>float</entry><entry>float</entry><entry>float</entry></row> + <row><entry>double</entry><entry>float</entry><entry>double</entry></row> + <row><entry>string</entry><entry>unicode</entry><entry>java.lang.String</entry></row> + <row><entry>qpid::types::Uuid</entry><entry>uuid</entry><entry>java.util.UUID</entry></row> + <row><entry>Variant::Map</entry><entry>dict</entry><entry>java.util.Map</entry></row> + <row><entry>Variant::List</entry><entry>list</entry><entry>java.util.List</entry></row> + </tbody> + </tgroup> + </table> + </section> + + <section id="section-dotnet-Maps"> + <title>Qpid Maps and Lists in .NET</title> + + + <para> + The .NET binding for the Qpid Messaging API binds .NET managed data types + to C++ <classname>Variant</classname> data types. The following code shows how to + send Map and List structures in a message: + </para> + + <example> + <?dbfo keep-together="auto" ?> + <title>Sending Qpid Maps and Lists in .NET C#</title> + <programlisting><![CDATA[ + using System; + using Org.Apache.Qpid.Messaging; + + // !!! SNIP !!! + + Dictionary<string, object> content = new Dictionary<string, object>(); + Dictionary<string, object> subMap = new Dictionary<string, object>(); + Collection<object> colors = new Collection<object>(); + + // add simple types + content["id"] = 987654321; + content["name"] = "Widget"; + content["percent"] = 0.99; + + // add nested amqp/map + subMap["name"] = "Smith"; + subMap["number"] = 354; + content["nestedMap"] = subMap; + + // add an amqp/list + colors.Add("red"); + colors.Add("green"); + colors.Add("white"); + content["colorsList"] = colors; + + // add one of each supported amqp data type + bool mybool = true; + content["mybool"] = mybool; + + byte mybyte = 4; + content["mybyte"] = mybyte; + + UInt16 myUInt16 = 5; + content["myUInt16"] = myUInt16; + + UInt32 myUInt32 = 6; + content["myUInt32"] = myUInt32; + + UInt64 myUInt64 = 7; + content["myUInt64"] = myUInt64; + + char mychar = 'h'; + content["mychar"] = mychar; + + Int16 myInt16 = 9; + content["myInt16"] = myInt16; + + Int32 myInt32 = 10; + content["myInt32"] = myInt32; + + Int64 myInt64 = 11; + content["myInt64"] = myInt64; + + Single mySingle = (Single)12.12; + content["mySingle"] = mySingle; + + Double myDouble = 13.13; + content["myDouble"] = myDouble; + + Guid myGuid = new Guid("000102030405060708090a0b0c0d0e0f"); + content["myGuid"] = myGuid; + + Message message = new Message(content); + Send(message, true); + ]]> </programlisting> + </example> + + <para> + The following table shows the mapping between datatypes in .NET and C++. + </para> + + <table id="table-dotnet-Maps"> + <title>Datatype Mapping between C++ and .NET binding</title> + <tgroup cols="2"> + <thead> + <row> + <entry>C++ Datatype</entry> + <entry>→ .NET binding</entry> + </row> + </thead> + <tbody> + <row><entry>void</entry><entry>nullptr</entry></row> + <row><entry>bool</entry><entry>bool</entry></row> + <row><entry>uint8</entry><entry>byte</entry></row> + <row><entry>uint16</entry><entry>UInt16</entry></row> + <row><entry>uint32</entry><entry>UInt32</entry></row> + <row><entry>uint64</entry><entry>UInt64</entry></row> + <row><entry>uint8</entry><entry>char</entry></row> + <row><entry>int16</entry><entry>Int16</entry></row> + <row><entry>int32</entry><entry>Int32</entry></row> + <row><entry>int64</entry><entry>Int64</entry></row> + <row><entry>float</entry><entry>Single</entry></row> + <row><entry>double</entry><entry>Double</entry></row> + <row><entry>string</entry><entry>string + <footnote id="callout-dotnet-string"> + <para>Strings are currently interpreted only with UTF-8 encoding.</para> + </footnote></entry></row> + <row><entry>qpid::types::Uuid</entry><entry>Guid</entry></row> + <row><entry>Variant::Map</entry><entry><![CDATA[Dictionary<string, object>]]> + <footnoteref linkend="callout-dotnet-string"/></entry></row> + <row><entry>Variant::List</entry><entry><![CDATA[Collection<object>]]> + <footnoteref linkend="callout-dotnet-string"/></entry></row> + </tbody> + </tgroup> + </table> + + + </section> + + + </section> + + <section> + <title>The Request / Response Pattern</title> + <para>Request / Response applications use the reply-to property, + described in <xref + linkend="table-amqp0-10-message-properties"/>, to allow a server + to respond to the client that sent a message. A server sets up a + service queue, with a name known to clients. A client creates a + private queue for the server's response, creates a message for a + request, sets the request's reply-to property to the address of + the client's response queue, and sends the request to the + service queue. The server sends the response to the address + specified in the request's reply-to property. + </para> + <example> + <title>Request / Response Applications in C++</title> + + <para>This example shows the C++ code for a client and server + that use the request / response pattern.</para> + + <para>The server creates a service queue and waits for a + message to arrive. If it receives a message, it sends a + message back to the sender.</para> + + <programlisting><![CDATA[Receiver receiver = session.createReceiver("service_queue; {create: always}"); + + Message request = receiver.fetch(); + const Address& address = request.getReplyTo(); // Get "reply-to" from request ... + if (address) { + Sender sender = session.createSender(address); // ... send response to "reply-to" + Message response("pong!"); + sender.send(response); + session.acknowledge(); + } + ]]></programlisting> + + <para>The client creates a sender for the service queue, and + also creates a response queue that is deleted when the + client closes the receiver for the response queue. In the C++ + client, if the address starts with the character + <literal>#</literal>, it is given a unique name.</para> + + <programlisting><![CDATA[ + Sender sender = session.createSender("service_queue"); + + Address responseQueue("#response-queue; {create:always, delete:always}"); + Receiver receiver = session.createReceiver(responseQueue); + + Message request; + request.setReplyTo(responseQueue); + request.setContent("ping"); + sender.send(request); + Message response = receiver.fetch(); + std::cout << request.getContent() << " -> " << response.getContent() << std::endl; + ]]> </programlisting> + + <para>The client sends the string <literal>ping</literal> to + the server. The server sends the response + <literal>pong</literal> back to the same client, using the + <varname>replyTo</varname> property.</para> + + </example> + <!-- + <example> + <title>Request / Response Applications in Python</title> + <programlisting>### TODO</programlisting> + </example> + --> + </section> + + + <section> + <title>Performance Tips</title> + + <itemizedlist> + <listitem> + <para>Consider prefetching messages for receivers (see + <xref linkend="prefetch"/>). This helps eliminate roundtrips + and increases throughput. Prefetch is disabled by default, + and enabling it is the most effective means of improving + throughput of received messages.</para> + </listitem> + <listitem> + <para>Send messages asynchronously. Again, this helps + eliminate roundtrips and increases throughput. The C++ and + .NET clients send asynchronously by default, however the + python client defaults to synchronous sends. </para> + </listitem> + <listitem> + <para>Acknowledge messages in batches (see + <xref linkend="acknowledgements"/>). Rather than + acknowledging each message individually, consider issuing + acknowledgements after n messages and/or after a particular + duration has elapsed.</para> + </listitem> + <listitem> + <para>Tune the sender capacity (see + <xref linkend="replay"/>). If the capacity is too low the + sender may block waiting for the broker to confirm receipt + of messages, before it can free up more capacity.</para> + </listitem> + <listitem> + <para>If you are setting a reply-to address on messages + being sent by the c++ client, make sure the address type is + set to either queue or topic as appropriate. This avoids the + client having to determine which type of node is being + refered to, which is required when hanling reply-to in AMQP + 0-10. </para> + </listitem> + <listitem> + <para>For latency sensitive applications, setting tcp-nodelay + on qpidd and on client connections can help reduce the + latency.</para> + </listitem> + </itemizedlist> + </section> + + <section> + <title>Cluster Failover</title> + + <para>The messaging broker can be run in clustering mode, which provides high reliability through replicating state between brokers in the cluster. If one broker in a cluster fails, clients can choose another broker in the cluster and continue their work. Each broker in the cluster also advertises the addresses of all known brokers + + <footnote><para>This is done via the amq.failover exchange in AMQP 0-10</para></footnote> + + . A client can use this information to dynamically keep the list of reconnection urls up to date.</para> + + <para>In C++, the <classname>FailoverUpdates</classname> class provides this functionality:</para> + + <example> + <title>Tracking cluster membership</title> + + <para>In C++:</para> + + <programlisting><![CDATA[ + #include <qpid/messaging/FailoverUpdates.h> + ... + Connection connection("localhost:5672"); + connection.setOption("reconnect", true); + try { + connection.open(); + std::auto_ptr<FailoverUpdates> updates(new FailoverUpdates(connection)); + ]]> + </programlisting> + + <para>In python:</para> + + <programlisting><![CDATA[ + import qpid.messaging.util + ... + connection = Connection("localhost:5672") + connection.reconnect = True + try: + connection.open() + auto_fetch_reconnect_urls(connection) + ]]> + </programlisting> + <para> + In .NET C#: + </para> + + <programlisting> + using Org.Apache.Qpid.Messaging; + ... + connection = new Connection("localhost:5672"); + connection.SetOption("reconnect", true); + try { + connection.Open(); + FailoverUpdates failover = new FailoverUpdates(connection); + + </programlisting> + + + </example> + </section> + + + + <section> + <title>Logging</title> + + <para>To simplify debugging, Qpid provides a logging facility + that prints out messaging events.</para> + + <section> + <title>Logging in C++</title> + <para> + The Qpidd broker and C++ clients can both use environment variables to enable logging. Linux and Windows systems use the same named environment variables and values. + </para> + <para>Use QPID_LOG_ENABLE to set the level of logging you are interested in (trace, debug, info, notice, warning, error, or critical): + </para> + + <screen> + export QPID_LOG_ENABLE="warning+" + </screen> + <para> + The Qpidd broker and C++ clients use QPID_LOG_OUTPUT to determine where logging output should be sent. This is either a file name or the special values stderr, stdout, or syslog: + </para> + + <screen> + export QPID_LOG_TO_FILE="/tmp/myclient.out" + </screen> + + <para> + From a Windows command prompt, use the following command format to set the environment variables: + </para> + + <screen> + set QPID_LOG_ENABLE=warning+ + set QPID_LOG_TO_FILE=D:\tmp\myclient.out + </screen> + </section> + + <section> + <title>Logging in Python</title> + <para> + The Python client library supports logging using the standard Python logging module. The easiest way to do logging is to use the <command>basicConfig()</command>, which reports all warnings and errors: + </para> + + <programlisting>from logging import basicConfig + basicConfig() + </programlisting> + <para> + Qpidd also provides a convenience method that makes it easy to specify the level of logging desired. For instance, the following code enables logging at the <command>DEBUG</command> level: + </para> + + <programlisting>from qpid.log import enable, DEBUG + enable("qpid.messaging.io", DEBUG) + </programlisting> + <para> + For more information on Python logging, see <ulink url="http://docs.python.org/lib/node425.html">http://docs.python.org/lib/node425.html</ulink>. For more information on Qpid logging, use <command>$ pydoc qpid.log</command>. + </para> + </section> + </section> + + + + <section id="section-amqp0-10-mapping"> + <title>The AMQP 0-10 mapping</title> + + <para> + This section describes the AMQP 0-10 mapping for the Qpid + Messaging API. + </para> + <para> + The interaction with the broker triggered by creating a sender + or receiver depends on what the specified address resolves + to. Where the node type is not specified in the address, the + client queries the broker to determine whether it refers to a + queue or an exchange. + </para> + <para> + When sending to a queue, the queue's name is set as the + routing key and the message is transfered to the default (or + nameless) exchange. When sending to an exchange, the message + is transfered to that exchange and the routing key is set to + the message subject if one is specified. A default subject may + be specified in the target address. The subject may also be + set on each message individually to override the default if + required. In each case any specified subject is also added as + a qpid.subject entry in the application-headers field of the + message-properties. + </para> + <para> + When receiving from a queue, any subject in the source address + is currently ignored. The client sends a message-subscribe + request for the queue in question. The accept-mode is + determined by the reliability option in the link properties; + for unreliable links the accept-mode is none, for reliable + links it is explicit. The default for a queue is reliable. The + acquire-mode is determined by the value of the mode option. If + the mode is set to browse the acquire mode is not-acquired, + otherwise it is set to pre-acquired. The exclusive and + arguments fields in the message-subscribe command can be + controlled using the x-subscribe map. + </para> + <para> + When receiving from an exchange, the client creates a + subscription queue and binds that to the exchange. The + subscription queue's arguments can be specified using the + x-declare map within the link properties. The reliability + option determines most of the other parameters. If the + reliability is set to unreliable then an auto-deleted, + exclusive queue is used meaning that if the client or + connection fails messages may be lost. For exactly-once the + queue is not set to be auto-deleted. The durability of the + subscription queue is determined by the durable option in the + link properties. The binding process depends on the type of + the exchange the source address resolves to. + </para> + + <itemizedlist> + <listitem> + <para> + For a topic exchange, if no subject is specified and no + x-bindings are defined for the link, the subscription + queue is bound using a wildcard matching any routing key + (thus satisfying the expectation that any message sent to + that address will be received from it). If a subject is + specified in the source address however, it is used for + the binding key (this means that the subject in the source + address may be a binding pattern including wildcards). + </para> + </listitem> + <listitem> + <para> + For a fanout exchange the binding key is irrelevant to + matching. A receiver created from a source address that + resolves to a fanout exchange receives all messages
[... 4017 lines stripped ...] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
