Chapter 11. Flamingo Messaging

Server Push is a mechanism for the client side application notifying about changes on the server side.

11.1. Real-world Scenarios

This technology was first applied when developing system for automation of loan enquiries origination.

Business logic:

Some of loan enquiries can be processed automatically according stated rules. If loan enquiry can't be processed automatically, Credit manager should get notifications via his UI. Credit manager needed to make a decision about enquiry through WEB/JavaFX interface. Each loan enquiry should be processed no more than 5 minutes. If enquiry is processed more than 5 minutes, this enquiry needed to be canceled.

Another good example of this technology is the system for monitoring IT infrastructure state.

Conditions for this system are: Number of servers for monitoring is able to change in time. Each server has own period of monitoring.

If we create classic web-application for monitoring servers' state, we need to update in predefined intervals the whole page for updating diagrams, labels, etc. In this case information about all servers will be requested, and a great amount of data should be sent from the server to a client. When many clients use this application, server-side can simply hang up. The following mechanism can be used as a solution to this problem: a client-side application is notified about changes on the server side. Implementation of this mechanism is well known as Server Push.

Please read the "Push technology" article on Wikipedia to get more information about this technology.

11.2. Server-Side Push

Flamingo server-side Push mechanism employs the following components:

  • ClientSideBroker provides asynchronous communication with server and subscribing mechanism for a client-side application.

  • Flamingo Listener is a part of the subscribing mechanism.

  • Transport Provider is a subsystem, which is responsible for maintaining and restoring internal objects of Flamingo. The simplest provider is serialization mechanism. Three providers are implemented: a) with the help of serialization, b) based on ProtocolBuffers, c) based on Hessian protocol

  • Polling Servlet receives requests from ClientSideBroker and provides connection with JMS server, converting and serialization of Flamingo Message (by Transport Provider).

  • JMS Server provides mechanism for saving, delivering and filtering messages on the server side.

Server Push implementation in Flamingo

Figure 11.1. Server Push implementation in Flamingo


Minimal package that circulates between server and client side is Flamingo Message. It has a special structure (set of properties):

  • Destination channel

  • Time stamp

  • Expiration time

  • Properties (like JMS Message properties)

  • Application level object

Flamingo Push mechanism is illustrated on the diagram below:

Flamingo Push Life Cycle

Figure 11.2. Flamingo Push Life Cycle


Let us explain the life cycle of Flamingo server push implementation in more detail. As you can see from the figure the life cycle has several phase which are as follows:

  • Client application using the Flamingo components is connected to the Flamingo Polling Servlet (also provided by Flamingo)

  • Servlet opens a connection to JMS

  • Connection starts receiving messages, strips the Flamingo message from JMS, saves message using TransportProvider and sends message back to client

  • When receive() times out the servlet aborts connection with client

  • Client with the use of the destination attribute from Flamingo message delivers the payload to subscribers.

  • Client waits a while (configurable, depends on the application, or provided by client dynamically), connects back to check for more messages

11.2.1. Details of Usage

A common component for both client and server side is Flamingo message.

Data transferred between the client and the server has special a format - Flamingo Message. The object has the following properties:

  • Destination Channel

  • Time stamp

  • Expiration time

  • Properties (like JMS Message properties)

  • Application level object

In Java the Flamingo Message is defined like this:


...
  package com.exadel.flamingo.push;
......
public interface FlamingoMessage extends Serializable {
    Object getPayloadData() throws FlamingoException;
    Long getTimestamp() throws FlamingoException;
    Long getExpiration() throws FlamingoException;
    Map<String, Object> getProperties();
    FlamingoClient    getClient() throws FlamingoException;
    FlamingoDestination getDestination() throws FlamingoException;
    boolean propertyExists(String name) throws FlamingoException;
    <Type> Type getProperty(String name) throws FlamingoException;
    Enumeration<String> getPropertyNames() throws FlamingoException;
    void clearProperties() throws FlamingoException;
}
...

When sending information from the server side to the client side (or backwards), a message needs to be created, all required attributes should be set with appropriate values and the payload information should be passed. This process is shown in the example bellow.


...
FlamingoObjectsFactory objectsFactory = FlamingoObjectsFactory.getFactory();
EditableFlamingoMessage message = objectsFactory.createEditableMessage();
message.setDestination(objectsFactory.createDestination(destination));
message.setPayloadData(data);
...

11.2.2. Server-Side Configuration

For adding Flamingo Server Push capability to our application you should make sure the application has all necessary files (and possibly their dependencies), and settings. For the server side we need the following files:

  • flamingo-push-common.jar

  • flamingo-push-server-common.jar

  • flamingo-services-common.jar

  • hessian.jar

  • One of the flamingo-push-server-spring/seam files (depending on the type of application)

  • One of the transport provider files (for example flamingo-push-transport-protobuf.jar)

For the client side we need the following files:

  • flamingo-push-common.jar

  • flamingo-services-common.jar

  • hessian.jar and one of the transport provider files

Additionally you need to ensure the availability of libraries to support your type of client interface, for example for JavaFX you will need the following libraries:

  • flamingo-java-client.jar

  • flamingo-javafx.jar

  • flamingo-push-client-javafx.jar

Note:

You must have a compatible, but for some providers, such as "Serialization Transport Provider" identical versions of libraries. You must not use different types of providers on the client and on the server (for example the client has flamingo-push-transport-protobuf.jar, but the server has flamingo-push-transport-hessian.jar). To avoid possible errors it is important to ensure that there is only one transportation library in the classpath as a mechanism to support providers in finding more than one library may choose at any point in time any of them. Thus we can get a mismatch transmitted between the client and server data.

Configuration of the server side requires three small steps:

  1. Register Flamingo Poll Servlet and define a mapping for it in web.xml (example for Seam, example for Spring)

    
    ...
    <servlet>
        <servlet-name>Flamingo Poll Servlet</servlet-name>
        <servlet-class>com.exadel.flamingo.push.seam.SeamPollServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Flamingo Poll Servlet</servlet-name>
        <url-pattern>/poll/*</url-pattern>
    </servlet-mapping>
    ...

    The configuration performed in the first step for Seam and Spring projects are very similar: the difference only in the names of classes support.

  2. Configure the association between Flamingo-destination and JMS consumer/producer

    Description of the association does not depend on the type of application and can be set using the Flamingo Push API or from an external file, for example:

    
    ...
    destination = FlamingoObjectsFactory.getFactory().createDestination(test);
    <instance of FlamingoDestinationTranslator>.add(destination, producer, consumer, selector);
    ...

    Otherwise you can set up the assosiation parameters in translator.xml like this:

    
    ...
    <?xml version="1.0" encoding="UTF-8"?>
    <transformations  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
       xmlns='http://flamingo.exadel.com/schema/translator'
       xsi:schemaLocation='http://flamingo.exadel.com/schema/translator /translator.xsd'>
        <transformation>
            <flamingo>
                <destination>*</destination>
            </flamingo> 
            <jms>
                <destination>/queue/testQueue</destination>
            </jms>
        </transformation>
        <transformation>
            <flamingo>
                <destination>dev01.exadel.com</destination>
            </flamingo>
            <jms>
                <destination>/queue/C</destination>
            </jms>
        </transformation>
        <transformation>
            <flamingo>
                <destination>db.exadel.com</destination>
            </flamingo>
            <jms>
                <producer>
                    <destination>/queue/A</destination>
                </producer>
                <consumer>
                    <destination>/queue/A</destination>
                    <selector>cpuWarning=true</selector>
                </consumer>
            </jms>
        </transformation>
        <transformation>
            <flamingo>
                <destination>localhost</destination>
            </flamingo>
            <jms>
                <producer>
                    <destination>/queue/B</destination>
                </producer>
                <consumer>
                    <destination>/queue/B</destination>
                </consumer>
            </jms>
        </transformation>
    </transformations>
    ...

    The listing above needs to be clarified. The subsystem configuration provides a special parameter for Flamingo destination "*" (asterisk), it means that any, clearly not specified destination. For instance, for the configuration described above, if the servlet receives a request to check the messages for the destination “localhost23”, then the servlet will check the JMS queue “/queue/testQueue”, since it is listed as a point of reading by default. You can configure a separate producer and consumer (transformation item 4) for JMS messages, or specify them together (transformation item 2). You can also optionally specify a selector for the receiver of the message, if you need some way to distinguish between a message in a single JMS flow, then creation of a separate FlamingoDestination for this goal is impossible (transformation item 3).

  3. Configure the instances of Flamingo component (this step depends on the type of server application and is described in corresponding section)

11.2.3. Server-Side Push in Seam Environment

In this section you will know how to set up server-side push in a Seam application.

11.2.3.1. Registering Flamingo Poll Servlet

To register Flamingo Poll Servlet in your Seam application you should add Flamingo Poll Servlet and define a mapping in web.xml as shown below:


...
<servlet>
<servlet-name>Flamingo Poll Servlet</servlet-name>
<servlet-class>com.exadel.flamingo.push.seam.SeamPollServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Flamingo Poll Servlet</servlet-name>
<url-pattern>/poll/*</url-pattern>
</servlet-mapping>
...

11.2.3.2. Configuring the Instances of Flamingo Component

To configure the Flamingo Push you can use standard mechanisms that used in a Seam applications. For example, the configuration of association between FlamingoDestination and JMS is set up in an external file ( for details see here) and configuring a JMS connection factory could be done as follows:


...
<?xml version="1.0" encoding="UTF-8"?>
<components xsi:schemaLocation="http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd                  http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.1.xsd                  http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.1.xsd                  http://jboss.com/products/seam/bpm http://jboss.com/products/seam/bpm-2.1.xsd                  http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.1.xsd                  http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.1.xsd                  http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd">
<component name="com.exadel.flamingo.push.server.translator" auto-create="true">
<property name="transformationFileURL">/translator.xml</property>
</component>
<component name="com.exadel.flamingo.push.server.configuration">
<property name="JMSConnectionFactoryName">ConnectionFactory</property>
</component>
</components>
...

For more information about the configuration of Seam-applications can be obtained from the examples of the use (examples/push/web-seam) or in the http://docs.jboss.org/seam/2.1.1.GA/reference/en-US/html/xml.html chapter of the Seam - Contextual Components guide.)

11.2.4. Server-Side Push in Spring Environment

In this section you will know how to set up server-side push in a Spring application.

11.2.4.1. Registering Flamingo Poll Servlet

To register Flamingo Poll Servlet in your Spring application you should add Flamingo Poll Servlet and define a mapping in web.xml as shown bellow:


...
<servlet>
<servlet-name>Flamingo Poll Servlet</servlet-name>
<servlet-class>com.exadel.flamingo.push.spring.SpringPollServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Flamingo Poll Servlet</servlet-name>
<url-pattern>/poll/*</url-pattern>
</servlet-mapping>
...

11.2.4.2. Configuring the Instances of Flamingo Component

You need to change the default configuration, which is adopted for the Flamingo Push, you should register a listener to work with web-context and to specify the configuration file for the application in web.xml file. It may look like this:


...
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
...

Next step is to configure the application and specify the necessary data for the components and their relationships. For example it can be done as follows:


...
<beans xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="com.exadel.flamingo.push">
<context:exclude-filter type="assignable" expression="com.exadel.flamingo.push.spring.SpringFlamingoPushServerConfiguration"/>
<context:exclude-filter type="assignable" expression="com.exadel.flamingo.push.spring.SpringFlamingoDestinationTranslator"/>
</context:component-scan>
<bean name="com.exadel.flamingo.push.server.configuration" class="com.exadel.flamingo.push.spring.SpringFlamingoPushServerConfiguration">
<property name="JMSConnectionFactoryName" value="ConnectionFactory"/>
</bean>
<bean name="com.exadel.flamingo.push.server.translator" class="com.exadel.flamingo.push.spring.SpringFlamingoDestinationTranslator">
<property name="transformationFileURL" value="/translator.xml"/>
</bean>
</beans>
...

This example describes the configuration of the association mechanism between FlamingoDestination and JMS, defined in an external file ( for details see here) and how to configure JMS connection factory.

Note:

You must explicitly exclude components that you configure in the file, otherwise you will get an error when deploying applications on the availability in the application context with two implementations of one component (context:exclude-filter in the example above).

For more information about the configuration of Spring applications can be obtained from the examples of the use in (examples/push/web-spring) or in The Spring Framework - Reference Documentation)

11.3. Client-Side Messaging

This chapter tell how to implement server push using client technologies.

Starting from the 2.1 version the Flamingo framework supports the clients written in Java (support for JavaFX is extending support for Java), so all the possibilities that are described in the documentation for JavaFX client can be used in pure Java client (excluding the names of classes, JavaFX implementation often contains name prefix FX, which is absent in the Java-implementation).

11.3.1. JavaFX

In this section you will know how to use Flamingo messaging with JavaFX.

  • First you should define a handler to implement the logic related to Server Push. We suggest to extend the FlamingoListener class this way:

    
    ...
    class Listener extends FlamingoListener {
        override function onMessage(message: FlamingoMessage) {
        }
        override function onException(exception: FlamingoException) {
            Alert.inform(exception.getLocalizedMessage());
        }
    }
    ...

    Table 11.1. Methods

    MethodDescription
    onMessage(message: FlamingoMessage) Method is called any time when Flamingo message with appropriate destination is received from server
    onException(exception: FlamingoException) Method is called when an error occurs

  • The next step when handler is already defined is to instantiate FXClientSideBroker and assign listeners to it.

    The FXClientSideBroker class provides capabilities to communicate with the server side using Flamingo Messaging. Server-side Flamingo Messaging configuration is accessible on cilent-side - it allows easy to reuse same destination configuration in same purposes. If you want to send a FlamingoMessage from the client side application to a specified destination point you should use the FXClientSideBroker.send(message: FlamingoMessage) method.

    
    ...
    var BROKER: FXClientSideBroker;
    def listener = Listener {
    };
    var destination = FlamingoObjectsFactory.getFactory().createDestination( test );
    ...
    BROKER = FXClientSideBroker {
        pollingServletURL:  http://localhost:8080/push-sample/poll
        messagesPollPeriod: 2s;
    }
    ...
    BROKER.subscribe(destination, listener);
    ...
        def objectsFactory = FlamingoObjectsFactory.getFactory();
        var message = objectsFactory.createEditableMessage();
        message.setPayloadData("Some test");
        message.setDestination(objectsFactory.createDestination(DESTINATION_NAME));
        BROKER.send(message);
    ...
    ...

    Table 11.2. Parameters

    ParameterDescription
    pollingServletURLthe address of polling servlet. This URL should be defined according to server side configuration in web.xml. (For example: "http://localhost:8080/push-sample/poll")
    messagesPollPeriodTime interval for checking the new messages on the server side

11.3.1.1. Offline Messaging

Offline Messaging is a feature that enables you to save messages after connection between Client-Side JavaFX application and Server-side is lost. Stored messages will be sent to Server-Side after connection establishment. For this purpose Flamingo Messaging uses API provided by MessageStorage interface. Flamingo implements this interface in the FXDefaultMessageStorage class which is based on JavaFX Storage API. Thus, all saved messages will be accessible in your application after restart.

If you need to get access to the stored messages inside storage you should create an instance of FlamingoMessage class.


... 
    def objectsFactory = FlamingoObjectsFactory.getFactory();
    def messagesStorage = FXDefaultMessageStorage {}
        if (messagesStorage.isEmpty()) {
        ....
        var message = objectsFactory.createEditableMessage();
        messagesStorage.save(message);
        ...
    } else {
        // remove all messages
        messagesStorage.forAllMessages(
                function(messageId: String, message: FlamingoMessage): Void {
                    messagesStorage.remove(messageId);
                });
    }
...

11.3.2. Flex

To implement client-side messaging for an application where Flex is used on the client you need to follow these steps:

  • First you should define a handler to implement the logic related to Server Push. To accomplish this, we suggest to implement the FlamingoListener class (similar approach is used in JavaFX implementation ) like this:

    
    ...
    public class SimpleListener implements FlamingoListener {
        public function onException(exception:Object):void {
            Alert.show(exception.toString(), "Exception");
        }   
        public function onMessage(message:FlamingoMessage):void {
            trace(message.getPayloadData());
        }   
    }  
    ...
  • The next step is to configure Flamingo push Flex library. There are several configuration parameters you can use.

    Table 11.3. Global Configuration Parameters

    ParameterDescription
    FlamingoPushConfiguration.serverSpecifies the technology used on server side (Seam or Spring). Default value is Seam.
    FlamingoPushConfiguration.protocol Specifies data exchange protocol to be used (AMF or Hessian). Default value is AMF.

    Here is an example:

    
    ...
     <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" >
        <mx:Script>
            <![CDATA[
    import com.exadel.flamingo.flex.components.flamingo.push.FlamingoPushConfiguration;
    FlamingoPushConfiguration.server = FlamingoPushConfiguration.SERVER_SPRING;
    FlamingoPushConfiguration.protocol = FlamingoPushConfiguration.PROTOCOL_HESSIAN;
    ...
    ...
  • Instantiate FxClientSideBroker component and assign listeners to it:

    
    ...
    <client:FxClientSideBroker id="broker" startOnCreate="true" />
    <mx:Script>
    ...
        var listener:SimpleListener = new SimpleListener(); 
        var destination:FlamingoDestination = FlamingoObjectsFactory.createDestination("test");     broker.subscribe(destination, listener);
    ...
    </mx:Script>
    ...

    or you can instantiate FxClientSideBroker and send a message using the send() method of the class in Action Script:

    
    ...
      <mx:Script>
      ...
        var broker:FxClientSideBroker = new FxClientSideBroker();   
        broker.start();
      ...
      var m:EditableFlamingoMessage = FlamingoObjectsFactory.createEditableMessage();
      m.setDestination(FlamingoObjectsFactory.createDestination("test"));
      m.setPayloadData("Text example");
      broker.send(m);
      ...
      </mx:Script>

    ...

11.3.2.1. Main Classes and Interfaces

Here are the Flamingo push Action Script 3 classes and interfaces you can use in your Flex application:

Table 11.4. Title of the table

Class/InterfaceDescription
FlamingoPushConfigurationStatic properties to configure your Flamingo Flex push application (in case if server or protocol values that you want to use differs from defaults)
FxClientSideBrokerThe class provides asynchronous communication with server and subscribing mechanism for a client-side application
FlamingoObjectsFactoryA helper class with a number of static methods that can help you create instances of FlamingoMessage, EditableFlamingoMessage and FlamingoDestination
FlamingoMessageAn Interface that represents message used by flamingo push library
EditableFlamingoMessageAn Interface that represents editable (you can change attributes values) message used by Flamingo push library
FlamingoDestination An Interface represents destination that you can subscribe / unsubscribe listeners to or send messages to
FlamingoListener Describes a Flamingo push listener that is a part of subscribing mechanism. It defines methods that implementing classes has to provide implementation for.

11.3.2.2. Offline Messages Storage

Offline messages storage in Flamingo push Flex library is implemented using the SharedObject class. It stores all messages sent from client to server in case if connection between Flex client and Server-side is lost. Stored messages will be sent to server after connection reestablished. To get messages currently stored in off-line storage use the FxClientSideBroker.storedMessages read-only property.

11.3.2.3.  Known Issues

  • Hessian server to client de-serialization issue. In case if you are planning to use Hessian as a transport protocol in your application and you are planning to pass custom classes instances as a payload of FlamingoMessage during data transmission from server to client Hessian will create instances of Object type with all fields from original Java class instead of creating instance of corresponding Action Script 3 class. To overcome this issue see SimpleFlamingoMessage class constructor. It takes optional obj:Object parameter. If passed its fields values are used to populate fields of new instance.

  • HessianService fault event dispatched on each server call regardless of call result or fault.