MQ Application Program
MQ Application Program
V7.5
Progress SonicMQ Application Programming Guide V7.5
Copyright © 2007 Sonic Software Corporation. All rights reserved. Sonic Software Corporation is
a wholly-owned subsidiary of Progress Software Corporation.
The Sonic Software products referred to in this document are also copyrighted, and all rights are
reserved by Sonic Software Corporation and/or its licensors, if any. This manual may not, in whole
or in part, be copied, translated, or reduced to any electronic medium or machine-readable form
without prior consent, in writing, from Sonic Software Corporation.
The information in this manual is subject to change without notice, and Sonic Software Corporation
assumes no responsibility for any errors that may appear in this document. The references in this
manual to specific platforms supported are subject to change.
Dynamic Routing Architecture, Sonic ESB, SonicMQ, Sonic Software (and design), Sonic
Orchestration Server, and SonicSynergy are registered trademarks of Sonic Software Corporation in
the U.S. and other countries. Connect Everything. Achieve Anything., Sonic SOA Suite, Sonic
Business Integration Suite, Sonic Collaboration Server, Sonic Continuous Availability Architecture,
Sonic Database Service, Sonic eXtensible Information Server, Sonic Workbench, and Sonic XML
Server are trademarks of Sonic Software Corporation in the U.S. and other countries. Progress is a
registered trademark of Progress Software Corporation in the U.S. and other countries. IBM is a
registered trademark of IBM Corporation. Java and all Java-based marks are trademarks or
registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. Any other
trademarks or service marks contained herein are the property of their respective owners.
SonicMQ Product Family includes code licensed from RSA Security, Inc. Some portions licensed
from IBM are available at http://oss.software.ibm.com/icu4j/.
SonicMQ Product Family includes code licensed from Mort Bay Consulting Pty. Ltd. The Jetty
Package is Copyright © 1998 Mort Bay Consulting Pty. Ltd. (Australia) and others.
SonicMQ Product Family includes the JMX Technology from Sun Microsystems, Inc. Use and
Distribution is subject to the Sun Community Source License available at
http://sun.com/software/communitysource.
SonicMQ Product Family includes files that are subject to the Netscape Public License Version 1.1
(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.mozilla.org/NPL/. Software distributed under the License is
distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing rights and limitations under the
License. The Original Code is Mozilla Communicator client code, released March 31, 1998. The
Initial Developer of the Original Code is Netscape Communications Corporation. Portions created
by Netscape are Copyright 1998-1999 Netscape Communications Corporation. All Rights
Reserved.
SonicMQ Product Family includes a version of the Saxon XSLT and XQuery Processor from
Saxonica Limited that has been modified by Progress Software Corporation. The contents of the
Saxon source code and the modified source code file (Configuration.java) are subject to the Mozilla
Public License Version 1.0 (the "License"); you may not use these files except in compliance with
the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ and a copy of
the license (MPL-1.0.html) can also be found in the installation directory, in the
Docs7.5/third_party_licenses folder, along with a copy of the modified code (Configuration.java);
and a description of the modifications can be found in the Progress SonicMQ v7.5 README file.
Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY
OF ANY KIND, either express or implied. See the License for the specific language governing
rights and limitations under the License. The Original Code is The SAXON XSLT and XQuery
Processor from Saxonica Limited. The Initial Developer of the Original Code is Michael Kay
http://www.saxonica.com/products.html). Portions created by Michael Kay are Copyright © 2001-
2005. All rights reserved. Portions created by Progress Software Corporation are Copyright © 2007.
All rights reserved.
April 2007
Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
About This Manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Typographical Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
SonicMQ Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Worldwide Technical Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Topic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .321
MessageProducer (Publisher) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .322
Creating the MessageProducer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
Creating the Message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
Sending Messages to a Topic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
MessageConsumer (Subscriber) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .324
Durable Subscriptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .324
Clusterwide Access to Durable Subscriptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Dynamic Routing with Pub/Sub Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .329
Administrative Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
Application Programming Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
Message Delivery with Remote Publishing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
Shared Subscriptions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .332
Features of Using Shared Subscriptions in Your Applications . . . . . . . . . . . . . . . . . . . . . . . 335
Usage Scenarios for Shared Subscriptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
Defining Shared Subscription Topic Subscribers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
Message Delivery to a Broker with Shared Subscriptions . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
JMS Interactions with Shared Subscriptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
Shared Subscriptions with Remote Publishing and Subscribing . . . . . . . . . . . . . . . . . . . . . . 348
MultiTopics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .352
Format of a MultiTopic String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
Creating MultiTopics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Adding Component Topics to a MultiTopic. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Publishing and Subscribing to MultiTopics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
MultiTopic Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
Typographical Conventions
This section describes the text-formatting conventions used in this guide and a description
of notes, warnings, and important messages. This guide uses the following typographical
conventions:
● Bold typeface in this font indicates keyboard key names (such as Tab or Enter) and
the names of windows, menu commands, buttons, and other Sonic user-interface
elements. For example, “From the File menu, choose Open.”
● Bold typeface in this font emphasizes new terms when they are introduced.
● Monospace typeface indicates text that might appear on a computer screen other than
the names of Sonic user-interface elements, including:
■ Code examples and code text that the user must enter
■ System output such as responses and error messages
■ Filenames, pathnames, and software component names, such as method names
● Bold monospace typeface emphasizes text that would otherwise appear in monospace
typeface to emphasize some computer input or output in context.
● Monospace typeface in italics or Bold monospace typeface in italics (depending
on context) indicates variables or placeholders for values you supply or that might
vary from one case to another.
This manual uses the following syntax notation conventions:
● Brackets ([ ]) in syntax statements indicate parameters that are optional.
● Braces ({ }) indicate that one (and only one) of the enclosed items is required. A
vertical bar (|) separates the alternative selections.
● Ellipses (...) indicate that you can choose one or more of the preceding items.
This guide highlights special kinds of information by shading the information area, and
indicating the type of alert in the left margin, as shown:
Note A Note flag indicates information that complements the main text flow. Such information
is especially helpful for understanding the concept or procedure being discussed.
Important An Important flag indicates information that must be acted upon within the given context
to successfully complete the procedure or task.
Warning A Warning flag indicates information that can cause loss of data or other damage if
ignored.
SonicMQ Documentation
SonicMQ provides the following documentation:
Getting Started with Progress SonicMQ An overview of the SonicMQ architecture and messaging
(PDF) concepts. Guides the user through some of the SonicMQ
sample applications to demonstrate basic messaging features.
Progress SonicMQ Application Takes you through the Java sample applications to illustrate
Programming Guide the design patterns they offer to your applications. Details
(PDF) each facet of the client functionality: connections, sessions,
transactions, producers and consumers, destinations,
messaging models, message types and message elements.
Complete information is included on hierarchical
namespaces, recoverable file channels, and distributed
transactions.
SonicMQ API Reference Online JavaDoc compilation of the exposed SonicMQ Java
(HTML) messaging client APIs.
Progress SonicMQ Configuration and Describes the container and broker configuration toolset in
Management Guide detail plus how to use the JNDI store for administered objects,
(PDF) and the certificate manager. Shows how to manage and
monitor deployed components including their metrics and
notifications.
Progress SonicMQ Administrative Provides information about moving development projects into
Programming Guide test and production environments. Describes recommended
(PDF) build procedures, domain mappings, and reporting features.
Management Application API Reference Online JavaDoc compilation of the exposed SonicMQ
(HTML) management configuration and runtime APIs.
Metrics and Notifications API Reference Online JavaDoc of the exposed SonicMQ management
(HTML) monitoring APIs.
Progress SonicMQ Performance Tuning Illustrates the buffers and caches that control message flow
Guide and capacities to help you understand how combinations of
(PDF) parameters can improve both throughput and service levels.
Sonic Event Monitor User’s Guide Provides a logging framework to track, record, or redirect
(PDF) metrics and notifications that monitor and manage
applications.
Edition platform, the JMS API is an integral part of the platform. JMS is a strategic
technology for J2EE. JMS will work in concert with other technologies to provide
reliable, asynchronous communication between components in a distributed computing
environment. The JMS specification notes that it does not address load balancing, fault
tolerance, error notification, administration, security, and repositories.
Programming Concepts
The design of SonicMQ provides full implementation of the Java Message Service (JMS)
specification with additional features that comprise a solution that is resilient enough for
Internet E-commerce in major enterprises.
Messaging involves the loose coupling of applications. This is accomplished by
maintaining an intelligent broker structure. A client can establish one or more connections
to a broker.
Client Client
Application Application
A B
Client Client
Broker
Application Application
F C
Client Client
Application Application
E D
The broker can join with other brokers to form clusters. Clusters and stand-alone brokers
are nearly equivalent when looked at as routing nodes.
Client Application
In JMS Version 1.1, the common interfaces were enhanced, allowing application
programmers to use the common interfaces to directly implement model-specific
functionality, rather than using the model-specific interfaces. These enhancements
provide two important benefits:
● A single transaction can now include Point-to-Point and Pub/Sub messages.
● The JMS programming model is simplified. Application programmers can now focus
on the common interfaces, without being forced to use two model-specific interfaces.
Also, if a single application requires both Point-to-Point and Pub/Sub functionality,
the application programmer is no longer forced to create redundant code.
Although the JMS 1.1 common interfaces effectively replace many of the model-specific
interfaces, the model-specific interfaces continue to be fully supported. This makes the
JMS 1.1 API fully backwards compatible. Any JMS application written before JMS 1.1
will continue to work as expected.
Despite the fact that the model-specific interfaces continue to be supported, the JMS 1.1
specification states that some of these interfaces might be deprecated in the future.
Consequently, if you are developing new JMS client applications, it is recommended that,
wherever possible, you use the common interfaces in place of the older model-specific
interfaces.
ConnectionFactory
Creates
Connection
Creates
Creates
MessageProducer MessageConsumer
Message
Sends Messages To Receives Messages From
Destination Destination
(Queue or Topic) (Queue or Topic)
ConnectionFactory
A ConnectionFactory is an object whose job is to create one or more Connection objects,
each of which establishes a connection to a SonicMQ broker (or cluster). A
ConnectionFactory can be implemented as an administered object.
Connection
A Connection is a conduit for communication between your client application and a
SonicMQ broker (or cluster). Each Connection is a single point for all communications
between the client application and the broker.
Session
A Connection can create one or more Session objects. A Session object is a single-
threaded context for producing and consuming messages. A Session object can create
Message objects, MessageProducer objects (which send outbound messages), and
MessageConsumer objects (which receive inbound messages). Each MessageProducer and
MessageConsumer object operates in the context of the Session that created it.
Transactions are scoped to Session objects. Starting in JMS 1.1, a transaction can include
both Point-to-Point and Pub/Sub messages.
The general terms consumer and producer are used to refer, respectively, to entities that
receive and send messages. Figure 4 illustrates the roles of producers and consumers.
C S
O E
N S
N PRODUCER publishes, sends
S
E I Messages Broker
C O CONSUMER subscribes, receives
T N
I
O
N
Destination
A Destination object represents a named location to which messages can be sent. A
Destination must be either a Topic or a Queue (both of which extend the Destination
interface). A Destination can be implemented as an administered object.
When you write an application using the JMS 1.1 common interfaces, you can use the
same interfaces for both messaging models, but you cannot create a “common”
Destination object. You create either a Topic or a Queue, which you can then upcast to a
Destination, if needed.
Message
A Message object holds your business data. For more information about messages, see
Chapter 6, “Messages.”
REPLY MECHANISM Consumer reacts to a Programmatic procedure See “Request and Reply
Consumer replies to the JMSReplyTo request where the consumer Samples” on page 115. See
producer’s request for by producing a publishes a reply. The also “Session Objects” on
reply. message to the topic content of the reply is not page 213 and “Reply-to
name in the specified. Typically the Mechanisms” on page 290.
JMSReplyTo field. JMSCorrelationID would
be replicated.
DEAD MESSAGE Set the properties Programmatic procedure See “Message Properties”
QUEUE that tell the broker to where the sender chooses on page 259. See also the
Sender/publisher can set provide special to set the property Dynamic Routing
properties to either or both handling when the JMS_SonicMQ information in the Progress
re-enqueue undelivered message is declared _preserveUndelivered to SonicMQ Deployment
messages and send an dead. true to store the dead Guide.
administrative notice. message until handled and
to set the property
JMS_SonicMQ_
notifyUndelivered to
true to send a notification
to the broker’s
administrator.
Clients
The techniques and interfaces described in this book describe the methods and design
patterns for running SonicMQ in a console session. There are several client types that all
provide JMS client functionality.
Java Client
SonicMQ clients are a set of Java archives that provide libraries of functionality that
enable applets, bridges, proxy servers, servlet engines, and JavaBeans.
Bridges
When you install SonicMQ Bridges, the client software for the SonicMQ side of every
bridge is copied from client libraries of a SonicMQ Java client installation. See the Sonic
Software Web site for more information about the SonicMQ Bridges—WebSphere MQ,
Tibco Rendezvous, other JMS, mail, and FTP.
Java Applet
SonicMQ can work in a Java applet running in a browser context to invoke classes that
implement JMS functionality.
C# Client
The SonicMQ C# Client lets you write applications in a variety of Microsoft
programming languages including C# .NET and Visual Basic .NET. The C# API enables
interoperability between .NET applications and Java applications, thereby leveraging and
extending the range of SonicMQ brokers. The C# Client includes features for fault
tolerant connections, and transactional support. It is a native .NET component with fully-
managed .NET code so that it works under the Microsoft CLR.
C/C++ Clients
SonicMQ can act as a pure C++ or pure ANSI C application on your system yet interface
with a SonicMQ broker with the same behaviors as a pure JMS client. This provides
legacy systems with integration and Web connection opportunities within the familiar
operating characteristics of C and C++.
COM Client
SonicMQ provides a COM wrapper to the C++ client so that it can enable pure COM
application on your system that interface with a SonicMQ broker with the same behaviors
as a true JMS client. Examples are provided that demonstrate use of the COM client in
Active Server Pages, Visual C++, Visual Basic, and VBScript applications.
SonicMQ API
The SonicMQ API provides Java and SonicMQ packages containing interfaces and
methods you can use in your SonicMQ programming. The SonicMQ API documentation
is located in your SonicMQ installation directory at
MQ7.5_install_root\docs\sonicmq_api. The SonicMQ API contains the following
interfaces:
● Java Extension Package:
■ javax.jms
● SonicMQ Packages:
■ progress.message.jclient — Contains interfaces and classes used with
SonicMQ
■ progress.message.jclient.channel — Contains the RecoverableFileChannel
interface
■ progress.message.xa — Contains interfaces and classes used with XA
Transactions
■ com.sonicsw.stream — Contains the SonicStream interface
When you develop a messaging application, you want to be sure your messages have the
correct content and are delivered as expected to the correct destinations. The SonicMQ
JMS Test client is a useful graphical tool that helps you do this. With this tool, you can
create message producers (QueueSenders and Publishers) and message consumers
(QueueReceivers and Subscribers); you can also create messages, send the messages to
selected queues and topics, and visually inspect the messages after they are delivered.
Many of the samples described in this book require you to use the JMS Test client.
This chapter includes the following sections that describe how to use the JMS Test client
with the PTP and Pub/Sub messaging models:
● “Testing Point-to-point Messaging”
● “Testing Publish and Subscribe Messaging”
◆ To start the broker and container from the Windows Start menu:
Select Start > Programs > Progress Sonic > SonicMQ 7.5 > SonicMQ DomainManager.
SonicMQ starts the container and broker, as indicated in the SonicMQ DomainManager
console window:
Sonic Management
Release 3.0.0 Build Number nnn
Copyright (c) 2006 Sonic Software Corporation.
All rights reserved.
ID=MgmtBroker (info)
Registering node "sonic.http" of "$RNN.sonic$http.$HTTP.undefinedroutingurl"
for routing reverse lookup
Starting recovery...
Recovery complete.
Restoring queues ...
Starting queue "SonicMQ.deadMessage" - Local Non-clustered Shared 1536 16384
Starting queue "SampleQ4" - Local Non-clustered Shared 1536 1000
Starting queue "SonicMQ.routingQueue" - Local Non-clustered Shared 1536 1024
Starting queue "SampleQ3" - Local Non-clustered Shared 1536 1000
Starting queue "SampleQ2" - Local Non-clustered Shared 1536 1000
Starting queue "SampleQ1" - Local Non-clustered Shared 1536 1000
TCP_ACCEPTOR: accepting connections on tcp://thishostname:2506
(info) ...startup complete
ID=MgmtBroker (info) SonicMQ Broker started
(info) Management connection (re)established
The SonicMQ container and broker start. The console window is dedicated to the broker
process and displays:
(info) Management connection (re)established
Important You can minimize the console window. Closing the window, however, stops the broker.
The samples default to localhost:2506—a broker using port 2506 on the same system,
localhost. If you use a different host or port, you need to specify the host:port parameter
when you start each sample. For example:
..\..\SonicMQ Chat -u Market_Maker -b Eagle:2345
The session appears in the left panel with Senders, Receivers, and Browsers nodes
as shown in Figure 6.
Note You can only create a sender to an existing queue. See the Progress SonicMQ
Configuration and Management Guide for information about viewing existing
queues and creating new queues.
A node for the new receiver appears under the Receivers node and the name of the
queue appears in the Established Receivers list, as shown in Figure 8.
Note You can only create a receiver to an existing Queue. See the Progress SonicMQ
Configuration and Management Guide for information about viewing existing
queues and creating new queues.
5. To create additional receivers, select Clear and then repeat steps 6 through 9 of this
procedure.
With senders and receivers established, you can send and receive messages.
◆ To send messages:
1. Select a Sender in the left panel of the JMS Test Client window.
The right panel displays three tabs: Header, Properties, and Body. You can examine
the default values under these tabs. In this example, you do not need to change any
default settings or specify any message properties or body content.
2. Select Send to send the message.
The next procedure shows you how to view the message just sent to SampleQ1 on the
receiver you created for that queue.
The Header, Properties, and Body tabs in the lower right panel contain information
for the received message, as shown in Figure 9.
3. To delete one or more messages without acknowledging them, select the messages
and click Delete.
4. To explicitly acknowledge one or more messages, select the messages and click
Acknowledge.
An acknowledgement is sent back to the broker if the session was established in
Client Acknowledged mode. (Messages can also be automatically acknowledged,
depending on how the session was established.)
Note By default, the number of viewable messages held in the Received Messages table
is 50.
3. Select the queue browser for the queue you want to view, and select the appropriate
position-selector (black arrow heads) to position the browser cursor to the messages
you want to see, as shown in Figure 11.
Note To start browsing messages, you must first choose the left-most position-selector.
You can restart the browse by choosing the same selector when the browser cursor is
at any position in the queue. The two right-most position-selectors are active only
when there are more messages on the queue than the specified buffer size. The move-
forward position-selector shows the next buffer-size number of messages in the
queue. The move-to-end position-selector shows the last buffer-size number of
messages in the queue.
◆ To create a publisher:
1. Select the Publishers node in the left panel.
The right panel displays established publishers for this topic session (if any), and
allows you to create and manage publishers, as shown in Figure 13.
The test client supports basic topics, hierarchical topics, node qualified topics, and
MultiTopics as defined in the Topic Builder shown in Figure 14.
See “MultiTopics” on page 352 for information about the syntax and behaviors of
MultiTopic publishers and subscribers.
2. To create a new publisher, enter the name of the topic where messages are to be
published in the Topic field and click Create.
A node for the new publisher appears under the Publishers node and the name of the
connection.
This example creates a publisher to the topic SampleT1, as shown in Figure 15.
◆ To create a subscriber:
1. Select the Subscribers node in the left panel.
The right panel displays the Established Subscribers (if any) and allows you to
create new subscribers.
2. In the Topic field, enter the name of the topic to which you want to subscribe.
This example creates a subscriber to the topic SampleT1.
3. Optionally, you can use the Message Selector field to enter query values based on
JMS header fields and properties to filter out unwanted messages. (For information
on the syntax of this string, see Chapter 6, “Messages.”)
4. Optionally, you can create a durable subscription by checking Durable and entering a
name in the Name field.
5. Optionally, you can check No Local Delivery, and the subscriber will not receive
messages from publishers on the same connection.
6. Click Create.
The new subscriber appears under the Subscribers node in the left panel, and the
topic appears in the list of Established Subscribers in the right panel, as shown in
Figure 16.
See Chapter 13, “Hierarchical Name Spaces,” for information about naming conventions.
With publishers and subscribers established, you can publish and subscribe to messages.
Publishing Messages
The following procedure describes how to publish messages.
Note Before continuing with this section, make sure you have completed the procedures in
“Creating Publishers and Subscribers to Topics” on page 57.
◆ To publish messages:
1. Select a publisher in the left panel of the JMS Test Client window.
The right panel displays three tabs: Header, Properties, and Body. You can examine
the default values under these tabs.
2. The Header tab, shown in Figure 17, displays the header properties.
■ You can edit only the following items in the header list:
❑ JMSCorrelationID
❑ JMSReplyTo
❑ JMSType
■ In the Summary section, you can specify:
❑ Message Type — Message, Text Message, or XML Message
❑ Delivery Mode — DISCARDABLE, NON_PERSISTENT,
NON_PERSISTENT_REPLICATED, PERSISTENT
❑ Priority — Integer values 0 (the highest) through 9 (the lowest)
❑ Time To Live — In milliseconds, with 0 indicating no expiration
3. Select the Properties tab to define property values, as shown in Figure 18, including
the following SonicMQ-specific properties:
■ JMS_SonicMQ_preserveUndelivered
■ JMS_SonicMQ_notifyUndelivered
4. Select the Body tab to compose the body of the message, as shown in Figure 19.
Note By default, the number of viewable messages held in the Subscribed Messages table
is 50.
3. To delete one or more messages without explicitly acknowledging them, select the
messages and select Delete.
4. To acknowledge one or more messages, select the messages and select Acknowledge.
An acknowledgment is sent back to the broker if the session was established in Client
Acknowledged mode.
Messages can also be automatically acknowledged, depending on how the session
was established.
This chapter explains how to run the sample applications included with SonicMQ. These
samples illustrate some of the messaging functionality of SonicMQ. This chapter includes
the following sections:
● “About SonicMQ Samples”
● “Running the SonicMQ Samples”
● “Chat and Talk Samples”
● “MultiTopicChat Sample”
● “Samples of Additional Message Types”
● “Sample of Channels for Large Message Transfers”
● “Message Traffic Monitor Samples”
● “Transaction Samples”
● “Reliable, Persistent, and Durable Messaging Samples”
● “Request and Reply Samples”
● “Selection and Wild Card Samples”
● “Test Loop Sample”
● “Enhancing the Basic Samples”
Important Table 4, “Restricted Characters for Names” on page 142 lists the characters that are not
allowed in SonicMQ names.
● Selection and Wild Cards — The message selector samples use SQL syntax to
qualify the messages that are visible to an application while the HierarchicalChat
sample uses template characters to subscribe to a set of topics that is qualified when
messages are published:
■ Message Selection — SelectorTalk (PTP), SelectorChat (Pub/Sub)
■ Wild Cards — HierarchicalChat (Pub/Sub)
● Test Loop — This sample shows how quickly messages can be sent and received in
a test loop:
■ Queue Test Loop — QueueRoundTrip (PTP)
See the Progress SonicMQ Deployment Guide for information about the HTTP Direct
samples.
● JNDI SPI — Samples are provided to describe programming using the Sonic service
provider implementation (SPI) for the Java Naming and Directory Interface (JNDI)
See Appendix A, “Using the Sonic JNDI SPI,” for information.
● Management Runtime and Configuration APIs — Samples are provided to
demonstrate the use of the SonicMQ Runtime and Configuration APIs. See the
Progress SonicMQ Administrative Programming Guide for information.
● Replicated (High Availability) Brokers — See the Progress SonicMQ Deployment
Guide for an example of how you can set up brokers as a primary/backup pair. When
the brokers are running and replicating, you can stop the active broker, causing the
standby broker to fail over. You can run the fault tolerant example—see “Modifying
the Chat Example for Fault-Tolerance” on page 194—to see the client application
seamlessly continue its session on the broker that becomes active.
● Secure Socket Layer (SSL) — SSL samples show how to reconfigure the broker for
SSL security, how to run client-side applications that connect through SSL, and how
to use certificates. See Part II of the Progress SonicMQ Deployment Guide for
complete SSL implementations you can explore; these implementations use the RSA
security software and credential samples installed with SonicMQ.
● Security Enabled Dynamic Routing — See the Progress SonicMQ Deployment
Guide for an example of how you can set up multiple brokers and security to realize
secure dynamic routing across nodes.
3. If you did not enable security when you installed, you can accept the defaults in the
General tab in the Create Connection dialog box.
If you enabled security when you installed SonicMQ, enter your password.
See the Progress SonicMQ Configuration and Management Guide for information
about setting parameters under the Advanced tab. For these samples, you do not have
to set any advanced parameters.
4. Click OK.
A Connecting... dialog box and the status bar indicate that the Sonic Management
Console is connecting to the broker.
The Sonic Management Console opens to the Configure view, as shown in Figure 22.
See the Progress SonicMQ Configuration and Management Guide for information about
configuration and management using the Management Console.
◆ To start the SonicMQ container and broker from a Linux or UNIX console window:
❖ In a new console window set to the SonicMQ install directory, type
bin/startcontainer.sh and press Enter.
The broker starts. The console window is dedicated to the process and, when running,
displays:
SonicMQ Broker started, now accepting tcp connections on port 2506...
Important You can minimize the console window. Closing the window, however, stops the broker.
The samples default to localhost:2506—a broker using port 2506 on the same system,
localhost. If you use a different host or port, you need to specify the host:port parameter
when you start each sample. For example:
..\..\SonicMQ Chat -u User1-b hostname:2345
◆ To open the Sonic Management Console from a Linux or UNIX console window:
❖ In a new console window set to the SonicMQ install directory, type bin/startmc.sh
and press Enter. The Sonic Management Console opens.
◆ To Chat:
1. In one of the Chat windows, type any text and then press Enter. The text is displayed
in both Chat windows, preceded by the name of the user that initiated that text.
2. In the other Chat window, type text and then press Enter. The text is displayed in both
Chat windows preceded by that username.
◆ To Talk:
1. In the Talker2 window, type any text and then press Enter.
The text is displayed in only the Talker1 window, preceded by the name of the user
who sent the message.
2. In the Talker1 window, type text and then press Enter.
The text is displayed in only the Talker2 window, preceded by the username of the sender.
MultiTopicChat Sample
This sample demonstrates how an application can publish to multiple topics in a single
operation using a MultiTopic. It also demonstrates how an application can subscribe to
many topics in a single operation by using MultiTopic.
mapMessage.setInt("FiscalYearEnd", 10)
mapMessage.setString("Distribution", "global")
mapMessage.setBoolean("LineOfCredit", true)
You can extract the data from the received message in any order. Use a get( ) method to
cast a data value into an acceptable data type. For example:
mapMessage.getShort("FiscalYearEnd")
mapMessage.getString("Distribution")
mapMessage.getString("LineOfCredit")
The message receiver casts the message as a MapMessage. If that casting is unsuccessful,
MapTalk reports that an invalid message arrived. The MapMessage is decomposed and
displayed as shown in the following source code of the sample MapTalk.java:
XML Messages
XML data definitions with tagged text are useful for communicating structured sets of
defined data records or transacted message sets over the Internet. The XML parser
included with SonicMQ, the Apache Xerces XML Parser, interprets the data using
Document Object Model (DOM) Element nodes. The message receiver window echoes
its translation of the XML-tagged code derived from your text entry. For example, if you
(as the sender Catalog_Update) enter Item One, the XML-tagged code is packaged as
shown in Code Sample 1, an excerpt of the sample file XMLDOMChat.java.
Code Sample 1. XMLDOMChat.java: XML-Tagged Code
{
progress.message.jclient.XMLMessage xMsg =
((progress.message.jclient.Session) pubSession).createXMLMessage();
StringBuffer msg = new StringBuffer();
msg.append ("<?xml version=\"1.0\"?>\n");
msg.append ("<message>\n");
msg.append (" <sender>" + username + "</sender>\n");
msg.append (" <content>" + content +s + "</content>\n");
msg.append ("</message>\n");
xMsg.setText(msg.toString());
publisher.send(xMsg);
}
<?xml version="1.0"?>
<message>
<sender>sender</sender>
<content>message_content</content>
</message>
In the DOM samples, when the message is received, the embedded DOM2 XML parser
is invoked. The message is interpreted to display the DOM nodes, as shown:
[XML from 'DOMSend'] Hello
ELEMENT: message
|--NEWLINE
+--ELEMENT: sender
|--TEXT_NODE: DOMSend
|--NEWLINE
+--ELEMENT: content
|--TEXT_NODE: Hello
|--NEWLINEXML DOM2 Messages (PTP)
In the SAX samples, when the message is received, the embedded SAX XML parser is
invoked. The message is interpreted to display the message in XML format, as shown:
<?xml version="1.0"?>
<message>
<sender>SAXSend</sender>
<content>Bonjour</content>
</message>
XMLDOMTalk (PTP)
In this example, the first XMLDOMTalk session sends on the first queue and receives to the
second queue while the other session does the opposite.
XMLSAXTalk (PTP)
In this example, the first XMLSAXTalk session sends on the first queue and receives to the
second queue while the other session does the opposite.
XMLDOMChat (Pub/Sub)
In this example, the XMLDOMChat sessions publish and subscribe on the topic
jms.samples.chat.
XMLSAXChat (Pub/Sub)
In this example, the XMLSAXChat sessions publish and subscribe on the topic
jms.samples.chat.
<?xml version="1.0"?>
<message>
<sender>SAXPub</sender>
<content>Hello</content>
</message>
received MutipartMessage....
******* Beginning of MultipartMessage ******
Extend_type property = x-sonicmq-multipart
partCount of this MultipartMessage = 4
--------Beginning of part 1
Part.contentType = application/x-sonicmq-textmessage
Part.contentId = CONTENTID1
content in TextMessage... this is a JMS TextMessage
--------end of part 1
--------Beginning of part 2
Part.contentType = myBytes
Part.contentId = CONTENTID2
...size : 38
...content :
This string is sending as a byte array
--------end of part 2
--------Beginning of part 2
Part.contentType = myBytes
Part.contentId = CONTENTID2
...size : 38
...content :
This string is sending as a byte array
--------end of part 2
--------Beginning of part 3
Part.contentType = text/plain
Part.contentId = CONTENTID3
...size : 37
...content :
a simple text string to put in part 3
--------end of part 3
When a part is complete, the receiving application can act on that part. The message parts
should be handled in a transactional way so that the messages parts can be rolled back if
the process fails before it completes all its parts.
SonicMQ with the ClientPlus option provides large message support by allowing a JMS
message to be associated with an instance of a recoverable channel. The file that will be
transferred will move through the recoverable channel dedicated to the sender. Internally,
the file is sent in fragments. Fragment loss or duplication due to failure is handled
internally.
The progress of the message transfer is displayed in the FileSender console window
shown:
MQ7.5_install_root\samples\ClientPlus\LargeMessageSupport>
..\..\SonicMQ FileSender -u aSender -p passwordSend -qs SampleQ1
-f MQ7.5_install_root\docs\program.pdf
Session is created
When the message transfer is complete, the source file remains intact and the received
file resides in the target directory. The aSender and aReceiver Channel folders are
empty.
If the transfer failed in progress—either the sender or the receiver—the recoverable
file channel info is accessible to both the sender and the receiver applications when
they re-establish the channel through the broker.
QueueMonitor MessageMonitor
What messages Undelivered. Delivered.
are displayed?
When does the When you click the Browse When a message is published
display update? Queues button, the list is to a subscribed topic, it is
refreshed. added to the displayed list.
When does the When the message is When the display is cleared
message go delivered (or when it for any reason.
away? expires).
What happens Listed messages marked As messages are listed at the
when the broker PERSISTENT are stored in the moment they are delivered,
and monitor are broker persistent storage there are no messages in the
restarted?
mechanism. They are MessageMonitor until new
redisplayed when the broker deliveries occur.
and the QueueMonitor
restart and then choose to
browse queues.
◆ To start QueueMonitor:
1. Open a console window to the QueuePTP\QueueMonitor folder.
2. Type ..\..\SonicMQ QueueMonitor and press Enter.
The Queue Monitor browser window opens.
◆ To start MessageMonitor:
❖ Open a console window to the TopicPubSub\MessageMonitor folder, then enter:
..\..\SonicMQ MessageMonitor
The MessageMonitor Java window opens.
The text is displayed in the Chat window and the MessageMonitor window. If you send
more messages, each one appends to the list displayed, as shown in Figure 24.
Transaction Samples
Transacted messages are a group of messages that form a single unit of work. Much like
an accounting transaction made up of a set of balancing entries, a messaging example
might be a set of financial statistics where each entry is a completely formed message and
the full set of data comprises the update.
A session is declared as transacted when the session is created. While producers—PTP
Senders and Pub/Sub Publishers—produce messages as usual, the messages are stored at
the broker until the broker is notified to act on the transaction by delivering or deleting the
messages. To determine when the transaction is complete, the programmer must:
● Call the method to commit the set of messages. The session commit( ) method tells
the broker to sequentially release each of the messages that have been cached since
the last transaction. In this sample, the commit case is set for the string OVER.
● Call the method to roll back the set of messages. The session rollback( ) method
tells the broker to flush all the messages that have been cached since the last
transaction ended. In this sample, the rollback case is set for the string OOPS!.
Note If you are interested in exploring global transactions with two-phase commits in a
sample, see the sample in Chapter 14, “Distributed Transactions Using XA Resources.”
receiveSession =
connect.createSession(true,javax.jms.Session.AUTO_ACKNOWLEDGE);
Then follow the send session commit line and send session rollback line with similar
statements for the receive session like this:
sendSession.rollback();
receiveSession.rollback();
...
sendSession.commit();
receiveSession.commit();
Start the two sessions described in the TransactedTalk sample, then run QueueMonitor
sample. Notice that whether you commit or roll back, no messages stay in the queue.
Stop the TransactedTalk sessions and the refresh the queue monitor. Note that the
messages sent since the last commit were all reinstated in the queue.
For more information, see “Transacted Sessions” on page 210.
Reliable Connections
The ReliableTalk and ReliableChat samples show techniques for monitoring a
connection for exceptions and re-establishing the connection if it has been dropped.
Note The Reliable samples use an aggressive technique (CTRL+C) that emulates an unexpected
broker interruption.
An intentional shutdown invokes an administrative Shutdown function on the broker.
This function is a command in the Management Console runtime.
In a Talk session, if the broker stopped and you sent a message, you would see:
javax.jms.IllegalStateException: The session is closed
This error occurs because Talk sample assumes that the connection is established and
available. The Talk sample does not consider the possibility that a problem occurred with
the connection (such as the network failing or the broker failing).
The ReliableTalk and ReliableChat samples, in contrast, are written to handle exceptions.
Both samples use a connection setup routine for retrying connections that fail for some
reason.
The ReliableTalk and ReliableChat samples also use the PERSISTENT delivery mode
option ensures that messages are logged before they are acknowledged and are nonvolatile
in the event of a broker failure. Consequently, as shown in the ReliableTalk example, the
application tries repeatedly to reconnect.
A unique SonicMQ feature monitors the heartbeat of the broker by pinging the broker at
a preset interval, letting the thread sleep for a while but initiating reconnection if the
broker does not respond. For more information, see “Creating and Monitoring a
Connection” on page 160.
These examples demonstrate techniques an application programmer can use to explicitly
handle connection exceptions. These samples do not, however, take advantage of an
important SonicMQ feature: fault-tolerant connections.
Fault-tolerant connections automatically detect problems with a connection and
seamlessly reconnect, if possible, either to the same broker or possibly to a backup broker
(if your deployment is set up to perform broker replication). This feature significantly
enhances the reliability of a connection.
The exception handling logic in the ReliableTalk and ReliableChat programs is devoted
to retrying a connection after the connection fails for some reason. This logic, as written,
would not be necessary with a fault-tolerant connection, because the fault-tolerant
connection is able to automatically retry the connection on your behalf. A fault-tolerant
connection can attempt to reconnect indefinitely or for a fixed period of time, depending
on how it is set up.
When a fault-tolerant connection encounters a problem and is able to reconnect, your
application does not get an exception and continues processing after the connecting is
reestablished.
When a fault-tolerant connection times out without successfully reconnecting, the
connection is dropped and an exception is generated. Your exception handling logic can
decide what to do the exception. Retrying the connection might not make sense if the
automatic retry was unsuccessful.
For detailed information about fault-tolerant connections, see “Fault-Tolerant
Connections” on page 167.
4. Restart the container and broker by using the Windows Start menu command or the
startmf script. The ReliableTalk application reconnects, as shown:
a. Enter a value in the Cleanup Interval field. For this example, enter 60 [seconds].
b. Make sure the Enable Cleanup Interval check box is activated.
c. Click OK.
The cleanup interval is set for the broker.
5. Reload the broker to activate the new cleanup interval:
a. In the Management Console, click the Manage tab.
b. In the left panel, expand the Containers node and right-click the node for your
broker.
c. From the pop-up menu, choose Operations > Reload.
d. Click Yes in the confirmation dialog box that opens.
The broker is reloaded, and the cleanup interval is activated.
2. To connect to a broker, click Message Brokers in the left panel of the JMS Test client
window.
In the right panel:
a. In the Broker Host field, enter localhost:2506.
b. In the Connect ID field, enter Conn1.
c. In the User field, enter Administrator.
d. In the Password field, enter Administrator.
e. Click Connect.
A node for this connection appears under the Message Brokers node in the left panel,
and the connection appears in the list of connections in the lower right panel.
3. To create a new queue session, in the left panel click the node for the broker you just
connected to: localhost:2506:Conn1.
In Create New Session area of the right panel:
a. In the Name field, enter Session1 for the new session.
b. In the Type field, select Queue from the pull-down list.
c. Click Create.
A node for the new queue session appears under the node for your broker connection
in the left panel. The queue session is listed in the Established Sessions area in the
right panel.
4. To create a queue sender, in the left panel, click the Senders node under
QueueSession:Session1 node.
In the Create New Sender area of the right panel:
a. In the Queue field, enter SampleQ1 as the queue name.
b. Click Create.
A node for SampleQ1 appears under the Senders node in the left panel, and the new
sender is listed in the Established Senders area in the right panel. This new sender
will send messages to SampleQ1.
5. Create queue browsers for SampleQ1 and the DMQ:
a. In the left panel, click the Browsers node.
b. In the Create New Browser area, enter SampleQ1 in the Name field. Click Create.
A queue browser is created for SampleQ1.
c. In the Create New Browser area, enter SonicMQ.deadMessage in the Name field,
then click Create.
A queue browser is created for the DMQ.
Figure 27 shows the two queue browsers created for this queue session.
You will use these browsers to watch the message move to the dead message queue
after it has expired and the cleanup interval has passed.
The properties include the original settings to preserve and notify when undelivered.
The undelivered timestamp indicates the time of dequeuing into the DMQ. The reason
code, 1, indicates that the message expired.
4. Click the Body tab.
The body is unchanged, as shown in Figure 31.
5. Click the left arrow in the SampleQ1 browser to see that the message has been
removed from that queue.
Expired messages are examined and, with the appropriate properties set, are transferred
to the dead message queue. The property you set instructs the broker to transfer the
expired message to the DMQ, placing it under administrative control with no expiration.
The message must now be explicitly flushed or dequeued. You can remove this message
from the DMQ by creating a receiver to that queue, or by running an application that takes
a message off the DMQ. The following procedure explains how to run the Dead Message
browser sample application to remove the message from the DMQ and display it in a Java
window.
4. In the JMS Test client, click the SonicMQ.deadMessages browser node, then click
the left arrow in the right panel.
The message has been removed from the DMQ by the Dead Message browser sample
application.
A management application might clone the body into a new message and use some
business logic to reroute the message to an optional or fallback destination.
While expiration is common to all messaging deployments, there are several other reasons
a messages could be in-doubt or undeliverable in a dynamic routing architecture.
See the Progress SonicMQ Application Programming Guide for information about using
the dead message queue and the dynamic routing architecture.
subscriber = subSession.createConsumer(topic);
As with ReliableChat, using the PERSISTENT delivery mode ensures that messages are
logged before they are acknowledged and are nonvolatile in the event of a broker failure.
Figure 33 shows what occurs when the subscriber requests an extra effort to ensure
delivery.
Broker's
Connection Broker Persistent
Producer Consumer
Factory Connection Session Topic Message process Data Store
(Publisher) (Subscriber)
request connection
Connection:
- New connection
- Set ClientId
- New session
topic
Topic:
- Create Topic
- Create Producer subscriber name, topic
- Create Consumer if DURABLE
topic
acknowledge
Connection:
- Start
topic
for
durable
topic subscribers
Message:
- Publish to topic produce
message
Message:
- Listen (asynch) Time
- Consume to
YES Is the subscriber
consume
session active?
live
message
NO
Restart
Connection
subscriber name .
Message: name,
- Durable Subscription YES topic
Are there
- Consume consume messages waiting?
message
close connection
While the ReliableTalk sample (see “ReliableChat Application (Pub/Sub)” on page 99)
showed that the client can reconnect when the broker is again available, other features
enable the client to continue its work when it is sending messages and the broker
connections fails. The SonicMQ ClientPlus has an extended capability that enables the
client to establish a message cache on the client where a definable volume of sent
messages can be buffered while a connection is re-established. When the connection and
session are again active, the oldest messages buffered are sent normally and more recent
messages sent continue to accrue in the buffer. When the local store is empty, the use of
the local store is transparent.
The applications in the LocalStore sample provide the extended feature of client
persistence, a way for client application to continue sending messages despite losing
connection with the broker. Messages sent by the client are buffered in a persistent store
on the client system until connection is established at which time the accrued messages
are sent. This section includes two sets of samples, one for each messaging domain. Each
set runs a continuous producer that sends and displays a sequence number and a consumer
that receives the messages sent. The broker is stopped to effect the local store of produced
message. When the broker restarts, the messages are sent and the receiver displays them.
4. Restart the broker by using its Windows Start menu command or the startmf script.
Both applications reconnect to the broker.
After reconnecting, the MessageReceiver application gets all the sent messages from
its local store, including those sent while the broker connection was broken, as shown
in Figure 35.
You can stop both sessions by pressing Ctrl+C in the sender and receiver console windows
before proceeding to the next sample.
The ClientPlus feature of persistence on the client shows how clients can store messages
to provide a higher level of reliability to supporting applications that need to produce
messages at will. There are also other facets to consider for optimal QoS, including the
various security, encryption, access control, and transport protocols. See the Progress
SonicMQ Deployment Guide for information about security and protocols.
The sample applications use JMS sample classes, TopicRequestor and QueueRequestor.
You should create the Request/Reply helper classes that are appropriate for your
application.
3. In the AAA window, type any text and then press Enter.
The message is enqueued but there is no receiver. The BBB selector string does not see
any enqueued messages except those that evaluate to South.
4. Stop the BBB session by pressing Ctrl+C.
5. In the BBB window start a new session, changing the selector string:
..\..\SonicMQ SelectorTalk -u BBB -s North -qr SampleQ2 -qs Sample q1
The session starts and the message that was enqueued is immediately received.
6. In the AAA window, again type any text and then press Enter.
The message is enqueued and the BBB selector string qualifies the message for
immediate delivery.
4. In the Presenter window, type any text and then press Enter.
The text is only displayed in the Presenter window. The Closer selector string
excludes the Marketing message.
5. Stop the Closer session by pressing Ctrl+C.
6. In the Closer window start a new session, changing the selector string:
..\..\SonicMQ SelectorChat -u Closer -s Marketing
◆ To run HierarchicalChat:
1. In the HQ window, type text and then press Enter.
The text is displayed in only the HQ window because HQ subscribes to all topics in the
sales hierarchy while America is subscribing to only the sales.usa topic.
2. In the America window, type text and then press Enter.
The text is displayed in both windows because:
■ America subscribes to the sales.usa topic.
■ HQ subscribes to all topics that start with sales.
◆ To run QueueRoundTrip:
1. Open a console window to the QueuePTP\QueueRoundTrip folder then enter:
..\..\SonicMQ QueueRoundTrip -n 100
This command starts a QueueRoundTrip session that sends a message on 100 round
trips to a temporary queue.
The QueueRoundTrip window displays information about the cycles, as shown:
Note This sample lets you evaluate features and is not intended as a performance tool. For
information on performance, see the Progress SonicMQ Performance Tuning Guide.
A counter is incremented and the message is sent for another trip. After completing the
number of cycles you entered when you started the test, the run completes by displaying
summary and average statistics.
4. Look at the results and compare them to other round trips (see “QueueRoundTrip
Application (PTP)” on page 122).
The following exercise adds some mixed data types to the MapTalk source file before the
message is sent. Then the receiver takes the data in a different sequence and formats it for
display.
The example uses typed set( ) methods to populate the message with name-typedValue
pairs. The get( ) methods retrieve the named properties and attempt coercion if the data
type is dissimilar.
◆ To extend the MapTalk sample to use and display other data types:
1. Edit the SonicMQ sample file MapTalk.java at the lines:
javax.jms.MapMessage msg = sendSession.createMapMessage();
msg.setString("sender", username);
msg.setString("content", s);
2. Add the lines for the set( ) methods (or your similar lines):
msg.setInt("FiscalYearEnd", 10);
msg.setString("Distribution", "global");
msg.setBoolean("LineOfCredit", true);
3. You must extract the additional data by get( ) methods to expose the values in the
receiving application. Because the sample is a text-based display, you can include the
getString ( ) methods in the construction of the string that will display in the console.
Change this:
String content = mapMessage.getString("content");
System.out.println(sender + “: “ + content);
to:
SString content =
("Content: " + mapMessage.getString("content") + "\n" +
"Distribution: " + mapMessage.getString("Distribution") + "\n" +
"FiscalYearEnd: " + mapMessage.getString("FiscalYearEnd") + "\n" +
"LineOfCredit: " + mapMessage.getString("LineOfCredit") + "\n");
System.out.println("MapMessage from " + sender + "\n------- \n" +
content);
2. Insert the formatted, tagged XML lines you want to append to the message. For
example:
msg.append ("<RFP>\n");
msg.append ("<REQUEST>\n");
msg.append ("<REQ_ID>1125-2000-225</REQ_ID> \n");
msg.append ("<FOB>Portland Maine</FOB> \n");
msg.append ("<RFP_DUE>31-JAN-2000</RFP_DUE> \n");
msg.append ("<DELIVERY_DUE>15-AUG-2000</DELIVERY_DUE> \n");
msg.append ("<CATEGORY>Grains</CATEGORY> \n");
msg.append ("<LINE_ITEMS>\n");
msg.append ("<LINE>\n");
msg.append ("<ITEM>1125-2000-225.1 Wheat</ITEM> \n");
msg.append ("<QTY>10000 tons</QTY>\n");
msg.append ("</LINE> \n");
msg.append ("<LINE>\n");
msg.append ("<ITEM>1125-2000-225.2 Rice</ITEM> \n");
msg.append ("<QTY>20000 tons</QTY>\n");
msg.append ("</LINE>\n");
msg.append ("<LINE>\n");
msg.append ("<ITEM>1125-2000-225.3 Corn</ITEM> \n");
msg.append ("<QTY>40000 tons</QTY> \n");
msg.append ("</LINE> \n");
msg.append ("</LINE_ITEMS> \n");
msg.append ("</REQUEST> \n");
msg.append ("</RFP> \n");
msg.append ("</message> \n");
When you run the application and enter a basic text message, the complete document
object model (DOM) is also displayed, similar to the subscriber session listing in
Figure 36.
Because the data is interpreted in the DOM format only when the message is an instance
of an XMLMessage, a Chat session displays the same message as a TextMessage—
the XML-tagged text without DOM interpretation, as shown in Figure 37.
Note You could have appended the XML tagged lines without the \n, suppressing the blank
TEXT_MODE lines in the DOM. It would, however, make one unbroken text line for general
text or raw XML review.
You can continue working with the samples by changing broker settings to explore
connection protocols and protocol handlers. You can also enable security on the broker
persistent storage mechanism then examine the protocols that provide connection
security. For information about using protocols and security, see the Progress SonicMQ
Configuration and Management Guide.
This chapter explains the programming concepts and actions required to establish and
maintain SonicMQ connections. This chapter contains the following sections:
● “Overview of SonicMQ Connections”
● “Protocols”
● “JVM Command Options”
● “Connection Factories and Connections”
● “Client Persistence”
● “Fault-Tolerant Connections”
● “Starting, Stopping, and Closing Connections”
● “Using Multiple Connections”
● “Communication Layer”
Client Application
ConnectionFactory C
O
S
N
E SonicMQ
N
S Broker
E
S
C
I
T
O
I
N
O
N
Multiple sessions can be established on a single connection. Once the connection and
sessions are established, the broker traffic can be either:
● A message producer delivering a message to its broker
● A broker delivering a message to an application that will consume it
In the example shown in Figure 39, two sessions exist on the same connection.
S
E C
S O
S
I N SonicMQ
O N Broker
N E PRODUCER publishes, sends
S C Messages DESTINATION
E T CONSUMER subscribes, receives
S I
S
I O
O N
N
See Chapter 7, “Message Producers and Consumers,” for more information about
message producers and consumers.
Protocols
This section describes the protocols that client applications use for broker communication
from a JMS client application:
● “TCP”
● “SSL”
● “HTTP”
● “HTTPS”
These protocols are nearly transparent within the application. When the port acceptor on
the broker matches the connection factory parameter from the application, connection can
be established under that protocol.
See “Connecting to SonicMQ Directly” on page 149 for details on explicit use of the
protocol value.
TCP
TCP is the default socket type for SonicMQ. Client applications that are Internet-enabled
generally use TCP/IP protocols.
SSL
SonicMQ supports encryption at the connection level through SSL. SonicMQ ships with
BSAFE-J SSL by RSA Security to ensure secure connections. SonicMQ also supports
(but does not include) Java Secure Socket Extensions (JSSE) SSL.
See the Progress SonicMQ Deployment Guide for more information about SSL and how
to configure SSL on the broker and between brokers.
Authentication
Authentication is the process of presenting an identity to the broker and then providing a
password or certificate that certifies the user’s credentials.
◆ To run the Talk sample application using SSL with password-based client
authentication:
Note The following steps on the broker use the Management Console. For detailed procedures
to perform these steps, see the section “Configuring SSL on Acceptors” in the chapter
“Configuring Acceptors” in the Progress SonicMQ Configuration and Management
Guide.
1. On the broker:
a. Set up or choose an acceptor for SSL connections.
b. Clear the option to enable client authentication, as shown in this view from the
Management Console:
2. On the client:
a. Open a console window at the directory level of the application you want to run.
For example:
MQ7.5_install_root\samples\QueuePTP\Talk
b. Enter the following code as a single line in the console window:
..\..\SonicMQ -DSSL_CA_CERTIFICATES_DIR=MQ7.5_install_root\certs\CA
Talk -b ssl://localhost:2506 -u aUser -p aPassword
-qr SampleQ1 -qs SampleQ2
3. You can send messages between clients using SSL. To demonstrate this process with
the Talk sample application, you can either administratively add another user to the
broker’s authentication domain or start another instance of the user already added, as
follows:
a. Open another console window to the directory level of the Talk application, and
start the Talk application in this window:
..\..\SonicMQ -DSSL_CA_CERTIFICATES_DIR=MQ7.5_install_root\certs\CA
Talk -b ssl://localhost:2506 -u aUser -p aPassword
-qr SampleQ2 -qs SampleQ1
b. Send messages from each console window and observe the messages as each is
received in the other window.
◆ To run the Talk sample application with client authentication via a client certificate:
Note The following steps on the broker use the Management Console. For detailed procedures
to perform these steps, see the section “Configuring SSL on Acceptors” in the chapter
“Configuring Acceptors” in the Progress SonicMQ Configuration and Management
Guide.
◆ On the broker:
a. Set up or choose an acceptor for SSL connections.
b. Choose the option to enable client authentication and enter the relative path to the
certificates, as shown in this view from the Management Console:
4. On the client:
a. Open a console window at the directory level of the application you want to run.
For example:
MQ7.5_install_root\samples\QueuePTP\Talk
b. Enter the following code as a single line in the console window:
..\..\SonicMQ
-DSSL_CA_CERTIFICATES_DIR=MQ7.5_install_root\certs\CA
-DSSL_CERTIFICATE_CHAIN=MQ7.5_install_root\certs\client.p7c
-DSSL_PRIVATE_KEY=MQ7.5_install_root\certs\clientKey.pkcs8
-DSSL_PRIVATE_KEY_PASSWORD=password
-DSSL_CERTIFICATE_CHAIN_FORM=PKCS7
Talk -b ssl://localhost:2506 -u AUTHENTICATED
-qr SampleQ1 -qs SampleQ2
..\..\SonicMQ
-DSSL_CA_CERTIFICATES_DIR=MQ7.5_install_root\certs\CA
-DSSL_CERTIFICATE_CHAIN=MQ7.5_install_root\certs\client.p7c
-DSSL_PRIVATE_KEY=MQ7.5_install_root\certs\clientKey.pkcs8
-DSSL_PRIVATE_KEY_PASSWORD=password
-DSSL_CERTIFICATE_CHAIN_FORM=PKCS7
Talk -b ssl://localhost:2506 -u bUser -p bPassword
-qr SampleQ2 -qs SampleQ1
Now you can open more clients and work with the Talk sample application, or implement
SSL for other sample applications included with SonicMQ. For each client application,
you must either:
● Import a certificate and include the user parameter with username AUTHENTICATED
when running the sample application.
● Add the user with username and password and provide the password parameter with
the password when running the sample application.
HTTP
HTTP is used extensively in SonicMQ. This book focuses on HTTP as a way to establish
and maintain client connection to a messaging broker on host port. HTTP's other
functionality is discussed in the following sections of the Progress SonicMQ Deployment
Guide:
● HTTP Tunneling — How to set up firewalls and proxy servers is discussed in the
Progress SonicMQ Deployment Guide chapter “Security Considerations in System
Design.”
● HTTP Direct — SonicMQ can interface with pure HTTP Web applications and Web
Servers. For example:
■ Inbound to the SonicMQ broker, protocol handlers on acceptors let SonicMQ act
as a Web Server, transforming received HTTP documents into JMS messages.
■ Outbound from the SonicMQ broker, sending JMS messages to routing
connections that translate the JMS message into a well-formed HTTP message
before sending to the designated URL (typically a Web server).
HTTP Direct is a way to handle messages one-by-one at the broker without
establishing connections and sessions. Other than programmatically setting X-HTTP-*
properties on the JMS message outbound to the routing node (see page 263 for
details), this book does not discuss the general functionality of HTTP Direct. See the
Progress SonicMQ Deployment Guide section on “Using HTTP(S) Direct” for
information about HTTP Direct.
Using HTTP in a connection attempts to use the host and port that you designate as an
entry point to HTTP tunneling. See the “TCP and HTTP Tunneling Protocols” chapter of
the Progress SonicMQ Deployment Guide for information about HTTP tunneling.
HTTPS
HTTPS tunneling is similar to HTTP except that data is transmitted over a secure socket
layer instead of a normal socket connection. The broker has a different acceptor
(configured for HTTPS) than the acceptor that accepts HTTP requests.
Secured HTTP tunneling is discussed in the chapter “SSL and HTTPS Tunneling
Protocols” in the Progress SonicMQ Deployment Guide.
HTTPS can be implemented:
● In client-to-broker or broker-to-broker connections
● With or without proxy servers
● Under HTTP forward proxy
Specifying Credentials
There are several ways to specify the username and password to use for HTTP
authentication. (When running from an applet, this is not necessary as the browser handles
HTTP credentials.)
● By setting the following System properties:
–Dsonic.http.proxyUsername=username
-Dsonic.http.proxyPassword=password
NTLM Authentication
A client can perform NTLMv1 authentication if a proxy requests it. For regular Java
applications SonicMQ NTLM authentication is supported on all platforms. When running
from an Applet NTLM authentication is currently only supported on Windows machines
and it is up to the browswer plugin to handle NTLM authentication.
When NTLM is used as the authentication scheme a Windows domain name must be
provided. This can be done in one of two ways:
● By prepending the domain to the username separated by a backslash, as shown:
DOMAIN\<username>.
This method takes precedence over the following one.
2. Register an Authenticator
A concrete subclass of java.net.Authenticator is required to handle proxy
authentication. Applications register an authenticator programatically using the static
method setDefault of the java.net.Authenticator class.
Instead, you can direct the Sonic runtime to install an authenticator by specifying the
package qualified class name as the -D system property
sonic.https.proxyAuthenticator on the client's Java command line.
A default authenticator for BASIC authentication is provided if the system properties
sonic.https.proxyUsername and sonic.https.proxyPassword are specified.
SSL/HTTPS
The following SSL command options were shown in the procedure for running the Talk
sample application with client authentication via a client certificate on page 134:
-DSSL_CA_CERTIFICATES_DIR=MQ7.5_install_root\certs\CA
-DSSL_CERTIFICATE_CHAIN=MQ7.5_install_root\certs\client.p7c
-DSSL_PRIVATE_KEY=MQ7.5_install_root\certs\clientKey.pkcs8
-DSSL_PRIVATE_KEY_PASSWORD=password
-DSSL_CERTIFICATE_CHAIN_FORM=PKCS7
Nagle Algorithm
The Nagle algorithm allows buffering of small data before sending the data as a fully
constructed IP packet. By default, this algorithm is disabled.
To enable this algorithm, set -DSonicMQ.TCP_NODELAY=false on the JVM command line;
to disable it, set -DSonicMQ.TCP_NODELAY=true.
Connection Factories
To establish a Java connection with the SonicMQ broker, a Java client uses a
ConnectionFactory object. Prior to JMS 1.1, model-specific factories were required for
the Pub/Sub and Point-to-Point message models; however, beginning in JMS 1.1,
common connection factories can be used for both models.
These common connection factories are:
● ConnectionFactory
● XAConnectionFactory
Java clients can obtain a connection factory in the following three ways:
● Instantiating a new connection factory object by specifying connection information
in the object constructor (and possibly customizing further using set methods on the
factory)
● Obtaining a preconfigured connection factory object from a JNDI store
● Deserializing a preconfigured factory object from a file
Each of these techniques is described in this chapter.
SonicMQ connection factory objects encapsulate the information needed to connect and
configure the SonicMQ JMS client connection. This information might be specified or
defaulted to include:
● Host, port, and protocol information
● User, password, and other identity information
● Load balancing, fault-tolerance, selector location, and similar connection or session
behavioral settings
The most important connection factory, and hence connection, settings are discussed
below. Some of the settings are identifiers that differentiate and distinguish JMS client
registrations. These identifiers have specific name restrictions, shown in Table 4.
Important Table 4, “Restricted Characters for Names,” lists characters that are not allowed in
SonicMQ. You must not use these restricted characters in your identifier names.See also
Appendix A of Progress SonicMQ Installation and Upgrade Guide for a complete
reference to use of characters in SonicMQ names.
Note Although a Durable Subscription name is not a connection factory setting, it is included
in Table 4 for completeness.
URL
The Uniform Resource Locator identifies the broker where the connection is intended.
The URL is in the form:
[protocol://]hostname[:port]
where:
■ protocol is the broker’s communication protocol (default value: tcp).
■ hostname is a networked SonicMQ broker machine.
■ port is the port on the host where the broker is listening. The broker’s default port
value is 2506.
■ For HTTP direct, you can also add a url extension that determines the
parameters and factories.
ConnectID
The ConnectID determines whether the broker allows multiple connections to be
established using a single username/ConnectID combination. You control the broker’s
behavior by calling the ConnectionFactory.setConnectID(String connectID) method:
● To allow only one connection, provide a valid connectID.
● To allow unlimited connections, use null as the connectID.
You can create a valid ConnectID by combining the username with some additional
identifier.
Note See Table 4, “Restricted Characters for Names” on page 142 for a list of restricted
characters for ConnectID names.
Note See Table 4, “Restricted Characters for Names” on page 142 for a list of restricted
characters for usernames.
ClientID
The ClientID is a unique identifier that can avoid conflicts for durable subscriptions when
many clients might be using the same username and the same subscription name.
❖ Set the ClientID in the ConnectionFactory. You can either preconfigure the ClientID
via the JMS Administered Objects tool in the Sonic Management Console, or you can
call ConnectionFactory.setClientID(String clientid) in the client application.
If you preconfigure the ClientID, calling ConnectionFactory.setClientID(String
clientid) throws an IllegalStateException.
See Table 4, “Restricted Characters for Names” on page 142 for a list of restricted
characters for ClientID names.
Load Balancing
Any broker in a cluster can redirect incoming client connections to another broker in the
same cluster for the purpose of load balancing. Load balancing must be configured on the
broker. The client must also be configured to indicate that it is willing to have a connect
request re-directed to another broker.
See the Progress SonicMQ Configuration and Management Guide for information about
configuring broker load balancing from the Management Console.
javax.jms.ConnectionFactory factory;
factory = (new progress.message.jclient.ConnectionFactory (broker));
factory.setSelectorAtBroker(true);
connect = factory.createConnection (username, password);
Choosing where message selectors do their filtering does not effect the messages
processed, but might drastically reduce the message traffic at the expense of some
additional overhead on the broker. These options can also be set on the factory through
the Management Console. (See the Progress SonicMQ Configuration and Management
Guide for information.)
SonicMQ Broker
connect = factory.createConnection (username, password)
Client brokerURL
Connection
There are several supported constructors for creating a ConnectionFactory object. The
constructors use combinations of the brokerURL, brokerHostName, brokerPort,
brokerProtocol, connectID, defaultUsername, and defaultPassword parameters.
Note When user identification is omitted when creating a connection, the connection uses the
default values from the ConnectionFactory. If authentication is enabled and the username
is invalid, a javax.jms.JMSSecurityException is thrown.
You can use the common name from a certificate when you use SSL mutual
authentication. See the Progress SonicMQ Deployment Guide for more about SSL and
security.
Note The JMS 1.1 specification states that some JMS version 1.02b (model-specific) interfaces
might be deprecated in the future. Consequently, if you are developing new JMS client
applications, it is recommended that, wherever possible, you use the common interfaces
in place of the older model-specific interfaces. Here, you should use ConnectionFactory
and XAConnectionFactory instead of the model-specific interfaces.
● Destinations
■ Queue
■ Topic
JMS client applications obtain instances of SonicMQ connection factory objects (see
“Lookup Using the Sonic JNDI SPI” on page 152) and use JMS specified factory methods
on those objects to create connections. (See “Lookup and Use of Administered Objects”
on page 151.)
This type of lookup submits a name to the JNDI store for lookup. In Figure 41 the factory
name TalkQCF (a simple arbitrary name for a ConnectionFactory object used in these
examples) is submitted in the format TalkQCF.
lookup TalkQCF
SonicMQ
JNDI
ConnectionFactory Object for 'TalkQCF' JNDI Store
SonicMQ containing: host:port, user, password, options
Broker
Client
connect host:port, user, password, options brokerURL
Connection
Note The context name can also be submitted in the LDAP format: cn=TalkQCF, but this format
is not required.
The provider URL you entered appears in the Object Stores list in the JMS
Administered Objects window, and a node for this provider URL appears in the left
panel.
4. Set up the connection factory. For the example:
a. In the left panel of the Sonic Management Console, choose the connection you
just established to the JNDI Naming Service.
b. In the right panel, choose the Connection Factories tab then click New.
c. In the Lookup Name field, enter a new record with TalkQCF as the name value.
d. From the Factory Type pull-down list, choose ConnectionFactory.
e. Enter an URL for the application connection, such as localhost:2506
Do not enter a user or password. The example will override the username and
password and show how they can be supplied in application parameters, thus
enabling varied authorizations for applications that use the lookup information.
f. Enter a Connect ID such as First. This is a value that will be changed in the
example to demonstrate how administrative changes to the lookup value are
passed through the connections that use the connection factory.
g. Click Update.
The TalkQCF object is entered in the JNDI store.
One of the connections lists its Connect ID as First, the name used for the
ConnectionFactory stored in the JNDI store.
If you use the JMS Administered Objects window to update the TalkQCF object to have
a Connect ID of Next, that value will not be reflected in the connections until the
connection factory is looked up again. By stopping the JNDITalk application and then
restarting it, the connection listed in the Management Console will display the revised
Connect ID for TalkQCF.
Connection
Figure 43. Alternate Connection Techniques Using Factory Objects or JNDI Lookup
From a client program, select an external LDAP server such as the JNDI store by setting
the system property “javax.naming.Context.INITIAL_CONTEXT_FACTORY” to
“com.sun.jndi.ldap.LdapContextFactory”. The property
“javax.naming.Context.PROVIDER_URL” specifies how to locate to LDAP server and
establish the initial JNDI naming context. For example:
“ldap://mypc.a.sonicmq.com:389/ou=jmsao,ou=sonicMQ,o=a.sonicmq.com”
See “Java JNDI SPI Sample” on page 480 for information about a sample application that
demonstrates using the Sonic JNDI SPI.
Connection
Connections
After instantiating a ConnectionFactory object, the factories’ createConnection( )
methods are used to create a connection. The first action a client must take is to identify
and establish connection with a broker. The following constructors use a connection
factory object to get the connection.
Important The JMS specification states that an application should not use a Java constructor to
create connections directly, otherwise applications will not be portable.
Creating a Connection
A Connection is an active connection to a SonicMQ broker. A client application uses a
connection to create one or more Sessions, the threads used for producing and consuming
messages.
You create Connection by using a ConnectionFactory object. There are two variants of the
createConnection( ) method:
● Use the default username and password:
connect = factory.createConnection( );
Important Use this method only when you are not concerned about security, or when your JNDI
store is very secure.
The broker is required to respond to each ping sent by the client. If the client does not
receive any traffic within a ping interval, then the client assumes the connection is bad and
drops the connection. See “Handling Dropped Connection Errors” on page 161 for more
information. But this is true only when the connection is not fault tolerant. When a
connection is fault tolerant, pings are still necessary for monitoring the network. When a
fault tolerant connection is in use, the ping is activated by default and is set to 30 seconds.
However, unlike non-fault tolerant connections, a ping response is not required from the
broker, and will not cause a connection drop. See “Fault-Tolerant Connections” on
page 167 for additional information about fault tolerant connections.
You can also configure active pings in the ConnectionFactory by invoking the
ConnectionFactory.setPingInterval(interval_in_seconds) method, or by preconfiguring
ConnectionFactory administered objects with a ping interval.
Note Avoid setting a small ping interval. This wastes cycles and your application will be
burdened with temporary network unavailability. Also, if you set a ping interval that is
too small, it might give false connection drops.
In the case of network failure, when a broker becomes disconnected from the network
JMS clients generally notice some time after they try to publish or send a message. If the
application is only acting as a subscriber, network failure might not be detected by the
client. Enabling active ping will ensure timely detection of loss of network.
Client Persistence
SonicMQ installations that provide ClientPlus features have the option of enabling client
persistence. Client persistence provides a higher level of reliability than is defined in the
JMS specification. Where a network failure during a JMS send would normally cause a
message being sent to be effectively lost unless the user application takes additional
precautions, client persistence enables client-based logging of messages sent until the
broker connection is re-established. This feature enhances delivery guarantees and
provides disconnected operation.
When flow control forces a message producer to pause, clients that have enabled client
persistence continue to produce messages into the persistent store. When producer flow
control is no longer in effect, persisted messages flow to the broker in order while the
message producer continues to add messages to the local store. When the local store is
cleared, messages flow directly from the producer to the broker.
The persistent store is a set of files in a directory name specified by the user in association
with a JMS connection. The client run time uses the files to store messages and manage
their delivery to the SonicMQ broker.
The characteristics of the client persistence store and the wait time before flow controlled
messages are persisted in the store can be set programmatically on the connection factory
or on connection factory administered objects.
For each connection using persistence, the persistence directory on the client includes a
subdirectory identified by ClientId that contains:
● One or more files to store messages rejected by the broker
● Recovery files for logging restart information
● For each session on the connection, a file that records all messages sent while in the
disconnected state
When the client has an active connection to the broker, the client operates normally.
Messages are not written to the persistent store until a network outage or a flow control
pause is detected.
When a network outage or a flow control pause is detected, the currently active message
is written to the persistent store and the client switches to writing all messages—persistent
and non-persistent—to the store. The size of the session log is limited by the local store
size. Non-persistent messages in memory when the outage is detected are dropped.
While disconnected because of a network outage, the client runtime tries to reconnect to
a broker. It is possible for the client to reconnect to a different broker (than it originally
connected to) if you provided a list of brokers in the connection parameters of the factory,
or if broker load balancing is configured and the client elected load balancing.
When a connection is reestablished or the flow control is no longer in effect, the client
runtime sends all persistent messages in memory at the time of the disconnect, then
replays the session log. New messages are accepted while the messages in the session log
are sent to the broker and acknowledged. The persistent client controls the rate of
accepting messages into the store relative to the rate of sending stored messages out of the
local store to the broker in an effort to drain the backlog of messages. The sender
experiences a slower producer rate while messages are being restored. However, it is
possible for messages to accumulate in the store faster than they can be sent to the broker.
If this occurs, the local store size might be exceeded in which case the sender gets an
exception.
Files are deleted after all messages have been sent to the broker and acknowledged and all
rejections have been processed by the RejectionListener. An application should
explicitly close sessions and connections to allow the client runtime to perform cleanup.
In the event of an abnormal end to the client connection, the next startup will send
unacknowledged messages and cleanup unneeded files.
connect = (progress.message.jclient.QueueConnection)
factory.createQueueConnection(m_username, m_password);
This sample accepts the default values of the parameters for reconnect timeout and
reconnect interval. The parameters in this example are similarly used for
TopicConnectionFactory in the ContinuousPublisher sample.
Rejection Listener
The client is notified of delivery failures by a RejectionListener established by a method
of progress.message.jclient.Connection:
setRejectionListener (RejectionListener rl);
The user must provide an implementation of the RejectionListener interface:
The message is removed from the persistent store when onRejectedMessage returns.
Coding Limitations
A message that is in transit when a disconnect occurs is resent when the connection is
reestablished. A consumer receiving messages sent by a persistent client should be
prepared to handle duplicates.
Transacted sessions and message consumers are not supported in sessions where the
connection implements client persistence. The following methods return an error when
the connection has local persistence:
● Connection: Creation of transacted sessions, createConnectionConsumer,
createDurableConnectionConsumer
● Session: createBrowser, createDurableSubscriber, createReceiver,
createSubscriber, createTemporaryQueue, createTemporaryTopic,
setMessageListener
Note An application can create a separate connection without persistence to use message
consumers and transacted sessions.
Client persistence can be combined with fault-tolerant connections. For information about
the considerations involved, see “Client Persistence and Fault-Tolerant Connections” on
page 180.
Fault-Tolerant Connections
The client aspect of the Sonic Continuous Availability Architecture is client connections
that are fault tolerant. A fault-tolerant connection is designed to be resilient when it
detects problems with the broker or network. A standard connection, in contrast, is
immediately dropped when the broker or network fails. Because the standard connection
is immediately dropped, your client application has to explicitly deal with the situation,
possibly trying to create a new connection and resolve any in-doubt messages.
A fault-tolerant connection, unlike a standard connection, is kept alive when the broker or
network fails. It automatically performs several tasks on your behalf when a problem
occurs. For example, it automatically attempts to reconnect when it encounters a problem
with a connection. If it successfully reconnects, it immediately executes several state and
synchronization protocol exchanges, allowing it to resynchronize client and broker state
and resolve in-doubt messages. When the connection successfully resynchronizes client
and broker state, the connection is said to be resumed, and your client application can
continue its operations without any directly visible disruption.
A fault-tolerant connection can respond to broker or network failure in a variety of ways.
How it responds depends on how you have deployed SonicMQ and on the nature of the
failure. There are several possibilities:
● If the network experiences a transient failure, the fault-tolerant connection can
repeatedly try to recover the connection until the network returns to normal.
● If your client application has redundant network pathways to the broker, one pathway
can fail, and the fault-tolerant connection can use the other pathway to resume the
connection.
● If your client application is connected to a standalone broker, which fails, the fault-
tolerant connection can repeatedly try to reconnect to the broker, until it is recovered
and restarted.
● If you have configured and deployed a backup broker, and the primary broker fails,
the fault-tolerant connection can connect to the backup broker.
The following code snippet demonstrates how to make the client runtime randomly
choose a broker from a list:
//cf is a ConnectionFactory
cf.setSequential(false);
The following code snippet demonstrates how to make the client runtime choose a broker
by starting at the beginning of the list:
cf.setSequential(true);
The following code snippet also demonstrates how to make the client runtime choose a
broker by starting at the beginning of the list:
cf.setSequential(true);
However, in this snippet, the primary brokers are listed before their corresponding backup
brokers. This approach would be appropriate, for example, if the backup brokers were on
slower machines than the primary brokers.
To get the ConnectionFactory’s current fault-tolerance setting, call the following method:
Boolean ConnectionFactory.getFaultTolerant( )
You cannot create a fault-tolerant connection unless the broker is licensed to support-fault
tolerance. A broker that is not licensed to support fault tolerance will effectively ignore
the ConnectionFactory setting. You can determine if a connection is fault tolerant by
calling the progress.message.jclient.Connection.isFaultTolerant( ) method.
When you call the setInitialConnectTimeout( ) method, valid values are as follows:
● Positive non-zero value — Specifies a timeout; the client runtime will abandon
further connection attempts if the timeout is exceeded.
● Zero (0) — Specifies no timeout; the client runtime will try indefinitely.
● Negative one (-1) — Specifies that each URL is tried one time only; the client
runtime will try each URL sequentially one at a time until a successful connection is
made or until all URLs have been tried. This sequence is the same as the connection
sequence used for standard connections.
If a connection cannot be established within the allocated time, a connection exception
will be thrown.
To set the fault tolerant reconnect timeout, call the following method:
ConnectionFactory.setFaultTolerantReconnectTimeout(Integer timeout)
The client’s ability to reconnect is also influenced by the advanced broker parameter
CONNECTION_TUNING_PARAMETERS.CLIENT_RECONNECT_TIMEOUT .
The default timeout is 10 minutes. By setting this parameter, the administrator can limit
the overall length of time the broker will maintain state for any fault-tolerant connection
that fails and cannot reconnect. The maximum length of time that a broker maintains state
is the lesser of the client-specified fault tolerant reconnect timeout and the
CLIENT_RECONNECT_TIMEOUT setting.
If the client fails to reconnect in the allocated time, the client is completely disconnected
by the broker. A fault-tolerant client runtime that attempts to reconnect late and after the
broker has discarded state will encounter a connection failure.
When a fault tolerant connection encounters a problem and cannot communicate with the
broker, the client runtime does not immediately drop the connection. Instead, it tries to
resume the connection. While it is trying to resume the connection, it defers passing any
exceptions to the client application. If it fails in its attempt to reconnect, it then passes the
exceptions to the client application, in the same manner as it would for a standard
connection.
While the client runtime is trying to resume a fault-tolerant connection, the client
application appears to block. However, the client application can stay informed about the
state of the connection by implementing a ConnectionStateChangeListener and
registering it with the appropriate Connection object.
Whenever the state of the connection changes, the client runtime calls the listener’s
connectionStateChanged(int state) method. This method accepts the following valid
values (each value represents a different connection state):
● progress.message.jclient.Constants.ACTIVE — The connection is active.
● progress.message.jclient.Constants.RECONNECTING — The connection is
unavailable, but the client runtime is trying to resume the connection.
● progress.message.jclient.Constants.FAILED — The client runtime has tried to
reconnect and failed.
● progress.message.jclient.Constants.CLOSED — The connection is closed.
A client application can obtain the connection’s current state by calling the following
method on the Connection object:
int getConnectionState( )
When a fault tolerant connection is working normally, the connection state is ACTIVE. If a
problem occurs with the connection, the client runtime changes the state to RECONNECTING
and attempts to resume the connection. If the attempt is successful, the client runtime
changes the state back to ACTIVE; if all attempts to reconnect fail, the client runtime
changes the state to FAILED. Finally, if an ExceptionListener is registered, the client
runtime calls its onException( ) method.
When you implement a ConnectionStateChangeListener, you must not perform any JMS
operations related to the connection, except for calling the following informational
methods:
● progress.message.jclient.Connection.getConnectionState( )
● progress.message.jclient.Connection.getBrokerURL( )
● progress.message.jclient.Connection.getBrokerReconnectURLs( )
● progress.message.jclient.Connection.getBrokerStandbyReconnectURLs( )
It is recommended that you do not perform any time- or CPU-intensive processing in the
connectionStateChanged( ) method, as this may impede the client reconnect.
This method returns the URL of the currently connected broker. If the current connection
state is RECONNECTING, this method returns the URL of the last broker connected when the
connection state was ACTIVE. This method may be called after the connection is closed.
Both of these methods are used for purely informational purposes, such as for writing to
an audit log; the reconnect logic is automatically performed by the client runtime. These
methods can both be called after the fault-tolerant connection is closed.
Reconnect Errors
A fault-tolerant connection might fail to reconnect for a variety of reasons. When a failure
occurs, the ERR_CONNECTION_DROPPED error code is included in the exception returned to the
Connection’s ExceptionListener; a linked exception provides more information about the
specific cause of the failure.
To get the reconnect URLs of the broker that the client connects to as a result of load
balancing, call getBrokerReconnectURLs( ) on the connection object.
To get the URLs of the backup broker for the broker that the client connects to as a result
of load balancing, call getStandbyBrokerReconnectURLs( ) on the connection object.
progress.message.jclient.ConnectionFactory
Setting Method
Initial connect timeout setInitialConnectTimeout( )
When the client runtime initially establishes a fault-tolerant connection, it checks the
value of the initial connect timeout, set with the setInitialConnectTimeout( ) method.
This method determines how long the client runtime tries to establish an initial fault-
tolerant connection.
After the fault-tolerant connection is successfully established, it will continue to operate
normally until a problem occurs with the network or broker. If a problem occurs, the client
runtime will try to resume the connection. The setFaultTolerantReconnectTimeout( )
method determines how long the client runtime attempts to resume the connection.
While the client runtime tries to resume the fault-tolerant connection, the persistent client
is still online. However, once the fault-tolerant reconnect timeout expires, the persistent
client goes offline, and JMS message sends are saved to the client’s local disk.
While offline, the persistent client runtime internally attempts to reconnect. This process
is controlled by two persistent client settings: reconnect interval and reconnect timeout.
The setReconnectInterval( ) method determines the interval between reconnect
attempts. The setReconnectTimeout( ) method determines how long the client runtime
tries to reconnect before returning an exception to the application; this method effectively
puts a cap on how long the persistent client is willing to operate offline.
The client persistence feature is essentially indifferent to the type of connection you are
using, whether standard or fault-tolerant. The only difference between a standard
connection and a fault-tolerant connection is when the transition to client persistence
takes place. If a standard connection has a problem with the broker or network, the
connection is immediately dropped, and the transition to client persistence immediately
follows. If a fault-tolerant connection has a problem with the broker or network, it tries to
resume the connection, delaying the transition to client persistence until the fault-tolerant
reconnect timeout expires.
Consider the following example. Suppose a client application wants to use the client
persistence feature and combine it with fault-tolerant connections. Further suppose the
client application uses the following settings:
● Initial connect timeout — 30 seconds
● Fault-tolerant reconnect timeout — 60 seconds
● Reconnect timeout — 360 minutes
● Reconnect interval — 600 seconds
When the client application initially connects to the broker, it does so within 25 seconds,
so the fault-tolerant connection succeeds. The persistent client application goes online.
When the client is online, JMS messages are transmitted directly to the broker. Later, the
network fails, and the client runtime attempts to resume the connection, but fails to do so
within 60 seconds, so the fault-tolerant reconnect timeout expires. At this time, the
persistent client goes offline.
When the client is offline, JMS messages are saved on the client's local disk. The offline
persistent client runtime continues to save JMS messages, but internally the runtime is
attempting to reconnect to the broker. This process is controlled by two persistent client
settings: reconnect interval and reconnect timeout. After every reconnect interval, the
persistent client will attempt to reconnect. If the reconnect timeout is exceeded the
persistent client will fail and return an exception to the application. By default, the
reconnect timeout is set to 0, which means that the client runtime will continually try and
connect to the broker.
Continuing this example, suppose the broker restarts after 15 minutes. Since the reconnect
interval is set to 600 seconds (10 minutes), on the second reconnect attempt the client will
succeed and go back online. In this case the client operates offline for a period of 20
minutes.
Reconnect Conflict
Connect conflicts are possible during client connection recovery. Conflicts can happen at
the JMS connection level and at the durable subscriber level.
Message Reliability
Table 9 describes message reliability levels for clients that reconnect to the broker or its
backup after a failure. The reconnect is automatic for fault-tolerant connections, and
application driven for standard connections. This table assumes that clients reconnecting
using standard connections do not resend in-doubt messages upon reconnecting.
Note The only way to guarantee exactly-once delivery is to use a fault-tolerant persistent
MessageProducer and a fault-tolerant MessageConsumer.
The standby broker B1_BU runs in the STANDALONE replication state until its peer
broker, B1, restarts, establishes a replication connection between B1 and B1_BU, and
starts synchronizing its data to the active broker’s data. When the brokers are fully
synchronized, B1_BU assumes the active role and B1 assumes the standby role.
● A client application submits a message to a broker. In this type of failure, the broker
does not have to be configured for replication but it does have to be licensed for fault-
tolerance.
Important Exactly-once recovery for the broker’s recovery logs must be enabled on your broker.
This feature is enabled by default on every broker but if you had been advised by your
Sonic representative to clear the XONCE Recovery option on the broker’s Tuning
properties, consult with your Sonic representative to determine whether the setting
can be selected (set to true) at this time.
● A client application experiences a transient network failure. In this type of failure, the
SonicMQ client runtime successfully resumes its connection at the same broker. The
message is not lost. Also, the message is not redelivered to its consumers, provided
that both the producer and the consumers use fault-tolerant connections.
Lost Messages
In these cases, NON_PERSISTENT_REPLICATED messages might be lost once the brokers are
restarted even if client applications resume their connections and sessions without
receiving an exception. Some messages can be lost because they have not been written to
the recovery log.
Redelivered/Duplicated Messages
In these case,s NON_PERSISTENT_REPLICATED messages might be redelivered. This might
happen because when a consumer acknowledges a NON_PERSISTENT_REPLICATED message,
the broker does not record the acknowledgement in its recovery log.
The default delivery mode is used when an application calls a variation of the send( ) or
publish( ) methods that do not have the delivery mode as one of their arguments. These
methods are defined in the MessageProducer, QueueSender and TopicPublisher classes.
Note While an application can pass the NON_PERSISTENT_REPLICATED delivery mode to the
setJMSDeliveryMode( ) method in the javax.jms.Message interface, the value set by this
method is used only to return it when the application calls the getJMSDeliveryMode( )
method. You can use this setting to restate the selected delivery mode into the message so
that it can be retrieved by the consumer as for informational use.
try
{
javax.jms.ConnectionFactory factory;
factory = (new progress.message.jclient.ConnectionFactory (broker));
pubSession =
connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE);
subSession =
connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE);
}
6. Near the end of the file, after the printUsage( ) method body, insert the following
internal class definition:
class ConnectionStateMonitor
implements progress.message.jclient.ConnectionStateChangeListener
{
public void connectionStateChanged(int status)
{
System.out.println("++++++++++++++++\n");
// Check status and write appropriate message to the console
switch (status)
{
case progress.message.jclient.Constants.RECONNECTING:
System.out.println("SYSTEM: Connection is inactive. " +
"Trying to reconnect. Please wait."); break;
case progress.message.jclient.Constants.ACTIVE:
System.out.println("SYSTEM: Connection is active" +
" and operating normally."); break;
case progress.message.jclient.Constants.FAILED:
System.out.println("SYSTEM: Connection has failed." +
" Cannot reconnect."); break;
case progress.message.jclient.Constants.CLOSED:
System.out.println("SYSTEM: Connection is closed.");
}
// Write the reconnect and standby URLs to the console
String[] brokerURLs = ((progress.message.jclient.Connection)connect).
getBrokerReconnectURLs();
String[] standbyURLs = ((progress.message.jclient.Connection)connect).
getStandbyBrokerReconnectURLs();
if (brokerURLs == null)
System.out.println("SYSTEM: No broker reconnect URLs provided.");
if (brokerURLs != null){
System.out.println("SYSTEM: The broker reconnect URLs are as follows:");
for ( int i = 0; i < brokerURLs.length; ++i ){
System.out.println("Reconnect URL[" + i + "] is " + brokerURLs[i]);
}
}
if (standbyURLs == null)
System.out.println("SYSTEM: No standby broker URLs provided.");
4. In the ChatFT console window, type some text and press Enter.
The ChatFT console window and the Chat console window both display the text you
entered, preceded by:
SALES:
5. In the ChatFT console window, type some text and press Enter.
The Chat console window and the ChatFT console window both display the text you
entered, preceded by:
MARKETING:
6. In the SonicMQ Container1 console window (in which the broker is running), enter
Ctrl-C.
This causes the broker to shut down and close all active connections. You are
prompted whether you want to terminate the batch job.
7. In the SonicMQ Container1 console window, enter Y to terminate the batch job.
The following output is displayed in the ChatFT console window:
++++++++++++++++
This occurs because the session was closed when the standard connection to the
broker was closed.
9. In the ChatFT console window, type some text and press Enter.
Notice that the client application appears to block. This behavior occurs because all
client operations are suspended when the connection is unavailable. Also notice that
no exception is displayed.
10. Restart the broker by selecting:
Start > Programs > Progress Sonic > SonicMQ 7.5 > SonicMQ DomainManager
When the broker is restarted, the fault-tolerant connection is resumed. This causes the
client runtime to call the connectionStateChanged(int state) method again, resulting
in the following output:
++++++++++++++++
The ChatFT console window also displays the text you entered while the connection
was unavailable, preceded by:
SALES:
You have completed this example. You can experiment further, or you can close the
ChatFT and Chat console windows.
Starting a Connection
To start delivery of incoming messages through a connection, use the connect.start( )
method. If you stop delivery, messages are still saved for the connection. Under a restart,
delivery begins with the oldest unacknowledged message. Starting an already started
session is ignored. Use the following syntax to start delivery through a connection:
connect.start( )
Stopping a Connection
To stop delivery of incoming messages through a connection, use the connect.stop( )
method. After stopping, no messages are delivered to any message consumers under that
connection. If synchronous receivers are used, they will block. A stopped connection can
still send or publish messages. Stopping an already stopped session is ignored. Use the
following syntax to stop delivery through a connection:
connect.stop( )
When a connection is stopped, that connection is in effect paused. The message producers
continue to perform their functions. The consumers, however, are not active until the
connection restarts. When the stop( ) method is called, the stop will wait until all the
message listeners have returned before it returns. MessageConsumers that are active can
receive null messages if they are using receive(timeout) or receiveNoWait( ).
Closing a Connection
To close a connection, use the connect.close( ) method.
When a connection is closed, all message processing within the connection’s one or more
sessions is terminated. If a message is available at the time of the close, the message (or
a null) can be returned, but the message consumer might get exceptions by trying to use
facilities within the closed connection.
When a transacted session is closed, the transaction in progress is marked for rollback.
This is true whether the shutdown was orderly or unplanned, such as a broker or network
failure.
The message objects can be used in a closed connection with the exception of the
message’s acknowledge methods.
See Chapter 5, “SonicMQ Client Sessions,” for information about coding connections and
sessions and handling exceptions on connections.
Client Application
S
E
S C
S O
I N
O N
N
E
S
C
E T
S I
S O
I SonicMQ
O
N
N Broker
S
E
S C
S O
I N
O N
N
E
S
C
E T
S I
S O
I
O
N
N
Communication Layer
The SonicMQ broker works in concert with the network layer to provide asynchronous
message communications between client applications. As shown in Figure 46, a client can
send and receive messages through the SonicMQ API and interfaces to communicate on
network connection to a broker. Messages might be stored in a message store as an
optional service specified by the message producer.
Client Client
API API
Network
Broker
The connection layer, as shown in Figure 47, involves getting a ConnectionFactory, then
creating a Connection, and finally creating a Session. A Session holds MessageProducer
and MessageConsumer objects.
ConnectionFactory
Connection
Session
MessageProducer
MessageConsumer
This chapter explains the programming concepts and actions required to establish and
maintain SonicMQ client sessions. This chapter contains the following sections:
● “Overview of Client Sessions”
● “Session Objects”
● “Flow Control”
● “Flow to Disk”
● “Using Sessions and Consumers”
● “JMS Messaging Domains”
● “Integration with Application Servers”
A Connection provides a createSession() method for creating a Session. This method can
be called multiple times to create multiple Session objects, each of which remains
associated with the Connection throughout its lifespan. The signature of the
createSession() method is as follows:
javax.jms.Session createSession(boolean transacted, int acknowledgeMode)
where:
● transacted — [true | false]
If true, the session will be transacted.
● acknowledgeMode — [AUTO_ACKNOWLEDGE | CLIENT_ACKNOWLEDGE |
SINGLE_MESSAGE_ACKNOWLEDGE | DUPS_OK_ACKNOWLEDGE]
Indicates whether the client will acknowledge any messages it receives.
AUTO_ACKNOWLEDGE, CLIENT_ACKNOWLEDGE, and DUPS_OK_ACKNOWLEDGE are defined in
javax.jms.Session. SINGLE_MESSAGE_ACKNOWLEDGE is defined in
progress.message.jclient.Session.
The parameters of a Session are qualified so that when the Session is transacted, the
acknowledgementMode setting has no effect, because the transaction implicitly handles
acknowledgement. Similarly, acknowledgementMode has no effect when a Session is only
producing messages.
Acknowledgement Mode
Communication between the broker and the message consumer involves an indication of
receipt of the message. One of the following acknowledgement modes is enforced for all
messages in a session:
● AUTO_ACKNOWLEDGE — The session automatically acknowledges the client’s receipt of
a message before the next call to receive (synchronous mode) or when the session
MessageListener successfully returns (asynchronous mode). In the event of a failure,
the last message might be redelivered.
● CLIENT_ACKNOWLEDGE — An explicit acknowledge( ) on a message acknowledges the
receipt of all messages that have been produced and consumed by the session that
gives the acknowledgement. In the event of a failure, all unacknowledged messages
might be redelivered.
● SINGLE_MESSAGE_ACKNOWLEDGE — An explicit acknowledge( ) on a message
acknowledges only the current message and no preceding messages. In the event of a
failure, all unacknowledged messages might be redelivered. This mode is a SonicMQ
extension to the JMS standard.
● DUPS_OK_ACKNOWLEDGE — The session “lazily” acknowledges the delivery of messages
to consumers, possibly allowing multiple deliveries of messages after a system
outage.
Warning While acknowledgement sets standards for delivery from the client to the broker, there is
no reply to the sending application. If an application requires a reply to the sender, use
the JMSReplyTo header field to indicate the request and program your application to
respond to this header field. The requestor can also append a correlation identifier that
will ensure that the reply matches its request.
Recover
A client might build up a large number of unacknowledged messages while attempting to
process them. A session’s recover( ) method is used to stop a session and restart it with
its first unacknowledged message.
A recover( ) action notification tells SonicMQ to stop message delivery in the session,
set the redelivered flag on unacknowledged messages it will redeliver under the recovery,
and then resume delivery of messages, possibly in a different order than originally
delivered.
The need for the recover( ) method is most apparent when the acknowledgement mode
is CLIENT_ACKNOWLEDGE or SINGLE_MESSAGE_ACKNOWLEDGE.
The circumstances under which a message can be redelivered to the consumer depend on
the session’s acknowledgement mode:
● AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE — Nontransacted sessions
that choose AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE acknowledgement have
messages redelivered to a consumer when the application's onMessage() method
throws an exception. The client runtime catches the exception, and then calls
onMessage() again. Exceptions are caught and reported to the Connection's
ExceptionListener. Setting a limit to redelivery attempts limits the redelivery count.
● SINGLE_MESSAGE_ACKNOWLEDGE or CLIENT_ACKNOWLEDGE — For
nontransacted sessions that choose SINGLE_MESSAGE_ACKNOWLEDGE or
CLIENT_ACKNOWLEDGE acknowledgement, messages are redelivered when the
application calls Session.recover().
● TRANSACTED — Messages are redelivered when an application rolls back the
transaction.
The JMS defined property JMSXDeliveryCount uses an int to specify the number of
delivery attempts for a message. The value of this property is incremented every time a
message is given to a consumer.
Delivery counters are maintained in the client runtime for messages waiting to be
delivered to a consumer object. Applications for which redelivery limit detection is
effective are those that create long-lived Consumers: in other words.,
ConnectionConsumers, or MessageConsumers that are created once and reused. If a
consumer is closed and recreated, the counter for each message sent to the conumer is
reset to 0.
For more information about setting and getting the maximum delivery count for a PTP
receiver:
● As set programmatically for a Point-to-point receiver on a ConnectionFactory,
see “Setting the Maximum DeliveryCount” on page 148.
● As set administratively in a JMS Administered Object, see the “JMS Administered
Objects Tool” chapter in the Progress SonicMQ Configuration and Management
Guide.
Explicit Acknowledgement
The Message interface provides an acknowledge( ) method, which explicitly
acknowledges a message. However, the behavior of this method depends on how the
Session was created.
Transacted Sessions
When a Session is transacted, that Session will combine a group of one or more
messages with client-to-broker ACID properties: Atomic, Consistent, Isolated, and
Durable.
When a Session is transacted, message input and message output are staged on the broker
system but not completed until you call the method to complete the transaction.
Completion of a transaction, determined by your code, does one of the following:
● Commit — The series of messages is sent to consumers.
● RollBack — The series of messages (if any) is destroyed.
The completion of a Session’s current transaction automatically begins the next
transaction. A transacted Session impacts producers and consumers in the ways described
in Table 10.
Table 10. Transacted Session Events by Message Role
Producer Delivers the series of messages staged Disposes of the series of produced
since the last call. messages staged since the last call.
Consumer Acknowledges the series of messages Redelivers the series of received
received since the last call. messages retained since the last call.
When a rollback is done in a session that is both sending and receiving, its produced
messages are destroyed and its consumed messages are automatically recovered.
Rollbacks can be either explicit or implicit. Explicit rollbacks occur when the client calls
the rollback( ) method. Implicit rollbacks occur when either:
● The session or connection are closed without finishing the transaction
● The application, connection, or broker experience failure
To check whether a session is transacted, use the getTransacted( ) method. The return
value is true if the session is in transacted mode.
A transacted session only completes successfully when an explicit commit( ) is invoked.
The timeout interval can be 0 (an indicator to never idle-out a transaction in process) or
any positive integer value that represents the number of minutes of inactivity before the
broker managed timeout is enforced. As you have no way of knowing the broker’s rules,
you should take best efforts to complete transactions as soon as possible.
Without broker-managed timeouts, the transaction will still rollback when the application
disconnects or shuts down.
Distributed Transactions
When transactions are contained within a session, the transaction is on a single
communication with a broker. The control of the transaction is entirely local.
More sophisticated transactions arise where two sessions enclose the complete
transaction. In such cases, applications can implement X/Open’s XA protocol to enable
transaction identification and transaction demarcation. These global transactions can be
further abstracted by interfacing with a transaction manager.
Distributed transactions are discussed in Chapter 14, “Distributed Transactions Using
XA Resources.”
where transactionID is a UUID and timeToLive is the intended lifespan of the indexed
identifier in milliseconds. If you omit the timeToLive, the target broker’s advanced
property DUPLICATE_DETECTION_PARAMETERS.INDEXED_TXN_DEFAULT_LIFESPAN sets the
lifespan of the indexed identifier. You can configure advanced properties on a broker from
the Sonic Management Console by selecting the Broker Properties and, under the
Advanced tab, clicking Edit in the Properties section.
You can alternatively use a hashcode calculated over the message payload instead of a
UUID for the transactionID. You must ensure that the hashcode is unique for each unique
transaction being tracked within the transaction age limit you have set.
See “Duplicate Message Detection Overview” on page 362 for more information about
detecting duplicate messages.
Session Objects
The primary session objects allow creation of the destinations, producers, consumers, and
messages that are used in the session, as shown in Figure 48.
ConnectionFactory
Connection
Session
MessageProducer
Destination
MessageConsumer
MessageListener
Figure 49 shows the types of message objects that are created from session methods. The
message types are common and extended into both JMS domains.
The XMLMessage type is unique to SonicMQ and is an extension of the TextMessage type.
The MultipartMessage type is unique to SonicMQ and is an extension of the Message type.
Message
BytesMessage
MapMessage
ObjectMessage
StreamMessage
TextMessage XMLMessage
MultiPartMessage
Part
MessagePart
Creating a Destination
Destinations are administered objects that can be controlled by an administrator and can
be retrieved through JNDI or other object storage mechanisms.
See “JMS Administered Objects Tool” in the Progress SonicMQ Configuration and
Management Guide to learn how the JMS Administered Object tool in the Sonic
Management Console allows you to create destinations in both JNDI and file stores.
Important Security enabled brokers can deny access to destinations. See the chapter “Security
Considerations in System Design:” in the SonicMQ Deployment Guide for information
about access control.
Destination Objects
There are two destination creation methods:
Point-to-point: createQueue
javax.jms.Queue queue = session.createQueue(queueName)
where:
queueName is a String name. Its meaning is evaluated from the destination name syntax
you use. When the queueName is JMS destination, a queue by that name must exist on
the broker. If security is enabled on the broker, access control might deny the user
from reading or writing to a queue.
Either createQueue
Name Description create Queue create Topic or createTopic
Destination name Q T http://U
Temporary Destinations
Temporary destinations (TemporaryTopic or TemporaryQueue) can be created for request-
and-reply mechanisms.
See “Reply-to Mechanisms” on page 290 for more information.
Creating a MessageProducer
A MessageProducer sends messages to one or more destinations.
You create a MessageProducer object by calling a Session object’s createProducer( )
method. The signature for this method is:
public java.jms.MessageProducer createProducer(java.jms.Destination destination)
throws JMSException
Queue and Topic both inherit from Destination, so they are valid parameters. If you
provide a Destination, the returned MessageProducer uses the Destination as its default.
If you use null as the Destination, the returned MessageProducer is not tied to any
particular Destination.
Creating a MessageConsumer
A MessageConsumer receives messages from a single destination.
You create a MessageConsumer object by calling one of the Session object’s
createConsumer( ) methods:
● public javax.jms.MessageConsumer
createConsumer(javax.jms.Destination destination)
throws JMSException
● public MessageConsumer
createConsumer(javax.jms.Destination destination,
java.lang.String messageSelector)
throws JMSException
● public MessageConsumer
createConsumer(javax.jms.Destination destination,
java.lang.String messageSelector,
boolean NoLocal)
throws JMSException
Since both Queue and Topic inherit from Destination, either is a valid Destination.
The MessageConsumer object returned by these methods is dedicated to the Destination
you provide. If the MessageConsumer is created with a Queue, it honor the JMS semantics
for the P2P messaging model; if a Topic, the Pub/Sub messaging model.
If you want to create a MessageConsumer that is durable subscriber to a Topic, you call one
of the Session object’s createDurableSubscriber( ) methods:
● public javax.jms.TopicSubscriber
createDurableSubscriber(javax.jms.Topic,
java.lang.String name)
throws JMSException
● public javax.jms.TopicSubscriber
createDurableSubscriber(javax.jms.Topic,
java.lang.String name)
java.lang.String messageSelector,
boolean NoLocal)
throws JMSException
Since TopicSubscriber inherits from MessageConsumer, you can assign the returned
TopicSubscriber to a MessageConsumer reference; this allows you to use the
MessageConsumer interface to manipulate the object, rather than using the TopicSubscriber
interface, which might be deprecated in future JMS versions.
Creating a Message
The message type is created from a Session method in the general form:
javax.jms.[type]Message msg = sendSession.create[type]Message( )
The XMLMessage and MultipartMessage types are SonicMQ extensions to the JMS
standard. You cannot create them from a javax.jms.Session, because the required
methods are not defined for that interface. However, you can cast the javax.jms.Session
to a progress.message.jclient.Session first, as shown:
progress.message.jclient.Session pSendSession;
progress.message.jclient.XMLMessage xMsg;
progress.message.jclient.MultipartMessage multipartMsg;
pSendSession = (progress.message.jclient.Session)sendSession;
xMsg = xSendSession.createXMLMessage( );
mutipartMsg = xSendSession.createMultipartMessage( );
See Chapter 6, “Messages,” for information about message interfaces, structure, and fields.
Closing a Session
Each session should only have a single thread of execution. The close( ) method is the
only Session method that can be called while some other session method is being
executed in another thread.
Closing a CLIENT_ACKNOWLEDGE session does not force an acknowledge( ) to occur.
Attempts to use a closed connection’s session objects throws an IllegalStateException.
Starting a started connection or closing a closed connection has no effect and does not
throw an exception.
The Message objects can be used in a closed session (with the exception of the message’s
acknowledge( ) method).
Flow Control
The asynchronous benefits of SonicMQ are not limited to simply receiving without
blocking. They also include:
● Send and receive buffers that stage messages in transit between a client application
and a broker
● An optimized persistence mechanism to maximize broker performance for
guaranteed message delivery
● Concurrent Transacted Cache technology that uses in-memory cache and high-
speed log files to increase throughput for short-duration persistent messages
● Queues defined with specified amounts of memory and disk space reserved for the
queue content
Any of these resources might be offered more data than can be managed. If flow control
is active, SonicMQ will throttle back the message flow from the producer, allowing the
next message to flow into the buffers only when space is available.
In Pub/Sub and PTP you can disable flow control so that when resources are nearly
exhausted, SonicMQ can, under programmatic control, throw exceptions until flow
control conditions are cleared.
When flow control is active, the messages might be sent to consumers at a rate that is
faster than that at which the messages are actually consumed. When the buffers that store
unprocessed messages approach the flow control threshold, flow control can stop new
additions until the buffers fall below a threshold level.
The back pressure from slower consumption might start to impact the buffers for queues
or durable subscriptions. When system or queue capacities are filled with messages in
process, flow control is activated against producers. The message acceptance rate drops,
which eventually results in back pressure at the producers, causing them to either tolerate
the slowdowns or, with flow control disabled, to throw an exception so that you can handle
the situation. For example, you can catch the exception and have the application wait
some period of time before resending.
To avoid the invocation of flow control you can:
● Optimize application processing on incoming messages.
● Adjust the consumer buffer (on the broker side).
● Increase the size of queues.
● Decrease the message expiration time of messages.
● Set the DeliveryMode on messages to DISCARDABLE.
Note Messages sent to a queue will only expire after they have been placed on the queue, so
expiration detection can only result from:
● Dequeue operations by receivers
● Processing by the queue cleanup thread
Browsing the queue does not detect expiration.
Monitoring Intervals
The monitoring interval is a property of the ConnectionFactory that is set before
connections are created. You set the monitoring level by calling
ConnectionFactory.setMonitorInterval(java.lang.Integer interval).
The value found in the factory when a connection is created applies to any sessions
created by that connection, and cannot be subsequently modified. The property defines
the duration of the monitoring interval in seconds, where 0 indicates that flow control
monitoring is disabled for all sessions on the connection.
Since flow control pause notifications are generated after the session has been blocked for
one full monitoring interval, it might take as long as another monitoring interval from the
time the session became blocked before a notification is generated.
The block-detection logic monitors whether one or more produced messages remain
blocked in the client buffers due to flow control. The logic does not monitor conditions
where the client is unable to send a message due to network congestion or other load-
related factors.
If a producer session remains blocked over multiple monitoring intervals, a flow control
pause notification is generated at the end of each monitoring interval as long as the
producer session remains blocked. When the session becomes unblocked, a flow control
resume notification is generated.
Pub/Sub
In Pub/Sub messaging, when a block is sustained throughout a monitoring interval, an
administrative notification is generated that identifies:
● Username and ConnectID of the blocked producer session
● Username, ConnectID, and Topic of any non-durable subscriber that is blocking the
producer session
● Username, JMS ClientID, and JMS subscriber name of any durable subscriber that is
blocking the producer session
When the block is relieved, another administrative notification is generated identifying
the Username and ConnectID of the now-unblocked producer session.
PTP
In PTP messaging, when a block is sustained throughout a monitoring interval, an
administrative notification is generated that identifies:
● Username and ConnectID of the blocked producer session
● Name of queue that is blocking the producer session or routing queue
When the block is relieved, another administrative notification is generated identifying
the Username and ConnectID of the now-unblocked producer session.
Notification Interface
Notifications are collected and displayed in the Sonic Management Console and delivered
to any management client that has registered an appropriate notification listener (see the
Progress SonicMQ Administrative Programming Guide for more information). This
interface has a callback that handles all notification types.
To view flow control notifications in the Sonic Management Console, select the
Containers node in the Manage view. Under the container instance node, right-click the
broker instance where you want to view the notifications. In the window that opens, select
the flow control events under the Applications node. See the Progress SonicMQ
Configuration and Management Guide for more information about viewing flow control
notifications in the Sonic Management Console.
Flow to Disk
If flow control is active, MessageProducers may block, waiting for MessageConsumers to
process messages that have accumulated in in-memory buffers. The flow-to-disk feature
relieves this problem by temporarily writing messages to disk, allowing message
production to continue despite slow message consumption. This feature is designed for
Pub/Sub messaging, in which one slow consumer might hold up message production for
other consumers.
For a detailed description of flow-to-disk functionality, see the Progress SonicMQ
Performance Tuning Guide.
An administrator can enable this feature for all clients connected to a broker by setting a
broker configuration parameter (FLOW_TO_DISK). As an application programmer, you can
explicitly override the broker setting.
To override this setting for all Sessions, call the following method:
ConnectionFactory.setFlowToDisk(Integer flowSetting)
To override this setting for a single Session, call the following method:
Session.setFlowToDisk(int flowSetting)
where the allowable values for flowSetting are the same as for the
ConnectionFactory.setFlowToDisk( ) method, except that parameters are passed as ints,
not Integers.
Only a subscriber can meaningfully set the FLOW_TO_DISK setting. If a session exclusively
produces messages, calling the Session.setFlowToDisk( ) method will have no effect.
Client Application
S
E
S C
S O
I N SonicMQ
O N
N Broker
E
S
C
E T
S I
S O
I
O
N
N
Connection Consumer
An application server creates a ConnectionConsumer to asynchronously receive messages
and pass them to a ServerSessionPool where the messages are assigned to server sessions.
The ConnectionConsumer receives messages through the connection for the destination it
specified, filtering the preferred messages through its message selector, then distributes
the message to sessions, as shown in Figure 51. This behavior enables the consumer’s
messages to be processed concurrently by several sessions.
Application
Application Server JMS Runtime
The create method for the ConnectionConsumer indicates the ServerSessionPool that is an
object implemented by an application server to provide a pool of ServerSessions for
processing the messages of the ConnectionConsumer.
where:
● destination is the Queue or Topic to access
● topic is the Topic to access
● messageSelector is the String with the message selector definition
● sessionPool is the ServerSessionPool to associate with this connection consumer
● subscriptionName is the name of the durable subscription
● maxMessages is the maximum number of messages that can be assigned to a server
session at one time
Server Session
A connection consumer executes a getServerSession( ) method to return a
ServerSession from the pool. A ServerSession is an application server object that
associates a thread with a JMS session. It offers two methods, getSession( ) to return the
ServerSession's JMS session, and start( ) to start the execution of the ServerSession
thread that results in the execution of the JMS Session's run( ) method.
ServerSession
Session start ( )
message
Message Listener
onMessage
The ServerSession will register some object it provides as the ServerSession's thread run
object. The ServerSession's start method will call the thread's start( ) method, which
will start the new thread, and from it, call the run method of the ServerSession's run
object. This object will do some housekeeping and then call the Session's run method.
When run( ) returns, the ServerSession's run object can return the ServerSession to the
ServerSessionPool, and the cycle starts again.
After the container accesses a bean from a pool of available instances, the received
message is passed to the onMessage( ) method of the MDB instance. When the
onMessage( ) method completes, the bean is returned to the pool of available instances.
Shared Subscriptions
SonicMQ allows shared subscriptions for topics across multiple application servers.
Server session pools can be used in combination with shared subscriptions to allow round-
robin delivery between application servers, which, in turn, allows round-robin between
members of the server session pool.
XA Resources
Distributed transactions, discussed in Chapter 14, require XAResources so that they can
integrate with a Transaction Manager and application servers.
Figure 53 describes SonicMQ XA interface objects.
ConnectionFactory Connection
Session XAResource
XAConnectionFactory XAConnection
XASession
The connections and sessions for XA interfaces are similar relationships to those in the
standard interface. Some examples of object relationships are:
● The XASession is created by the XAConnection.
● The XASession inherits from the Session.
This chapter provides information about creating and handling messages in SonicMQ,
and contains the following sections:
● “About Messages”
● “Message Type”
● “Working With Messages That Have Multiple Parts”
● “Message Structure”
● “Message Header Fields”
● “Message Properties”
● “Message Body”
About Messages
A SonicMQ message is a package of bytes that encapsulates the message body as a
payload and then exposes metadata that identify, at a minimum, the message and its
timestamp, destination, and priority. The instanceof the object identifies the type of JMS
message.
When a text message is published, it might be coded as shown:
Message Type
The JMS specification defines five types of messages, all derived from the Message
interface, which also defines message headers and the acknowledge( ) method used by all
JMS messages. SonicMQ provides an XMLMessage type as an extension of the JMS
TextMessage. Figure 54 lists the SonicMQ message types.
Message
BytesMessage
MapMessage
ObjectMessage
StreamMessage
TextMessage XMLMessage
MultiPartMessage
Part
MessagePart
Creating a Message
Create a message type from a session method in the form:
Use the following session methods to create the different message types:
● javax.jms.Message msg = session.createMessage( )
● javax.jms.BytesMessage msg = session.createBytesMessage( )
● javax.jms.MapMessage msg = session.createMapMessage( )
● javax.jms.ObjectMessage msg = session.createObjectMessage( )
● javax.jms.StreamMessage msg = session.createStreamMessage( )
● javax.jms.TextMessage msg = session.createTextMessage( )
The MultipartMessage type, described on page 242, extends the Message type. The
XMLMessage type, described in the following section, extends the TextMessage type.
JAXP Support
The Java API for XML Parsing (JAXP) is the JavaSoft standard for a Java application to
access an XML-conformant parser. An application can swap XML parsers to move
between high performance and memory conservation without changing application code.
To use a different compliant SAX or DOM parser, pass the system property in a command
line as shown in these command line examples:
● java –Djavax.xml.parsers.SAXParserFactory=
org.apache.crimson.jaxp.SAXParserFactoryImpl myApp
● java –Djavax.xml.parsers.DocumentBuilderFactory=
org.apache.crimson.jaxp.DocmentBuilderFactoryImpl myApp
JAXP Interfaces
JAXP provides the following interfaces:
● DocumentBuilder — The Document Builder defines the API to obtain DOM
Document instances from an XML document. Using this class, you can get a
org.w3c.dom.Document from XML tagged text. An instance of this class is obtained
from the DocumentBuilderFactory.newDocumentBuilder method. Then XML can be
DOM parsed from a variety of input sources including InputStreams, files, URLs, and
SAX InputSources.
● DocumentBuilderFactory — The Document Builder Factory defines a factory API
that lets applications get a parser that produces DOM object trees from XML
documents. The system property that controls the Factory implementation to create is
javax.xml.parsers.DocumentBuilderFactory. The property names a class that is a
concrete subclass of this abstract class. If none is defined, the default is used. When
an application has a reference to a DocumentBuilderFactory it can use the factory to
configure and obtain parser instances.
Method Description
void Takes the org.w3c.dom.Document aDoc and stores it
setDocument(org.w3c.dom.Document aDoc) as the internal document for this message.
Method Description
void Sets the class name for the implementation of
setSAXParserFactory(java.lang.String classname) JAXP1.1 SAXParserFactory interface to override the
default Apache Xerces parser.
DOM Support
The Document Object Model (DOM) provides a tree of objects with interfaces for
traversing the tree and writing and XML version of it. Code Sample 9, excerpted from the
XMLDOMChat sample application, provides an example of DOM support.
SAX Support
SAX (Simple API for XML) provides an event-driven mechanism for parsing XML
which is optimized for parsing large XML documents. This is the protocol that most
servlets and network-oriented programs use to transmit and receive XML documents
because of its speed in a modest memory footprint.
However, the SAX protocol requires more program logic than the Document Object
Model (DOM). As an event-driven model, SAX is more obscure—you provide the
callback methods and the parser invokes them as it reads the XML data. Also, you cannot
reposition in or rearrange the document as it is interpreted in a serial data stream.
If your application calls for modifying and displaying an XML document, the DOM is
better suited to that task.
The XMLSAXChat sample excerpt in Code Sample 10 shows how a publisher sends an XML
message and the subscriber calls getSAXInputSource( ) on the message. That method
returns an org.xml.sax.InputSource (rather than the org.w3c.dom.Document returned in the
XMLDOMChat sample). Event parsing is done on the XML message and the message is
printed out to the screen.
Code Sample 10. XMLSAXChat: SAX Support
public void onMessage(javax.jms.Message aMessage)
{
try
{
// Test the message type.
if (aMessage instanceof progress.message.jclient.XMLMessage)
{
// Cast the message as a XML message.
progress.message.jclient.XMLMessage xmlMessage =
(progress.message.jclient.XMLMessage) aMessage;
else
{
// Cast the message as a text message and display it.
javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage;
System.out.println("[TextMessage] " + textMessage.getText());
}
}
catch ...
Composition of a MultipartMessage
The structure of a MultipartMessage is a wrapper surrounding a series of Parts, as shown
in Figure 55.
MultipartMessage
Part
Header
Content
Figure 55. MultipartMessage Wraps Parts That Have Header and Content
Each MultipartMessage can have JMS properties and zero or more parts. Each part has
content and a header that declares at least the part’s content type.
MultipartMessages,their headers, and their parts are interfaces:
● progress.message.jclient.MultipartMessage
● progress.message.jclient.Part
● progress.message.jclient.Header
MultipartMessage Type
The MultipartMessage type is a subclass JMS Message and follows the JMS semantics for
interactions with sessions, producers and consumers. A MultipartMessage is limited to 10
megabytes, must be completely created on the producer, and must be sent to the broker as
a single logical transfer.
Producing a MultipartMessage
To produce a MultipartMessage, create the parts and add them to an instance of a
MultipartMessage.The following example describes sending objects as message parts.
When wrapping a message in a MultipartMessage the entire message is wrapped,
including the message header and properties. This process provides a technique for
handling undeliverable or indoubt messages. If a message needs to be re-routed, it can
packaged in a MultipartMessage with the problem message as a Part and routed to a
special destination for analysis and processing. The following code excerpts are from the
MultipartMessage sample application, describing the assembly of a multipart message
from five distinct parts:
● part1 is a TextMessage:
● part2 is a byte[]:
mm.addPart(part1);
mm.addPart(part2);
mm.addPart(part3);
mm.addPart(part4);
mm.addPart(part5);
sender.send(mm);
The producer methods in the MultipartMessage interface are listed in Table 14.
Table 14. Producer Methods in the MultipartMessage Interface
Method Description
Part Creates an empty part.
createPart()
Receiving a MultipartMessage is the same as any other JMS message. Consuming the
message is done with standard MessageListeners or calls to receive( ).
In the MultipartMessage sample where onMessage delivers an instanceof
MultipartMessage, it then passes it through its unpackMM pattern to determine how many
parts the message contains. The sample application then iterates through the handling of
each part, as shown:
indent(n);
for (int i = 0; i < partCount; i++)
{
Part part = mm.getPart(i);
Each part is evaluated to see if it should be treated as a JMS message part or evaluated as
a MIME content type:
if (mm.isMessagePart(i))
{
javax.jms.Message msg = mm.getMessageFromPart(i);
if (msg instanceof MultipartMessage)
unpackMM(msg, ++depth);
else
unpackJMSMessage(msg, n);
}
else
{
unpackPart(part, n);
}
}
The methods for the MultipartMessage consumer are listed in Table 15.
Table 15. Consumer Methods in MultipartMessage Interface
Method Description
java.lang.String Return the extended type or profile that was used to
getProfileName() create this message
Message Gets a JMS message from the part of the message with
getMessageFromPart(java.lang.String cid) the content ID cid
JMS_SonicMQ_ExtendedType Property
A MultipartMessage is not only identified as an instance of MultipartMessage. For the
convenience of older SonicMQ versions or other JMS providers, a String property,
JMS_SonicMQ_ExtendedType, is also set when a MultipartMessage type is sent to carry the
profile of the message. The name is also accessible in
progress.message.jclient.Constants as:
Parts of a MultipartMessage
A part of a MultipartMessage follows the design pattern for Java handling of MIME used
in JAXM and JavaMail™ through the JavaBeans Activation Framework. Each part has an
associated Header and content, as described:
● The Header contains name/value pair to represent header objects such as ContentType
and ContentId. The Header can be implemented separately from a MessagePart, or as
methods on the Part itself.
● The content of a Part is accessed through a javax.activation.DataHandler in the
following formats:
■ DataHandler by using the getDataHandler( ) method or through a
javax.activation.DataHandler object. The DataHandler object lets you discover
the operations available on the content and then instantiate the appropriate
component to perform those operations. A DataContentHandler class for the
specified type must be available to ensure the expected result. For example,
setContent(mycontents, "application/x-mytype")
expects a DataContentHandler for application/x-mytype.
■ Input stream by using the getInputStream( ) method.
■ Java object by using the getContent( ) method. This method returns the content
as a Java object.
Method Description
setContent(java.lang.Object object, Sets the part's content as a Java Object of content
java.lang.String type) type type
MessagePart Subclass
A subclass of the Part is the MessagePart, used by an application to wrap one or more JMS
messages into a MultipartMessage. The ContentType of SonicMQ MessageParts is set
implicitly, as shown in Table 17:
Table 17. Implicit Content-Type for JMS Message Types
BytesMessage application/x-sonicmq-bytesmessage
MapMessage application/x-sonicmq-mapmessage
ObjectMessage application/x-sonicmq-objectmessage
StreamMessage application/x-sonicmq-streammessage
TextMessage application/x-sonicmq-textmessage
The MessagePart interface inherits all its methods from the Part interface.
Method Description
void Sets the ContentId header of the message or
setContentId(java.lang.String cid) attachment part to the value cid.
Method Description
void Sets the value of a header field name to the String
setHeaderField(java.lang.String name, value
java.lang.String value)
SOAP Body
Attachment 1
Attachment 2
Message Structure
JMS Messages are composed of the following parts:
● Header Fields (JMS) — All messages support the same set of header fields. Header
fields contain values used by clients and brokers to identify and route messages.
● User-defined Properties — User-defined name-value pairs that can be used for
filtering and application requirements.
● Provider-defined Properties — Properties defined and typed by SonicMQ for
carrying information used by SonicMQ features.
● Supported JMS-defined Properties (JMSX) — Predefined name-value pairs that
are an efficient mechanism for supporting message filtering.
● Body — JMS defines several types of message bodies, which cover the majority of
messaging styles currently in use.
Note While the JMS message system provides programmatic access to all components of a
message, message selectors and routing data are constrained to the header fields and
properties—not the message body.
JMSMessageID String SonicMQ field for a A message ID value must While required, the
Required. unique identifier. start with “ID:”. algorithm that
calculates the ID on the
Set by the producer
client can be bypassed,
send/publish method.
which sets the
JMSMessageID to null.
JMSType String Contains the name Recommended for This is not, by default,
Optional. of a message's systems where the the message type.
definition as found repository needs the
Set by producer
in an external message type sent to the
method.
message type application.
repository.
publisher.publish(Message message)
Three of the message header fields have default values as static final variables:
● DEFAULT_DELIVERY_MODE = NON_PERSISTENT
● DEFAULT_PRIORITY = 4
● DEFAULT_TIME_TO_LIVE = 0
publisher.publish(Message message,
int deliveryMode,
int priority,
long timeToLive)
If you use this format of the method but do not intend to override some of the default
values, you can substitute the values back into the parameter list. For example:
Message Properties
Properties are optional fields that are associated with a message. No message properties
are required for any message producer. The property values are used for message selection
criteria and data required by applications and other messaging infrastructures. The order
of property values is not defined.
Although the JMS specification does not define a policy for what should or should not be
made a property, application developers should note that data is handled in a message's
body more efficiently than data in a message's properties. For best performance,
applications should only use message properties when they need to customize a message's
header. The primary reason for doing this is to support customized message selection.
Property names must obey the rules for a message-selector identifier. Property values can
be boolean, byte, short, int, long, float, double, and String. A String property is limited
to 65,535 characters.
Property values are set prior to sending a message. When a client receives a message, its
properties are in read-only mode. If clearProperties is called, the properties are erased
and then can be set.
Review the sample “Persistent Storage Application (PTP)” on page 100 to see how the
first properties are used. See Chapter 10, “Guaranteeing Messages,” for detailed
information about how these properties contribute to handling undeliverable messages in
local brokers and dynamic routing nodes.
JMS_SonicMQ_perMessageEncryption=true
aMessage.setBooleanProperty
(progress.message.jclient.Constants.ENCRYPT_MESSAGE, true);
if (connect.isSecure())
{
aMessage.setBooleanProperty
(progress.message.jclient.Constants.ENCRYPT_MESSAGE, true);
}
else
{
//Handle condition where broker is insecure...
}
For more information about setting the sending user name of the message in JMSXUserID:
● As used in basic authentication with HTTP Direct, see the “HTTP(S) Direct
Acceptors and Routings” chapter of the Progress SonicMQ Deployment Guide.
● As a brokerwide setting for TCP and SSL connections, see advanced broker property
settings in the “Configuring SonicMQ Brokers” chapter of the Progress SonicMQ
Configuration and Management Guide.
For more information about JMSXDeliveryCount:
● As used to programmatically handle excessive redeliveries, see “Setting Maximum
Delivery Count” on page 209.
● As used to automatically handle excessive redeliveries, see “Setting Maximum
Delivery Count” on page 209, and the “JMS Administered Objects Tool” chapter in
the Progress SonicMQ Configuration and Management Guide.
User-defined Properties
A message supports application-defined property values, providing a mechanism for
adding application-specific header fields to a message. For examples:
● Identifiers for audits or reconcilement of undeliverable messages. These might be
properties you define such as OriginatorHostID, AuditID, RealTimeDeviceID, RFID, or
similar.
● Hints for rerouting undeliverables such as ReturnURL, AlternateURL,
ReturnDestination, ReturnEmail, or similar.
● Settings that are used by SonicMQ’s outbound routing to provide the security and
attributes for routing pure HTTP messages to HTTP Web servers. These are described
in the “Using HTTP(S) Direct” part of the Progress SonicMQ Deployment Guide.
Examples of such properties are:
■ X-HTTP-AuthUser and X-HTTP-AuthPassword for Web server authentication.
■ X-HTTP-ReplyAsSOAP, X-HTTP-RequestTimeout, X-HTTP-Retries, and X-HTTP-
RetryInterval These are not attached to the HTTP message as header propertiers.
They define the HTTP Direct outbound routing connection attempts and, in the
case of X-HTTP-ReplyAsSOAP, the reply format of internally generated error replies.
■ X-HTTP-GroupID to define message grouping for ordered delivery.
■ SSL-related properties for HTTPS Web server authentication:
X-HTTPS-CipherSuites, X-HTTPS-CACertificatePath,
X-HTTPS-ClientAuthCertificate, X-HTTPS-PrivateKey,
X-HTTPS-PrivateKeyPassword, X-HTTPS-ClientAuthCertificateForm,
For more information, see the “Grouping Messages by Destination URL” section of the
“HTTP(S) Direct Acceptors and Routings” chapter in the Progress SonicMQ Deployment
Guide.
The active pending queues can be monitored through the Sonic Management Console’s
Manage tab where a broker’s Routing Statistics can be viewed. For more information, see
the “Routing Statistics” section of the “Managing SonicMQ Broker Activities” chapter in
the Progress SonicMQ Configuration and Management Guide.
Property Methods
JMSX properties can be referenced in message selectors whether or not they are
supported by a connection. If values for these properties are not included, they are treated
like any other absent property. The setting and getting of message properties allows a full
range of data types when the property is established. The properties can be retrieved as a
list. A property value can be retrieved by using a get( ) method for the property name.
While JMS-defined properties are typed, user-defined properties are not. Data typing is
defined by the set method used, such as setIntProperty( ).
When data is retrieved, the get( ) method for user-defined properties can attempt to
coerce the data into that data type when the value is retrieved.
where name is the name of the property to test. Returns TRUE if it exists.
setBooleanProperty(“reconciled”,true).
Property values can be coerced. The accepted conversions are listed in Table 22 where a
value written as the row type can be read as the column type. For example, a short
property can be read as a short or coerced into an int, long or String. An attempt to coerce
a short into another data type is an error.
Valid coercions are indicated with Yes; those intersections marked with No throw a
JMSException. A string-to-primitive conversion might throw a run-time exception if the
primitives valueOf() method does not accept it as a valid string representation of the
primitive.
266 Progress SonicMQ Application Programming Guide V7.5
Message Body
Message Body
The message body has no default value and is not required to have any content. The
message body is populated by the message set( ) method for the message type. The
following sections explain how to use the set( ) and get( ) methods for the message
body.
msg.setText(aMessage);
Important If you use setText(String string) where string is the string containing the message’s
data, you set the string containing this message’s data, overriding setText in class
TextMessage.
For information about setting XMLMessage body, see “Working with XML Messages” on
page 237 and the samples “XML Messages” on page 79.
For information about setting the Parts into a MultipartMessage, see “Composition of a
MultipartMessage” on page 242.
msg.getText(aMessage);
For information about getting XMLMessage body and interpreting it with DOM or SAX
parsers, see “Working with XML Messages” on page 237 and the samples “XML
Messages” on page 79.
For information about getting Parts of a MultipartMessage and distinguishing JMS
message types from other MIME types, see “MultipartMessage Type” on page 243 and
the sample “Decomposing Multipart Messages” on page 83.
This chapter describes the generic programming model for messaging that is common to
both messaging models, Publish and Subscribe (Pub/Sub) and Point-to-point (PTP).
These two messaging models are described in Chapter 8, “Point-to-point Messaging,” and
Chapter 9, “Publish and Subscribe Messaging.” This chapter covers the following topics:
● “About Message Producers and Message Consumers”
● “Message Ordering and Reliability”
● “Destinations”
● “Steps in Message Production”
● “Message Management by the Broker”
● “Message Receivers, Listeners, and Selectors”
● “Steps in Listening, Receiving, and Consuming Messages”
● “Reply-to Mechanisms”
● “Producers and Consumers in JMS Messaging Domains”
ConnectionFactory
Connection
Session
MessageProducer
Destination
MessageConsumer
MessageListener
Message ordering and reliability common to all messaging domains are described in this
chapter. See also “Message Ordering and Reliability in PTP” on page 298 and “Message
Ordering and Reliability in Pub/Sub” on page 320 for details about message ordering and
reliability within those domains.
Messages can be delivered with a range of options to modify message ordering and invoke
features that improve reliability:
● The producer can set the time-to-live of the message so that obsolete messages can
expire. If message A is set at one minute, message B at five seconds, and message C
at one hour, then after three minutes with no deliveries, only message C will still exist.
Ordering is maintained while expiration is a user-defined value.
● The producer can set the delivery mode of messages so that the broker confirms
persistent storage of the message before acknowledgement is sent. In the event of a
broker failure, a message that the broker acknowledged before it was persisted might
be lost. The delivery mode of a message characterizes the message for its entire life.
If a non-persistent message is waiting in a durable subscription or a queue when the
broker restarts, the message does not exist when the broker comes back up.
● The producer can set the priority of a message so that the broker can take efforts to
position a more recent message before an older one.
● The producer uses a synchronous process to put the message on the broker’s message
store; when it is released, the message is acknowledged as delivered to its interim
destination.
● The consumer can use listeners to get messages as they are made available.
● Messages sent in the NON_PERSISTENT delivery mode can arrive prior to messages that
are PERSISTENT.
● The consumer starts a session by expressing its preferred acknowledgement
technique—transactional or not, explicit or implicit.
● Connections can be monitored and, when broken, techniques can automatically
attempt to reconnect. (This might not be necessary if you are using fault-tolerant
connections. See “Fault-Tolerant Connections” on page 167.)
● Message senders in the Internet environment are not guaranteed consistent
communication times. Transmission latencies can cause messages to be produced
before other messages. As a result, two messages from two sessions are not
required—and cannot be reliably expected—to be in any specific sequence.
Destinations
Destinations are objects that provide the producer, broker, and consumer with a context
for delivery of messages. Destinations can be JMS Administered Objects (static objects
under administrative control), dynamic objects created as needed (topics only), or
temporary objects created for very limited use. The destination name length limit is 256
characters.
For topics, SonicMQ provides extended management and security with hierarchical
name spaces; for example, jms.samples.chat. See Chapter 13, “Hierarchical Name
Spaces,” for more information.
Important Table 4, “Restricted Characters for Names” on page 142 lists characters that are not
allowed in SonicMQ names. Refer to this list for restricted characters must not use in
your topic or queue names.
Create a Session
After establishing a connection, the Chat sample creates a Session:
javax.jms.Session pubSession;
...
pubSession = connect.createSession(false,javax.jms.Session.AUTO_ACKNOWLEDGE);
You can use a Session for P2P messaging, Pub/Sub messaging, or both. The Chat sample
names the Session pubSession, because it is intended for Pub/Sub messaging.
MessageProducer objects can send messages to any Destination, both Queues and Topics.
Here, a Topic is the Destination passed to the createProducer( ) method. When a valid
Destination is passed to the createProducer( ) method, the returned MessageProducer
object uses that Destination as its default.
The MessageProducer object’s send( ) method (the form that specifies no target
Destination) uses the default Destination as its target Destination. You can explicitly
specify a different target Destination if you use a different form of the send( ) method.
String s = stdin.readLine();
javax.jms.TextMessage msg = pubSession.createTextMessage();
msg.setText( username + ": " + s );
When the sample is run, if the user Sales enters “Hello.”, the message content would be
“Sales: Hello.”
setJMSType("CentralFiles")
For example:
setLongProperty(“OurInfo_AuditTrail”,“6789”)
setBooleanProperty(progress.message.jclient.Constants.ENCRYPT_MESSAGE, true);
publisher.send( msg );
The form of send( ) used in the DurableChat sample application sets three important
message parameters at the moment the send( ) method is executed, as follows:
This form of the send( ) method passes along either the default values or the entered
values for:
● JMSDeliveryMode is [NON_PERSISTENT|PERSISTENT|NON_PERSISTENT_SYNC|
NON_PERSISTENT_ASYNC|NON_PERSISTENT_REPLICATED|DISCARDABLE]
● JMSPriority is [0...9] where 0 is lowest, 9 is highest, 4 is the default
● timeToLive, the message lifespan that will calculate the JMSExpiration, is [0...n]
where 0 is “forever” and any other positive value n is in milliseconds
The release of the synchronous block by the broker returns only a boolean indicating
whether the message production completed successfully.
Important While the JMSExpiration is calculated from the client system clock at the time of the send,
it is enforced on the broker’s clock. To accommodate variances between client and broker
clocks, the broker adjusts the message expiration to its clock. When the message is
forwarded to another broker, the remaining timeToLive value (expiration minus current
broker GMT time) is forwarded. The time that elapses until the first packet of the message
in transit is received is effectively ignored.
Message Receiver
The receiver methods are synchronous calls to fetch messages. The different methods
manage the potential block by either not waiting if there are no messages or timing out
after a specified period.
Receive
To receive the next message produced for the consumer, use the receive( ) method:
Message receive()
This call blocks indefinitely until a message is produced. When a receive( ) method is
called in a transacted session, the message remains with the consumer until the
transaction commits. The return value is the next message produced for this consumer. If
a session is closed while blocking, the return is null.
Receive No Wait
To receive the next available message immediately or instantly timeout, use te
receiveNoWait( ) method:
Message receiveNoWait()
The receiveNoWait( ) method receives the next message if one is available. The return
value is the next message produced for this consumer, or null if one is not available.
Note The ReceiveNoWait( ) method is unlikely to provide effective message consumption in
the Pub/Sub paradigm.The no-wait concept is useful for durable subscriptions, but is
unlikely to produce results for normal subscriptions.
The method is very useful in the PTP paradigm where messages wait on a static queue.
Message Listeners
Invoke a message listener to initiate asynchronous monitoring of the session thread for
consumer messages by using the following method:
setMessageListener(MessageListener listener)
javax.jms.MessageConsumer receiver =
session.createConsumer(queue, java.lang.String messageSelector);
receiver.setMessageListener(this);
javax.jms.MessageConsumer subscriber =
session.createConsumer(topic, java.lang.String messageSelector);
subscriber.setMessageListener(this);
Message Selection
While some messaging applications expect to get every message produced to a
destination, other applications might want to receive only certain messages produced to a
destination. The following techniques that can reduce the flow of irrelevant messages to
a message consumer:
● Subscription to hierarchical name spaces (Pub/Sub) — SonicMQ’s hierarchical
name spaces let subscribers point to content nodes (and, optionally, to sets of relevant
subordinate nodes) to focus publishers into meaningful spaces. For more information,
see Chapter 13, “Hierarchical Name Spaces.”
● Applying a message selector — As shown in the preceding code examples, JMS can
create consumers with a String parameter that holds a syntax that is a subset of SQL-
92 conditional expressions. This SQL allows a consumer on a destination to filter and
categorize messages in the message header and properties based on specified criteria.
For example, the following message selector might be set up on a Bidders topic to retrieve
only high-priority quotes that are requesting a reply:
“Priority > 7 AND Form = ’Bid’ AND Amount is NOT NULL”
SonicMQ- JMS_SonicMQ
defined _preserve
properties Undelivered
Application- Audit_Team
specific
property
names
(do not start
with ‘JMS’)
● Multiply * or divide / a * 3
● Add + or subtract - a - 3
Conditional Include:
● Pure conditional expressions
● Comparison operations 7>6
● Logical operations a > 7 OR b = true
● Identifiers with Boolean values
a = true
● Boolean literals (true, false)
true
When the received message type is uncertain, special message handling is required. In the
following XMLDOMChat sample, the message is tested to determine whether or not it is an
instance of XMLMessage and then handled appropriately:
When the received message is an XML message, your application can parse the message
to extract data from the message fields. The following code sample shows how to parse
an XML message and extract data:
get[type]Property(String)
For example:
getIntProperty(“OurInfo_AuditTrail”)
Warning This example gets an int property that was set with (and stored as) a long. Attempting to
get a property type that is not the type with which the property was set will force coercion
of the value to the declared type. If the conversion is not valid, an exception is thrown.
See Table 22, “Permitted Type Conversions for Message Properties.”
Reply-to Mechanisms
The typical design pattern for request/reply is:
● Create a message you want to send
● Make a temporary destination
● Set the JMSReplyTo header to this destination
● Create a MessageConsumer on the destination
● Send the message
● Call MessageConsumer.receive( timeout) on the message
The JMSReplyTo message header field contains the destination where a reply to the current
message should be sent. Messages with a JMSReplyTo value are typically expecting a
response. If the JMSReplyTo value is null, no reply is expected. A response can be optional, and
client code must handle the action. These messages are called requests.
A message sent in response to a request is called a reply. Message replies often use the
JMSCorrelationID to ensure that replies synchronize with their requests. A
JMSCorrelationID would typically contain the JMSMessageID of the request.
Requestor Application
The following code excerpt from the TopicPubSub Requestor sample application uses the
helper class TopicRequestor:
Replier Application
Synchronous requests leave the originator of a request waiting for a reply. To prevent a
requestor from waiting, a well-designed application uses code similar to the following
excerpts from the TopicPubSub Replier sample application:
//get the mesage
public void onMessage( javax.jms.Message aMessage)
{
javax.jms.TextMessage textMessage = (javax.jms.TextMessage) aMessage;
String string = textMessage.getText();
}
...
//Look for the header specifying JMSReplyTo:
javax.jms.Topic replyTopic = (javax.jms.Topic) aMessage.getJMSReplyTo();
if (replyTopic != null)
...
//Send a reply to the topic specified in JMSReplyTo:
javax.jms.TextMessage reply = session.createTextMessage();
reply.setJMSCorrelationID(aMessage.getJMSMessageID());
replier.send(replyTopic, reply);
session.commit();
This chapter describes the Point-to-point (PTP) messaging model and explains how to use
the features of that model. The chapter contains the following sections:
● “About Point-to-point Messaging”
● “Message Ordering and Reliability in PTP”
● “Using Multiple MessageConsumers”
● “Setting Prefetch Count and Threshold”
● “Browsing a Queue”
● “Handling Undelivered Messages”
● “Life Cycle of a Guaranteed Message”
● “Detecting Duplicate Messages”
● “Forwarding Messages Reliably”
● “Dynamic Routing with PTP Messaging”
● “Clusterwide Access to Queues”
ConnectionFactory
createConnection( )
Connection
createSession( )
createQueue(String)
Session
createProducer(queue)
MessageProducer
createConsumer(queue) Queue
MessageConsumer
setMessageListener
MessageListener
Queues must be created by the administrator before they can be used (except for
temporary queues, which are created dynamically by users). See the Progress SonicMQ
Configuration and Management Guide for information about maintaining queues.
The QueuePTP sample application, Talk, provides an example of how PTP applications are
coded. The command that starts the Talk application specifies the sending queue and the
receiving queue that will be used:
where:
● broker:port specifies the host on which the broker is running and the port on which
it is listening.
● user and pwd are the unique user name and the user’s password.
● -qs queue is the name of the queue for sending messages.
● -qr queue is the name of the queue for receiving messages.
Code Sample 14, from the Talk sample, shows how to create the objects used in PTP
communication.
Code Sample 14. Talk Sample: Creating Objects for PTP
// Create a connection. (try/catch)
javax.jms.ConnectionFactory factory;
factory = (new progress.message.jclient.ConnectionFactory (broker));
connect = factory.createConnection (username, password);
sendSession = connect.createSession (false,javax.jms.Session.AUTO_ACKNOWLEDGE);
receiveSession = connect.createSession (false,javax.jms.Session.AUTO_ACKNOWLEDGE);
// Create Sender and Receiver 'Talk' queues. (try/catch)
if (sQueue != null)
{
javax.jms.Queue sendQueue = sendSession.createQueue (sQueue);
sender = sendSession.createProducer (sendQueue);
if (rQueue != null)
{
javax.jms.Queue receiveQueue = receiveSession.createQueue (rQueue);
javax.jms.MessageConsumer qReceiver = receiveSession.createConsumer(receiveQueue);
qReceiver.setMessageListener(this);
// The 'receive' setup is complete. Start the Connection
connect.start();
...
}
}
Message Ordering
Queued delivery allows each message to be processed by one and only one message
consumers. As a result, a series of messages might be consumed by several different
message consumers, each taking a few messages.
Messages on a queue have factors that impact the ordering and reliability of messages:
● When a new message is put onto a queue with a high priority set by the sender, an
active message consumer takes the new message off the queue before taking an older
message with a lower priority (provided that a message selector is not being used by
the consumer).
● Queued messages that are not acknowledged are placed back on the queue
(reenqueued) for delivery to the next qualified consumer. In the interim, a newer
message might have been received by a consumer.
● MessageConsumer objects have a prefetch parameter that retrieves a number of
messages and caches them, for the client, for processing. If these messages are not
processed by the client, they are returned to the queue.
Message Delivery
The following factors can impact the delivery of messages on a queue:
● Message selectors can limit the number of messages that a client will receive.
Messages can stay on the queue until a consumer provides either a suitable message
selector or no message selector at all. A queue might appear empty to a consumer if
none of the currently enqueued messages match the consumer’s selection criteria.
● An administrator permanently disposes of queued messages (by clearing the queue).
● Message removal due to expiration might result in permanent disposal of a message
or, if the message is flagged by the producer, the message being placed on the broker’s
DMQ. An administrative application can set up an authorized consumer on the DMQ
to determine whether to recast the message, resend it as is, or discard it.
● Duplicate messages can be detected when transacted sessions are used if the broker
is set up to manage the identifiers filed for a specified lifespan. With this broker setup,
a commit to the specified identifier will clear the index value, but any intervening
sends that specify an already recorded identifier are rejected.
Note The effects of dynamic routing on message ordering and delivery are discussed at greater
length in the scenarios in the Progress SonicMQ Deployment Guide.
298 Progress SonicMQ Application Programming Guide V7.5
Using Multiple MessageConsumers
setMessageListener(MessageListener listener)
As a result, asynchronous message receipt becomes exclusive for the consumer. Message
sending is not limited when message listeners are in use. Sending is always synchronous unless
you use the delivery mode NON_PERSISTENT_ASYNC, which results in asynchronous sending.
MessageConsumer
The MessageConsumer interface provides methods for synchronous calls to fetch messages.
Variants allow for not waiting if there are no messages currently enqueued or for timing
out after a specified wait period. These call methods are described in the following
sections.
Receive
To synchronously receive the next message produced for the MessageConsumer, use the
method:
Message receive( )
This call blocks indefinitely until a message is enqueued. When a receive( ) method is
called in a transacted session, the message remains with the MessageConsumer until the
transaction is committed or rolled back. The return value is the next message delivered to
this consumer. If a session is closed while blocking, the return value is null.
Receive No Wait
To immediately receive the next available message on the queue or, otherwise, instantly
timeout, use the method:
Message receiveNoWait( )
This call receives the next message if one is available. The return value is the next message
delivered for this consumer, or null if one is not available.
Browsing a Queue
A QueueBrowser enables a client to look at messages in a queue without removing them.
Queue browsing in SonicMQ provides a dynamic view of a queue. As messages may be
enqueued and/or dequeued very rapidly, browsing might not show every message on a
queue over a given time interval. Browsing is very useful for assessing queue size and
rates of growth. Instead of getting actual message data, you can also use the enumeration
method to return just the integer count of messages on the queue.
Create the browser with a session method as follows:
Session.createBrowser(Queue queue)
where:
● queue is the queue you want to browse
● messageSelector is the selector string that qualifies the messages you want to browse
Use the following methods to get browser information and close the browser:
● getMessageSelector:
You can get the message selector expression being used with:
String getMessageSelector()
● getEnumeration:
You can get an enumeration for browsing the current queue messages in the sequence
that messages would be received with:
java.util.Enumeration getEnumeration()
● getQueue:
You can get the queue name associated with an active browser with:
getQueue()
● close:
Always close resources when they are no longer needed with:
close()
// Create a browser on the queue and show the messages waiting in it.
See “Browsing Clusterwide Queues” on page 315 for information about browsing
clustered queues.
As a programmer, you can elect to request that undeliverable messages be placed on the
DMQ. You can set messages to:
● Be placed in the DMQ when the messages are found to be expired
● Be placed in the DMQ when a message cannot be delivered (for example, when the
destination queue is not found)
● Request that a notification (an administrative event) be sent when the message is
placed in the DMQ
Note There are several other reasons a message could be undelivered in a dynamic routing
deployment. See Progress SonicMQ Deployment Guide for more about undelivered
messages in the Dynamic Routing Architecture.
qsender.send(msg,
javax.jms.DeliveryMode.NON_PERSISTENT,
8, // Expedite at a high priority
600000); // 10 minutes
In this example, when an administrative notification is received, you will know whether
delivery times are large.
msg.setBooleanProperty(progress.message.jclient.Constants.PRESERVE_UNDELIVERED, true);
msg.setBooleanProperty(progress.message.jclient.Constants.NOTIFY_UNDELIVERED, true);
where:
● transactionId is the UUID for duplicate transaction detection
● lifespan is the length of life of the UUID (in milliseconds)
If a transaction gets rolled back because a duplicate UUID is detected, the following
exception is thrown: TransactionRolledBackException.
Note Transactions using this commit feature will be slower than normal transactions.
For more information about duplicate messages, see “Duplicate Message Detection
Overview” on page 362.
receiveSession =
connect.createSession(false,progress.message.jclient.Session.SINGLE_MESSAGE_ACKNOWLEDGE);
4. Save the modified file and then compile it into a class file.
5. Run the modified ReliableTalk sample.
Because of the change you made, the application automatically acknowledges any
message it receives and forwards it to the send queue.
Administrative Requirements
In all cases of Sonic dynamic routing deployments, an administrator must establish
routing nodes and routing definitions, and must define users with routing ACLs. For
dynamic routing of queue messages, an administrator must also establish global queues.
See the chapters “Configuring Routings” and “Managing SonicMQ Broker Activities” in
the Progress SonicMQ Configuration and Management Guide for information about how
to perform these administrative tasks.
The Progress SonicMQ Deployment Guide provides examples of how you can implement
dynamic routing in your applications. For detailed information about the different types
of routing that SonicMQ provides, see the following:
● The chapter “Multiple Nodes and Dynamic Routing” in the Progress SonicMQ
Deployment Guide provides information about dynamic routing for queues in the PTP
messaging model and dynamic routing for topics in the Pub/Sub messaging model.
● The chapter “HTTP Direct Acceptors and Routings” in the Progress SonicMQ
Deployment Guide provides information about HTTP Direct routing.
The direct interaction of the producers with an instance of the queue on the local broker
also means that transactions involving sending to clustered queues behave in the same
way as transactions involving sending to local queues.
The clustered queue attempts to pull messages from corresponding neighbor brokers’
clustered queue instances when:
● The clustered queue is empty and has consumers requesting messages.
● The clustered queue has more room and none of the requests can be satisfied by the
existing messages on the queue.
This chapter describes the Publish and Subscribe (Pub/Sub) messaging model and
contains the following sections:
● “About Publish and Subscribe Messaging”
● “Message Ordering and Reliability in Pub/Sub”
● “Topic”
● “MessageProducer (Publisher)”
● “MessageConsumer (Subscriber)”
● “Durable Subscriptions”
● “Dynamic Routing with Pub/Sub Messaging”
● “Shared Subscriptions”
● “MultiTopics”
ConnectionFactory
createConnection( )
Connection
createSession( )
createTopic(String)
Session
createProducer(topic)
MessageProducer
createConsumer(topic) Topic
createDurableSubscriber(topic)
MessageConsumer
setMessageListener( )
MessageListener
Mechanisms exist to allow messages to persist for consumers who have a durable
subscription to a topic. The characteristics of durable subscriptions are discussed in
“Durable Subscriptions” on page 324.
See Chapter 13, “Hierarchical Name Spaces,” for information about how SonicMQ
applications can subscribe to sets of topics.
Code Sample 19, from the Chat sample application, shows how to create the objects used
in a Session for Pub/Sub communication: Topic, MessageConsumer, MessageProducer, and
Message.
General Services
Asynchronous message delivery allows messages to be delivered with a range of options
that ensure an appropriate quality of service:
● The producer can set the message life span, delivery mode, and message priority.
● The broker stores the message for later delivery and manages both acknowledgement
to the producer and acknowledgement from the consumer.
● The consumer can express a durable interest in a topic (durable subscriber).
Reliable message delivery also deals with questions of ordering and redelivery.
Message Ordering
A predictable sequence of messages is a series of messages that have the same priority
from a single producer in a single session. Even if transacted, the messages are delivered
sequentially from the broker to the consumers. The sequence of messages received by a
consumer can be influenced by the following factors in Pub/Sub domains:
● Changing a priority on a message from a producer can result in a delivery of a high
priority message to a newly activated or reactivated subscription before an older
message.
● Messages from other sessions and other connections are not required to be in
specified sequence relative to messages from another session or connection.
● If a non-durable subscriber closes and then reconnects, it counts as a new subscriber.
Order is only guaranteed within each connected session, not between the sessions.
● Messages that are not acknowledged are redelivered to durable subscribers with an
indication of the redelivery attempt. As a result, a redelivered message could be
received after a message that was timestamped later.
● Durable subscriber disconnects and reconnects at a different broker. You can specify
strict message order to ensure that messages will be received in the order they are
sent, regardless of other factors that can affect that order. For information about
message ordering with durable subscriptions, see “Message Order with Clusterwide
Durable Subscriptions” on page 326.
Reliability
The assurance that a message will be received by a consumer has several other influences
in Pub/Sub domains:
● A producer is never guaranteed that any consumer exists for a topic where messages
are published.
● Consumer message selectors limit the number of messages that a client will receive.
Regular subscriptions and durable subscriptions with a message selector definition
that excludes a message will never get that message.
Message destruction due to expiration or administrator action (removing a durable
subscription) permanently disposes of stored messages.
Topic
Topics are objects that provide the producer, broker, and consumer with a destination for
JMS methods. Topics can be predefined objects under administrative control, dynamic
objects created as needed, or temporary objects created for very limited use. The topic
name is a java.lang.String, up to 256 characters.
SonicMQ provides extended topic management and security with hierarchical name
spaces; for example, jms.samples.chat. Some characters and strings are reserved for the
features of hierarchical topic structures, such as:
● . (period) delimits hierarchical nodes.
● * (asterisk) and # (pound) are used as template characters.
● $ (dollar sign) is used for internal topics (starting with $SYS or $ISB).
● : (colon) is used for dynamic routing.
● [[ ]] (double brackets) are used for shared subscriptions.
● | (vertical bar) is used only with MultiTopics.
See Chapter 13, “Hierarchical Name Spaces,” for more information.
Important Table 4, “Restricted Characters for Names” on page 142 lists characters that are not
allowed in SonicMQ names. Refer to this list for characters you can use in topic names.
You can programmatically store and retrieve topics in a directory service such as LDAP
or the embedded Java Naming and Directory Interface (JNDI) service. With SonicMQ,
you can store topic names in a JNDI or a simple file store, and then reference the object
indirectly (by name) in some context. See Chapter 4, “SonicMQ Connections,” for more
information.
MessageProducer (Publisher)
If you want your client application to send messages to a Topic, you must first create a
MessageProducer in the session for the selected Topic. When you create a MessageProducer
(via the Session.createProducer( ) method), you can specify a default destination. If you
specify a default destination, you do not need to specify a destination when you send a
message.
You can also create a MessageProducer that is not bound to a default destination. You can
do this by passing a null destination to the createProducer( ) method. Then, to use the
MessageProducer to send a message, you must explicitly call a form of the send( ) method
that specifies a valid destination. The following code creates a MessageProducer without a
default Topic:
publisher = session.createProducer(null);
topic = session.createTopic(jms.sample.chat”);
publisher.send(topic, msg);
and send the input as a text message, prepended with the username of the
MessageProducer:
while ( true )
{
String s = stdin.readLine();
if ( s == null )
exit();
else if ( s.length() > 0 )
{
javax.jms.TextMessage msg = session.createTextMessage();
msg.setText( username + ": " + s );
publisher.send( msg );
}
}
or
where:
● message is a javax.jms.Message
● deliveryMode is [NON_PERSISTENT|PERSISTENT|NON_PERSISTENT_SYNC|
NON_PERSISTENT_ASYNC|NON_PERSISTENT_REPLICATED|DISCARDABLE]
● priority is [0...9] where 0 is lowest and 9 is highest
● timeToLive is [0...n] where 0 is “forever” and any other positive value n is in
milliseconds
MessageConsumer (Subscriber)
A MessageConsumer can subscribe to a topic. The createConsumer( ) method, which
creates a non-durable subscription, has the following parameters:
or
where:
● topic is a Topic object you want to access
● messageSelector is a string that defines selection criteria
● noLocal is a boolean where true sets the option not to receive messages from
subscribed topics that were published locally (by the same connection)
In a Session, multiple MessageConsumer objects can have overlapping subscriptions
defined in their message selectors and hierarchical topics. In this case, all of the message
consumers would get a copy of the message delivered.
Durable Subscriptions
A MessageConsumer can also express a durable interest in a topic (this is called a durable
subscription). This means the MessageConsumer receives all the messages published on a
topic even when the client connection is not active. When the MessageConsumer expresses
a durable interest in a topic, the broker ensures that all messages from the topic's
publishers are retained until they either are acknowledged by the MessageConsumer or have
expired. The Session.createDurableSubscriber( ) method has the following signatures:
or
where:
● topic is a Topic object that specifies the destination of the subscription.
● subscriptionName is a string of arbitrary alphanumeric text. A subscription name is an
identifier that allows a client to reconnect to a durable subscription.
● messageSelector is a string that defines selection criteria. If the durable subscriber is
also part of a shared subscription, the message selector must match the selector of
other members of the shared subscription.
● noLocal is a boolean. When set to true, the subscriber does not receive messages from
subscribed topics that were published locally.
Note SonicMQ extends the PubSub Message Consumer to enable shared subscriptions. When
a MessageConsumer is also a member of a shared subscription, the MessageConsumer does
not receive all messages published to the topic; instead, the MessageConsumer receives a
subset of the messages, because delivery of the messages is load-balanced to all members
of the shared subscription. See “Shared Subscriptions” on page 332 for more
information.
See Table 4, “Restricted Characters for Names” on page 142 for a list of restricted
characters for durable subscriber names.
A durable subscription is not allowed for a temporary topic. An attempt to create a durable
subscriber on a TemporaryTopic will throw a JMSException.
While you can stop listening to a topic, there is broker overhead expended when trying to
deliver messages to subscribers, especially when the messages might be persistent and the
unsubscribe(String name)
A similar situation can occur if an application publishes some messages, closes its JMS
session, then connects to a different broker in the cluster and continues publishing
messages to the same topic. The strict order of messages delivered to the subscribers of
the topic is not guaranteed across different JMS publisher sessions.
In a single broker configuration that does not use shared subscriptions, message ordering
to durable subscribers is always guaranteed. In a clustered environment, SonicMQ
supports optional strict message ordering to durable subscribers (unless they are members
of a shared subscription). This feature is optional in a clustered scenario because
enforcing strict message order can lead to delays in delivery when messages intended for
the durable subscriber get trapped on a crashed or partitioned broker. Applications that
elect strict message ordering for durables, therefore, must be able to tolerate delays in
message delivery.
If an application is receiving messages from a durable subscription and the broker goes
down or the application closes the current session, the application can later connect to any
broker in the cluster and continue receiving messages from the durable subscription. In
this situation, messages will be received in the order they were sent even though the
application started a new session (only if setDurableMessageOrder was enabled).
Strict message ordering is not enabled by default. An application can select strict message
order enforcement in the ConnectionFactory or in the topic session. The setting made at
the topic session always takes precedence over any settings made at the
ConnectionFactory. You can enable preservation of message order for reconnecting
durable subscribers as follows:
● When set in the connection factory, all durable subscribers created on one of the
connections are created with message ordering enforced:
progress.message.jclient.ConnectionFactory.setDurableMessageOrder
(boolean durableSubscriberMessageOrder)
● When set in the session, all durable subscribers created on the session use this value,
which overrides the value set in the connection factory:
progress.message.jclient.Session.setDurableMessageOrder
(boolean durableSubscriberMessageOrder)
It is possible to change the message ordering of a durable subscriber each time it connects.
However, once the durable has connected with message ordering disabled, there is no
guarantee how long it will take to restore message order after reconnecting it with
message ordering enabled. It is possible to have messages out of order in this case.
Broker
DS
B
This static design can result in a high messaging volume on some brokers. While load
balancing and clustering can force clients to try other connections, those solutions can be
time-intensive and the results are a static list of connections instead of a static connection.
The SonicMQ DRA provides remote publishing and subscribing for topic messages. This
feature allows applications to publish messages to remote nodes, and enables subscribers
to receive messages published from remote nodes.
Note There are two ways to use dynamic routing with Pub/Sub messaging: with global
subscription rules or with remote publishing. For more information on these topics, see
the Progress SonicMQ Deployment Guide.
Administrative Requirements
In all cases of dynamic routing and remote publishing, an administrator must establish
routing nodes and routing definitions, and must define users with routing ACLs. Remote
subscribing requires the administrator to establish subscription rules for each remote
subscriber.
See the chapters “Configuring Routings” and “Managing SonicMQ Broker Activities” in
the Progress SonicMQ Configuration and Management Guide for information about how
to perform these administrative tasks.
The Progress SonicMQ Deployment Guide provides examples of how you can implement
dynamic routing or remote publishing in your applications. For detailed information
about the different types of routing that SonicMQ provides, see the following:
● The chapter “Multiple Nodes and Dynamic Routing” in the Progress SonicMQ
Deployment Guide provides information about dynamic routing for queues in the
point-to-point domain and dynamic routing for topics in the publish and subscribe
domain.
● The chapter “HTTP Direct Acceptors and Routings” in the Progress SonicMQ
Deployment Guide provides information about HTTP Direct routing.
Shared Subscriptions
A problem in JMS topic subscriptions is that there are often cases where one application
acting as a topic subscriber cannot process messages as fast as messages are being
published. This leads to a bottleneck, where the subscribing application falls farther and
farther behind.
Three typical JMS solutions are:
● ServerSessionPools — A single application server consumes messages on a single
subscription in parallel. The shortcoming of this architecture is that all subscribers
share one subscription in one JVM (for example, a J2EE AppServer) on one machine,
which might be the bottleneck.
● Forward messages to Queue — A single application consumes messages from the
topic and forwards the messages to a queue. JMS provides a model for shared
MessageConsumers (reading from a queue) running in parallel on multiple machines.
The shortcomings of this approach are:
■ The need to guarantee the operation of the forwarding application, which must be
transacted to guarantee delivery
■ The extra hops required for the topic/queue forwarding
● Multiple Standard Subscribers — By creating multiple subscribers, each
subscriber gets every message and application logic must either serialize requests
against a common resource such as a persistent storage mechanism, or check with
central controlling program to resolve the duplicates.
SonicMQ provides a solution to the bottleneck problem by letting you establish groups
of topic subscribers that share subscriptions to allocate the message load between them
such that, while every message is delivered to the group, each message is delivered to (and
consumed by) only one member of the group. These group members can be located on
dispersed computers over diverse JMS connections. The implementation is compatible
with clusters of brokers so that the members of a consumer group can connect to different
brokers in a SonicMQ cluster. Regular subscribers, durable subscribers, and participants
in a shared subscription can be active concurrently on a broker.
Figure 61 shows an example where the clients, including those within a group, might be
connected to different brokers. In this example, the publishers are producing to one topic
and all the subscribers are actively consuming on that topic. Using shared subscriptions
within the two subscriber groups provides the following performance:
● Consumer 1 and Consumer 2 receive every message only once.
● Group 1 and Group 2 receive every message only once. The members of the group
each receive a subset of the complete set of messages.
P P
Table 28 shows how messages are received in the shared subscription configuration
shown in Figure 61. In the scenario shown in Table 28, ten sequential messages are sent.
The X’s in the table indicate which subscribers received and acknowledged each message.
In this scenario, Subscriber 6 (a non-durable subscription) does not acknowledge receipt
of Message 6 before it fails, so all subsequent messages for Group 2 are delivered to the
remaining member of that shared subscription group, Subscriber 7.
Table 28. Example of Messages Received Under Load Balancing
Shared Subscription Shared Subscription
Message # Normal Subscribers Group 1 Group 2
Sub 1 Sub 2 Sub 3 Sub 4 Sub 5 Sub 6 Sub 7
1 X X X X
2 X X X X
3 X X X X
4 X X X X
5 X X X X
6 X X X X
Subscriber 6 Fails
7 X X X X
8 X X X X
9 X X X X
10 X X X X
If Subscriber 6 were a durable subscription, Message 7 and Message 9 would have been
stored for Subscriber 6 until it reconnected to its duable subscription.
Fault Resilience
In Figure 62, the goal is to guarantee that a topic subscriber application receives messages
exactly once, and is resilient to both broker and application failures.
In this example, subscribers g1 and g2 are in a shared subscription group. The messages
stream from publishers P1 and P2 are divided between the two of them (with indirect
routing from broker A to brokers B and C). This configuration continues to process
messages even if any of the following components fail:
● Broker B
● Broker C
● Topic subscriber g1
● Topic subscriber g2
The normal JMS subscriber (1) receives all messages.
Cluster g1
Broker
P1 B
Broker
A n = Normal JMS Subscriber
Broker
gn = Subscribers in Shared Subscription Group 1
P2 C
g2
Pn
P = Publishers
Figure 62. Fault Resistance Across Shared Subscription Topic Subscribers on a Cluster
Note This configuration is fault resistant rather than fault tolerant because this is not a
message replication scheme. A failure of broker B, for example, might still cause
messages to be trapped or lost on broker B (as group members are non-durable).
Newer messages will be redirected entirely to topic subscriber g2.
P1
g1 n = Normal JMS Subscriber
Broker
A gn = Subscribers in Shared Subscription Group 1
g2
P2
Pn
P = Publishers
Figure 63. Application in a Shared Subscription Group Processes Messages Once at Most
Pure Load-balancing
The throughput in Pub/Sub messaging is effectively limited to a speed of the slowest topic
subscriber. If you want to divide the slowest application across two computers, you can
have two identical topic subscribers acting as a single consumer.
Effectively, the goal is to have a shared subscription group act similarly to a single
subscriber with similar durability and acknowledgement modes. If message selectors are
used in the subscriptions, all the subscribers in the group must use the same message
selector definition so that the end result of the shared effort is consistent and predictable.
javax.jms.Session.createTopic(String name)
Session.createConsumer(Topic T)
Session.createConsumer(Topic T, boolean nolocal, String selector)
■ A group is created when the group name and the topic name are identical. The
nolocal parameter can be set to true for individual subscribers in the group.
For examples:
❑ [[group1]]topic1 and [[group1]]topic3 are distinct shared subscriptions.
❑ [[group1]]topic1 and [[group2]]topic1 are distinct shared subscriptions.
SonicMQ treats a shared subscription to a MultiTopic as separate from a shared
subscription to an ordinary topic, even if the group name and topic name are the
same. See “MultiTopics” on page 352 for more information
For examples:
❑ [[group1]]T1 and [[group1]]MULTITOPIC:T1 are distinct shared subscriptions.
❑ [[group1]]MULTITOPIC:T1|T2 and [[group1]]MULTITOPIC:T1|T2|T3 are the
same shared subscription.
■ Access control is based on the destination, without the group name. That is, for
[[prefix]]topic, only the topic part is checked in the authorization policy.
● The following methods creates a durable subscriber on topic T, as part of the group:
javax.jms.Message.setJMSReplyTo(Topic T)
1
P1
2
n = Normal JMS Subscriber
Broker
A gn = Subscribers in Shared Subscription Group 1
g1
P2 Pn = Publishers
g2 P
g3
g4
Cluster
Broker
P1 B
Broker
A 2 n = Normal JMS Subscriber
Broker
gn = Subscribers in Shared Subscription Group 1
C g3
g1
Pn
P = Publishers
g2
1
P2
When a broker receives a message over an interbroker connection with a list of group
subscriptions, the receiving broker takes responsibility for message delivery. The
receiving broker tries to deliver the message to shared subscription local subscribers. If
all locally connected subscribers are closed or are full, the receiving broker must either:
● Discard the message, if no broker is known to have active subscribers
● Forward the message to another broker where subscribers exist
Messages are not forwarded in a loop. At most, message delivery is attempted on every
broker. The last broker always accepts the message at:
● Connected local subscribers (even if flow controlled)
● Disconnected durable subscribers
If no subscribers exist, the message is discarded.
Connection Consumers
The example shown in Figure 66 involves multiple group members, some of which are
connection consumers. In SonicMQ, a non-durable topic connection consumer allows a
J2EE AppServer to have multiple threads on the client side processing messages. The
SonicMQ broker sees this configuration as a single client context shipping messages to a
single socket.
1
P1
2
n = Normal JMS Subscriber
Broker
A g1
gn = Subscribers in Shared Subscription Group 1
P2 g2
Pn
P = Publishers
g3
J2EE AppServer
While the SonicMQ broker is able to determine that shared subscription group g3 is a
connection consumer, the broker does not determine how much faster that subscriber is
than normal subscribers. If, for example, there are six threads handling messages at g3, a
simple allocation of messages to each context will only give one third of the messages to
that group. You can use connection consumers for shared subscriptions, but there is an
assumption that all the subscriptions are similar in capabilities and configuration.
In Figure 67, similarly configured connection consumers have comparable session pool
sizes.
g1
P1
J2EE AppServer n = Normal JMS Subscriber
Broker
A gn = Subscribers in Shared Subscription Group 1
P2 Pn = Publishers
P
g2
J2EE AppServer
Session Recovery
When the recover( ) method is called on a session, the normal JMS behavior occurs. That
is, message delivery is stopped in the session, then restarted with the oldest
unacknowledged message for that session. The redelivered messages will have their
JMSRedelivered flag set to true—a setting that can be made only by the broker.
No reallocation of messages will occur from one shared subscription topic subscriber to
another.
Transacted Sessions
A transacted session will delay acknowledgement of messages received on it until the
commit( ) method is called. For non-durable subscriptions, closing or failing the session
will cause those unacknowledged messages to be discarded.
For durable subscriptions, closing or crashing the session will cause the messages
originally delivered to the subscriber to be stored in the persistent storage mechanism.
The behavior of shared subscription topic subscribers is similar to normal subscribers in
failure situations. That is, messages are not acknowledged, and are discarded.
Similarly, if the rollback( ) method is called on a transacted session, then the normal
SonicMQ behavior is followed. That is, message delivery is restarted with the
unacknowledged messages in the transaction. These messages are redelivered to the same
client session and are not reallocated to different members of the group.
Transacted sessions give no additional protection from message loss for these non-durable
shared subscription topic subscribers. No reallocation of messages will occur from one
load-balanced subscriber to another (except when the subscription is durable).
Figure 68 shows an example that illustrates the behavior of remote publishing and global
subscriptions with shared subscriptions. This example includes the two routing nodes
SingleNode and ClusteredNode:
● SingleNode — A single broker routing node containing broker C
● ClusteredNode — A multi-broker routing node containing brokers A and B
Cluster G2
Pn
P = Publishers
Figure 68. Clustered Node with Publishers and Single Node with Shared Subscriber Groups
The routing node SingleNode has the following rules and subscribers:
● Rules on SingleNode:
■ Topic Pattern T.# -> Propagated to ClusteredNode
■ Topic Pattern jms.sample.chat -> Propagated to ClusteredNode
● Subscribers on SingleNode:
■ Three subscribers in the shared subscription group g: [[g]]T.A
■ Two subscribers in the shared subscription group G: [[G]]T
■ One non-shared subscriber to ClusteredNode
The following sections describe the message routing behavior of this configuration for
different scenarios using remote publishing and global subscriptions with shared
subscription groups.
MultiTopics
A MultiTopic combines multiple underlying component topics into a single destination.
A message producer can use a MultiTopic to publish a message to multiple topics in a
single operation (which is significantly faster than publishing to multiple topics
individually). A message consumer can also use a MultiTopic to subscribe to multiple
topics in a single subscription. (Alternatively, a message consumer can accomplish this
via hierarchical namespaces and template characters. However, MultiTopics differ from
hierarchical namespaces in how they define a set of topics for subscription. See
Chapter 13, “Hierarchical Name Spaces.”)
MultiTopic publishing and MultiTopic subscribing are separate features. A message
producer can publish to a MultiTopic even if no message consumer subscribes to the
MultiTopic (although different message consumers can subscribe to different component
topics within the MultiTopic). Similarly, a message consumer can subscribe to a
MultiTopic even if no message producer publishes to it (although different message
producers might publish to different component topics within the MultiTopic).
When a message producer publishes a message to a MultiTopic, the message is received
by any message consumer subscribed to any of the underlying component topics. When a
message consumer subscribes to a MultiTopic, the consumer receives messages published
to any of its component topics.
Creating MultiTopics
When you create a MultiTopic object, it is initially empty and has no component topics.
There are two ways to create a MultiTopic object, using a Session object or a
DestinationFactory object.
You can add a MultiTopic to a MultiTopic; this is equivalent to adding each component
topic individually.
You can add a TemporaryTopic to a MultiTopic. However, you cannot create a durable
subscriber on a MultiTopic that contains one or more temporary topics. If a temporary
topic is deleted, a subsequent attempt to publish using a MultiTopic that contains the
deleted temporary topic fails immediately with an InvalidDestinationException. When a
MultiTopic is saved to an object store, its component temporary topics are not serialized.
In this situation, there are two different ways that a message consumer can receive
multiple copies of the message: the message consumer can subcribe to a MultiTopic with
overlapping component topics (for example, MULTITOPIC:foo.a||foo.c); or the message
consumer can subscribe to a set of topics in a hierarchical namespace (for example,
foo.*).
A message consumer in this situation can control whether to receive multiple copies or a
single copy of the message. The message consumer controls this by calling the
setSplitMultiTopicDelivery(boolean value) method on either a Session or on a
ConnectionFactory. When called on a Session, it affects only that session. When called on
a ConnectionFactory, it affects all sessions created from the ConnectionFactory.
When the split delivery value is set to true, new subscribers take delivery of one copy of
a message MultiTopic published for each topic in its MultiTopic list that match topics in
the publisher's list. In each message, the value of the JMSDestination header is the
component topic where the message was published.
Important Set the split delivery option before creating subscribers. Once subscribers are created,
changes to this setting are not applied.
Remote Publishing
A message producer can publish a message destined for multiple topics on a remote node
via one method call. However, a message producer cannot publish a message to more than
one node using a single MultiTopic.
If a message producer publishes to a remote node using a MultiTopic, and an ACL check
on the remote node fails for a component topic, the message is not published to that
component topic. However, the message is published to any component topic that passes
an ACL check. For example, if the message producer on NodeA publishes a message to
NodeB::MULTITOPIC:topic1||topic2||topic3, and ACL check fails on topic2, the
message is published only to topic1 and topic3.
For more information about remote publishing, see the “Multiple Nodes and Dynamic
Routing” chapter in the Progress SonicMQ Deployment Guide.
Global Subscriptions
A subscription created in a publishing node on behalf of a subscribing node is referred to
as a global subscription. The subscribing node requests creation of a global subscription
when a local subscriber connects to the subscribing node.
For more information about global subscriptions, see the “Multiple Nodes and Dynamic
Routing” chapter in the Progress SonicMQ Deployment Guide.
Global subscription rules on a subscribing node can specify topics for which global
subscriptions on the publishing node are created. These global subscriptions allow the
subscribing node to effectively subscribe to the publishing node and deliver to its own
subscribers the messages it receives from the publishing node.
Important A global subscription rule cannot use a MultiTopic in its definition.
MultiTopic Considerations
When you use MultiTopics, there are several issues to consider, described in the following
sections.
JMSReplyTo
You cannot specify a MultiTopic as the destination value for JMSReplyTo. An attempt to do
so will throw a JMSException.
Durable Subscriptions
Changing the split delivery value on a subsequent reconnect will not result in the
subscription being unsubscribed.
A DurableSubscriber that is subscribed on a MultiTopic can reconnect with a different set
of topics as long as the new MultiTopic has at least one topic in common with the previous
subscription. In this case messages stored on behalf will not be deleted. Instead they will
be filtered as they are delivered to remove any Topics that no longer match the
subscription. Note that the criteria for having one topic in common is that the topics
exactly match. For example a change from MultiTopic:Topic.1||Topic.2 to
MultiTopic:topic.* would result in an unsubscribe while a change from
MultiTopic:topic.1||topic.2 to MultiTopic:topic.1||topic.3 would not.
Shared Subscriptions
A message consumer can be a member of a shared subscription to a MultiTopic, provided
all component topics are part of the same shared subscription.
The following MultiTopic string indicates a shared subscription:
[[group1]]MULTITOPIC:topic1||topic2
A MultiTopic subscription group is defined by the set of intersecting topics for any group
prefix. For example, consider two message consumers, each subscribed to one of the
following MultiTopics:
[[group1]]MULTITOPIC:topic1||topic2
[[group1]]MULTITOPIC:topic1||topic3
Because the group prefix is the same, both subscriptions are considered part of the same
shared subscription, but only on their intersecting topic topic1. Messages that would be
delivered to topic2 and topic3 subscribers are discarded.
When a message consumer subscribes with a smaller set of topics, the shared subscription
is reduced.
Note It is a good practice for all group members to use the same MultiTopic, perhaps
maintained and accessed as an adminstered Destination object.
Non multi-topic group subscriptions are load balanced separately from MultiTopic
subscriptions.
HTTP Direct
You can specify a MultiTopic for HTTP Direct inbound. The MultiTopic format is
recognized and handled for inbound JMS over HTTP requests. If reply to is specified as
a MultiTopic an errorcode is returned.
Flow Control
Multi-publishes are subject to normal pub/sub flow control. If any prior publish causes the
publisher to flow control, a publish operation may block until there is room on the
publisher's output queue. It will be up to the Administrator to determine which slow
subscribers are problematic by use of the pubpause notification. If the subscriber has a
Multi-subscription all subjects from that subscription will be listed in the notification.
Since messages that are routed to other brokers are placed in the routing queue, it is
possible that the routing queue will fill up. If this happens the application will be flow
controlled on all component topics until space becomes available on the routing queue.
This chapter provides information about preventing duplicate messages and guaranteeing
message delivery. The first part of this chapter explains how you can detect duplicate
messages and prevent messages from being delivered more than once. The second part of
the chapter provides information about how you can use the SonicMQ Dead Message
Queue (DMQ) features to guarantee that messages will not be discarded until a client has
processed them. The chapter contains the following sections:
● “Introduction”
● “Duplicate Message Detection Overview”
● “Dead Message Queue Overview”
● “Handling Undelivered Messages”
● “Specifying a Destination for Undelivered Messages”
● “Undelivered Message Reason Codes”
Introduction
SonicMQ can guarantee message delivery when the broker system to which a client
connects can be certain that:
● Messages are not duplicates of ones already delivered.
● Messages that are undeliverable can be channeled into a holding area for
administrative handling.
Session.commit(transactionID, lifespan)
where:
● transactionID is a universally unique identifier generated by the application that is
guaranteed to be unique
● lifespan is the duration for which the transactionID is intended to be saved (in
milliseconds)
The indexed commit operation is supported on SonicMQ transacted queue sessions and
topic sessions. It functions as shown in Table 30.
Table 30. Session.commit Behavior
Condition Action
transactionID exists Throws an exception.
transactionID does not exist Store a new value of transactionID and continue with normal
Session.commit( ) behavior.
Note The same duplicate detection transactionID table is used for JMS-for-HTTP (with HTTP
Direct) and for large message support.
Guaranteeing Delivery
When you use the DMQ, any expired or undeliverable message is guaranteed to be
preserved on the broker. To ensure that expired or undeliverable messages are preserved,
you must configure your application to:
● Request that expired or undelivered messages be preserved
● Monitor the DMQs
● Handle all messages that arrive in the DMQ
Note Queue messages that are enqueued in the DMQ retain their original JMSDestination and
JMSExpiration values.The destination value of a topic message on the DMQ changes to
include the node name to which it was routed. For example, the destination might be
changed from “MyTopic” to “NodeA::MyTopic” to reflect the node to which the message
was undeliverable.
Ensure that QueueBrowsers and QueueReceivers on the DMQ check the
(javax.jms.Message) m.getJMSDestination() for the original topic or queue.
The settings for save threshold and maximum queue size are highly specific to an
application. Therefore, you should change these from their default settings to values
appropriate to your application.
The administrator can modify all the parameters of the SonicMQ.deadMessage queue,
except the Name and Global setting, using the Management Console. See the “Configuring
Queues” chapter in the Progress SonicMQ Configuration and Management Guide for
information about modifying the DMQ parameters.
The administrator can also modify Access Control for the DMQ through the parameter
security settings. See the “Configuring Queues” chapter in the Progress SonicMQ
Configuration and Management Guide for information about administrative
modifications to Access Control for the DMQ.
● JMS_SonicMQ_notifyUndelivered
Set this boolean property to true for every message that should raise an administration
notification when noted as being undeliverable. (Ignored for DISCARDABLE topic
messages.)
● JMS_SonicMQ_undeliveredReasonCode
Read this int property to determine why SonicMQ declared this message as
undeliverable. The broker sets this property when messages are moved to a dead
message queue.
● JMS_SonicMQ_undeliveredTimestamp
Read this long property to determine when SonicMQ declared this message as
undeliverable. The broker sets this property when messages are moved to a dead
message queue.
These property names are available as standard constants in
progress.message.jclient.Constants. Table 32 provides the values for these constants.
Table 32. JMS SonicMQ Properties
UNDELIVERED_TIMESTAMP “JMS_SonicMQ_undeliveredTimestamp”
// Static setup
private static String Q_NAME = <Various>
// Set the msg to be preserved in the Dead Message Queue.
msg.setBooleanProperty(“JMS_SonicMQ_preserveUndelivered”, true);
Figure 69 shows a message received on a global queue on the remote broker with the
property that retains the message in the DMQ set to true. Notice that the message
identifier at the top of the right panel indicates the global routing taken by this message.
JMS_SonicMQ_preserveUndelivered
4. The special processing object determines whether to send a notification that the
message has been sent to the DMQ or that the message is a queue message that has
expired (expired topic messages are not sent to the DMQ).
The message is checked for the boolean property:
JMS_SonicMQ_notifyUndelivered
Note Reason codes are defined as public final static int in the
progress.message.jclient.Constants class.
In this case, the SonicMQ broker determines that a message has expired. This failure type
applies only to queue messages.
This dead message event is the simplest case and the one that most developers consider
when thinking about dead message queues.
When sending messages, you can optionally set the parameter time to live (TTL). This
TTL is converted to an expiration time and stored in the message header (in GMT).
When a SonicMQ broker tries to deliver a message, it notes the expiration time (based on
the GMT as calculated from the broker’s system clock) and might decide not to deliver
the message due to expiration.
Checks for expiration are done only periodically within a broker (in order to avoid extra
overhead). Messages are always guaranteed not to be delivered if they have expired.
However, the actual time they are moved to the dead message queue might be significantly
later than the expiration date in the header. You can enable queue cleanup and set the
cleanup interval from the Management Console in the Edit Queues Properties dialog box,
as shown in Figure 70. See the “Configuring Queues” chapter in the Progress SonicMQ
Configuration and Management Guide for information about using the Management
Console to set the queue cleanup parameters.
Other cases where messages might be sent to the DMQ occur in scenarios involving
dynamic routing, remote publishing, and global subscribing. See the chapter “Multiple
Nodes and Dynamic Routing” in the Progress SonicMQ Deployment Guide for
information about these topics.
The previous example uses the createQueue method to create a Queue object dynamically.
More commonly, applications use a Queue or Topic object created by an administrator as
a JMS administered object and stored in a JNDI namespace.
Applications can also use TemporaryTopic and TemporaryQueue objects created by the
corresponding methods in the javax.jms.Session interface.
SonicMQ validates the destination in the setDestinationProperty( ) method as follows:
● It can be set for either topic or queue messages.
● It can be a Queue or a Topic.
● It can be a global queue that resides in a different routing node or in the local node.
● It can be a remote topic such as Node::Topic.
● It can be a clustered queue, global or not global.
● It can be a temporary queue or a temporary topic.
● It can be the SonicMQ.deadMessage queue at the broker where the application is
connected.
An application can also use the Message.setStringProperty( ) method to set the value of
the JMS_SonicMQ_destinationUndelivered property, but this is not recommended. The
correct way to set this property is to use the Message.setDestinationProperty( ) method.
When applied to a property whose value is a destination, the
Message.getObjectProperty( ) method returns the value of the property as a string, which
has the format described above. Likewise, the Message.setObjectProperty( ) method also
expects a string value for a property of the destination type (and the string must also have
the format described above).
If an application needs to copy message properties from one message to another it can use
the Message.setObjectProperty( ) method, as shown:
message2.setObjectProperty(message1.getObjectProperty(propName));
}
An application can use this method to loop through a list of message properties obtained
by the Message.getPropertyNames( ) method. If a property is a destination property, the
application can use the Message.getDestinationProperty( ) method to retrieve the value
of the property. Otherwise, the application can use the Message.getObjectproperty( )
method.
The Message.isDestinationProperty( ) method returns true only if the name of the
property is either JMS_SonicMQ_destinationUndelivered or
JMS_SonicMQ_undeliveredOriginalJMSDestination.
Note that in some cases, SonicMQ can detect the failure as soon as a message becomes
undelivered—in that case, SonicMQ preserves the message in the DMQ at the broker
processing the undelivered message. However, if the specified undelivered destination
requires SonicMQ to send the message to another broker or node, the failure may take
place at a different broker and SonicMQ places the message in the DMQ at that broker.
In the DRA environment, the publish permission check is done by every broker that
receives the message as it is being routed to its destination. Since each node can use its
own authentication and its own security policy, it is possible that the message is received
successfully by the local broker but is later rejected by the remote node. In that case, the
application doesn't receive an exception but the message is dropped at the remote node.
Whenever the publish permission check on the undelivered destination fails, the broker
writes an error a message in its log as it always does for the publish permission check on
the message destination.
Table 33 lists the reason codes that relate to messages whose delivery attempts to a
receiver exceeded the specified maximum redelivery attempts.
Table 34. Reason Code for Undelivered Messages
Table 35 contains a reason code that can occur only for undelivered queue messages under
dynamic routing. See the chapter “Multiple Nodes and Dynamic Routing” in the Progress
SonicMQ Deployment Guide for some examples of dynamic routing of queue messages.
Table 35. Reason Codes for Undelivered Queue Routing Messages
Table 36 lists the reason codes that can occur for undelivered messages under dynamic
routing and, in some cases as indicated, in remote publishing or subscribing. See the
chapter “Multiple Nodes and Dynamic Routing” in the Progress SonicMQ Deployment
Guide for some examples of dynamic routing of queue messages and examples of remote
publishing and subscribing.
Table 36. Reason Codes for Undelivered Routing Messages (All Domains)
Table 37 lists the reason codes that occur only for undelivered topic messages. See the
chapter “Multiple Nodes and Dynamic Routing” in the Progress SonicMQ Deployment
Guide for some examples of remote publishing of topic messages and remote subscribing
to topic messages.
Table 37. Reason Codes for Undelivered Topic Routing Messages
Table 38 lists the reason codes that can occur for undelivered HTTP Direct routing
messages. See the chapter “HTTP Direct Acceptors and Routings” in the Progress
SonicMQ Deployment Guide for information about HTTP Direct routing and examples of
how to implement it in your deployments.
Table 38. Reason Codes for Undelivered HTTP Direct Routing Messages
Normally, HTTP Direct routing extensions are used when a SonicMQ broker is sending
to a non-Sonic Web server. However, there is nothing to stop you from using HTTP Direct
to talk to another broker that has inbound HTTP Direct acceptors. In this case, the
additional errors listed in Table 39 might occur.
Table 39. Additional Reason Codes for Undelivered HTTP Direct Routing Messages
Table 40 contains reason code that can occur for undelivered messages when an
undelivered destination has been defined by the user application. See “Specifying a
Destination for Undelivered Messages” on page 375.
Table 40. Reason Codes That Relate To a User-Specified Dead Message Queue
Note If the undelivered destination resides in a different routing node and the message can't be
delivered there because of a DRA-related problem , the reason code is one of the DRA-
related codes in the progress.message.jclient.Constants class.
This chapter explains the recoverable file channel feature available with SonicMQ
installations containing ClientPlus. This chapter includes the following sections:
● “About Recoverable File Channels for Large Messages”
● “Tips and Techniques for Using File Channels”
Step 3
Local Disk
Local Disk
Step 4 File Channel
C:\foo.file
/usr/bar
myApp/Recovery1 Step 5 /user/recovery1
4. The sender fragments and packages a portion of the source file. The sender will
continue to do this until it reaches its defined window size at which point it waits for
acknowledgement.
5. When the transfer completes successfully the receiver lets the sender know that the
transfer is done.
Step 3
Local Disk
Local Disk
Step 4 File Channel
C:\foo.file
/usr/bar
myApp/Recovery1 Step 5 /user/recovery1
Figure 72. File Transfer Across Accessible Global Queues on Two Brokers
Global Queues
When a connection is dropped you might want to establish a QueueConnection on a
different broker and recover the transfer. This is a good tactic when you are using the
Dynamic Routing Architecture provided that the broker is part of the same routing node.
If you connect to a different routing node, the transfer will not continue successfully.
Timeout
When a JMS message is sent with a RecoverableFileChannel attached, the send call
blocks until a channel is established. This behavior helps manage channels because the
sender knows at send time whether or not a receiver has accepted the channel. If a channel
is not established in Timeout time, a JMSException is thrown with the error code:
progress.message.jclient.ErrorCodes.ERR_CHANNEL_TIMEOUT.
These commands take effect immediately when called from within the ChannelListener
callback, when set before completeConnect() is executed, or before the channel is
sent.While these values can be set from outside the ChannelListener callback, they will
not take effect until the underlying code notices the change.
On the receiver, these methods can be used to timeout internal sender/receiver
communication:
● If a fragment is not received in retryCount * retryInterval time, the receiver’s
ChannelListener callback is executed.
● The retry count and retry interval values are used when there is a call to
completeConnect() on the receiver. If the sender does not respond, the receiver
attempts to contact the sender retryCount times, waiting for retryInterval time for
an acknowledgement. After that time, the completeConnect() call throws a
JMSException with the error code
progress.message.jclient.ErrorCodes.ERR_RETRY_TIMEOUT.
Fragment Size
When the transfer breaks a large file into fragments, the fragments are sent, each
expecting an acknowledgement from the receiving client. Performance can be optimized
by configuring the fragment size. The larger the fragment size, the more memory needed
on the client and broker to transfer the message.
To determine an optimal fragment size setting, first determine the actual TCPIP packet
size for your environment, then make the fragment size a direct multiple of the TCPIP
packet size. For example, if the packet size is 512 bytes, make the fragment size 512,
1024, or another multiple of 512 bytes. This method of determining fragment size
optimizes the amount of breakage of the message that is sent. For example, if the packet
size were 512 bytes and you set the fragment size to 768 bytes, you send 33% more
packets than needed. This setting fills one packet to 512 bytes and the next to only 256,
wasting the remaining 256 bytes of that packet.
Window Size
The window size is the number of fragments that can be sent before an acknowledgement
must be received. The larger the window size, the more time will elapse before an
acknowledgement is required. A larger window size is preferred for messaging setups
with a high degree of latency between the sender and receiver. A larger window size could
also cause the sender to take more time before noticing the receiver is unavailable. A
smaller window size could cause the sending of the file to halt too often to wait for
acknowledgements from the receiving client.
ChannelID
Every channel has a unique channel ID that is assigned to it when the channel is
established. While this value is primarily for internal purposes, the channel ID is
accessible by the user to help associate information with a channel ID. The channel ID is
available across failures. The channelID is null until after the channel has been sent, or
until getChannel has been called on the receiver.
Class:
● ConnectionFactory — The method that is essential to client local persistence is also
used with RecoverableFileChannels to set a local store directory for recovery
information for the channel. If this method is not set, the working directory is used to
store recovery information
● RecoverableFileChannelFactory — The constructor class for the recoverable file
channel in the method:
static RecoverableFileChannel createRecoverableFileChannel(java.io.File file)
Interfaces: (progress.message.jclient)
● Message — Added methods for setting and getting channels on a message.
● QueueConnection — Added methods for getting reference to channels and working
with unfinished channels.
● Channel — Encapsulates the control and recovery logic for sending a message data
stream. A sender application instantiates an implementation of this class to send a
large message.
● channel.RecoverableFileChannelFactory — Creates recoverable file channels.
● channel.RecoverableFileChannel — A Channel implementation to send files on a
channel.
● ChannelListener — Defines a method for notifying a sender or receiver application
that a large message transmission has completed successfully.
● ChannelStatus — Enables queries to a channel for its status.
The methods that are relevant to recoverable file channels are listed in Table 41.
ChannelListener
A channel listener is set on a channel to inform an application asynchronously of status
and error conditions. If there is a problem transferring a file such as a timeout and there
is no ChannelListener or the program logic does not perform a continueTransfer in the
channel listener, the channel is implicitly cancelled in which case the QueueConnection’s
onException listener is called with
progress.message.jclient.ErrorCodes.ERR_CHANNEL_IMPLICITLY_CANCELLED.
The onChannelStatus method is called with a reference to the Channel in question, and an
Exception e that triggered this channel status call.
Important Thread Safety — While an application should manipulate a channel while inside a
ChannelListener, the application can safely call a number of methods from outside the
ChannelListener thread without error:
● Channel.close()
● Channel.getChannelStatus() and any method of the ChannelStatus object.
● RecoverableFileChannel.cancel()
● RecoverableFilechannel.isSaving()
The following methods can be called from outside the ChannelListener callback but they
might not take effect immediately:
● Channel.setRetryCount()
● Channel.setRetryInterval()
All other methods should not be considered thread safe and should only be called within
a ChannelListener or before the channel is established.
Channel Status
A channel status object is a user’s window into the current status of a channel. The channel
status object is retrieved by a getChannelStatus call on the
progress.message.jclient.Channel interface. The current channel state can be retrieved
by the channel.getChannelStatus().getStatus() method, then the application should use
a switch statement on the status code to set the course of action. Many channel commands
are not thread safe or are not valid when a channel is in a given state; therefore, it is
recommended that the developer put the logic that affects the transfer of the channel
within the ChannelListener. The ChannelStatus.getStatus indicates the status of the file
transfer as listed in Table 42.
RFC_PRECONNECT_ATTEMPT The file channel has been restored but continueTransfer() has not been called.
RFC_DISCONNECT The QueueConnection to the broker has disconnected.
RFC_LOCAL_ERROR There has been a fatal error—such as an IOException while attempting to access
the file—on the local client. Examine the Exception that is passed in to the
ChannelListener to determine the problem. The channel is closed, and the
recovery information is retained.
RFC_REMOTE_ERROR There has been a fatal error—such as running out of disk space—on the remote
client. Examine the Exception that is passed in to the ChannelListener to
determine the problem. The channel is closed, and the recovery information is
retained.
Note The Large Message mechanism is not recommended as the only communication
in an application. It is optimized for reliability across failures and is not the best
solution for messages that can be transferred as a single JMS message. Large
Message transfers involve protocol overhead and disk I/O that would not be
incurred in a normal JMS Message.
//set message property (name of a file being sent), which is used by receiver
//to get file name
msg.setStringProperty( "SenderFileName", file.getName() );
//send message
System.out.println( "\nTry to send header message and establish channel to send file - "
+ file.getAbsolutePath() );
sender.send( msg );
System.out.println( rfc.getChannelID() + " channel established!" );
while( enum.hasMoreElements() )
{
progress.message.jclient.channel.RecoverableFileChannel channel = null;
channel = (progress.message.jclient.
channel.RecoverableFileChannel)enum.nextElement();
try
{
// Set a channel listener
channel.setChannelListener( new LMSChannelListener() );
System.out.println("\nTry to restore channel - " + channel.getChannelID());
The RestoreChannels pattern in the FileReceiver, shown in Code Sample 25, is slightly
different from the FileSender.
Code Sample 25. FileReceiver Sample: RestoreChannels Pattern
private void restoreChannels()
{
// First clean the property log file. There can be extra records
// ( see comments in onMessage() )
cleanPropFile();
try
{
// Check if there are any unfinished channels to restore
if( connection.hasUnfinishedChannels() )
{
// Retrieve an enumeration of Strings contains all available
// channelIDs
Enumeration enum = connection.getUnfinishedChannelIDs();
while( enum.hasMoreElements() )
{
String channelID = null;
progress.message.jclient.channel.RecoverableFileChannel channel = null;
String channelFile = null;
// Get the channelID
channelID = (String)enum.nextElement();
Log Files
Log files are produced on both the sender and the receiver when a file transfer starts.
These logs enable recovery. Both the sender and the receiver use their logs to re-establish
contact. As such, if one of the participants in file transfers prefers to cancel unfinished
channels while the other intends to complete them, the logs do not exist to complete
recovery. The identity of the channel and the bytes transferred enable the receiver to
specify the point where recovery should resume.
These files and folders should never be shared. Two applications on the same system
should provide distinct folders. Note that the default folder name is the name of the
current working directory. You should make efforts to be more emphatic. For example,
the samples in ClientPlus/LargeMessageSupport are two applications in the same folder.
The user name provided in the startup of each application is assigned to the folder name
for the application so that the two applications do not share. You could hardwire a very
specific name into every sender and receiver application to ensure that recovery logs are
distinct.
Important This feature requires the SonicMQ installations of the participants in a SonicStream
transfer—the sending application, the broker, and the receiving applications—are
installed, upgraded, or updated to SonicMQ V7.5.
C C
SonicStreamAPI S
O O
S SonicStreamAPI
E E
Data SonicMQ S
N N
S SonicMQ
Sonic Output S
N
Topic N
S Sonic Input Data
to Client I
E E
I Client
Stream Runtime O
C StreamTopic C
Runtime
Stream Rec'd
send N
T T
O
N
I I
O O
N N
Figure 73. Sonic Publisher Sending a Stream to Multiple Stream Subscribers to a Stream Topic
The SonicStream API is a lean process. Any requirements for handling interruption of a
stream on the receiver side must be accomplished by the user’s application code.
The size of the streamed data does need to be known as long as the sender application
moderates the flow of data into the stream and the receiver applications offload the
received streamed data out of their respective receiver applications at a rate that allows the
receiver applications to stay within their allotted memory limits
A notification topic can be established so that a StreamSubscriber can post alerts of error
conditions. The StreamPublisher’s listener can read and act on the information. See
“NotificationTopic” on page 418 for details.
Constructors
The constructors for com.sonicsw.stream.SonicStreamFactory let you choose to set the
StreamTopic and the ApplicationName in the constructor:
● SonicStreamFactory()
● SonicStreamFactory(String streamTopic)
● SonicStreamFactory(String streamTopic, String.appName)
Methods
The setStreamTopic method can be used to set or reset the stream topic.
StreamTopic
● setStreamTopic(String topic)
● String getStreamTopic()
useTempStreamTopic
useTempStreamTopic(boolean value)
Instead of specifying the StreamTopic, setting this method to the value true specifies that
a unique temporary topic be used to send and receive the SonicStream messages.
ApplicationName
● setApplicationName(String appName)
● String getApplicationName()
An application name can be added as a property to associated factory instances and stream
controllers. The default APPLICATION_NAME_PROPERTY is StreamsApp.
NotificationTopic
● setNotificationTopic(java.lang.String topic)
● String getNotificationTopic()
Method to set the topic to which all notification messages are sent and received by stream
controllers created through the factory instance. See “Notifications” on page 424 for more
information.
SonicStream Interface
A SonicStream is a header message and a payload. The interface SonicStream lets you get
information:
● getHeaderMessage() — Gets the JMS Message that is sent as the first message of the
stream.
● getStreamId() — Gets the identifier that is used to distinguish a SonicStream instance
(and its associated segments) from other SonicStream instances.
● getInputStream() — Gets the java.io.InputStream instance of this SonicStream
instance.
● getOutputStream() — Gets the java.io.OutputStream instance of this SonicStream
instance.
● getNotificationDestination() — Gets the JMS Destination used by this
SonicStream instance. Data written to a SonicStream instance is published in the form
of JMS TextMessages to this Destination. Likewise, data will be received, in the form
of JMS TextMessages, from this Destination by a SonicStream instance.
● getStreamStatus() — Gets the current status of a SonicStream instance. The status is
returned as a StreamStatus object.
SonicStreamFactory
● createSonicOutputStreamController(Connection con)
● createSonicOutputStreamController(Connection con,
Destination streamTopic,
Destination notifyTopic)
SegmentSize
● setSegmentSize(int segmentSize)
● int getSegmentSize()
The SonicOutputStream supports the sending of data that is much larger than Sonic JMS
message size limits because the stream is broken down into a series of segments
(sometimes referred to as chunks.)
The segment size should be tuned to the buffer capacities on the broker. Set the number
of bytes in a segment as a positive integer value to a maximum of 10 MB, the maximum
supported message size for SonicMQ. The default segment size is 16384 bytes.
DeliveryMode
● setDeliveryMode(int mode)
● int getDeliveryMode()
On a sender, the delivery mode specifies the delivery mode of the BytesMessages that carry
the message segments.
The delivery mode is, by default, javax.jms.DeliveryMode.NON_PERSISTENT. If you are
using a security-enabled broker, the default is interpreted as NON_PERSISTENT_SYNC.
If you are using a broker that is not security-enabled, NON_PERSISTENT is interpreted as
NON_PERSISTENT_ASYNC, meaning that no acknowledgement is expected by the publisher.
You can set the DeliveryMode to
progress.message.jclient.DeliveryMode.NON_PERSISTENT_SYNC so that the publisher
method blocks to await acknowledgement by the broker.
The constant is typically used instead of the integer value:
setDeliveryMode(DeliveryMode.NON_PERSISTENT_SYNC)
SonicOutputStreamController Interface
Methods in the SonicOutputStreamController interface include:
● createStream(String id) — Creates a SonicOutputStream instance with an identifier
that will be in the header of every message sent through the stream.
● registerNotificationListener(SonicStreamListener listener) — Registers a
listener that will receive asynchronous notifications. Only a single notification
listener may be registered at a given time.
● registerExceptionListener(SonicStreamExceptionListener listener) — Registers
a listener that will receive notification of asynchronous exceptions. Only a single
exception listener may be registered at a given time.
The sending application should register one of each type of listener object with the output
stream controller so that it can be notified of any asynchronous notifications or
exceptions. The listener object handles notifications sent from any receiver applications,
such as notice of a corrupted stream or a dropped connection. The sending application is
responsible for handling such errors. For example, it might be appropriate for the sending
application to clear the stream, and then write the original data to the output stream again.
StreamStatus Interface
int getCurrentStreamStatus()
The StreamStatus interface provides methods to determine whether the stream has been
created, the transfer is in progress, the transfer completed, and if a stream has encountered
an error.
The StreamStatus interface also provides methods that get stream information:
● String getStreamId() — Identifies the stream instance.
● long getTransferStart() — Time when the transfer started.
● long getSegmentDiscarded() — Segments discarded by the stream.
● long getBytesTransferred() — Bytes transferred in the stream.
● long getSegmentsTransferred() — Segments transferred in the stream.
● long getTransferEnd() — Time when the transfer ended.
● long getTransferTime() — Elapsed time of the transfer.
SonicStreamFactory
● createSonicInputStreamController(Connection con)
● createSonicInputStreamController(Connection con,
Destination streamTopic,
Destination notifyTopic)
setDeliveryMode
● setDeliveryMode(int mode)
● int getDeliveryMode()
The delivery mode for the stream controller instance. On a stream receiver, the delivery
mode applies to the notifications it emits.
setReadAheadWindowSize
● setReadaheadWindowSize(int kbytes)
● int getReadaheadWindowSize()
The ReadAheadWindow is a buffer on the receiver that helps to control memory consumption
by the input stream controller. The content of the buffer consists of segments that have
been read but not yet consumed by the application.
Set the number of kilobytes in the read ahead buffer as a positive integer value.
The default read ahead window size is 500 kilobytes.
setSegment Timeout
● setSegmentTimeout(int secs)
● int getSegmentTimeout()
Sets how many seconds the SonicInputStream will wait for a segment of an opened stream
to arrive. When the timeout expires, an exception is thrown by the SonicInputStream. The
default value is 60 seconds.
SonicInputStreamController Interface
Stream Handlers
● getNextStream(long timeToWait) — Returns the next available stream to be read.
● getNextFullStream(long timeToWait) — Returns the next available full stream to be
read. Note that use of this method will likely result in the use of more heap memory
than the getNextStream method, as the entire content of the SonicStream is assembled
in memory before the SonicStream may be read. An exception will be thrown if the
getNextFullStream method is invoked and the streams runtime detects that there is not
enough space to hold the stream. Applications should ensure that enough space will
be available by increasing the readahead window size via the setReadaheadWindowSize
method of SonicStreamFactory prior to creating a SonicInputController instance.
● isFullStreamAvailable() — Boolean that indicates whether the entire content of a
SonicStream is available to be read.
● isPartStreamAvailable() — Boolean that indicates whether at least a portion of a
SonicStream is available to be read.
● releaseStream(SonicStream ss) — Closes the specified SonicStream instance and
releases its associated resources.
● close() — Closes all streams managed by the SonicInputStreamController instance
and closes the JMS objects created by the controller instance.
Notifications
● registerExceptionListener(SonicStreamExceptionListener listener)
● buildSenderNotification(int type, SonicStreamException ex)
● buildSenderNotification(int type, SonicStream ss, SonicStreamException ex)
In the event of a corrupted stream or a dropped connection, the receiver application can
send a notification to the sender application, as illustrated in Figure 2. The receiving
application can register a listener object with the input stream controller. The listener
object implementation must determine how to respond to the error condition.
A typical scenario is the listener object sends a notification from the input stream back to
the sending application’s stream object, indicating conditions such as an apparently
corrupt stream or an interrupted connection. If the sender application is designed to resend
the stream, the receiving application would clear the input stream by either closing the
current stream (and wait for the next stream), or close the controller (and then create a new
controller for a different stream topic, if appropriate).
If connection drops on the receiving application side, the receiving application is
responsible for restoring the connection. (If the loss of connection is intermittent, the
stream would realize missed segments which the broker would redeliver.) Once the
connection is restored, the input stream implementation is responsible for sending a
notification to the sending application. This notification is received by the listener
registered with the sending application's output stream instance. It is up to the sending
application how to proceed upon receipt of such a notification. The sending application
might choose to write the original data to the stream again, or it might write it so that the
data will only be sent to the receiving application instance(s) that did not get it.
Handling Errors
The design of SonicStreams takes into account that the application designers typically
know how they want to handle stream disruption. For example, if a receiver-side
application suffers a dropped connection and subsequently reconnects to the Broker, then
the receiver-side application instance would typically alert the sender-side of the
disruption that took place. And the sender-side application instance could then resend the
original data to the affected receiver-side application instance. Thus, the application could
avoid resending the data to all receivers; the sending side would resend it only to the
receiver application that did not receive the original data.
The JMS Connection supplied to the SonicStreamFactory should set the ping interval.
SonicStreams do not permit an application that uses multiple threads to access the same
stream instance. Only one thread should write to a single output stream instance, and only
one thread should read from a single input stream instance.
Samples of SonicStreams
Sonic provides sample applications that demonstrate SonicStream functionality that
allows a publisher to send large objects to multiple subscribers using SonicMQ
messaging. The Stream Publisher application supplies data to the SonicStreams runtime
using the java.io.OutputStream API while the Stream Subscriber applications receive the
data using the java.io.InputStream API.
There are two pairs of sample applications for the SonicStream API:
● SonicStreams — The StreamSender and StreamReceiver applications demonstrate
basic functionality where the sender sends a hashed memory object and the receiver
receives it.
● SonicStreams with Retry — The StreamSender_Retry and StreamReceiver_Retry
applications demonstrates advanced functionality. The sender sends streams, and
accepts requests for resend from stream receivers. Resending provides an example of
the handling of errors in stream transfers.
The sample applications are provided in source code text form so that you can use them
as a basis for your SonicStreams applications. The topic name StreamTopic is arbitrary.
The source code is extensively annotated with comments to help clarify their patterns and
behaviors.
Note User authentication on security-enabled brokers — The connection to the broker you
use for these samples requires valid user credentials if you chose to enable security when
installing or configuring the broker. If security is enabled, you can either setup the sample
user identities specified in the command line for each sample, or supply your own user
identities. If you have not created or maintained users, the default user is Administrator
with the password Administrator.
Note Hostname and port — The samples refer to localhost. If the broker where you want to
connect is on a host other than localhost, use that host’s name. The default port defined
at installation of the broker is 2506. Use a port number that corresponds to an acceptor on
the broker you want to use.
If your acceptor definition is not tcp://localhost:2506, you need to specify the URL in
a sample’s command line with the -b parameter, such as:
..\..\SonicMQ StreamReceiver -u user -p pwd -st StreamTopic -b tcp://myHost:3456
SonicStreams Sample
In this pair of sample applications, you are guided to start the receiver application and then
start the sender application.
1. Start the SonicMQ broker you want to use in the sample.
2. Open a console window to install_dir/MQ7.5/samples/TopicPubSub/SonicStreams.
3. Enter:
■ On a Windows system:
..\..\SonicMQ StreamReceiver -u user -p pwd -st StreamTopic
■ On a UNIX or Linux system:
../../SonicMQ.sh StreamReceiver -u user -p pwd -st StreamTopic
The StreamReceiver’s console window displays:
Waiting 120 sec. for a stream...
The sender streams consist of a few different hypothesized DataOutputStreams. Note that
there are occasional pauses in the sending and receiving of the streams to make the
demonstration behaviors easier to observe.
Sender
Start StreamSender_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending stream: Stream_10000;dest= StreamTopic
***Notification: Receiver StreamsApp started receiving Stream_10000
Stream_10000: Wrote 1000
Stream_10000: Wrote 2000
Stream_10000: Wrote 3000
...
Receiver
Start StreamReceiver_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending retry request for streamId ALL to TheNotifyTopic
Waiting 60 sec for more Streams on topic StreamTopic
Receiving stream: Stream_10000
Stream_10000: Read 1000
Stream_10000: Read 2000
Stream_10000: Read 3000
...
Stream_10000: Read 8000
Stream_10000: Read 9000
Stream_10000: Read 10000
*** Completed successful receive; Stream Stream_10000: bytesReceived= 80004
chunksReceived= 83 time= 11196
Waiting 60 sec for more Streams on topic StreamTopic
Receiving stream: Stream_20000
Stream_20000: Read 1000
Stream_20000: Read 2000
Stream_20000: Read 3000
...
Stream_20000: Read 18000
Stream_20000: Read 19000
Stream_20000: Read 20000
*** Completed successful receive; Stream Stream_20000: bytesReceived= 160004
chunksReceived= 163 time= 21301
Waiting 60 sec for more Streams on topic StreamTopic
Receiving stream: Stream_40000
Stream_40000: Read 1000
Stream_40000: Read 2000
Stream_40000: Read 3000
...
Stream_40000: Read 38000
Stream_40000: Read 39000
Stream_40000: Read 40000
*** Completed successful receive; Stream Stream_40000: bytesReceived= 320004
chunksReceived= 323 time= 41521
Waiting 60 sec for more Streams on topic StreamTopic
No more streams available; ...
Performing retries: countToRetry= 0
Shutting down...
Sender
Start StreamSender_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending stream: Stream_10000;dest= StreamTopic
***Notification: Receiver StreamsApp started receiving Stream_10000
Stream_10000: Wrote 1000
Stream_10000: Wrote 2000
Stream_10000: Wrote 3000
...
Stream_10000: Wrote 8000
Stream_10000: Wrote 9000
Stream_10000: Wrote 10000
CompletedSend; Stream Stream_10000: bytesSent= 80004 chunksSent= 83 time= 10195
Sending stream: Stream_20000;dest= StreamTopic
Stream_20000: Wrote 1000
***Notification: Receiver StreamsApp retry request for streamId: ALL replyDest=
StreamTopic
Stream_20000: Wrote 2000
Stream_20000: Wrote 3000
...
Stream_20000: Wrote 18000
Stream_20000: Wrote 19000
Stream_20000: Wrote 20000
CompletedSend; Stream Stream_20000: bytesSent= 160004 chunksSent= 163 time= 20360
Sending stream: Stream_40000;dest= StreamTopic
***Notification: Receiver StreamsApp started receiving Stream_40000
Stream_40000: Wrote 1000
Stream_40000: Wrote 2000
Stream_40000: Wrote 3000
...
Stream_40000: Wrote 7000
Stream_40000: Wrote 8000
Receiver
Start StreamReceiver_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending retry request for streamId ALL to TheNotifyTopic
Waiting 60 sec for more Streams on topic StreamTopic
Receiving stream: Stream_10000
Stream_10000: Read 1000
Stream_10000: Read 2000
Stream_10000: Read 3000
...
Stream_10000: Read 8000
Stream_10000: Read 9000
Stream_10000: Read 10000
*** Completed successful receive; Stream Stream_10000: bytesReceived= 80004
chunksReceived= 83 time= 20200
Waiting 60 sec for more Streams on topic StreamTopic
Receiving stream: Stream_20000
Stream_20000: Read 1000
Waiting 60 sec for more Streams on topic StreamTopic
***** Releasing duplicate stream Stream_10000
Waiting 60 sec for more Streams on topic StreamTopic
onStreamException: StreamId= Stream_10000; error: errorcode= 3
com.sonicsw.stream.SonicStreamException: Received segment 1 for
stream Stream_10000 ; stream does not exist; msg discarded
Receiving stream: Stream_20000
Stream_20000: Read 1000
Stream_20000: Read 2000
Stream_20000: Read 3000
...
Stream_20000: Read 8000
Stream_20000: Read 9000
Stream_20000: Read 10000
CTRL-C
Restart StreamReceiver_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending retry request for streamId ALL to TheNotifyTopic
Waiting 60 sec for more Streams on topic StreamTopic
onStreamException: StreamId= Stream_20000; error: errorcode= 3
com.sonicsw.stream.SonicStreamException: Received segment 121 fo
r stream Stream_20000 ; stream does not exist; msg discarded
Receiving stream: Stream_40000
Stream_40000: Read 1000
Stream_40000: Read 2000
CTRL-C
Sender
Start StreamSender_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending stream: Stream_10000;dest= StreamTopic
***Notification: Receiver StreamsApp started receiving Stream_10000
Stream_10000: Wrote 1000
Stream_10000: Wrote 2000
Stream_10000: Wrote 3000
...
Stream_10000: Wrote 7000
Stream_10000: Wrote 8000
Stream_10000: Wrote 9000
Stream_10000: Wrote 10000
CompletedSend; Stream Stream_10000: bytesSent= 80004 chunksSent= 83 time= 19208
Sending stream: Stream_20000;dest= StreamTopic
Stream_20000: Wrote 1000
***Notification: Receiver StreamsApp completed receiving Stream_10000
***Notification: Receiver StreamsApp started receiving Stream_20000
Stream_20000: Wrote 2000
CRTL-C
Restart StreamSender_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending stream: Stream_10000;dest= StreamTopic
Stream_10000: Wrote 1000
Stream_10000: Wrote 2000
Stream_10000: Wrote 3000
...
Stream_10000: Wrote 8000
Stream_10000: Wrote 9000
Stream_10000: Wrote 10000
CompletedSend; Stream Stream_10000: bytesSent= 80004 chunksSent= 83 time= 10135
Sending stream: Stream_20000;dest= StreamTopic
***Notification: Receiver StreamsApp started receiving Stream_20000
Stream_20000: Wrote 1000
Stream_20000: Wrote 2000
Stream_20000: Wrote 3000
...
Stream_20000: Wrote 13000
Stream_20000: Wrote 14000
Stream_20000: Wrote 15000
***Notification: Receiver StreamsApp retry request for streamId: ALL replyDest=
StreamTopic
Stream_20000: Wrote 16000
Stream_20000: Wrote 17000
Stream_20000: Wrote 18000
Stream_20000: Wrote 19000
Stream_20000: Wrote 20000
CompletedSend; Stream Stream_20000: bytesSent= 160004 chunksSent= 163 time= 29943
Sending stream: Stream_40000;dest= StreamTopic
Stream_40000: Wrote 1000
***Notification: Receiver StreamsApp started receiving Stream_40000
Stream_40000: Wrote 2000
Stream_40000: Wrote 3000
CTRL-C
Receiver
Start StreamReceiver_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending stream: Stream_10000;dest= StreamTopic
***Notification: Receiver StreamsApp started receiving Stream_10000
Stream_10000: Wrote 1000
Stream_10000: Wrote 2000
Stream_10000: Wrote 3000
...
Stream_10000: Wrote 8000
Stream_10000: Wrote 9000
Stream_10000: Wrote 10000
CompletedSend; Stream Stream_10000: bytesSent= 80004 chunksSent= 83 time= 10195
Sending stream: Stream_20000;dest= StreamTopic
Stream_20000: Wrote 1000
***Notification: Receiver StreamsApp retry request for streamId: ALL replyDest=
StreamTopic
Stream_20000: Wrote 2000
Stream_20000: Wrote 3000
Stream_20000: Wrote 4000
...
Stream_20000: Wrote 18000
Stream_20000: Wrote 19000
Stream_20000: Wrote 20000
CompletedSend; Stream Stream_20000: bytesSent= 160004 chunksSent= 163 time= 20360
Sending stream: Stream_40000;dest= StreamTopic
***Notification: Receiver StreamsApp started receiving Stream_40000
Stream_40000: Wrote 1000
Stream_40000: Wrote 2000
Stream_40000: Wrote 3000
...
Stream_40000: Wrote 7000
Stream_40000: Wrote 8000
Sender
Start StreamSender_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending stream: Stream_10000;dest= StreamTopic
Stream_10000: Wrote 1000
Stream_10000: Wrote 2000
Stream_10000: Wrote 3000
Stream_10000: Wrote 4000
***Notification: Receiver StreamsApp retry request for streamId: ALL replyDest=
StreamTopic
Stream_10000: Wrote 5000
Stream_10000: Wrote 6000
Stream_10000: Wrote 7000
Stream_10000: Wrote 8000
Stream_10000: Wrote 9000
Stream_10000: Wrote 10000
CompletedSend; Stream Stream_10000: bytesSent= 80004 chunksSent= 83 time= 10225
Sending stream: Stream_20000;dest= StreamTopic
***Notification: Receiver StreamsApp started receiving Stream_20000
Stream_20000: Wrote 1000
Stream_20000: Wrote 2000
Stream_20000: Wrote 3000
onStreamException: StreamId= Stream_20000; error: errorcode= 7
com.sonicsw.stream.SonicStreamException: JMSException
Attempting to create connection...
Cannot connect to broker: localhost:2506. Pausing 10 seconds before retry.
Attempting to create connection...
Cannot connect to broker: localhost:2506. Pausing 10 seconds before retry.
...
Cannot connect to broker: localhost:2506. Pausing 10 seconds before retry.
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending stream: Stream_40000;dest= StreamTopic
Stream_40000: Wrote 1000
***Notification: Receiver StreamsApp started receiving Stream_40000
Stream_40000: Wrote 2000
Stream_40000: Wrote 3000
...
Stream_40000: Wrote 8000
Stream_40000: Wrote 9000
CTRL-C
Receiver
Start StreamReceiver_Retry
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending retry request for streamId ALL to TheNotifyTopic
Waiting 60 sec for more Streams on topic StreamTopic
onStreamException: StreamId= Stream_10000; error: errorcode= 3
com.sonicsw.stream.SonicStreamException: Received segment 33 for
stream Stream_10000 ; stream does not exist; msg discarded
Receiving stream: Stream_20000
Stream_20000: Read 1000
Stream_20000: Read 2000
onStreamException: StreamId= Stream_20000; error: errorcode= 7
com.sonicsw.stream.SonicStreamException: JMSException
Attempting to create connection...
Cannot connect to broker: localhost:2506. Pausing 10 seconds before retry.
Attempting to create connection...
...
Cannot connect to broker: localhost:2506. Pausing 10 seconds before retry.
Attempting to create connection...
Created new controller; dest= StreamTopic
Sending retry request for streamId ALL to TheNotifyTopic
Waiting 60 sec for more Streams on topic StreamTopic
Receiving stream: Stream_40000
Stream_40000: Read 1000
Stream_40000: Read 2000
Stream_40000: Read 3000
...
Stream_40000: Read 8000
CTRL-C
This chapter provides information about hierarchical name spaces, which allow you to
create a hierarchy of contents by delimiting nodes when you name a topic. This feature
can be useful in the naming and management of topics. This chapter contains the
following sections:
● “About Hierarchical Name Spaces”
● “Publishing a Message to a Topic”
● “Broker Management of Topic Hierarchies”
● “Subscribing to Nodes in the Topic Hierarchy”
● “Examples of a Topic Name Space”
Without hierarchies, many topics are stacked onto one level. When many topics are used,
it gets increasingly difficult to maintain access to the naming structure and to denote topic
relationships.
While hierarchical topics enable powerful security and accelerate the retrieval of topics
by the broker, SonicMQ topic hierarchies enable unique multiple topic subscriptions,
allowing you to:
● Subscribe to many topics quickly
● Subscribe to topics whose complete name is unknown
● Traverse topic structures in powerful ways
When you use topic hierarchies, message selectors—an inherently slow and recurring
process—can often be eliminated.
Template Characters
Wild cards are special characters in a sample string that are interpreted when evaluating
a set of strings to form a list of qualified names. In this case, however, the special
characters are referred to as template characters because the entire string and its special
characters can be stored for later evaluation by durable subscriptions and security
permissions. The selection of topic names is dynamic, evaluated every time the topic is
requested.
The period (.) delimiter is used together with the asterisk (*) and the pound (#)template
characters when subscriptions are fulfilled. Using these characters avoids having to
subscribe to multiple topics and offers benefits to managers who might need to see
information or events across several areas. Client applications can only use template
characters when subscribing to a set of topics or binding a set of topics to a message
handler. Messages must be published on fully specified topic names.
Using template characters is somewhat different from using the usual wild cards, as
discussed below.
There are two SonicMQ template characters:
● asterisk (*) — Selects all topics at this content node.
● pound (#) — Selects all topics at this content node and the subordinate hierarchy
(when used in the end position) or the superior hierarchy (when used in the first
position).
The intent of the template characters is to allow a set of managed topics to exist in a
message system in a way that lets subscribers choose broad subscription parameters that
will include preferred topics and avoid irrelevant topics.
There are some constraints:
● Unlike shell searches, you cannot qualify a selection, such as Alpha.B*.Charlie. You
can use Alpha.*.Charlie. At a content level, a template character precludes using
other template characters.
● The # symbol can only be used once. It is placed in the first node position or in the
last node position. You can use Alpha.#, or *.*.Charlie.#, or #.Beta.Charlie, or just
#, but not #.Beta.#. If use only #, you receive not only messaging traffic, but also
management messages sent between the domain manager and the broker.
● Character replacement, as used in shell searches with the question mark character (?),
is not allowed.
SonicMQ will deliver a message to more than one message handler if the message’s topic
matches bindings from multiple handlers.
The content levels in the topic name space consider the root level ““ as level 0.
Without this ability, you would have to subscribe to both *.U S A and
*.*.U S A to create the same subscriptions.
Note When you use the "#" template character as the leading character in an expression, you
can inadvertently reveal messages in unseen lower levels.
This chapter provides information about using distributed transactions and the XAResource
class. This chapter includes the following sections:
● “About Distributed Transactions”
● “Interfaces for Distributed Transactions”
● “In-doubt Global Transactions”
● “Distributed Transactions Models”
● “Running the Distributed Transaction Sample”
Transaction Types
A single SonicMQ application can contain both local and global transactions.
Local Transaction
A local transaction involves a single resource manager. In SonicMQ, a transaction
performed in a session with the transacted parameter set to true is a local transaction.
Global Transaction
A global transaction involves dispersed resources in the transaction. It is often referred to
as a distributed transaction. A distributed transaction system typically relies on an external
transaction manager to coordinate the participants in a transaction.
Using XA Resources
A SonicMQ application has access to all the required components for distributed
transactions. When a SonicMQ application imports javax.transaction.xa, the
XAResource and XID classes are included so that—together with the XA connection
factories, connections, and sessions in javax.jms—the Java Transaction API is enabled.
A SonicMQ application initiates a transaction by using the JTA to communicate with the
transaction manager. The RM uses the XA protocol to connect to the transaction manager
(TM), as shown in Figure 81.
Resource Transaction
Manager XA Manager
As RMs on diverse threads will participate in the distributed transaction, it is crucial that
the TM establish and maintain the state of the transaction as it evolves. A transaction
context logically envelopes all the operations performed on transactional resources
during the transaction.
An application server can bring additional resources into the transaction, referred to as
resource enlistment. In Figure 82, another RM is participating in xid1 in an XA
connection to the same TM as the owner.
Transaction
Manager
XA XA
Resource Resource
Manager Manager
The transaction context and resource enlistment can extend to other transaction managers,
as shown in Figure 83.
Resource Resource
RM 21 RM 22
Manager 11 Manager 12
XA XA
Transaction
TM 2
Manager 1
TM 3
RM 31 RM 32
Messages sent in the transaction context are not permanently recorded. Messages received
in the transaction context are not acknowledged until the owner ends the transaction
demarcation and passes control to its TM. This TM can coordinate with the other involved
TMs and Resource Managers to prepare and commit the global transaction.
The transaction owner explicitly ends the global transaction. If the transaction involved a
single transaction manager, the act of telling the TM to commit or rollback the transaction
would handle the transaction state on all the participating branches.
A global transaction that involves multiple resource managers needs a two-phase commit
protocol to request that the participants prepare for completion, and, when all are
prepared, perform the commitment. It is possible that a failure during the commitment
process leaves the state of the global transaction indoubt.
Phase 1: Prepare
Ready !
Resource
Phase 2: Commit Manager
Released
Commit Transaction
Application
Manager
Phase 1: Prepare
Ready !
Resource
Phase 2: Commit Manager
Released
In Figure 85, one of the resource managers indicated that its portion of the global
transaction could not be completed. As a result, a global rollback is issued for each branch
of xid1.
Phase 1: Prepare
Ready !
Resource
Phase 2: Rollback Manager
Discarded
Commit Transaction
Application
Manager
Phase 1: Prepare
Not Acceptable
Resource
Phase 2: Rollback Manager
Discarded
javax.transaction.xa Interfaces
The Java Transaction API (JTA) mapping of the industry standard XA interface based on
the X/Open CAE Specification provides, among other features:
● An external transaction manager and sophisticated application capability to
demarcate global transactions
● Assignment of a session to a distributed transaction
● Preparation and commitment of one or more transactions
● Recovery of a transaction in progress
The interfaces in javax.transaction.xa are XAResource, Xid, and XAException. See Sun’s
online JavaDoc at http://java.sun.com/products/jta/javadocs-1.0.1/index.html for
the exposed fields, methods and constructors in these interfaces.
XAConnectionFactory
XASonicMQ exposes its JTS support through the JMS XAConnectionFactory which
distributed transactions—and possibly application servers—use to create XASessions.
An XAConnectionFactory object is an administered object similar to a ConnectionFactory.
See “Connection Factories and Connections” on page 141 for information about lookup
as a serialized object in a file store or on a JNDI LDAP server.
XAConnection
An XAConnection is similar to a Connection except that is by definition transacted. You can
choose a default or a specified user identity.
XAConnectionFactory xacf =
new progress.message.jclient.xa.XAConnectionFactory (url, “myConID”, … ) ;
XAConection xac = xacf.createXAConnection();
Connection c = xac;
XASession
The XASession extends Session with access to SonicMQ’s support for the Java
Transaction API (JTA) by creating a javax.transaction.xa.XAResource object as defined
by the X/Open XA Resource standard.
The XAResource assigns the session to a transaction then prepares and commits (or rolls
back) the transaction. What appears as a regular JMS Session is actually controlled by the
transaction management of the underlying XASession.
Phase 1: Prepare
Ready !
Resource
Phase 2: Commit Manager
Released
Commit Transaction
Application
Manager
Phase 1: Prepare
Ready !
Resource
Manager
Phase 2: Commit
X
Figure 87. In-doubt Transaction During Two-Phase Commit
In this case, one branch of the global transaction might have been committed while the
other branch did not.
Transaction Recovery
If the transaction owner becomes unable to continue, a transaction manager can invoke a
recover method to a resource manager to determine the identity (xidnnn) and status of
transaction branches.
The TMSTARTRSCAN, TMENDRSCAN and TMNOFLAGS flags are supported in the implementation
of the XAResouce.recover(int flag) method in compliance with the XA specification. A
given recovery scan must be made by the same XAResource instance.
The following examples illustrate how these flags return results from the recover method:
Example 1: TMNOFLAGS
When recover(TMNOFLAGS) is called without having called recover(TMSTARTRSCAN), all in-
doubt transaction Xids are retrieved. This is the preferred technique for recovery.
The recovery scan state after recover(TMNOFLAGS) is called is unchanged.
The EJB might choose to use a container-managed transaction which means that
everything is managed by the application server.
Application Client
(EJB, Messaging...)
Application Server
Transaction Manager
SonicMQ Client
SonicMQ
JMS XA SPI XA Resource Broker
JMS Application
Transaction Manager
SonicMQ Client
SonicMQ
JMS XA SPI XA Resource Broker
...
try
{
tm.begin();
Transaction txn = tm.getTransaction();
The transaction manager is instructed to provide the XAResources for the session:
XAConnectionFactory xacf =
new progress.message.jclient.xa.XAConnectionFactory( url );
XAConnection xtc = xacf.createXAConnection( user, password );
Connection c = xtc;
XASession xas = xtc.createXASession();
XAResource xar = xas.getXAResource();
txn.enlistResource(xar);
...
pub.send(msg1);
sender.send(msgA);
...
txn.delistResource(xar, XAResource.TMSUCCESS);
} catch( Exception e )
{
tm.rollback();
}
...
try
{
tm.commit();
} catch( Exception e )
{
e.printStackTrace();
}
At the application level, the transaction work is done. The transaction manager handles
the preparation of the branches and the commit or rollback. If the prepares fail, the
exception is thrown. If the commit succeeds, a return indicates successful commitment. If
there are indoubt branches, the TM can work on the commit unless the TM or resource
manager crashes. A TM recovery will resume the transaction work.
JMS Application
SonicMQ Client
SonicMQ
JMS XA SPI XA Resource Broker
This constrains the application from the benefits of distributed transaction branches yet,
for cases where two sessions, one PTP and one Pub/Sub, need to coordinate in a
transaction, this model might be appropriate.
...
XAConnectionFactory xaqcf =
new progress.message.jclient.xa.XAConnectionFactory ( url);
XAConnection xaqc = xaqcf.createXAConnection(user, password);
Connection qc = xaqc;
An XASession is created, which then gets the XAResource. The methods of the XAResource
are now accessible. A regular Session is created for the standard procedures of creating a
queue and a sender in the context of the XASession:
...
XAConnectionFactory xatcf =
new progress.message.jclient.xa.XAConnectionFactory( url );
XAConnection xatc = xatcf.createXAConnection( user, password );
Connection tc = xatc;
Produce Messages
The transaction and its branches are distinguished in the sample code through the xid—
an object that the application must create—whose first digit indicates the transaction
identity and second digit represents the branch qualifier. The XAResource for both
branches is started, messages are sent in both sessions, and then the XAResource calls the
same identifiers to end the transaction branches:
...
try
{
xarq.start(xid11, XAResource.TMNOFLAGS);
xart.start(xid12, XAResource.TMNOFLAGS);
...
pub.send(msg1);
sender.send(msgA);
...
xarq.end(xid11, XAResource.TMSUCCESS);
xart.end(xid12, XAResource.TMSUCCESS);
} catch( JMSException e ) {
xarq.rollback(xid11);
xart.rollback(xid12);
}
...
try
{
xarq.prepare(xid11);
xart.prepare(xid12);
xarq.commit(xid11, false);
xart.commit(xid12, false);
} catch( JMSException e )
{
xarq.rollback(xid11);
xart.rollback(xid12);
}
If the first commit succeeded but the second failed, an in-doubt transaction would exist.
Be sure the SonicMQ container is running before executing any of the SonicMQ samples.
See “Starting the SonicMQ Container and Management Console” on page 70 for
instructions about starting SonicMQ. For more detailed information on working with the
Sonic Management Console, see the Progress SonicMQ Configuration and Management
Guide
This appendix describes programming using the Sonic service provider implementation
(SPI) for the Java Naming and Directory Interface (JNDI). It contains the following
sections:
● “Overview of the JNDI SPI”
● “Sonic JNDI SPI Samples”
Note The JNDI SPI and the environment properties do not have accompanying JavaDoc.
Table 44 lists describes the values that should be assigned to the standard JNDI
environment when using the Sonic SPI.
Table 44. Standard Environment Properties for the Sonic JNDI SPI
Table 45 lists custom environment properties you can use with the Sonic JNDI SPI:
Table 45. Custom Environment Properties for Sonic JNDI SPI
Table 45. Custom Environment Properties for Sonic JNDI SPI (continued)
The Sonic JNDI SPI sample applications provide examples of Java and JavaScript
applications that use the SPI. The following sections discuss these sample applications.
All samples directories have a readme.txt file that describes the contents more
completely. Where appropriate, the readme.txt file contains instructions for running more
complex samples.
Important To execute the CLI sample you must already have installed the SonicMQ container.
4. In the CLI shell you can get command help by typing a command followed by a
question mark (?) character. For example:
> create context ?
You can navigate around subcontext trees by moving a single subcontext at a time using
the command change context (or cc).
Each script assumes the installation default values were selected for domain name,
container name, broker port, etc. If you made non-default selections during installation,
you must modify the scripts appropriately.
Important To execute the JavaScript samples you must already have installed the SonicMQ
container.
A SSL
client certificates 134
access control lists 35, 38 username and password 132
applied to multitopics 357 authorization
propagation of changed permissions 216 consumer 38
ACID properties of a transaction 452 in samples 70
acknowledgement mode 206, 210 producer 35
AUTO_ACKNOWLEDGE 207 AUTO_ACKNOWLEDGE 207
CLIENT_ACKNOWLEDGE 207
DUPS_OK_ACKNOWLEDGE 207
lazy 207
SINGLE_MESSAGE_ACKNOWLEDGE 20
B
7, 310 bridges 40
active ping 160 broker properties
administered objects PREFERRED_ACTIVE 185
ConnectionFactories 141 broker storage
definition 150 NON_PERSISTENT_REPLICATED
destinations 217 messages 190
readFile 157 brokers
administrative notification 222, 364 failure 161
ANSI C 41 management
APIs destination parameters 278
JNDI 69, 475 topic hierarchies 441
SonicStream 416 starting in UNIX 46, 72, 73
applet 41 starting in Windows 45, 71
application browsing queues 302
server 228, 453 sample 88
application/x-sonicmq-* 249 BSAFE-J SSL 132
asynchronous 279, 299 BytesMessage type 235
authentication
consumer 38
in samples 70
producer 35
E H
encryption 34
per message 260, 276 header message 392
enumeration for queue browsing 302 headers
events default header field values 258
flow control 222 message 255
notify undelivered 304 SOAP 253
expiration 257, 277, 279, 306 hierarchical name spaces 440
QoS level 37 as message filters 324
expired message 364, 365 HierarchicalChat sample application 120
extended type 247, 260 hostname 143
HTTP Direct
multitopic 360
HTTP tunneling 137
HTTPS 137
I L
identifier large message support 86, 392
client 144 latency 271
connection 143 lazy acknowledgement 207
indoubt listeners 280
messages 385 channel 405
transaction state 456 message 299
indoubt message 365 rejection 164
initial connect timeout 147, 172 SonicStream
instanceof operator 85 exception 420
notification 420
local store 162, 165
J LocalStore sample application (PTP) 114
J2EE 27, 230 LocalStore sample application (Pub/Sub) 114
Java 27 log
applet 41 local store 162, 392
client 40 loop test 122
JRE 27
JVM 27
Transaction API 457 M
Transaction Service 453 management APIs
JAXP 237 JNDI SPI 69, 475
JMS provider 29 MapMessage
JMS_SonicMQ message properties 369 enhancing the sample 124
JMS_SonicMQ_ExtendedType 247 sample application 78, 124
JMSX properties 262 type 235
JMSXDeliveryCount 208, 262 MaxDeliveryCount 383
JMSXUserID 262 message
JNDI body
lookup of destinations 217 setting and getting 267
lookup of topics 272, 321
JNDI SPI 69, 475 Text 128
JNDI SPI sample applications 479 XML (DOM format) 127
JRE, installed 27 dead 364
JVM 27 delivery
PTP 298
expired 364, 365
file fragments 395
indoubt 365
JMS_SonicMQ properties 369
large 392
NON_PERSISTENT 364
ordering 270
PTP 298
Pub/Sub 320
T transactions
context 454
Talk sample application 75 distributed 212
TCP 132 effect of
TCP_RESET 161 NON_PERSISTENT_REPLICATED 190
technical support 23 global 452
template characters 442, 445 local 452
topics 272, 321 transaction manager 453
temporary destination 115, 291 TTL
TextMessage type 236 See time to live
threads 263 two-phase commit 456
threshold, prefetch 301 type 256, 275
time to live 374
timeout
client reconnect 172 U
connection retries 165
in client persistence 164 undeliverable message 364
in transacted sessions 211 undelivered
initial connect 147, 172 notify 39, 102, 260
on a file channel sender 394 preserve 39, 102, 260
on a receiver 280 reason codes 260, 307, 383
on receiveNoWait 280, 300 timestamp 260
on synchronous receive 300 undelivered destination
socket connect 147, 172 message properties 378
timestamp 255 undelivered message 365
undelivered 260 handling 371
time-to-live override
default value 258 destination name 376
DurableChat sample 111 destination type 376
message property 260 types 373
on the broker 279 undelivered message reason codes 383
publish parameter 323 UNDELIVERED_DELIVERY_LIMIT_EXCEE
TopicRequestor 292 DED 208, 383
topics unfinished channel 406
common in samples 123 unroutable message 365, 365
definition 321 unsubscribe 326
hierarchical name spaces 272, 321 URL 143
hierarchy 440 username 144
transacted sessions UUID 212
definition 210
session parameter 206
TransactedChat sample application 93
TransactedTalk sample application 92
V
valueOf method 266
W
wildcards 121
window size 395
X
XA sample application 471
XAResource 453
X-HTTP-* properties 263
XID 453
XML message
create method 219
enhanced sample 126
sample application 79
XML parser 80
XMLMessage sample application (PTP) 80
XMLMessage sample application
(Pub/Sub) 126
XMLMessage type 236, 237
XMLSAXChat sample application 82
XMLSAXTalk sample application 81
XMLTalk sample application 79