activemq-artemis/docs/user-manual/en/rest.xml

2151 lines
94 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!-- ============================================================================= -->
<!-- Licensed to the Apache Software Foundation (ASF) under one or more -->
<!-- contributor license agreements. See the NOTICE file distributed with -->
<!-- this work for additional information regarding copyright ownership. -->
<!-- The ASF licenses this file to You under the Apache License, Version 2.0 -->
<!-- (the "License"); you may not use this file except in compliance with -->
<!-- the License. You may obtain a copy of the License at -->
<!-- -->
<!-- http://www.apache.org/licenses/LICENSE-2.0 -->
<!-- -->
<!-- Unless required by applicable law or agreed to in writing, software -->
<!-- distributed under the License is distributed on an "AS IS" BASIS, -->
<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -->
<!-- See the License for the specific language governing permissions and -->
<!-- limitations under the License. -->
<!-- ============================================================================= -->
<chapter id="rest">
<title>REST Interface</title>
<para>The ActiveMQ REST interface allows you to leverage the reliability
and scalability features of ActiveMQ 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
&lt;order>
&lt;name>Bill&lt;/name>
&lt;item>iPhone 4&lt;/item>
&lt;cost>$199.99&lt;/cost>
&lt;/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 ActiveMQ as a JMS message and distributed through core ActiveMQ.
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 ActiveMQ'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 ActiveMQ 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 ActiveMQ.
</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 ActiveMQ (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
ActiveMQ's REST interface.
</para>
</listitem>
<listitem>
<para>Leverage the reliability, scalability, and clustering features
of ActiveMQ 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>ActiveMQ'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 ActiveMQ REST interface is a little bit different depending whether
ActiveMQ is already installed and configured for your environment (e.g.
you're deploying within JBoss AS 7) or you want the ActiveMQ REST
WAR to startup and manage the ActiveMQ 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 ActiveMQ REST
interface in an environment that already has ActiveMQ installed and
running, e.g. JBoss AS 7. You must create a Web archive
(.WAR) file with the following web.xml settings:
</para>
<programlisting>
&lt;web-app>
&lt;listener>
&lt;listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
&lt;/listener-class>
&lt;/listener>
&lt;listener>
&lt;listener-class>
org.apache.activemq.rest.integration.RestMessagingBootstrapListener
&lt;/listener-class>
&lt;/listener>
&lt;filter>
&lt;filter-name>Rest-Messaging&lt;/filter-name>
&lt;filter-class>
org.jboss.resteasy.plugins.server.servlet.FilterDispatcher
&lt;/filter-class>
&lt;/filter>
&lt;filter-mapping>
&lt;filter-name>Rest-Messaging&lt;/filter-name>
&lt;url-pattern>/*&lt;/url-pattern>
&lt;/filter-mapping>
&lt;/web-app></programlisting>
<para>Within your WEB-INF/lib directory you must have the
activemq-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>
&lt;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">
&lt;modelVersion>4.0.0&lt;/modelVersion>
&lt;groupId>org.somebody&lt;/groupId>
&lt;artifactId>myapp&lt;/artifactId>
&lt;packaging>war&lt;/packaging>
&lt;name>My App&lt;/name>
&lt;version>0.1-SNAPSHOT&lt;/version>
&lt;repositories>
&lt;repository>
&lt;id>jboss&lt;/id>
&lt;url>http://repository.jboss.org/nexus/content/groups/public/&lt;/url>
&lt;/repository>
&lt;/repositories>
&lt;build>
&lt;plugins>
&lt;plugin>
&lt;groupId>org.apache.maven.plugins&lt;/groupId>
&lt;artifactId>maven-compiler-plugin&lt;/artifactId>
&lt;configuration>
&lt;source>1.6&lt;/source>
&lt;target>1.6&lt;/target>
&lt;/configuration>
&lt;/plugin>
&lt;/plugins>
&lt;/build>
&lt;dependencies>
&lt;dependency>
&lt;groupId>org.apache.activemq.rest&lt;/groupId>
&lt;artifactId>activemq-rest&lt;/artifactId>
&lt;version>2.3.0-SNAPSHOT&lt;/version>
&lt;/dependency>
&lt;/dependencies>
&lt;/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.apache.activemq, org.jboss.netty</programlisting>
<para>You can add this to the<literal>&lt;plugins></literal>
section of the pom.xml to create this entry automatically:
</para>
<programlisting>
&lt;plugin>
&lt;groupId>org.apache.maven.plugins&lt;/groupId>
&lt;artifactId>maven-war-plugin&lt;/artifactId>
&lt;configuration>
&lt;archive>
&lt;manifestEntries>
&lt;Dependencies>org.apache.activemq, org.jboss.netty&lt;/Dependencies>
&lt;/manifestEntries>
&lt;/archive>
&lt;/configuration>
&lt;/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 "activemq-rest.war" then clients will access it at, e.g.
http://localhost:8080/activemq-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 ActiveMQ Along with REST</title>
<para>You can bootstrap ActiveMQ within your WAR as well. To do this, you
must have the ActiveMQ core and JMS jars along with Netty, Resteasy, and
the ActiveMQ REST jar within your WEB-INF/lib. You must also have a
activemq-configuration.xml, activemq-jms.xml, and activemq-users.xml config
files within WEB-INF/classes. The examples that come with the ActiveMQ
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>
&lt;web-app>
&lt;listener>
&lt;listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
&lt;/listener-class>
&lt;/listener>
&lt;listener>
&lt;listener-class>
org.apache.activemq.rest.integration.ActiveMQBootstrapListener
&lt;/listener-class>
&lt;/listener>
&lt;listener>
&lt;listener-class>
org.apache.activemq.rest.integration.RestMessagingBootstrapListener
&lt;/listener-class>
&lt;/listener>
&lt;filter>
&lt;filter-name>Rest-Messaging&lt;/filter-name>
&lt;filter-class>
org.jboss.resteasy.plugins.server.servlet.FilterDispatcher
&lt;/filter-class>
&lt;/filter>
&lt;filter-mapping>
&lt;filter-name>Rest-Messaging&lt;/filter-name>
&lt;url-pattern>/*&lt;/url-pattern>
&lt;/filter-mapping>
&lt;/web-app></programlisting>
<para>Here's a Maven pom.xml file for creating a WAR for this
environment. Make sure your activemq configuration files are within the
src/main/resources directory so that they are stuffed within the WAR's
WEB-INF/classes directory!
</para>
<programlisting>
&lt;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">
&lt;modelVersion>4.0.0&lt;/modelVersion>
&lt;groupId>org.somebody&lt;/groupId>
&lt;artifactId>myapp&lt;/artifactId>
&lt;packaging>war&lt;/packaging>
&lt;name>My App&lt;/name>
&lt;version>0.1-SNAPSHOT&lt;/version>
&lt;repositories>
&lt;repository>
&lt;id>jboss&lt;/id>
&lt;url>http://repository.jboss.org/nexus/content/groups/public/&lt;/url>
&lt;/repository>
&lt;/repositories>
&lt;build>
&lt;plugins>
&lt;plugin>
&lt;groupId>org.apache.maven.plugins&lt;/groupId>
&lt;artifactId>maven-compiler-plugin&lt;/artifactId>
&lt;configuration>
&lt;source>1.6&lt;/source>
&lt;target>1.6&lt;/target>
&lt;/configuration>
&lt;/plugin>
&lt;/plugins>
&lt;/build>
&lt;dependencies>
&lt;dependency>
&lt;groupId>org.apache.activemq&lt;/groupId>
&lt;artifactId>activemq-core&lt;/artifactId>
&lt;version>2.3.0-SNAPSHOT&lt;/version>
&lt;/dependency>
&lt;dependency>
&lt;groupId>io.netty&lt;/groupId>
&lt;artifactId>netty&lt;/artifactId>
&lt;version>3.4.5.Final&lt;/version>
&lt;/dependency>
&lt;dependency>
&lt;groupId>org.apache.activemq&lt;/groupId>
&lt;artifactId>activemq-jms&lt;/artifactId>
&lt;version>2.3.0-SNAPSHOT&lt;/version>
&lt;/dependency>
&lt;dependency>
&lt;groupId>org.jboss.spec.javax.jms&lt;/groupId>
&lt;artifactId>jboss-jms-api_2.0_spec&lt;/artifactId>
&lt;version>1.0.0.Final&lt;/version>
&lt;/dependency>
&lt;dependency>
&lt;groupId>org.apache.activemq.rest&lt;/groupId>
&lt;artifactId>activemq-rest&lt;/artifactId>
&lt;version>2.3.0-SNAPSHOT&lt;/version>
&lt;/dependency>
&lt;dependency>
&lt;groupId>org.jboss.resteasy&lt;/groupId>
&lt;artifactId>resteasy-jaxrs&lt;/artifactId>
&lt;version>2.3.4.Final&lt;/version>
&lt;/dependency>
&lt;dependency>
&lt;groupId>org.jboss.resteasy&lt;/groupId>
&lt;artifactId>resteasy-jaxb-provider&lt;/artifactId>
&lt;version>2.3.4.Final&lt;/version>
&lt;/dependency>
&lt;/dependencies>
&lt;/project></programlisting>
</section>
<section id="configuration">
<title>REST Configuration</title>
<para>The ActiveMQ 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>
&lt;rest-messaging>
&lt;server-in-vm-id>0&lt;/server-in-vm-id>
&lt;use-link-headers>false&lt;/use-link-headers>
&lt;default-durable-send>false&lt;/default-durable-send>
&lt;dups-ok>true&lt;/dups-ok>
&lt;topic-push-store-dir>topic-push-store&lt;/topic-push-store-dir>
&lt;queue-push-store-dir>queue-push-store&lt;/queue-push-store-dir>
&lt;producer-time-to-live>0&lt;/producer-time-to-live>
&lt;producer-session-pool-size>10&lt;/producer-session-pool-size>
&lt;session-timeout-task-interval>1&lt;/session-timeout-task-interval>
&lt;consumer-session-timeout-seconds>300&lt;/consumer-session-timeout-seconds>
&lt;consumer-window-size>-1&lt;/consumer-window-size>
&lt;/rest-messaging></programlisting>
<para>Let's give an explanation of each config option.</para>
<itemizedlist>
<listitem>
<para><literal>server-in-vm-id</literal>. The ActiveMQ REST
impl uses the IN-VM transport to communicate with ActiveMQ.
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 ActiveMQ 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 ActiveMQ 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 ActiveMQ one of the
same name. It will be used by sessions created by the
ActiveMQ REST implementation.
</para>
</listitem>
</itemizedlist>
</section>
</section>
<section id="basics">
<title>ActiveMQ REST Interface Basics</title>
<para>The ActiveMQ 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
ActiveMQ 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>activemq-jms.xml</literal> file, the URI name should be
"jms.topic.foo". If you have configured a JMS queue name "bar" within
your <literal>activemq-jms.xml</literal> file, the URI name should be
"jms.queue.bar". Internally, ActiveMQ 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 ActiveMQ 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 ActiveMQ 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 ActiveMQ 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">ActiveMQ 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 ActiveMQ 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
&lt;order>
&lt;name>Bill&lt;/name>
&lt;item>iPhone4&lt;/name>
&lt;cost>$199.99&lt;/cost>
&lt;/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
&lt;order>
&lt;name>Monica&lt;/name>
&lt;item>iPad&lt;/item>
&lt;cost>$499.99&lt;/cost>
&lt;/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 ActiveMQ
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">ActiveMQ 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
&lt;order>
&lt;name>Bill&lt;/name>
&lt;item>iPhone4&lt;/name>
&lt;cost>$199.99&lt;/cost>
&lt;/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
&lt;order>
&lt;name>Bill&lt;/name>
&lt;item>iPhone4&lt;/name>
&lt;cost>$199.99&lt;/cost>
&lt;/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
&lt;order>
&lt;name>Monica&lt;/name>
&lt;item>iPad&lt;/name>
&lt;cost>$499.99&lt;/cost>
&lt;/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 ActiveMQ 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 ActiveMQ'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.apache.activemq.core.server] (Thread-3 (ActiveMQ-remoting-threads-ActiveMQServerImpl::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 ActiveMQ'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
&lt;order>
&lt;name>Bill&lt;/name>
&lt;item>iPhone4&lt;/item>
&lt;cost>$199.99&lt;/cost>
&lt;/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&amp;priority=3
Host: example.com
Content-Type: application/xml
&lt;order>
&lt;name>Bill&lt;/name>
&lt;item>iPhone4&lt;/item>
&lt;cost>$199.99&lt;/cost>
&lt;/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 ActiveMQ 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">ActiveMQ 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 ActiveMQ 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
&lt;order>...&lt;/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 ActiveMQ 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
&lt;order>...&lt;/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&amp;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
&lt;order>...&lt;/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 ActiveMQ 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
&lt;order>...&lt;/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 ActiveMQ 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 ActiveMQ 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>
&lt;push-registration>
&lt;durable>false&lt;/durable>
&lt;selector>&lt;![CDATA[
SomeAttribute > 1
]]&gt;
&lt;/selector>
&lt;link rel="push" href="http://somewhere.com" type="application/json" method="PUT"/>
&lt;maxRetries>5&lt;/maxRetries>
&lt;retryWaitMillis>1000&lt;/retryWaitMillis>
&lt;disableOnFailure>true&lt;/disableOnFailure>
&lt;/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
ActiveMQ 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 ActiveMQ 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
ActiveMQ REST endpoint reliably. Here's an example:
</para>
<programlisting>
&lt;push-registration>
&lt;link rel="destination" href="http://somewhere.com/queues/jms.queue.foo"/>
&lt;/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>
&lt;push-registration>
&lt;link rel="template" href="http://somewhere.com/resources/{id}/messages" method="PUT"/>
&lt;/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>
&lt;push-registration>
&lt;link href="http://somewhere.com" type="application/json" method="PUT"/>
&lt;/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>
&lt;push-topic-registration>
&lt;durable>true&lt;/durable>
&lt;selector>&lt;![CDATA[
SomeAttribute > 1
]]&gt;
&lt;/selector>
&lt;link rel="template" href="http://somewhere.com/resources/{id}/messages" method="POST"/>
&lt;/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
&lt;push-registration>
&lt;link rel="destination" href="http://somewhere.com/queues/jms.queue.foo"/>
&lt;/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
&lt;push-registration>
&lt;link rel="template" href="http://somewhere.com/resources/{id}"/>
&lt;/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>
&lt;push-registration id="111">
&lt;destination>jms.queue.bar&lt;/destination>
&lt;durable>true&lt;/durable>
&lt;link rel="template" href="http://somewhere.com/resources/{id}/messages" method="PUT"/>
&lt;/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>
&lt;push-topic-registration id="112">
&lt;destination>my-subscription-1&lt;/destination
&lt;durable>true&lt;/durable>
&lt;link rel="template" href="http://somewhere.com/resources/{id}/messages" method="PUT"/>
&lt;topic>jms.topic.foo&lt;/topic>
&lt;/push-topic-registration></programlisting>
</section>
<section>
<title>Pushing to Authenticated Servers</title>
<para>Push subscriptions only support BASIC and DIGEST authentication
out of the box. Here is an example of adding BASIC
authentication:
</para>
<programlisting>
&lt;push-topic-registration>
&lt;durable>true&lt;/durable>
&lt;link rel="template" href="http://somewhere.com/resources/{id}/messages" method="POST"/>
&lt;authentication>
&lt;basic-auth>
&lt;username>guest&lt;/username>
&lt;password>geheim&lt;/password>
&lt;/basic-auth>
&lt;/authentication>
&lt;/push-topic registration></programlisting>
<para>For DIGEST, just replace basic-auth with digest-auth.</para>
<para>For other authentication mechanisms, you can register headers you
want transmitted with each request. Use the header element with the name
attribute representing the name of the header. Here's what custom
headers might look like:
</para>
<programlisting>
&lt;push-topic-registration>
&lt;durable>true&lt;/durable>
&lt;link rel="template" href="http://somewhere.com/resources/{id}/messages" method="POST"/>
&lt;header name="secret-header">jfdiwe3321&lt;/header>
&lt;/push-topic registration></programlisting>
</section>
</section>
<section>
<title>Creating Destinations</title>
<para>You can create a durable queue or topic through the REST interface.
Currently you cannot create a temporary queue or topic. To create a queue
you do a POST to the relative URL /queues with an XML representation of
the queue. The XML syntax is the same queue syntax that you would specify
in activemq-jms.xml if you were creating a queue there. For example:
</para>
<programlisting>
POST /queues
Host: example.com
Content-Type: application/activemq.jms.queue+xml
&lt;queue name="testQueue">
&lt;durable>true&lt;/durable>
&lt;/queue>
--- Response ---
HTTP/1.1 201 Created
Location: http://example.com/queues/jms.queue.testQueue</programlisting>
<para>Notice that the Content-Type is application/activemq.jms.queue+xml.</para>
<para>Here's what creating a topic would look like:</para>
<programlisting>
POST /topics
Host: example.com
Content-Type: application/activemq.jms.topic+xml
&lt;topic name="testTopic">
&lt;/topic>
--- Response ---
HTTP/1.1 201 Created
Location: http://example.com/topics/jms.topic.testTopic</programlisting>
</section>
<section>
<title>Securing the ActiveMQ REST Interface</title>
<section>
<title>Within JBoss Application server</title>
<para>Securing the ActiveMQ REST interface is very simple with the JBoss
Application Server. You turn on authentication for all URLs within your
WAR's web.xml, and let the user Principal to propagate to ActiveMQ. This
only works if you are using the JBossSecurityManager with ActiveMQ. See
the ActiveMQ documentation for more details.
</para>
</section>
<section>
<title>Security in other environments</title>
<para>To secure the ActiveMQ REST interface in other environments you
must role your own security by specifying security constraints with your
web.xml for every path of every queue and topic you have deployed. Here
is a list of URI patterns:
</para>
<table>
<tgroup cols="2">
<tbody>
<row>
<entry>/queues</entry>
<entry>secure the POST operation to secure queue creation</entry>
</row>
<row>
<entry>/queues/{queue-name}</entry>
<entry>secure the GET HEAD operation to getting information about the queue.</entry>
</row>
<row>
<entry>/queues/{queue-name}/create/*</entry>
<entry>secure this URL pattern for producing messages.</entry>
</row>
<row>
<entry>/queues/{queue-name}/pull-consumers/*</entry>
<entry>secure this URL pattern for pulling messages.</entry>
</row>
<row>
<entry>/queues/{queue-name}/push-consumers/*</entry>
<entry>secure this URL pattern for pushing messages.</entry>
</row>
<row>
<entry>/topics</entry>
<entry>secure the POST operation to secure topic creation</entry>
</row>
<row>
<entry>/topics/{topic-name}</entry>
<entry>secure the GET HEAD operation to getting information about the topic.</entry>
</row>
<row>
<entry>/topics/{topic-name}/create/*</entry>
<entry>secure this URL pattern for producing messages.</entry>
</row>
<row>
<entry>/topics/{topic-name}/pull-subscriptions/*</entry>
<entry>secure this URL pattern for pulling messages.</entry>
</row>
<row>
<entry>/topics/{topic-name}/push-subscriptions/*</entry>
<entry>secure this URL pattern for pushing messages.</entry>
</row>
</tbody>
</tgroup>
</table>
</section>
</section>
<section>
<title>Mixing JMS and REST</title>
<para>The ActiveMQ REST interface supports mixing JMS and REST producers
and consumers. You can send an ObjectMessage through a JMS Producer, and
have a REST client consume it. You can have a REST client POST a message
to a topic and have a JMS Consumer receive it. Some simple transformations
are supported if you have the correct RESTEasy providers installed.
</para>
<section>
<title>JMS Producers - REST Consumers</title>
<para>If you have a JMS producer, the ActiveMQ REST interface only
supports ObjectMessage type. If the JMS producer is aware that there may
be REST consumers, it should set a JMS property to specify what
Content-Type the Java object should be translated into by REST clients.
The ActiveMQ REST server will use RESTEasy content handlers
(MessageBodyReader/Writers) to transform the Java object to the type
desired. Here's an example of a JMS producer setting the content type of
the message.
</para>
<programlisting>
ObjectMessage message = session.createObjectMessage();
message.setStringProperty(org.apache.activemq.rest.HttpHeaderProperty.CONTENT_TYPE, "application/xml");</programlisting>
<para>If the JMS producer does not set the content-type, then this
information must be obtained from the REST consumer. If it is a pull
consumer, then the REST client should send an Accept header with the
desired media types it wants to convert the Java object into. If the
REST client is a push registration, then the type attribute of the link
element of the push registration should be set to the desired
type.
</para>
</section>
<section>
<title>REST Producers - JMS Consumers</title>
<para>If you have a REST client producing messages and a JMS consumer,
ActiveMQ REST has a simple helper class for you to transform the HTTP
body to a Java object. Here's some example code:
</para>
<programlisting>
public void onMessage(Message message)
{
MyType obj = org.apache.activemq.rest.Jms.getEntity(message, MyType.class);
}</programlisting>
<para>The way the <literal>getEntity()</literal> method works is that if
the message is an ObjectMessage, it will try to extract the desired type
from it like any other JMS message. If a REST producer sent the message,
then the method uses RESTEasy to convert the HTTP body to the Java
object you want. See the Javadoc of this class for more helper
methods.
</para>
</section>
</section>
</chapter>