Skip to content

Introduction to Message Subscriber Listener Notification

thofrey edited this page Apr 1, 2014 · 4 revisions

Table of Contents

  1. Overview
  2. Using Message / Subscriber Listener Notification
  3. Attributes Explained
  4. Advanced Techniques
  5. Additional Information and Considerations

Overview

Mach-II 1.6 allows multiple listener notifications via a single publish command. Any listeners that have a registered interest in a particular published message will receive the message which allows you to invoke multiple listener methods by publishing a single message. Message / subscriber listener notification provides more of a decoupled notification semantic over using the older notify command which is more of a direct listener notification semantic.

Publishing messages decouples listeners from the event handlers whereas using the notify command couples listeners to event handlers. The "publish" layer of abstraction would allow you to register an audit / logging layer by merely adding it as a subscriber to the published message instead of adding a notify to each and every event handler. Also, message publishing lets you easily swap listeners by changing the messages it is subscribed to and saves you from editing event handlers (and possibly introducing new errors). As a few people have pointed out in past discussions, these two reasons are a bigger maintenance issue than most developers realize.

This enhancement was developed under the Mach-II specification and feedback process (M2SFP).

Using Message / Subscriber Listener Notification

Traditionally in Mach-II event handlers, listeners have been notified using the <notify> command. For example, if in an event handler myListener needs to execute myMethod and store the results in myEventArg, the following code would be used:

    <notify listener="myListener" method="myMethod" resultArg="myEventArg" />

This is a convenient and direct way of doing things, and allows developers to see all of the listener notifications and method invocations by looking at the event handler code in the mach-ii.xml configuration file.

It is not uncommon, however, to notify numerous listeners during the course of an event, and in some cases these multiple listener notifications may be used on several events, potentially leading to redundant code between event handlers. Furthermore, some developers prefer to use a more decoupled approach to listener notification, in which a single message is broadcast as part of an event handler, and multiple listeners that have registered an interest in this message may all execute methods based on this single message broadcast.

To address this situation, Mach-II 1.6 introduces a new publish/subscribe method of listener notification, which allows a single message to be broadcast as part of an event handler, and this message broadcast in turn triggers multiple listener notifications. Note that the previous method of using a <notify> command for each listener notification is not being deprecated; the new publish/subscribe functionality is simply an additional option for listener notification.

To use this new method of notifying listeners, the event handler includes the new <publish> command:

    <event-handler event="myEvent" access="public">
        <publish message="needMyStuff"/>
    </event-handler>

Listeners interested in the needMyStuff message are registered in a new <message-subscribers> section of the mach-ii.xml configuration file, and it is here that the methods to be invoked based on the message publication are declared:

    <message-subscribers>
        <message name="needMyStuff" multithreaded="true" waitForThreads="true" timeout="60">
            <subscribe listener="listener1" method="method1" resultArg="stuff1"/>
            <subscribe listener="listener2" method="method2" resultArg="stuff2"/>
        </message>
    </message-subscribers>

With this code in place in the mach-ii.xml configuration file, when myEvent is announced, the message needMyStuff is published, and the subscribers to this message, which in this case are listener1 and listener2, invoke the appropriate methods and place the results to these method calls in the event object via the resultArg attribute of the subscribe command.

Mach-II Simplicity (1.8) supports subscribers of the bean type. Allows for integration with IoC containers such as the ColdSpringProperty to wire in a bean. Supported in Mach-II Simplicity (1.8) or higher. It also supports nested <arg> tags for bean subscribers to alleviate the problem of the args attribute being a mile long with configuration. This example shows using named arguments in which the <arg> name must match up the cfargument name in the target:

    <message-subscribers>
        <message name="needMyStuff" multithreaded="true" waitForThreads="true" timeout="60">
            <subscribe listener="listener1" method="method1" resultArg="stuff1"/>
            <subscribe bean="fantasyTeamService" method="getFantasyTeam" resultArg="fantasyTeam">
                <arg name="fantasyteam_id" value="${event.id}"/>
                <arg name="type" value="${event.foobar}"/>
            </subscribe>
        </message>
    </message-subscribers>

Also, subscribers of the bean type support nested <arg> tags using positional arguments as well:

    <message-subscribers>
        <message name="needMyStuff" multithreaded="true" waitForThreads="true" timeout="60">
            <subscribe listener="listener1" method="method1" resultArg="stuff1"/>
            <subscribe bean="fantasyTeamService" method="getFantasyTeam" resultArg="fantasyTeam">
                <arg value="${event.id}"/>
                <arg value="${event.foobar}"/>
            </subscribe>
        </message>
    </message-subscribers>

If you use nested arg tags when you subscribe a bean, you must either choose name/value pairs or positional arguments. A combination of both is not supported. Subscribe with bean works exactly like the call-method command available in event-handlers which was introduced in Mach-II Simplicity (1.8). Read our M2SFP on call-method for more information.

In addition to the convenience and decoupled aspect of this style of listener notification, by using publish/subscribe the calls to the listeners may be multithreaded by use of the multithreaded attribute in the message declaration. This functionality leverages the new <cfthread> tag in Adobe ColdFusion 8. Due to the differences in implementation of <cfthread> among the different CFML engines, the multithreaded attribute of message declaration is currently ignored in other CFML engines. We are targeting to add concrete adapters for Railo and OpenBD and in Mach-II 1.9 once those CFML engines support threading in a manner we can leverage.

See Using the Threading Adapter for more information on the threading package which is used by the message / subscriber listener notification feature.

By multithreading listener notifications, each notification within the message block will be executed simultaneously in an independent thread, which typically results in improved performance. The waitForThreads attribute indicates that all threads within the message block must complete before the execution of the event handler resumes. If one of the listener notifications is a background process, such as starting a lengthy report generation process, performing logging, etc. then waiting for all the threads to complete may not be necessary.

Attributes Explained

<message>

Name Type Required Default Description
name string true n/a Name of the message that can listen for itself by the <publish> command.
multithreaded boolean false true Whether or not subscribed listeners are run in a multi-threaded nature. Defaults to true.
waitForThreads boolean false true Whether or not we should wait for all subscribes to finish before returned any remaining commands in an event or subroutine. Defaults to true.
timeout numeric false 0 The amount of time in seconds to wait for all subscribers to complete processing. Zero (0) means to wait indefinitely (or unless the request timeout is exceeded).

<subscribe>

Name Type Required Default Description
listener string false (or bean) n/a Name of the listener to subscribe. Must be a registered listener in the <listeners> node.
bean string false (or listener) n/a The bean name to use. Allows for integration with IoC containers such as the ColdSpringProperty to wire in a bean. Supported in Mach-II Simplicity (1.8) or higher.
method string true n/a Name of the listener method to invoke when a message is published and a subscribed listener is called.
resultArg string false n/a Name of the event-arg to place returned data from the listener method invocation.

The listener or bean attributes cannot be used in conjunction with each other. You must use one or the other when you subscribe a listener or bean to a published message.

Advanced Techniques

These advanced techniques will show how to notify a message-subscriber programmatically within a listener, filter, or plugin.

Listener

In the listener, the AppManager is going to be the API access point for accessing the MessageHandler and EventContext objects. By design, we don't make the EventContext available in listeners (I'll leave out the laundry list of reasons) but the message handler needs it in this case. The MessageHandler is used to retrieve a specific Message object. The EventContext object is needed to pass as an argument in in the message handler. The MessageHandler.handleMessage() function returns a boolean if it was able to successfully broadcast all the messages

    <cfset var messageHandler = getAppManager().getMessageManager().getMessageHandler("nameOfMessage") />
    <cfset var eventContext = getAppManager().getRequestManager().getRequestHandler().getEventContext() />
    <cfset var result = messageHandler.handleMessage(arguments.event, eventContext) />

Filter

In the filter, we still need to use the AppManager object to access the MessageHandler, but the EventContext is already available as an argument so there is not need to retrieve it through the API as in the previous example.

    <cfset var messageHandler = getAppManager().getMessageManager().getMessageHandler("nameOfMessage") />
    <cfset var result = messageHandler.handleMessage(arguments.event, arguments.eventContext) />

Plugin

The EventContext is available as an argument to all of the plugin points, so the code to access the MessageHandler is going to be the same as with the Filter.

    <cfset var messageHandler = getAppManager().getMessageManager().getMessageHandler("nameOfMessage") />
    <cfset var result = messageHandler.handleMessage(arguments.event, arguments.eventContext) />

Additional Information and Considerations

  • Favor using "need" instead of "get" for message names. This is because the messages are indirect and thus "shout out" - "I need XZY" instead of "getting stuff directly".
  • waitForThreads and timeout attributes of the <message> tag are ignored if multithreaded is false.
  • All listeners that subscribe to a single message have access to the same event object.
  • Subscribed listeners are called in a "random" order" and are not invoked in the order they are defined due to the asynchronous nature of multithreaded code.
Clone this wiki locally