You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by jb...@apache.org on 2014/11/10 17:32:44 UTC
[33/51] [partial] activemq-6 git commit: ACTIVEMQ6-1 - Initial
HornetQ Donation Commit
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>