http://git-wip-us.apache.org/repos/asf/activemq-6/blob/8ecd255f/docs/user-manual/en/rest.xml ---------------------------------------------------------------------- diff --git a/docs/user-manual/en/rest.xml b/docs/user-manual/en/rest.xml new file mode 100644 index 0000000..ae5c416 --- /dev/null +++ b/docs/user-manual/en/rest.xml @@ -0,0 +1,2151 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- ============================================================================= --> +<!-- Copyright © 2009 Red Hat, Inc. and others. --> +<!-- --> +<!-- The text of and illustrations in this document are licensed by Red Hat under --> +<!-- a Creative Commons AttributionâShare Alike 3.0 Unported license ("CC-BY-SA"). --> +<!-- --> +<!-- An explanation of CC-BY-SA is available at --> +<!-- --> +<!-- http://creativecommons.org/licenses/by-sa/3.0/. --> +<!-- --> +<!-- In accordance with CC-BY-SA, if you distribute this document or an adaptation --> +<!-- of it, you must provide the URL for the original version. --> +<!-- --> +<!-- Red Hat, as the licensor of this document, waives the right to enforce, --> +<!-- and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent --> +<!-- permitted by applicable law. --> +<!-- ============================================================================= --> +<chapter id="rest"> + <title>REST Interface</title> + + <para>The HornetQ REST interface allows you to leverage the reliability + and scalability features of HornetQ over a simple REST/HTTP interface. + Messages are produced and consumed by sending and receiving simple HTTP + messages that contain the content you want to push around. For instance, + here's a simple example of posting an order to an order processing queue + express as an HTTP message: + </para> + + <programlisting> +POST /queue/orders/create HTTP/1.1 +Host: example.com +Content-Type: application/xml + +<order> + <name>Bill</name> + <item>iPhone 4</item> + <cost>$199.99</cost> +</order></programlisting> + + <para>As you can see, we're just posting some arbitrary XML + document to a URL. When the XML is received on the server is it processed + within HornetQ as a JMS message and distributed through core HornetQ. + Simple and easy. Consuming messages from a queue or topic looks very + similar. We'll discuss the entire interface in detail later in this + docbook. + </para> + + <section> + <title>Goals of REST Interface</title> + + <para>Why would you want to use HornetQ's REST interface? What are the + goals of the REST interface? + </para> + + <itemizedlist> + <listitem> + <para>Easily usable by machine-based (code) clients.</para> + </listitem> + + <listitem> + <para>Zero client footprint. We want HornetQ to be usable by any + client/programming language that has an adequate HTTP client + library. You shouldn't have to download, install, and configure a + special library to interact with HornetQ. + </para> + </listitem> + + <listitem> + <para>Lightweight interoperability. The HTTP protocol is strong + enough to be our message exchange protocol. Since interactions are + RESTful the HTTP uniform interface provides all the interoperability + you need to communicate between different languages, platforms, and + even messaging implementations that choose to implement the same + RESTful interface as HornetQ (i.e. the + <ulink url="http://rest-star.org">REST-*</ulink> effort.) + </para> + </listitem> + + <listitem> + <para>No envelope (e.g. SOAP) or feed (e.g. Atom) format + requirements. You shouldn't have to learn, use, or parse a specific + XML document format in order to send and receive messages through + HornetQ's REST interface. + </para> + </listitem> + + <listitem> + <para>Leverage the reliability, scalability, and clustering features + of HornetQ on the back end without sacrificing the simplicity of a + REST interface. + </para> + </listitem> + </itemizedlist> + </section> + + + <section id="install"> + <title>Installation and Configuration</title> + + <para>HornetQ's REST interface is installed as a Web archive (WAR). It + depends on the + <ulink url="http://jboss.org/resteasy">RESTEasy</ulink> + project and can currently only run within a servlet container. Installing + the HornetQ REST interface is a little bit different depending whether + HornetQ is already installed and configured for your environment (e.g. + you're deploying within JBoss AS 7) or you want the HornetQ REST + WAR to startup and manage the HornetQ server (e.g. you're deploying + within something like Apache Tomcat). + </para> + + <section> + <title>Installing Within Pre-configured Environment</title> + + <para>This section should be used when you want to use the HornetQ REST + interface in an environment that already has HornetQ installed and + running, e.g. JBoss AS 7. You must create a Web archive + (.WAR) file with the following web.xml settings: + </para> + + <programlisting> +<web-app> + <listener> + <listener-class> + org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap + </listener-class> + </listener> + + <listener> + <listener-class> + org.hornetq.rest.integration.RestMessagingBootstrapListener + </listener-class> + </listener> + + <filter> + <filter-name>Rest-Messaging</filter-name> + <filter-class> + org.jboss.resteasy.plugins.server.servlet.FilterDispatcher + </filter-class> + </filter> + + <filter-mapping> + <filter-name>Rest-Messaging</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> +</web-app></programlisting> + + <para>Within your WEB-INF/lib directory you must have the + hornetq-rest.jar file. If RESTEasy is not installed within your + environment, you must add the RESTEasy jar files within the lib + directory as well. Here's a sample Maven pom.xml that can build your WAR + for this case. + </para> + + <programlisting> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <groupId>org.somebody</groupId> + <artifactId>myapp</artifactId> + <packaging>war</packaging> + <name>My App</name> + <version>0.1-SNAPSHOT</version> + <repositories> + <repository> + <id>jboss</id> + <url>http://repository.jboss.org/nexus/content/groups/public/</url> + </repository> + </repositories> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.6</source> + <target>1.6</target> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.hornetq.rest</groupId> + <artifactId>hornetq-rest</artifactId> + <version>2.3.0-SNAPSHOT</version> + </dependency> + </dependencies> +</project></programlisting> + + <note> + <para>JBoss AS 7 loads classes differently than previous versions. + To work properly in AS 7 the WAR will need this in its MANIFEST.MF: + </para> + <programlisting>Dependencies: org.hornetq, org.jboss.netty</programlisting> + <para>You can add this to the<literal><plugins></literal> + section of the pom.xml to create this entry automatically: + </para> + <programlisting> +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <archive> + <manifestEntries> + <Dependencies>org.hornetq, org.jboss.netty</Dependencies> + </manifestEntries> + </archive> + </configuration> +</plugin></programlisting> + </note> + + <para> + It is worth noting that when deploying a WAR in a Java EE application server + like AS7 the URL for the resulting application will include the name of the + WAR by default. For example, if you've constructed a WAR as described above + named "hornetq-rest.war" then clients will access it at, e.g. + http://localhost:8080/hornetq-rest/[queues|topics]. We'll see more about + this later. + </para> + <note> + <para> + It is possible to put the WAR file at the "root context" of AS7, but + that is beyond the scope of this documentation. + </para> + </note> + </section> + + <section> + <title>Bootstrapping HornetQ Along with REST</title> + + <para>You can bootstrap HornetQ within your WAR as well. To do this, you + must have the HornetQ core and JMS jars along with Netty, Resteasy, and + the HornetQ REST jar within your WEB-INF/lib. You must also have a + hornetq-configuration.xml, hornetq-jms.xml, and hornetq-users.xml config + files within WEB-INF/classes. The examples that come with the HornetQ + REST distribution show how to do this. You must also add an additional + listener to your web.xml file. Here's an example: + </para> + + <programlisting> +<web-app> + <listener> + <listener-class> + org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap + </listener-class> + </listener> + + <listener> + <listener-class> + org.hornetq.rest.integration.HornetqBootstrapListener + </listener-class> + </listener> + + <listener> + <listener-class> + org.hornetq.rest.integration.RestMessagingBootstrapListener + </listener-class> + </listener> + + <filter> + <filter-name>Rest-Messaging</filter-name> + <filter-class> + org.jboss.resteasy.plugins.server.servlet.FilterDispatcher + </filter-class> + </filter> + + <filter-mapping> + <filter-name>Rest-Messaging</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> +</web-app></programlisting> + + <para>Here's a Maven pom.xml file for creating a WAR for this + environment. Make sure your hornetq configuration files are within the + src/main/resources directory so that they are stuffed within the WAR's + WEB-INF/classes directory! + </para> + + <programlisting> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <groupId>org.somebody</groupId> + <artifactId>myapp</artifactId> + <packaging>war</packaging> + <name>My App</name> + <version>0.1-SNAPSHOT</version> + <repositories> + <repository> + <id>jboss</id> + <url>http://repository.jboss.org/nexus/content/groups/public/</url> + </repository> + </repositories> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.6</source> + <target>1.6</target> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>org.hornetq</groupId> + <artifactId>hornetq-core</artifactId> + <version>2.3.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty</artifactId> + <version>3.4.5.Final</version> + </dependency> + <dependency> + <groupId>org.hornetq</groupId> + <artifactId>hornetq-jms</artifactId> + <version>2.3.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.jboss.spec.javax.jms</groupId> + <artifactId>jboss-jms-api_2.0_spec</artifactId> + <version>1.0.0.Final</version> + </dependency> + <dependency> + <groupId>org.hornetq.rest</groupId> + <artifactId>hornetq-rest</artifactId> + <version>2.3.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-jaxrs</artifactId> + <version>2.3.4.Final</version> + </dependency> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-jaxb-provider</artifactId> + <version>2.3.4.Final</version> + </dependency> + </dependencies> +</project></programlisting> + </section> + + <section id="configuration"> + <title>REST Configuration</title> + + <para>The HornetQ REST implementation does have some configuration + options. These are configured via XML configuration file that must be in + your WEB-INF/classes directory. You must set the web.xml context-param + <literal>rest.messaging.config.file</literal> to specify the name of the + configuration file. Below is the format of the XML configuration file + and the default values for each. + </para> + + <programlisting> +<rest-messaging> + <server-in-vm-id>0</server-in-vm-id> + <use-link-headers>false</use-link-headers> + <default-durable-send>false</default-durable-send> + <dups-ok>true</dups-ok> + <topic-push-store-dir>topic-push-store</topic-push-store-dir> + <queue-push-store-dir>queue-push-store</queue-push-store-dir> + <producer-time-to-live>0</producer-time-to-live> + <producer-session-pool-size>10</producer-session-pool-size> + <session-timeout-task-interval>1</session-timeout-task-interval> + <consumer-session-timeout-seconds>300</consumer-session-timeout-seconds> + <consumer-window-size>-1</consumer-window-size> +</rest-messaging></programlisting> + + <para>Let's give an explanation of each config option.</para> + + <itemizedlist> + <listitem> + <para><literal>server-in-vm-id</literal>. The HornetQ REST + impl uses the IN-VM transport to communicate with HornetQ. + It uses the default server id, which is "0". + </para> + </listitem> + <listitem> + <para><literal>use-link-headers</literal>. By default, all + links (URLs) are published using custom headers. You can + instead have the HornetQ REST implementation publish links + using the <ulink url="http://tools.ietf.org/html/draft-nottingham-http-link-header-10"> + Link Header specification + </ulink> instead if you desire. + </para> + </listitem> + <listitem> + <para><literal>default-durable-send</literal>. Whether a posted + message should be persisted by default if the user does not + specify a durable query parameter. + </para> + </listitem> + <listitem> + <para><literal>dups-ok</literal>. If this is true, no duplicate + detection protocol will be enforced for message posting. + </para> + </listitem> + <listitem> + <para><literal>topic-push-store-dir</literal>. This must be + a relative or absolute file system path. This is a directory + where push registrations for topics are stored. See + <link linkend="message-push">Pushing Messages</link>. + </para> + </listitem> + <listitem> + <para><literal>queue-push-store-dir</literal>. This must be + a relative or absolute file system path. This is a + directory where push registrations for queues are stored. + See <link linkend="message-push">Pushing Messages</link>. + </para> + </listitem> + <listitem> + <para><literal>producer-session-pool-size</literal>. The REST + implementation pools HornetQ sessions for sending messages. + This is the size of the pool. That number of sessions will + be created at startup time. + </para> + </listitem> + <listitem> + <para><literal>producer-time-to-live</literal>. Default time + to live for posted messages. Default is no ttl. + </para> + </listitem> + <listitem> + <para><literal>session-timeout-task-interval</literal>. Pull + consumers and pull subscriptions can time out. This is + the interval the thread that checks for timed-out sessions + will run at. A value of 1 means it will run every 1 second. + </para> + </listitem> + <listitem> + <para><literal>consumer-session-timeout-seconds</literal>. + Timeout in seconds for pull consumers/subscriptions that + remain idle for that amount of time. + </para> + </listitem> + <listitem> + <para><literal>consumer-window-size</literal>. For consumers, + this config option is the same as the HornetQ one of the + same name. It will be used by sessions created by the + HornetQ REST implementation. + </para> + </listitem> + </itemizedlist> + </section> + </section> + + + <section id="basics"> + <title>HornetQ REST Interface Basics</title> + + <para>The HornetQ REST interface publishes a variety of REST resources to + perform various tasks on a queue or topic. Only the top-level queue and + topic URI schemes are published to the outside world. You must discover + all over resources to interact with by looking for and traversing links. + You'll find published links within custom response headers and embedded in + published XML representations. Let's look at how this works. + </para> + + <section> + <title>Queue and Topic Resources</title> + + <para>To interact with a queue or topic you do a HEAD or GET request on + the following relative URI pattern: + </para> + + <programlisting> +/queues/{name} +/topics/{name}</programlisting> + + <para>The base of the URI is the base URL of the WAR you deployed the + HornetQ REST server within as defined in the + <link linkend="install">Installation and Configuration</link> + section of this document. Replace the <literal>{name}</literal> + string within the above URI pattern with the name of the queue or + topic you are interested in interacting with. For example if you + have configured a JMS topic named "foo" within your + <literal>hornetq-jms.xml</literal> file, the URI name should be + "jms.topic.foo". If you have configured a JMS queue name "bar" within + your <literal>hornetq-jms.xml</literal> file, the URI name should be + "jms.queue.bar". Internally, HornetQ prepends the "jms.topic" or + "jms.queue" strings to the name of the deployed destination. Next, + perform your HEAD or GET request on this URI. Here's what a + request/response would look like. + </para> + + <programlisting> +HEAD /queues/jms.queue.bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/queues/jms.queue.bar/create +msg-create-with-id: http://example.com/queues/jms.queue.bar/create/{id} +msg-pull-consumers: http://example.com/queues/jms.queue.bar/pull-consumers +msg-push-consumers: http://example.com/queues/jms.queue.bar/push-consumers</programlisting> + + <note> + <para> + You can use the "curl" utility to test this easily. Simply execute + a command like this: + </para> + + <programlisting> +curl --head http://example.com/queues/jms.queue.bar</programlisting> + </note> + + <para>The HEAD or GET response contains a number of custom response + headers that are URLs to additional REST resources that allow you to + interact with the queue or topic in different ways. It is important not + to rely on the scheme of the URLs returned within these headers as they + are an implementation detail. Treat them as opaque and query for them + each and every time you initially interact (at boot time) with the + server. If you treat all URLs as opaque then you will be isolated from + implementation changes as the HornetQ REST interface evolves over + time. + </para> + </section> + + <section> + <title>Queue Resource Response Headers</title> + + <para>Below is a list of response headers you should expect when + interacting with a Queue resource. + </para> + + <itemizedlist> + <listitem> + <para><literal>msg-create</literal>. This is a URL you POST messages + to. The semantics of this link are described in + <link linkend="posting-messages">Posting Messages</link>. + </para> + </listitem> + <listitem> + <para><literal>msg-create-with-id</literal>. This is a URL + <emphasis>template</emphasis> you can use to POST messages. + The semantics of this link are described in + <link linkend="posting-messages">Posting Messages</link>. + </para> + </listitem> + <listitem> + <para><literal>msg-pull-consumers</literal>. This is a URL for + creating consumers that will pull from a queue. The semantics + of this link are described in + <link linkend="message-pull">Consuming Messages via Pull</link>. + </para> + </listitem> + <listitem> + <para><literal>msg-push-consumers</literal>. This is a URL for + registering other URLs you want the HornetQ REST server to + push messages to. The semantics of this link are described + in <link linkend="message-push">Pushing Messages</link>. + </para> + </listitem> + </itemizedlist> + </section> + + <section> + <title>Topic Resource Response Headers</title> + + <para>Below is a list of response headers you should expect when + interacting with a Topic resource. + </para> + + <itemizedlist> + <listitem> + <para><literal>msg-create</literal>. This is a URL you POST + messages to. The semantics of this link are described in + <link linkend="posting-messages">Posting Messages</link>. + </para> + </listitem> + <listitem> + <para><literal>msg-create-with-id</literal>. This is a URL + <emphasis>template</emphasis> you can use to POST messages. + The semantics of this link are described in + <link linkend="posting-messages">Posting Messages</link>. + </para> + </listitem> + <listitem> + <para><literal>msg-pull-subscriptions</literal>. This is a + URL for creating subscribers that will pull from a topic. + The semantics of this link are described in + <link linkend="message-pull">Consuming Messages via Pull</link>. + </para> + </listitem> + <listitem> + <para><literal>msg-push-subscriptions</literal>. This is a + URL for registering other URLs you want the HornetQ REST + server to push messages to. The semantics of this link + are described in <link linkend="message-push">Pushing + Messages</link>. + </para> + </listitem> + </itemizedlist> + </section> + </section> + + + <section id="posting-messages"> + <title>Posting Messages</title> + + <para>This chapter discusses the protocol for posting messages to a queue + or a topic. In <link linkend="basics">HornetQ REST Interface Basics</link>, + you saw that a queue or topic resource publishes variable custom headers + that are links to other RESTful resources. The <literal>msg-create</literal> + header is a URL you can post a message to. Messages are published to a queue + or topic by sending a simple HTTP message to the URL published by the + <literal>msg-create</literal> header. The HTTP message contains whatever + content you want to publish to the HornetQ destination. Here's an example + scenario: + </para> + + <note> + <para>You can also post messages to the URL template found in + <literal>msg-create-with-id</literal>, but this is a more advanced + use-case involving duplicate detection that we will discuss later in + this section. + </para> + </note> + + <orderedlist> + <listitem> + <para>Obtain the starting <literal>msg-create</literal> header from + the queue or topic resource. + </para> + + <para> + <programlisting> +HEAD /queues/jms.queue.bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/queues/jms.queue.bar/create +msg-create-with-id: http://example.com/queues/jms.queue.bar/create/{id}</programlisting> + </para> + </listitem> + + <listitem> + <para>Do a POST to the URL contained in the <literal>msg-create</literal> + header. + </para> + + <programlisting> +POST /queues/jms.queue.bar/create +Host: example.com +Content-Type: application/xml + +<order> + <name>Bill</name> + <item>iPhone4</name> + <cost>$199.99</cost> +</order> + +--- Response --- +HTTP/1.1 201 Created +msg-create-next: http://example.com/queues/jms.queue.bar/create</programlisting> + + <note> + <para>You can use the "curl" utility to test this easily. Simply execute + a command like this: + </para> + <programlisting> +curl --verbose --data "123" http://example.com/queues/jms.queue.bar/create</programlisting> + </note> + + <para>A successful response will return a 201 response code. Also + notice that a <literal>msg-create-next</literal> response header + is sent as well. You must use this URL to POST your next message. + </para> + </listitem> + + <listitem> + <para>POST your next message to the queue using the URL returned in + the <literal>msg-create-next</literal> header. + </para> + + <programlisting> +POST /queues/jms.queue.bar/create +Host: example.com +Content-Type: application/xml + +<order> + <name>Monica</name> + <item>iPad</item> + <cost>$499.99</cost> +</order> + +--- Response -- +HTTP/1.1 201 Created +msg-create-next: http://example.com/queues/jms.queue.bar/create</programlisting> + <para>Continue using the new <literal>msg-create-next</literal> + header returned with each response. + </para> + </listitem> + </orderedlist> + + <warning> + <para>It is <emphasis>VERY IMPORTANT</emphasis> that you never re-use returned + <literal>msg-create-next</literal> headers to post new messages. If the + <literal>dups-ok</literal> configuration property is set to + <literal>false</literal> on the server then this URL will be uniquely + generated for each message and used for duplicate detection. If you lose + the URL within the <literal>msg-create-next</literal> header, then just + go back to the queue or topic resource to get the + <literal>msg-create</literal> URL again. + </para> + </warning> + + <section> + <title>Duplicate Detection</title> + + <para>Sometimes you might have network problems when posting new + messages to a queue or topic. You may do a POST and never receive a + response. Unfortunately, you don't know whether or not the server + received the message and so a re-post of the message might cause + duplicates to be posted to the queue or topic. By default, the HornetQ + REST interface is configured to accept and post duplicate messages. You + can change this by turning on duplicate message detection by setting the + <literal>dups-ok</literal> config option to <literal>false</literal> + as described in <link linkend="basics">HornetQ REST Interface Basics</link>. + When you do this, the initial POST to the <literal>msg-create</literal> + URL will redirect you, using the standard HTTP 307 redirection mechanism + to a unique URL to POST to. All other interactions remain the same as + discussed earlier. Here's an example: + </para> + + <orderedlist> + <listitem> + <para>Obtain the starting <literal>msg-create</literal> header from + the queue or topic resource. + </para> + + <para> + <programlisting> +HEAD /queues/jms.queue.bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/queues/jms.queue.bar/create +msg-create-with-id: http://example.com/queues/jms.queue.bar/create/{id}</programlisting> + </para> + </listitem> + + <listitem> + <para>Do a POST to the URL contained in the <literal>msg-create</literal> + header. + </para> + + <programlisting> +POST /queues/jms.queue.bar/create +Host: example.com +Content-Type: application/xml + +<order> + <name>Bill</name> + <item>iPhone4</name> + <cost>$199.99</cost> +</order> + +--- Response --- +HTTP/1.1 307 Redirect +Location: http://example.com/queues/jms.queue.bar/create/13582001787372</programlisting> + + <para>A successful response will return a 307 response code. This + is standard HTTP protocol. It is telling you that you must re-POST + to the URL contained within the <literal>Location</literal> + header. + </para> + </listitem> + + <listitem> + <para>re-POST your message to the URL provided within the + <literal>Location</literal> header. + </para> + + <programlisting> +POST /queues/jms.queue.bar/create/13582001787372 +Host: example.com +Content-Type: application/xml + +<order> + <name>Bill</name> + <item>iPhone4</name> + <cost>$199.99</cost> +</order> + +--- Response -- +HTTP/1.1 201 Created +msg-create-next: http://example.com/queues/jms.queue.bar/create/13582001787373</programlisting> + <para>You should receive a 201 Created response. If there is a + network failure, just re-POST to the Location header. For new + messages, use the returned <literal>msg-create-next</literal> + header returned with each response. + </para> + </listitem> + + <listitem> + <para>POST any new message to the returned + <literal>msg-create-next</literal> header. + </para> + + <programlisting> +POST /queues/jms.queue.bar/create/13582001787373 +Host: example.com +Content-Type: application/xml + +<order> + <name>Monica</name> + <item>iPad</name> + <cost>$499.99</cost> +</order> + +--- Response -- +HTTP/1.1 201 Created +msg-create-next: http://example.com/queues/jms.queue.bar/create/13582001787374</programlisting> + <para>If there ever is a network problem, just repost to the URL + provided in the <literal>msg-create-next</literal> header. + </para> + </listitem> + </orderedlist> + + <para>How can this work? As you can see, with each successful response, + the HornetQ REST server returns a uniquely generated URL within the + msg-create-next header. This URL is dedicated to the next new message + you want to post. Behind the scenes, the code extracts an identify from + the URL and uses HornetQ's duplicate detection mechanism by setting the + <literal>DUPLICATE_DETECTION_ID</literal> property of the JMS message + that is actually posted to the system. + </para> + + <para>If you happen to use the same ID more than once you'll see a message + like this on the server: + </para> + <programlisting> +WARN [org.hornetq.core.server] (Thread-3 (HornetQ-remoting-threads-HornetQServerImpl::serverUUID=8d6be6f8-5e8b-11e2-80db-51bbde66f473-26319292-267207)) HQ112098: Duplicate message detected - message will not be routed. Message information: +ServerMessage[messageID=20,priority=4, bodySize=1500,expiration=0, durable=true, address=jms.queue.bar,properties=TypedProperties[{http_content$type=application/x-www-form-urlencoded, http_content$length=3, postedAsHttpMessage=true, _HQ_DUPL_ID=42}]]@12835058</programlisting> + + <para>An alternative to this approach is to use the <literal>msg-create-with-id</literal> + header. This is not an invokable URL, but a URL template. The idea is that + the client provides the <literal>DUPLICATE_DETECTION_ID</literal> and creates + its own <literal>create-next</literal> URL. The <literal>msg-create-with-id</literal> + header looks like this (you've see it in previous examples, but we haven't used it): + </para> + + <programlisting> +msg-create-with-id: http://example.com/queues/jms.queue.bar/create/{id}</programlisting> + + <para>You see that it is a regular URL appended with a <literal>{id}</literal>. This + <literal>{id}</literal> is a pattern matching substring. A client would generate its + <literal>DUPLICATE_DETECTION_ID</literal> and replace <literal>{id}</literal> + with that generated id, then POST to the new URL. The URL the client creates + works exactly like a <literal>create-next</literal> URL described earlier. The + response of this POST would also return a new <literal>msg-create-next</literal> + header. The client can continue to generate its own DUPLICATE_DETECTION_ID, or + use the new URL returned via the <literal>msg-create-nex</literal>t header. + </para> + + <para>The advantage of this approach is that the client does not have to + repost the message. It also only has to come up with a unique + <literal>DUPLICATE_DETECTION_ID</literal> once. + </para> + </section> + + <section> + <title>Persistent Messages</title> + + <para>By default, posted messages are not durable and will not be + persisted in HornetQ's journal. You can create durable messages by + modifying the default configuration as expressed in Chapter 2 so that + all messages are persisted when sent. Alternatively, you can set a URL + query parameter called <literal>durable</literal> to true when you post + your messages to the URLs returned in the <literal>msg-create</literal>, + <literal>msg-create-with-id</literal>, or <literal>msg-create-next</literal> + headers. here's an example of that. + </para> + + <programlisting> +POST /queues/jms.queue.bar/create?durable=true +Host: example.com +Content-Type: application/xml + +<order> + <name>Bill</name> + <item>iPhone4</item> + <cost>$199.99</cost> +</order></programlisting> + </section> + + <section> + <title>TTL, Expiration and Priority</title> + + <para>You can set the time to live, expiration, and/or the priority of + the message in the queue or topic by setting an additional query + parameter. The <literal>expiration</literal> query parameter is an long + specify the time in milliseconds since epoch (a long date). The + <literal>ttl</literal> query parameter is a time in milliseconds you + want the message active. The <literal>priority</literal> is another + query parameter with an integer value between 0 and 9 expressing the + priority of the message. i.e.: + </para> + + <programlisting> +POST /queues/jms.queue.bar/create?expiration=30000&priority=3 +Host: example.com +Content-Type: application/xml + +<order> + <name>Bill</name> + <item>iPhone4</item> + <cost>$199.99</cost> +</order></programlisting> + </section> + </section> + + <section id="message-pull"> + <title>Consuming Messages via Pull</title> + + <para>There are two different ways to consume messages from a topic or + queue. You can wait and have the messaging server push them to you, or you + can continuously poll the server yourself to see if messages are + available. This chapter discusses the latter. Consuming messages via a + pull works almost identically for queues and topics with some minor, but + important caveats. To start consuming you must create a consumer resource + on the server that is dedicated to your client. Now, this pretty much + breaks the stateless principle of REST, but after much prototyping, this + is the best way to work most effectively with HornetQ through a REST + interface. + </para> + + <para>You create consumer resources by doing a simple POST to the URL + published by the <literal>msg-pull-consumers</literal> + response header if you are interacting with a queue, the + <literal>msg-pull-subscribers</literal> response header if you're + interacting with a topic. These headers are provided by the main queue or + topic resource discussed in <link linkend="basics">HornetQ REST Interface + Basics</link>. Doing an empty POST to one of these + URLs will create a consumer resource that follows an auto-acknowledge + protocol and, if you are interacting with a topic, creates a temporarily + subscription to the topic. If you want to use the acknowledgement protocol + and/or create a durable subscription (topics only), then you must use the + form parameters (<literal>application/x-www-form-urlencoded</literal>) + described below. + </para> + + <itemizedlist> + <listitem> + <para><literal>autoAck</literal>. A value of <literal>true</literal> + or <literal>false</literal> can be given. This defaults to + <literal>true</literal> if you do not pass this parameter. + </para> + </listitem> + <listitem> + <para><literal>durable</literal>. A value of <literal>true</literal> + or <literal>false</literal> can be given. This defaults to + <literal>false</literal> if you do not pass this parameter. + Only available on topics. This specifies whether you want a + durable subscription or not. A durable subscription persists + through server restart. + </para> + </listitem> + <listitem> + <para><literal>name</literal>. This is the name of the durable + subscription. If you do not provide this parameter, the name + will be automatically generated by the server. Only usable + on topics. + </para> + </listitem> + <listitem> + <para><literal>selector</literal>. This is an optional JMS selector + string. The HornetQ REST interface adds HTTP headers to the + JMS message for REST produced messages. HTTP headers are + prefixed with "http_" and every '-' character is converted + to a '$'. + </para> + </listitem> + <listitem> + <para><literal>idle-timeout</literal>. For a topic subscription, + idle time in milliseconds in which the consumer connections + will be closed if idle. + </para> + </listitem> + <listitem> + <para><literal>delete-when-idle</literal>. Boolean value, If + true, a topic subscription will be deleted (even if it is + durable) when an the idle timeout is reached. + </para> + </listitem> + </itemizedlist> + + <note> + <para>If you have multiple pull-consumers active at the same time + on the same destination be aware that unless the + <literal>consumer-window-size</literal> is 0 then one consumer + might buffer messages while the other consumer gets none. + </para> + </note> + + <section> + <title>Auto-Acknowledge</title> + + <para>This section focuses on the auto-acknowledge protocol for + consuming messages via a pull. Here's a list of the response + headers and URLs you'll be interested in. + </para> + + <itemizedlist> + <listitem> + <para><literal>msg-pull-consumers</literal>. The URL of + a factory resource for creating queue consumer + resources. You will pull from these created resources. + </para> + </listitem> + <listitem> + <para><literal>msg-pull-subscriptions</literal>. The URL + of a factory resource for creating topic subscription + resources. You will pull from the created resources. + </para> + </listitem> + <listitem> + <para><literal>msg-consume-next</literal>. The URL you + will pull the next message from. This is returned + with every response. + </para> + </listitem> + <listitem> + <para><literal>msg-consumer</literal>. This is a URL + pointing back to the consumer or subscription + resource created for the client. + </para> + </listitem> + </itemizedlist> + + <section> + <title>Creating an Auto-Ack Consumer or Subscription</title> + + <para>Here is an example of creating an auto-acknowledged + queue pull consumer. + </para> + + <orderedlist> + <listitem> + <para>Find the pull-consumers URL by doing a HEAD or + GET request to the base queue resource. + </para> + + <programlisting> +HEAD /queues/jms.queue.bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/queues/jms.queue.bar/create +msg-pull-consumers: http://example.com/queues/jms.queue.bar/pull-consumers +msg-push-consumers: http://example.com/queues/jms.queue.bar/push-consumers</programlisting> + </listitem> + + <listitem> + <para>Next do an empty POST to the URL returned in the + <literal>msg-pull-consumers</literal> + header. + </para> + + <programlisting> +POST /queues/jms.queue.bar/pull-consumers HTTP/1.1 +Host: example.com + +--- response --- +HTTP/1.1 201 Created +Location: http://example.com/queues/jms.queue.bar/pull-consumers/auto-ack/333 +msg-consume-next: http://example.com/queues/jms.queue.bar/pull-consumers/auto-ack/333/consume-next-1</programlisting> + + <para>The + <literal>Location</literal> + header points to the JMS + consumer resource that was created on the server. It is good to + remember this URL, although, as you'll see later, it is + transmitted with each response just to remind you. + </para> + </listitem> + </orderedlist> + + <para>Creating an auto-acknowledged consumer for a topic is pretty + much the same. Here's an example of creating a durable + auto-acknowledged topic pull subscription. + </para> + + <orderedlist> + <listitem> + <para>Find the + <literal>pull-subscriptions</literal> + URL by doing + a HEAD or GET request to the base topic resource + </para> + + <programlisting> +HEAD /topics/jms.topic.bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/topics/jms.topic.foo/create +msg-pull-subscriptions: http://example.com/topics/jms.topic.foo/pull-subscriptions +msg-push-subscriptions: http://example.com/topics/jms.topic.foo/push-subscriptions</programlisting> + </listitem> + + <listitem> + <para>Next do a POST to the URL returned in the + <literal>msg-pull-subscriptions</literal> + header passing in a <literal>true</literal> + value for the <literal>durable</literal> + form parameter. + </para> + + <programlisting> +POST /topics/jms.topic.foo/pull-subscriptions HTTP/1.1 +Host: example.com +Content-Type: application/x-www-form-urlencoded + +durable=true + +--- Response --- +HTTP/1.1 201 Created +Location: http://example.com/topics/jms.topic.foo/pull-subscriptions/auto-ack/222 +msg-consume-next: +http://example.com/topics/jms.topic.foo/pull-subscriptions/auto-ack/222/consume-next-1</programlisting> + + <para>The + <literal>Location</literal> + header points to the JMS + subscription resource that was created on the server. It is good + to remember this URL, although, as you'll see later, it is + transmitted with each response just to remind you. + </para> + </listitem> + </orderedlist> + </section> + + <section> + <title>Consuming Messages</title> + + <para>After you have created a consumer resource, you are ready to + start pulling messages from the server. Notice that when you created + the consumer for either the queue or topic, the response contained a + <literal>msg-consume-next</literal> response header. POST to the URL + contained within this header to consume the next message in the queue + or topic subscription. A successful POST causes the server to extract + a message from the queue or topic subscription, acknowledge it, and + return it to the consuming client. If there are no messages in the + queue or topic subscription, a 503 (Service Unavailable) HTTP code is + returned. + </para> + + <warning> + <para>For both successful and unsuccessful posts to the + msg-consume-next URL, the response will contain a new + msg-consume-next header. You must ALWAYS use this new URL returned + within the new msg-consume-next header to consume new + messages. + </para> + </warning> + + <para>Here's an example of pulling multiple messages from the consumer + resource. + </para> + + <orderedlist> + <listitem> + <para>Do a POST on the msg-consume-next URL that was returned with + the consumer or subscription resource discussed earlier. + </para> + + <programlisting> +POST /queues/jms.queue.bar/pull-consumers/consume-next-1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +Content-Type: application/xml +msg-consume-next: http://example.com/queues/jms.queue.bar/pull-consumers/333/consume-next-2 +msg-consumer: http://example.com/queues/jms.queue.bar/pull-consumers/333 + +<order>...</order></programlisting> + + <para>The POST returns the message consumed from the queue. It + also returns a new msg-consume-next link. Use this new link to get + the next message. Notice also a msg-consumer response header is + returned. This is a URL that points back to the consumer or + subscription resource. You will need that to clean up your + connection after you are finished using the queue or topic. + </para> + </listitem> + + <listitem> + <para>The POST returns the message consumed from the queue. It + also returns a new msg-consume-next link. Use this new link to get + the next message. + </para> + + <programlisting> +POST /queues/jms.queue.bar/pull-consumers/consume-next-2 +Host: example.com + +--- Response --- +Http/1.1 503 Service Unavailable +Retry-After: 5 +msg-consume-next: http://example.com/queues/jms.queue.bar/pull-consumers/333/consume-next-2</programlisting> + + <para>In this case, there are no messages in the queue, so we get + a 503 response back. As per the HTTP 1.1 spec, a 503 response may + return a Retry-After head specifying the time in seconds that you + should retry a post. Also notice, that another new + msg-consume-next URL is present. Although it probably is the same + URL you used last post, get in the habit of using URLs returned in + response headers as future versions of HornetQ REST might be + redirecting you or adding additional data to the URL after + timeouts like this. + </para> + </listitem> + + <listitem> + <para>POST to the URL within the last + <literal>msg-consume-next</literal> + to get the next + message. + </para> + + <programlisting> +POST /queues/jms.queue.bar/pull-consumers/consume-next-2 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +Content-Type: application/xml +msg-consume-next: http://example.com/queues/jms.queue.bar/pull-consumers/333/consume-next-3 + +<order>...</order></programlisting> + </listitem> + </orderedlist> + </section> + + <section> + <title>Recovering From Network Failures</title> + + <para>If you experience a network failure and do not know if your post + to a msg-consume-next URL was successful or not, just re-do your POST. + A POST to a msg-consume-next URL is idempotent, meaning that it will + return the same result if you execute on any one msg-consume-next URL + more than once. Behind the scenes, the consumer resource caches the + last consumed message so that if there is a message failure and you do + a re-post, the cached last message will be returned (along with a new + msg-consume-next URL). This is the reason why the protocol always + requires you to use the next new msg-consume-next URL returned with + each response. Information about what state the client is in is + embedded within the actual URL. + </para> + </section> + + <section> + <title>Recovering From Client or Server Crashes</title> + + <para>If the server crashes and you do a POST to the msg-consume-next + URL, the server will return a 412 (Preconditions Failed) response + code. This is telling you that the URL you are using is out of sync + with the server. The response will contain a new msg-consume-next + header to invoke on. + </para> + + <para>If the client crashes there are multiple ways you can recover. + If you have remembered the last msg-consume-next link, you can just + re-POST to it. If you have remembered the consumer resource URL, you + can do a GET or HEAD request to obtain a new msg-consume-next URL. If + you have created a topic subscription using the name parameter + discussed earlier, you can re-create the consumer. Re-creation will + return a msg-consume-next URL you can use. If you cannot do any of + these things, you will have to create a new consumer. + </para> + + <para>The problem with the auto-acknowledge protocol is that if the + client or server crashes, it is possible for you to skip messages. The + scenario would happen if the server crashes after auto-acknowledging a + message and before the client receives the message. If you want more + reliable messaging, then you must use the acknowledgement + protocol. + </para> + </section> + </section> + + <section> + <title>Manual Acknowledgement</title> + + <para>The manual acknowledgement protocol is similar to the auto-ack + protocol except there is an additional round trip to the server to tell + it that you have received the message and that the server can internally + ack the message. Here is a list of the response headers you will be + interested in. + </para> + + <itemizedlist> + <listitem> + <para><literal>msg-pull-consumers</literal>. The URL of a factory resource for creating queue + consumer + resources. You will pull from these created resources + </para> + </listitem> + <listitem> + <para><literal>msg-pull-subscriptions</literal>. The URL of a factory resource for creating topic + subscription resources. You will pull from the created + resources. + </para> + </listitem> + <listitem> + <para><literal>msg-acknowledge-next</literal>. URL used to obtain the next message in the queue or + topic + subscription. It does not acknowledge the message though. + </para> + </listitem> + <listitem> + <para><literal>msg-acknowledgement</literal>. URL used to acknowledge a message. + </para> + </listitem> + <listitem> + <para><literal>msg-consumer</literal>. This is a URL pointing back to the consumer or subscription + resource created for the client. + </para> + </listitem> + </itemizedlist> + + <section> + <title>Creating manually-acknowledged consumers or + subscriptions + </title> + + <para>Here is an example of creating an auto-acknowledged queue pull + consumer. + </para> + + <orderedlist> + <listitem> + <para>Find the pull-consumers URL by doing a HEAD or GET request + to the base queue resource. + </para> + + <programlisting> +HEAD /queues/jms.queue.bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/queues/jms.queue.bar/create +msg-pull-consumers: http://example.com/queues/jms.queue.bar/pull-consumers +msg-push-consumers: http://example.com/queues/jms.queue.bar/push-consumers</programlisting> + </listitem> + + <listitem> + <para>Next do a POST to the URL returned in the + <literal>msg-pull-consumers</literal> + header passing in a + <literal>false</literal> + value to the + <literal>autoAck</literal> + form parameter . + </para> + + <programlisting> +POST /queues/jms.queue.bar/pull-consumers HTTP/1.1 +Host: example.com +Content-Type: application/x-www-form-urlencoded + +autoAck=false + +--- response --- +HTTP/1.1 201 Created +Location: http://example.com/queues/jms.queue.bar/pull-consumers/acknowledged/333 +msg-acknowledge-next: http://example.com/queues/jms.queue.bar/pull-consumers/acknowledged/333/acknowledge-next-1</programlisting> + + <para>The + <literal>Location</literal> + header points to the JMS + consumer resource that was created on the server. It is good to + remember this URL, although, as you'll see later, it is + transmitted with each response just to remind you. + </para> + </listitem> + </orderedlist> + + <para>Creating an manually-acknowledged consumer for a topic is pretty + much the same. Here's an example of creating a durable + manually-acknowledged topic pull subscription. + </para> + + <orderedlist> + <listitem> + <para>Find the + <literal>pull-subscriptions</literal> + URL by doing + a HEAD or GET request to the base topic resource + </para> + + <programlisting> +HEAD /topics/jms.topic.bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/topics/jms.topic.foo/create +msg-pull-subscriptions: http://example.com/topics/jms.topic.foo/pull-subscriptions +msg-push-subscriptions: http://example.com/topics/jms.topic.foo/push-subscriptions</programlisting> + </listitem> + + <listitem> + <para>Next do a POST to the URL returned in the + <literal>msg-pull-subscriptions</literal> + header passing in a <literal>true</literal> + value for the <literal>durable</literal> + form parameter and a <literal>false</literal> + value to the <literal>autoAck</literal> + form parameter. + </para> + + <programlisting> +POST /topics/jms.topic.foo/pull-subscriptions HTTP/1.1 +Host: example.com +Content-Type: application/x-www-form-urlencoded + +durable=true&autoAck=false + +--- Response --- +HTTP/1.1 201 Created +Location: http://example.com/topics/jms.topic.foo/pull-subscriptions/acknowledged/222 +msg-acknowledge-next: +http://example.com/topics/jms.topic.foo/pull-subscriptions/acknowledged/222/consume-next-1</programlisting> + + <para>The + <literal>Location</literal> header points to the JMS + subscription resource that was created on the server. It is good + to remember this URL, although, as you'll see later, it is + transmitted with each response just to remind you. + </para> + </listitem> + </orderedlist> + </section> + + <section> + <title>Consuming and Acknowledging a Message</title> + + <para>After you have created a consumer resource, you are ready to + start pulling messages from the server. Notice that when you created + the consumer for either the queue or topic, the response contained a + <literal>msg-acknowledge-next</literal> response header. POST to the + URL contained within this header to consume the next message in the + queue or topic subscription. If there are no messages in the queue or + topic subscription, a 503 (Service Unavailable) HTTP code is returned. + A successful POST causes the server to extract a message from the + queue or topic subscription and return it to the consuming client. It + does not acknowledge the message though. The response will contain the + <literal>acknowledgement</literal> + header which you will use to + acknowledge the message. + </para> + + <para>Here's an example of pulling multiple messages from the consumer + resource. + </para> + + <orderedlist> + <listitem> + <para>Do a POST on the msg-acknowledge-next URL that was returned + with the consumer or subscription resource discussed + earlier. + </para> + + <programlisting> +POST /queues/jms.queue.bar/pull-consumers/consume-next-1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +Content-Type: application/xml +msg-acknowledgement: +http://example.com/queues/jms.queue.bar/pull-consumers/333/acknowledgement/2 +msg-consumer: http://example.com/queues/jms.queue.bar/pull-consumers/333 + +<order>...</order></programlisting> + + <para>The POST returns the message consumed from the queue. It + also returns a<literal>msg-acknowledgemen</literal>t link. You + will use this new link to acknowledge the message. Notice also a + <literal>msg-consumer</literal> response header is returned. This + is a URL that points back to the consumer or subscription + resource. You will need that to clean up your connection after you + are finished using the queue or topic. + </para> + </listitem> + + <listitem> + <para>Acknowledge or unacknowledge the message by doing a POST to + the URL contained in the <literal>msg-acknowledgement</literal> + header. You must pass an <literal>acknowledge</literal> + form parameter set to <literal>true</literal> + or <literal>false</literal> depending on whether you want to + acknowledge or unacknowledge the message on the server. + </para> + + <programlisting> +POST /queues/jms.queue.bar/pull-consumers/acknowledgement/2 +Host: example.com +Content-Type: application/x-www-form-urlencoded + +acknowledge=true + +--- Response --- +Http/1.1 200 Ok +msg-acknowledge-next: +http://example.com/queues/jms.queue.bar/pull-consumers/333/acknowledge-next-2</programlisting> + + <para>Whether you acknowledge or unacknowledge the message, the + response will contain a new msg-acknowledge-next header that you + must use to obtain the next message. + </para> + </listitem> + </orderedlist> + </section> + + <section> + <title>Recovering From Network Failures</title> + + <para>If you experience a network failure and do not know if your post + to a + <literal>msg-acknowledge-next</literal> + or + <literal>msg-acknowledgement</literal> URL was successful or not, just + re-do your POST. A POST to one of these URLs is idempotent, meaning + that it will return the same result if you re-post. Behind the scenes, + the consumer resource keeps track of its current state. If the last + action was a call to<literal>msg-acknowledge-next</literal>, it will + have the last message cached, so that if a re-post is done, it will + return the message again. Same goes with re-posting to + <literal>msg-acknowledgement</literal>. The server remembers its last + state and will return the same results. If you look at the URLs you'll + see that they contain information about the expected current state of + the server. This is how the server knows what the client is + expecting. + </para> + </section> + + <section> + <title>Recovering From Client or Server Crashes</title> + + <para>If the server crashes and while you are doing a POST to the + <literal>msg-acknowledge-next</literal> URL, just re-post. Everything + should reconnect all right. On the other hand, if the server crashes + while you are doing a POST to<literal>msg-acknowledgement</literal>, + the server will return a 412 (Preconditions Failed) response code. + This is telling you that the URL you are using is out of sync with the + server and the message you are acknowledging was probably re-enqueued. + The response will contain a new <literal>msg-acknowledge-next</literal> + header to invoke on. + </para> + + <para>As long as you have "bookmarked" the consumer resource URL + (returned from <literal>Location</literal> header on a create, or the + <literal>msg-consumer</literal> header), you can recover from client + crashes by doing a GET or HEAD request on the consumer resource to + obtain what state you are in. If the consumer resource is expecting + you to acknowledge a message, it will return a + <literal>msg-acknowledgement</literal> header in the response. If the + consumer resource is expecting you to pull for the next message, the + <literal>msg-acknowledge-next</literal> header will be in the + response. With manual acknowledgement you are pretty much guaranteed + to avoid skipped messages. For topic subscriptions that were created + with a name parameter, you do not have to "bookmark" the returned URL. + Instead, you can re-create the consumer resource with the same exact + name. The response will contain the same information as if you did a + GET or HEAD request on the consumer resource. + </para> + </section> + </section> + + <section> + <title>Blocking Pulls with Accept-Wait</title> + + <para>Unless your queue or topic has a high rate of message flowing + though it, if you use the pull protocol, you're going to be receiving a + lot of 503 responses as you continuously pull the server for new + messages. To alleviate this problem, the HornetQ REST interface provides + the <literal>Accept-Wait</literal> header. This is a generic HTTP + request header that is a hint to the server for how long the client is + willing to wait for a response from the server. The value of this header + is the time in seconds the client is willing to block for. You would + send this request header with your pull requests. Here's an + example: + </para> + + <programlisting> +POST /queues/jms.queue.bar/pull-consumers/consume-next-2 +Host: example.com +Accept-Wait: 30 + +--- Response --- +HTTP/1.1 200 Ok +Content-Type: application/xml +msg-consume-next: http://example.com/queues/jms.queue.bar/pull-consumers/333/consume-next-3 + +<order>...</order></programlisting> + + <para>In this example, we're posting to a msg-consume-next URL and + telling the server that we would be willing to block for 30 + seconds. + </para> + </section> + + <section> + <title>Clean Up Your Consumers!</title> + + <para>When the client is done with its consumer or topic subscription it + should do an HTTP DELETE call on the consumer URL passed back from the + Location header or the msg-consumer response header. The server will + time out a consumer with the value of + <literal>consumer-session-timeout-seconds</literal> configured from + <link linkend="configuration">REST configuration</link>, so you + don't have to clean up if you don't want to, but if you are a good kid, + you will clean up your messes. A consumer timeout for durable + subscriptions will not delete the underlying durable JMS subscription + though, only the server-side consumer resource (and underlying JMS + session). + </para> + </section> + </section> + + + <section id="message-push"> + <title>Pushing Messages</title> + + <para>You can configure the HornetQ REST server to push messages to a + registered URL either remotely through the REST interface, or by creating + a pre-configured XML file for the HornetQ REST server to load at boot + time. + </para> + + <section> + <title>The Queue Push Subscription XML</title> + + <para>Creating a push consumer for a queue first involves creating a + very simple XML document. This document tells the server if the push + subscription should survive server reboots (is it durable). It must + provide a URL to ship the forwarded message to. Finally, you have to + provide authentication information if the final endpoint requires + authentication. Here's a simple example: + </para> + + <programlisting> +<push-registration> + <durable>false</durable> + <selector><![CDATA[ + SomeAttribute > 1 + ]]> + </selector> + <link rel="push" href="http://somewhere.com" type="application/json" method="PUT"/> + <maxRetries>5</maxRetries> + <retryWaitMillis>1000</retryWaitMillis> + <disableOnFailure>true</disableOnFailure> +</push-registration></programlisting> + + <para>The <literal>durable</literal> element specifies whether the + registration should be saved to disk so that if there is a server + restart, the push subscription will still work. This element is not + required. If left out it defaults to<literal>false</literal>. If + durable is set to true, an XML file for the push subscription will be + created within the directory specified by the + <literal>queue-push-store-dir</literal> config variable defined in + Chapter 2 (<literal>topic-push-store-dir</literal> for topics). + </para> + + <para>The <literal>selector</literal> element is optional and defines a + JMS message selector. You should enclose it within CDATA blocks as some + of the selector characters are illegal XML. + </para> + + <para>The <literal>maxRetries</literal> element specifies how many times + a the server will try to push a message to a URL if there is a + connection failure. + </para> + + <para>The <literal>retryWaitMillis</literal> element specifies how long + to wait before performing a retry. + </para> + + <para>The + <literal>disableOnFailure</literal> element, if set to true, + will disable the registration if all retries have failed. It will not + disable the connection on non-connection-failure issues (like a bad + request for instance). In these cases, the dead letter queue logic of + HornetQ will take over. + </para> + + <para>The <literal>link</literal> element specifies the basis of the + interaction. The <literal>href</literal> attribute contains the URL you + want to interact with. It is the only required attribute. The + <literal>type</literal> attribute specifies the content-type of what the + push URL is expecting. The <literal>method</literal> attribute defines + what HTTP method the server will use when it sends the message to the + server. If it is not provided it defaults to POST. The + <literal>rel</literal> attribute is very important and the value of it + triggers different behavior. Here's the values a rel attribute can + have: + </para> + + <itemizedlist> + <listitem> + <para><literal>destination</literal>. The href URL is assumed to be a queue or topic resource of + another HornetQ REST server. The push registration will initially + do a HEAD request to this URL to obtain a msg-create-with-id + header. It will use this header to push new messages to the + HornetQ REST endpoint reliably. Here's an example: + </para> + + <programlisting> +<push-registration> + <link rel="destination" href="http://somewhere.com/queues/jms.queue.foo"/> +</push-registration></programlisting> + </listitem> + <listitem> + <para><literal>template</literal>. In this case, the server is expecting the link element's + href attribute to be a URL expression. The URL expression must + have one and only one URL parameter within it. The server will use + a unique value to create the endpoint URL. Here's an + example: + </para> + + <programlisting> +<push-registration> + <link rel="template" href="http://somewhere.com/resources/{id}/messages" method="PUT"/> +</push-registration></programlisting> + + <para>In this example, the {id} sub-string is the one and only one + URL parameter. + </para> + </listitem> + <listitem> + <para><literal>user defined</literal>. If the rel attributes is not destination or template (or is + empty or missing), then the server will send an HTTP message to + the href URL using the HTTP method defined in the method + attribute. Here's an example: + </para> + + <programlisting> +<push-registration> + <link href="http://somewhere.com" type="application/json" method="PUT"/> +</push-registration></programlisting> + </listitem> + </itemizedlist> + </section> + + <section> + <title>The Topic Push Subscription XML</title> + + <para>The push XML for a topic is the same except the root element is + push-topic-registration. (Also remember the <literal>selector</literal> + element is optional). The rest of the document is the same. Here's an + example of a template registration: + </para> + + <programlisting> +<push-topic-registration> + <durable>true</durable> + <selector><![CDATA[ + SomeAttribute > 1 + ]]> + </selector> + <link rel="template" href="http://somewhere.com/resources/{id}/messages" method="POST"/> +</push-topic registration></programlisting> + </section> + + <section> + <title>Creating a Push Subscription at Runtime</title> + + <para>Creating a push subscription at runtime involves getting the + factory resource URL from the msg-push-consumers header, if the + destination is a queue, or msg-push-subscriptions header, if the + destination is a topic. Here's an example of creating a push + registration for a queue: + </para> + + <orderedlist> + <listitem> + <para>First do a HEAD request to the queue resource:</para> + + <programlisting> +HEAD /queues/jms.queue.bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/queues/jms.queue.bar/create +msg-pull-consumers: http://example.com/queues/jms.queue.bar/pull-consumers +msg-push-consumers: http://example.com/queues/jms.queue.bar/push-consumers</programlisting> + </listitem> + + <listitem> + <para>Next POST your subscription XML to the URL returned from + msg-push-consumers header + </para> + + <programlisting> +POST /queues/jms.queue.bar/push-consumers +Host: example.com +Content-Type: application/xml + +<push-registration> + <link rel="destination" href="http://somewhere.com/queues/jms.queue.foo"/> +</push-registration> + +--- Response --- +HTTP/1.1 201 Created +Location: http://example.com/queues/jms.queue.bar/push-consumers/1-333-1212</programlisting> + + <para>The Location header contains the URL for the created resource. + If you want to unregister this, then do a HTTP DELETE on this + URL. + </para> + </listitem> + </orderedlist> + + <para>Here's an example of creating a push registration for a + topic: + </para> + + <orderedlist> + <listitem> + <para>First do a HEAD request to the topic resource:</para> + + <programlisting> +HEAD /topics/jms.topic.bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/topics/jms.topic.bar/create +msg-pull-subscriptions: http://example.com/topics/jms.topic.bar/pull-subscriptions +msg-push-subscriptions: http://example.com/topics/jms.topic.bar/push-subscriptions</programlisting> + </listitem> + + <listitem> + <para>Next POST your subscription XML to the URL returned from + msg-push-subscriptions header + </para> + + <programlisting> +POST /topics/jms.topic.bar/push-subscriptions +Host: example.com +Content-Type: application/xml + +<push-registration> + <link rel="template" href="http://somewhere.com/resources/{id}"/> +</push-registration> + +--- Response --- +HTTP/1.1 201 Created +Location: http://example.com/topics/jms.topic.bar/push-subscriptions/1-333-1212</programlisting> + + <para>The Location header contains the URL for the created resource. + If you want to unregister this, then do a HTTP DELETE on this + URL. + </para> + </listitem> + </orderedlist> + </section> + + <section> + <title>Creating a Push Subscription by Hand</title> + + <para>You can create a push XML file yourself if you do not want to go + through the REST interface to create a push subscription. There is some + additional information you need to provide though. First, in the root + element, you must define a unique id attribute. You must also define a + destination element to specify the queue you should register a consumer + with. For a topic, the destination element is the name of the + subscription that will be created. For a topic, you must also specify the + topic name within the topic element. + </para> + + <para>Here's an example of a hand-created queue registration. This file + must go in the directory specified by the queue-push-store-dir config + variable defined in Chapter 2: + </para> + + <programlisting> +<push-registration id="111"> + <destination>jms.queue.bar</destination> + <durable>true</durable> + <link rel="template" href="http://somewhere.com/resources/{id}/messages" method="PUT"/> +</push-registration></programlisting> + + <para>Here's an example of a hand-created topic registration. This file + must go in the directory specified by the topic-push-store-dir config + variable defined in Chapter 2: + </para> + + <programlisting> +<push-topic-registration id="112"> + <destination>my-subscription-1</destination + <durable>true</durable> + <link rel="template" href="http://somewhere.com/resources/{id}/messages" method="PUT"/> + <topic>jms.topic.foo</topic> +</push-topic-registration></programlisting> + </section> + + <section> +
<TRUNCATED>