This page describes the requirements and initial design of the new Qpid JMS Client that supports AMQP 1.0 (simply referred to as the "Qpid JMS Client" below).

Requirements

Functional requirements

The following sub-sections allocate the functional requirements to several sequential project phases.

Phase 0

This is the initial set-up of the basics.

This will implicitly require work on:

Phase 1
Phase 2

 

Non-functional requirements

Out of scope

Design

Layers

+================================+
|
| JMS
|
| Implementations of javax.jms
|
+================================+
               |
               |
              \|/
+================================+
|
| AMQP
|
| Wrappers around Proton's engine
| classes, primarily to add locking
|
+================================+
            |                 |
            |                 |
           \|/                |
+======================+      |
|       Proton         |      |
|                      |      |
| Message |  Engine    |      |
|         |            |      |
+=========+============+      |
                    /|\       |
                     |        |
                     |       \|/
                   +====================
                   |
                   | JMS Driver interface
                   |
                   +=====================
                            /|\
                             |
                        +--------------+
                        |              |
                        |              |
                        |              |
                  +===========+        +==============
                  |                    |
                  |  Socket            |
                  |  driver            | Another driver,
                  |  (might use        | e.g. in-VM
                  |  Proton's Driver)  |
                  |                    |
                  +===========+        +==============
                    |
                    | TCP/IP
                    |
                   \|/
+================================+
|
| Broker
|
+================================+

Public API

The public API consists of:

Note that the logging output and logging category names also constitute a public interface.

In the future, the public API may need to be extended or more precisely specified (e.g. to include details of the mapping between JMS and AMQP when using MapMessage and ObjectMessage etc).

Locking

Threads fall into three categories:

  1. Application threads using the JMS API. Only use the top half of the Proton API.
  2. One or more driver threads for I/O. Managed by the JMS client. Only use the bottom half of the Proton API.
  3. Threads managed by the JMS client that call application code (e.g. MessageListener.onMessage, ExceptionListener.onException and CompletionListener.xxx).

For a given connection:

  1. State shared by multiple application threads (namely the state of the objects in the JMS layer) is guarded by the JMS ConnectionLock.
  2. State shared by the application and driver threads is guarded by the AmqpConnection lock. This shared state is:
    1. A small number of flags the application and driver threads use to indicate to each other that "something has changed".
    2. The Proton objects. This sharing occurs inside Proton but needs to be guarded by the JMS client because Proton itself is not thread-safe.

TODO extend the locking scheme to include the JMS Client-owned threads that call application code.

Synchronous operations must follow the locking scheme indicated by the following pseudo-code:

Where operations need to wait for a "remote" operation to complete, they must follow the scheme indicated by the following pseudo-code:

This ensures that when the predicate object is invoked the thread will already possess both the JmsConnection lock and the AmqpConnection lock.

I/O layer

TODO - design I/O
Use Netty?

Logging

TODO

Testing strategy

Goals:

Test types

We will write a number of each of the following tests.

Module tests

The following diagram shows how module tests will work:

+================================+
|
| JUnit test
|
| 1. Create an in-process TestAmqpPeer
| 2. Set up TestAmqpPeer behaviour and expectations
| 3. Call some JMS methods
| 
+================================+
   |       |             |
   |       |             |
   |       |            \|/
   |       |    +================================+
   |       |    |
   |       |    | JMS client
   |       |    |
   |       |    +================================+
   |       |                |                 |
   |    latch/              |                 |
   |     unlatch etc        |                 |
   |       |               \|/                |
   |       |    +======================+      |
   |       |    |       Proton         |      |
expect/    |    |                      |      |
 assert    |    | Message |  Engine    |      |
   |       |    |         |            |      |
   |       |    +=========+============+      |
   |       |                /|\               |
   |       |                 |                |
   |      \|/               \|/              \|/
   |    +=======================================
   |    |
   |    | In-process Driver
   |    |
   |    +=======================================
   |                      /|\
   |                       |
   |                       | Bytes
   |                       |
  \|/                     \|/
+===================================+
|
| TestAmqpPeer
|
+===================================+
In-process Driver implementation
TestAmqpPeer implementation

Each test will give behaviour to a TestAmqpPeer. This behaviour will mostly be expressed in terms of AMQP frames. Here are prose examples (we don't yet know how these will be expressed in code)

Expected frame

Frame to respond with

An Open frame with container-id "xyz"

The following canned Open frame: ...

Expected frame

Frame to respond with

An Open frame with container-id "xyz"

The following sequence of frames representing a refused connection

Expected frame

Frame to respond with

Any Open

Canned Open frame: ...

Any Begin

Canned Begin frame: ...

An Attach matching the following criteria: ...

The following Attach: ...

In order to increase our confidence in the AMQP interoperabilty of the JMS client, we want to avoid using the same Proton stack in the test peer as in the client. Therefore, Decoding and encoding will be done using proton-api's Data class. The AmqpTestPeer will minimise its use of other Proton classes.

SASL

Most tests will perform minimal SASL negotiation, simulating simple, successful SASL authentication. We will write specific SASL tests to exercise more complex scenarios.