The purpose of The Message Bus is to allow components to be
loosely coupled, by enabling
them to send and receive messages (or events) without knowing what
components the messages are intended for or who sent them.
Loose coupling can be seen within Swing using the
event listener pattern (also known as the Observer/Observable and
Publisher/Subscriber patterns). For example, a JTable does not have to know who is
listening to its events, only that there exists one or more components
that wish to know when an event occurs.
Unfortunately, Swing doesn't allow for a level of loose
coupling many prefer. In reality, if a component nested 3 levels
deep offers events that it broadcasts, the interested observer
must know how to navigate to that component and add itself as a
listener. Otherwise, any interested listeners of the component
must register at the time of instantiation to ensure that a
handle to the component is available.
Consider a Swing panel composed using the
|- Tabbed Pane
|- Tab 1 Panel
|- Split Panel
|- Left Tree
|- Right Tree
Notice the level of depth that is required to navigate to the
either of the tree components. If anyone wants to attach as a
listener to this tree after its initial construction, it must
know how to navigate to the specific tree via the Swing APIs
or via an application-specific API. Not as loosely coupled as
people tend to think.
The Bus API solves this problem by enabling each tree
component to broadcast messages/events on a specific channel
with a specific base message class. Any interested component can
subscribe to the tree message via The Bus, without knowing where the
component lives, or even that it's a tree!
[Consider a better example here, or a few examples to show not only this problem, but
the growth of listeners as things get larger]
Note: To view the complete API of the Message Bus, refer to the Javadoc page for the Message Bus
The Message Bus can be considered a routing hub, which accepts incoming messages and
routes them to all interested subscribers. To reduce the amount of overall messages and enable an
application to partition its messages for greater flexibility, the concept of channels is
used. A message may be published to a channel, where all subscribers to that particular
message type within that channel will receive a notification of the event. Although most
components will communicate over the 'default' channel, other channels may be created using
either a configuration file or via a JMX interface (future).
For each channel, a message dispatching strategy may be defined, called a
Dispatcher. Currently, 2 dispatchers are provided:
SyncDispatcher and AsyncDispatcher. The default
dispatcher for a channel is the SyncDispatcher, unless specified in the configuration resource
The following examples demonstrate the typical usage of the Message Bus:
Example 1: A Basic Pub/Sub
This is a pretty simple example, where we see 2 components - 1 subscriber and 1
publisher - exchange a message.
Custom Channel Configuration
Channels are created implicitly whenever a component subscribes to a channel that
doesn't exist. However, the default settings for a channel may not be the settings desired for
the channel. The Message Bus looks for a resource in its CLASSPATH named messagebus-config.xml
and uses this to preconfigure channels. New channels may still be created implicity, but this
allows the message dispatcher to be declared on a channel-by-channel basis.
The following is an example of a custom configuration:
Additional options are available from within the configuration file, including passing
parameters to the dispatchers. Refer to the XSD for the configuration file (found in
main/src/spi/resources) for the complete schema. Also, refer to each dispatcher's
documentation for supported parameters.