2151 lines
94 KiB
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
|
|
|
|
<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 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>
|
|
<web-app>
|
|
<listener>
|
|
<listener-class>
|
|
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
|
|
</listener-class>
|
|
</listener>
|
|
|
|
<listener>
|
|
<listener-class>
|
|
org.apache.activemq.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
|
|
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>
|
|
<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.apache.activemq.rest</groupId>
|
|
<artifactId>activemq-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.apache.activemq, 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.apache.activemq, 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 "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>
|
|
<web-app>
|
|
<listener>
|
|
<listener-class>
|
|
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
|
|
</listener-class>
|
|
</listener>
|
|
|
|
<listener>
|
|
<listener-class>
|
|
org.apache.activemq.rest.integration.ActiveMQBootstrapListener
|
|
</listener-class>
|
|
</listener>
|
|
|
|
<listener>
|
|
<listener-class>
|
|
org.apache.activemq.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 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>
|
|
<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.apache.activemq</groupId>
|
|
<artifactId>activemq-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.apache.activemq</groupId>
|
|
<artifactId>activemq-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.apache.activemq.rest</groupId>
|
|
<artifactId>activemq-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 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>
|
|
<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 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
|
|
|
|
<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 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
|
|
|
|
<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 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
|
|
|
|
<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 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
|
|
|
|
<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 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
|
|
|
|
<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 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
|
|
|
|
<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 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>
|
|
<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
|
|
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>
|
|
<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>
|
|
<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>
|
|
<push-topic-registration>
|
|
<durable>true</durable>
|
|
<link rel="template" href="http://somewhere.com/resources/{id}/messages" method="POST"/>
|
|
<authentication>
|
|
<basic-auth>
|
|
<username>guest</username>
|
|
<password>geheim</password>
|
|
</basic-auth>
|
|
</authentication>
|
|
</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>
|
|
<push-topic-registration>
|
|
<durable>true</durable>
|
|
<link rel="template" href="http://somewhere.com/resources/{id}/messages" method="POST"/>
|
|
<header name="secret-header">jfdiwe3321</header>
|
|
</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
|
|
|
|
<queue name="testQueue">
|
|
<durable>true</durable>
|
|
</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
|
|
|
|
<topic name="testTopic">
|
|
</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>
|