Using the Progress Adapter for Fuse Message Broker

Using the OpenEdge Adapter for Fuse Message Broker

Getting started

Prerequisites

  • The message broker must be running. If the broker requires authentication
    you must know a user name and password.
  • The STOMP listener must be accessible over the network. You must know the
    host name and port number.
  • The directory stompAdapter from the adapter code distribution must be in
    the PROPATH.

Defining the connection with the broker

In JMS you need 2 objects to collect the connection parameters with the
message broker: a ConnectionFactory that knows where to connect,
and a Connection object that knows how to authenticate for a
session (next step). In this example we assume that the broker is running on
the develement machine and the STOMP listener uses the default port number.

factory = NEW nl.flusso.stomp.ConnectionFactory('localhost':U, 61613).
connection = factory:createConnection('username':U, 'password':U).

If your broker does not require authentication, you can supply the unknown
value for both user name and password:

connection = factory:createConnection(?, ?).

Starting a session and cleaning up

A JMS session encapsulates all communication with the broker (using an ABL
SOCKET object). After using the session object for exchanging messages with the
broker, we need to release operating system resources and tell the broker we do
not want to receive any more data. The broker uses this notification to clean
up resources allocated for us.

jmsSession = connection:createSession(FALSE, {&CLIENT_ACKNOWLEDGE}).
/* Exchange messages with the broker, see below. */
FINALLY:
  jmsSession:close().
END FINALLY.

In the example above we indicate that we do not want to group sent messages
using explicit transactions, but we do want to acknowledge the successful
receipt and processing of messages that the broker sent to us. If we would
choose automatic acknowledgement, messages for us are considered done when they
are sent by the STOMP subsystem of the broker, before we even know that there
are bytes on their way over the network channel.

Preparing for message exchange

A JMS Destination is an abstract concept that denotes the place
where we sent messages to or receive messages from. It could be either a
Queue or a Topic. We send messages to a
Destination using a MessageProducer and receive from
a Destination using a MessageConsumer.

destination = jmsSession:createQueue('SampleQ1':U).
producer = jmsSession:createProducer(destination).

or

destination = jmsSession:createQueue('SampleQ1':U).
consumer = jmsSession:createConsumer(destination).

Sending messages

After all the preparations above we are ready to create a message and send
it.

messageToSend = jmsSession:createTextMessage('Hello world':U).
producer:send(messageToSend).

Full example, now including all declarations:

USING javax.jms.*.
ROUTINE-LEVEL ON ERROR UNDO, THROW.
{javax/jms/Session.i}

DEFINE VARIABLE factory       AS ConnectionFactory NO-UNDO.
DEFINE VARIABLE connection    AS Connection        NO-UNDO.
DEFINE VARIABLE jmsSession    AS Session           NO-UNDO.
DEFINE VARIABLE destination   AS Queue             NO-UNDO.
DEFINE VARIABLE producer      AS MessageProducer   NO-UNDO.
DEFINE VARIABLE messageToSend AS TextMessage       NO-UNDO.

factory = NEW nl.flusso.stomp.ConnectionFactory('localhost':U, 61613).
connection = factory:createConnection('username':U, 'password':U).
jmsSession = connection:createSession(FALSE, {&CLIENT_ACKNOWLEDGE}).
destination = jmsSession:createQueue('SampleQ1':U).
producer = jmsSession:createProducer(destination).

messageToSend = jmsSession:createTextMessage('Hello world':U).
producer:send(messageToSend).

FINALLY:
  jmsSession:close().
END FINALLY.

Receiving messages

Receiving a message is somewhat more complicated than sending one, because
we need to find out what kind of message we received before we can process it.
In ABL we do that by trying to cast the message to a type that we can handle.
In the example below we ignore messages of other types, and in particular we do
not acknowledge the receipt of those messages. That way those messages will
remain available for other clients to process.

USING javax.jms.*.
ROUTINE-LEVEL ON ERROR UNDO, THROW.
{javax/jms/Session.i}

DEFINE VARIABLE factory        AS ConnectionFactory NO-UNDO.
DEFINE VARIABLE connection     AS Connection        NO-UNDO.
DEFINE VARIABLE jmsSession     AS Session           NO-UNDO.
DEFINE VARIABLE destination    AS Queue             NO-UNDO.
DEFINE VARIABLE consumer       AS MessageConsumer   NO-UNDO.
DEFINE VARIABLE genericMessage AS Message           NO-UNDO.
DEFINE VARIABLE textMessage    AS TextMessage       NO-UNDO.
DEFINE VARIABLE textBody       AS LONGCHAR          NO-UNDO.
DEFINE VARIABLE textLength     AS INTEGER           NO-UNDO.

factory = NEW nl.flusso.stomp.ConnectionFactory('localhost':U, 61613).
connection = factory:createConnection('username':U, 'password':U).
jmsSession = connection:createSession(FALSE, {&CLIENT_ACKNOWLEDGE}).
destination = jmsSession:createQueue('SampleQ1':U).
consumer = jmsSession:createConsumer(destination).

genericMessage = consumer:receive().
textMessage = CAST(genericMessage, TextMessage) NO-ERROR.
IF NOT ERROR-STATUS:ERROR THEN
DO:
  textBody = textMessage:text.
  textLength = LENGTH(textBody).
  MESSAGE (IF textLength < 100 THEN STRING(textBody) ELSE 'long message') VIEW-AS ALERT-BOX.
  textMessage:acknowledge().
END.

FINALLY:
  jmsSession:close().
END FINALLY.

Hints

Use interfaces

In the examples above all class-based object variables are defined as having
an ABL interface type. That is is a recommended practice for general OO
programming, but in particular when using the OpenEdge Adapter for Fuse Message
Broker. Developers reserve the right to change all implementation classes in
package nl.flusso.stomp, except ConnectionFactory. All operations
after obtaining a ConnectionFactory instance can be accomplished
using only interface type variables and parameters.

More examples

The stompTests directory in the source distribution contains our general
test cases. They show some more possible usage scenarios.

Note that those tests (and the adapter implementation itself) tend to delete
all objects explicitly, instead of relying on the 10.2 garbage collector. That
is not because we think it is better or we prefer to do unnecessary work, but
it is part of the preparation for a planned backport to 10.1C.

Full documentation

The OpenEdge Adapter for Fuse Message Broker implements a pure subset of the
standard JMS API. The part that is implemented, should follow the specification
faithfully. There are no intended extensions of the specification (except as
workarounds for OpenEdge™ limitations, see below).

In a separate document we describe what part of the
JMS API is currently supported. All details of the API are described in the
standard JMS 1.1 documentation (available online) and in
several works available at your favorite book seller.

Java API to ABL translation rules

The following rules were applied for translating the JMS API specification
to ABL definitions.

  • In general Java interfaces correspond with ABL interfaces, Java
    operations with ABL methods, etcetera. Exceptions are mentioned below.
  • Property getter and setter operations are translated to ABL PROPERTY
    members of classes and interfaces.
  • Constants (final static interface members) are translated to preprocessor
    definitions in an include named after the interface, e.g.
    javax/jms/Session.i.
  • We had to introduce a lot of workarounds for current OpenEdge™
    limitations. Many of the workarounds involved introducing extra method
    overloads to the JMS interfaces. We tried to make this transparent for
    users of the adapter, but in some cases you might find out that a
    workaround for a still missing OO feature is not the same as a
    straightforward implementation in a mature OO environment.