Messaging

Nexaweb provides the Internet Messaging Bus (IMB), a collection of APIs and transport options that enables secure and scalable message delivery.  When running a Nexaweb application, the Nexaweb client (the client) and the Nexaweb server (the server) communicate through a set of Networking APIs. This API is based on HTTP protocol, and supports SSL for secure connections.

Overview

The IMB provides the following APIs for messaging:

Publish/Subscribe messaging - available on the client and server using the publish and subscribe APIs
Request/Response - available one the client synchronously or asynchronously using the retrieve and retrieveAndProcess

This section describes this communication infrastructure as follows:

  1. Basic Client/Server Communication
  2. General Messaging use:
    1. Client side messaging
    2. Server side messaging
  3. In-depth topics
    1. Push connection
    2. Exception handling
  4. Reliable messaging

Basic Client/Server Communication

The client communicates with the server through the RequestService interface. This interface provides the user not only standard HTTP GET/POST equivalent functions, but also advanced functions for messaging, pushconnection and background polling. The server sends the client data and messages through: a response upon a standard request; or a PushConnection upon a client's PushConnection request.

Getting a RequestService

There is only one RequestService instance per Nexaweb client session instance. You can get the RequestService instance in a MCO by calling:

ClientSession cs = McoContainer.getClientSessionFromMco(this);     

RequestService requestService = cs.getRequestService();

HTTP GET/POST Function Equivalancies

Nexaweb IMB HTTP GET/POST function equivalencies include:

  • retrieve\retrieveAsynchronously
  • retrieveAndProcess\retrieveAndProcessAsynchronously

retrieve\retrieveAsynchronously

You can use this set of functions to retrieve data from the server. You can pass the retrievemethod either a string url or an HttpRequest object, and you get an HttpResponse after you make the retrieve call:

HttpResponse response = netService.retrieve("data.jsp");

or

HttpRequest request = netService.createHttpRequest();
request.setUri("data.jsp");
request.setContent("userid=1234&password=1234");
requestService.retrieve(request);

If the retrieve is going to take a long time based on your application, you can use retrieveAsychronously so that you can do something else or make your UI responsive. You can implement the NetServiceListener interface and add a listener to the asynchronous method so that when the response is available, the listener gets notified, for example:

NetServiceListener listener = new MyNetServiceListenerImpl();
requestService.retrieveAsynchronously("data_from_a_long_task.jsp", listener);

retrieveAndProcess\retrieveAndProcessAsynchronously

If the data you try to retrieve is Nexaweb-aware, such as you want to update some UI elements or update some document data, in many cases, a better API to use is retrieveAndProcess. TheretrieveAndProcess API has the exact same parameter requirements as the retrieve. But it does one more thing after the data is retrieved. It processes response data automatically with the DOM processor. This means that any UI or document-data update is automatically processed when the method ends. For example, in the following code:

client-side MCO

requestService.retrieveAndProcess("alertUser.jsp");

alertUser.jsp

<% if (pwd==null) {%>
<messageDialog title="Warning" message="You have to input your password." type="warning"/>
<%}else{%>
// your normal code logic
<%}%>

At the client side in your call, you can call retrieveAndProcess call. Then on the server side, you can decide to either move on with your normal logic or prompt the user for a warning message.

The retrieveAndProcessAsynchronously is designed for asynchronous usage. You need to pass an instance of the NetServiceListener, and then this instance is invoked when the process of this response completes.

PushConnection and polling

openPushConnection/closePushConnection

PushConnection provides a live connection for the data stream to flow from the server to the client. It's expensive to maintain for both the client and the server, however it provides the best value when the client needs instant data update. A client can establish only one push connection with the server. With the RequestService, You can open a push connection by calling:

requestService.openPushConnection();

or close a push connection with

requestService.closePushConnection();

Although there can only be one push connection per Nexaweb client session, this connection stays alive until it's closed. Many types of data can be transmitted from the server to the client through the PushConnection.  Here are the two rules when a PushConnection is open:

  • All the publish messages, client DOM updates will be carried over to the client through the PushConnection.
  • However, response from calling retrieve and retrieveAndProcess and their asynchronous methods, will not go through the PushConnection because these methods need to return a HttpResponse object from their own http request.

We will discuss more in-depth on the PushConnection, such as flushing and flush policies later.

startPolling()/stopPolling()

When building a web application, synchronizing the state between the client and the server poses two challenges to the developer. Typically a developer can:

  1. Manual request to the server via Synchronize or RetrieveAndProcess: lowest resource requirements but data is only updated when a refresh is done.
  2. Automatic refresh via Polling: best balance of resources and getting the data in real time.
  3. Maintain a live push connection with the server: resource intensive, one way (server to client), instant response times - best when true real time data is needed.

Polling is a solution to both the problems when the live data update is only required every a few seconds or longer. With a background thread sending a perodic synchronizing request to the server, polling provides a way to update the session data on both the client side and the server side. You can enable polling or disable polling on the client side by calling:

requestService.startPolling();

or

requestService.stopPolling();


PushConnection and Polling startup configuration

The above API provides a way to enable PushConnection and Polling after the client session is already started, however, you might want to enable them at the startup of the client session. You can configure them in the nexaweb-client.xml under WEB-INF folder, in the section of:
/session-configurations/default-session-configuration/configuration/

begin-polling-on-startup, setting this to true will enable the polling when the client session starts.
polling-interval, this is a long value in milliseconds on how often you want the polling. This is a relative maximum amount of time the polling thread will wait to synchronize with the server. The 'relative' means the waiting state will start over again relative to the last client/server communication, for example, a user hitting a button will trigger a retrieveAndProcess call, and the internal wait process will start all over again.
establish-push-connection-on-startup, setting this to true will make the client to establish the push connection right when it's launched.

Regardless of how a push connection or polling is started, further invoking openPushConnection and startPolling will just return. The only way to restart the push connection or polling is to stop them, then start them again.

You can find more information from the API document in the com.nexaweb.client.netservice package.

General Messaging Usage

Nexaweb platform provides a powerful messaging bus from client to server, from client to client, from server to server and from server to the client. There are symmetric APIs on both the client side and the server side. Messages flow through the messaging bus. Any end can publish a message, and any end can subscribe and receive a message.

Client Side Messaging

subscribe/unsubscribe/publish

With the Nexaweb server's messaging bus support, the client can easily subscribe to topics and publish messages. For example, if a user wants to subscribe to a specific top, the code can be written as:

requestService.subscribe("fianical_topic", myTopicMessageListener);

Then whenver someone, either on the server or on other clients, publishes a message in the "finanical_topic", this message will be sent to this client. myTopicMessageListener is your business specific implementation of the MessageListener interface. Its instance will be notified upon this message's arrival and your code will process this message the way you want. The listeners will keep receiving the messages on this topic until you call:

//this call will unsubscribe all the listeners from the topic
requestService.unsubscribe("finanical_topic");

or

//this call will unsubscribe this particular listener
requestService.unsubscribe("financial_topic", myTopicMessageListener);

Publishing a message is even easier. You can just call any of the six overloaded publish methods, the following is an example to publish a string message:

requestService.publish("financial_topic", "what is today's interest rate?");

Then all the people who are subscribing the "fianical_topic" topic will get this message.

Implementing a MessageListener

The previoius section touched briefly on the MessageListener. This section describes how you can implement a MessageListener.

You implement your own message listener by extending the abstract classcom.nexaweb.messaging.MessageListener.  You extend this abstract class by overriding the onMessage methods in which you are interested.
 
There are six overloaded onMessage calls for different types of messages:
  1. onMessage ( String topic, String message ) - for a string message
  2. onMessage ( String topic, byte[] bytes ) - for a byte array message
  3. onMessage ( String topic, com.nexaweb.xml.Document document ) - for a Nexaweb xml document message
  4. onMessage ( String topic, com.nexaweb.xml.Element element ) - for a Nexaweb xml elemet message
  5. onMessage ( String topic, Serializable serializable ) - for a Java serializable object, you need to make sure that this class's definition is available to the client at runtime.
  6. onMessage ( String topic, XmlSerializable xmlSerializable ) - for a Nexaweb xml serializable mesage. This is a better way to serialize an XML object because it won't rely on a class's serialization id to deserialize the stream into an object. It will deserialize the stream using the fromXml call from the xmlSerializable interface.
The six overloaded publish methods map to the six onMessage overloads:
  1. publish ( String topic, String message )
  2. publish ( String topic, byte[] bytes )
  3. publish ( String topic, com.nexaweb.xml.Document document )
  4. publish ( String topic, com.nexaweb.xml.Element element )
  5. publish ( String topic, Serializable serializable )
  6. publish ( String topic, XmlSerializable xmlSerializable )
Optionally, on the client side, if you want to access the Nexaweb client session object in the listener instance, you can just pass the clientSession object you obtain from a MCO into the constructor of your extended message listener.

You can find more information from the API document in the com.nexaweb.client.netservice package.

Server side messaging usage

On the server side, you can get the messaging service through:

MessagingService messagingService = ServiceManager.getMessagingService();


Subscribe a topic or unsubscribe from a topic

You can subscribe/unsubscribe a topic the same way as you subscribe a topic on the client side:

messagingService.subscribe("fianical_topic", myTopicMessageListener);

or

messagingService.unsubscribe("fianical_topic", myTopicMessageListener);

Publish a message to all the listeners

You can publish the messages the same way as you publish on the client side. Just call:

messagingService.publish("financial_topic", "what is today's interest rate?");


Send directed messages to a particular client session

If you want to send a message on a topic to a particular session, you can:

// find a particular session you are interested
SessionManager smgr = ServiceManager.getSessionManager();
ServerSession nxsession = smgr.findByRequest(request);

// check if this client session subscribes this topic, otherwise
// the client will never receive this message
if ( messagingService.isClientSubscribed( nxsession ) ){

   // it's recommened to call sendAsynchronously to put the message in the queue
   messagingService.sendAsynchronously(nxsession, "financial_topic", "this is today's rate");

   // or you can make a blocking call until the message is written to the client stream
   // messagingService.send(nxsession, "financial_topic", "this is today's rate");

You can find more information from the API document in the com.nexaweb.server.messaging package.

In-Depth Topics

Effectively Using the Push Connection

Different network setups, such as using proxy servers, and different client JVM implementations, such as connecting through SSL ,can limit push connection's usage. Nexaweb platform provides a set of fleixble and extensible flush policies to resolve these problems.

How it Works

You can configure a push connection with an instance of a FlushPolicy, one per connection. Set the policy to programmatically or configuration driven.
A policy introspects the content flowing out through the persistent connection and based on its configuration decides when to take action (that is,  call flush() on the push connection).
Different policies may be applied to different inbound connections based on the configuration specified in the nexaweb-client.xml file.
The policies may be configured to send whitespace down the connection to flush a proxy buffer or to close the stream.

You can specify flush policies in three ways:

  • Specifying policies are specified in nexaweb-client.xml
  • Children of default-session-configuration or session-configuration elements
  • Developers can write their own: either implement FlushPolicy or ConfigurableFlushPolicy defined in the server API

Here is an example that specifying a flush policy that will flush every 100 milliseconds with a 5K size with Microsoft JVM 1.1. The reason for this flush is Microsoft JVM 1.1 buffers the content with the push connection, so we need to flush it so that the Java API calls will get the data:

<default-session-configuration>
  <configuration>
    <push-connection-flush-policies>
      <policy>
        <rules>
          <match xpath="starts-with(/client-info/java-version, '1.1')" />
          <match xpath="starts-with(/client-info/java-vendor, 'Microsoft')" />
        </rules>
        <configuration class=
"com.nexaweb.server.pushconnection.flushpolicies.IdlePeriodFlushPolicy">
          <flush-size>5 K</flush-size>
          <period>100 millis</period>
        </configuration>
      </policy>
    </push-connection-flush-policies>
  </configuration>
</default-session-configuration>

Sample client info:

<client-info>
  <client-version>Nexaweb Platform v4.1</client-version>
  <java-version>1.4.2</java-version>
  <java-vendor>Sun Microsystems Inc.</java-vendor>
  <os>Windows XP</os>
  <os-version>4.1</os-version>
  <browser>sun.plugin</browser>
  <architecture>x86</architecture>
  <user-agent>Mozilla/4.0</user-agent>
  <screen-size>1600,1200</screen-size>
  <locale>en,US,</locale>
  <is-connection-secure>true</is-connection-secure>
</client-info>

Picking a flush policy depends a lot on the problem to solve. Nexaweb provides the following flush policies out of the box:

com.nexaweb.server.pushconnection.flushpolicies.IdlePeriodFlushPolicy
com.nexaweb.server.pushconnection.flushpolicies.
IdelPeriodWithSizeThresholdFlushPolicy
com.nexaweb.server.pushconnection.flushpolicies.ThroughputFlushPolicy

Please check the API documentation for details.

Exception handling

Typically, if there is a problem when using RequestService functions, NetServiceException will be thrown. Users can check NetServiceException.getCauseThrowable() object and NetServiceException.getErrorResponse() to get more information on the error response. Here is an example:

try {
   requestService.retrieveAndProcess("mySample.jsp");
}catch(NetServiceException e){
   Throwable e = e.getCauseThrowable();
   HttpResponse errorResponse = e.getErrorResponse();
   // your error handling code
}

Depending on each individual use case and different JVM implementation, getCauseThrowable() andgetErrorResponse() might return different results, including null, check the com.nexaweb.client.netservice package APIs for more information.

Reliable Messaging

Nexaweb platform provides a reliable messaging function. Reliable messaging provides reliability for certain types of messages on both client-to-server(c->s) and  server-to-client(s->c) messaging. These message types include:

  • Register/unregister a document
  • Publish a message
  • Subscribe/unsubscribe a topic (client only)

These messages get delivered with the following characteristics:

  • Guaranteed delivery
  • Each message is processed once and only once
  • Messages are processed in their original order

Both the c->s and the s->c message transfers are implemented on a Nexaweb specific message acknowledgment (ack/nak) protocol.

There is no API method for the reliable messaging feature. However, you can configure the reliable messaging in the nexaweb-client.xml under WEB-INF folder, in the section of:
 /session-configurations/default-session-configuration/configuration/

  • reliable-messaging-enabled - enable the reliable messaging.
  • message-time-to-live - configures how long the client or the server keeps the original messages before removing them, due to the other side's ack/nak or that they expired (all messages kept will be consuming the client or server's memory resources).
  • message-cache-cleanup-task-period - configures when the client or server cleans up the expired cached messages.

User should check the documentations for more details on these paramters. Please keep in mind, the current reliability of the messages is per Nexaweb session based. When the session ends or restarts, the previous reliability state is reset.