JBoss.orgCommunity Documentation

Chapter 2. Messaging

2.1. Messaging Overview
2.2. Messaging API Basics
2.3. Handling Errors
2.4. Single-Response Conversations & Psuedo-Synchronous Messaging
2.5. Broadcasting
2.6. Client-to-Client Communication
2.7. Asynchronous Message Tasks
2.8. Repeating Tasks
2.9. Sender Inferred Subjects
2.10. Message Routing Information
2.11. Queue Sessions
2.11.1. Lifecycle
2.11.2. Scopes

This section covers the core messaging concepts of the ErraiBus messaging framework.

ErraiBus forms the backbone of the Errai framework's approach to application design. Most importantly, it provides a straight-forward approach to a complex problem space. Providing common APIs across the client and server, developers will have no trouble working with complex messaging scenarios from building instant messaging clients, stock tickers, to monitoring instruments. There's no more messing with RPC APIs, or unweildy AJAX or COMET frameworks. We've built it all in to one, consice messaging framework. It's single-paradigm, and it's fun to work with.

It's important to understand the concept of how messaging works in ErraiBus. Service endpoints are given string-based names that are referenced by message senders. There is no difference between sending a message to a client-based service, or sending a message to a server-based service. In fact, a service of the same name may co-exist on both the client and the server and both will receive all messages bound for that service name, whether they are sent from the client or from the server.

Services are lightweight in ErraiBus, and can be declared liberally and extensively within your application to provide a message-based infrastructure for your web application. It can be tempting to think of ErraiBus simply as a client-server communication platform, but there is a plethora of possibilities for using ErraiBus purely with the GWT client context, such as a way to advertise and expose components dynamically, to get around the lack of reflection in GWT.

In fact, ErraiBus was originally designed to run completely within the client but quickly evolved into having the capabilities it now has today. So keep that in mind when you run up against problems in the client space that could benefit from runtime federation.

The MessageBuilder is the heart of the messaging API in ErraiBus. It provides a fluent / builder API, that is used for constructing messages. All three major message patterns can be constructed from the MessageBuilder .

Components that want to receive messages need to implement the MessageCallback interface.

But before we dive into the details, let look at some use cases first.

Sending Messages with the Client BusIn order to send a message from a client you need to create a Message and send it through an instance of MessageBus . In this simple example we send it to the subject 'HelloWorldService'.

In the above example we build and send a message every time the button is clicked. Here's an explanation of what's going on as annotated above:

Recieving Messages on the Server Bus / Server ServicesEvery message has a sender and at least one receiver. A receiver is as it sounds--it receives the message and does something with it. Implementing a receiver (also referred to as a service) is as simple as implementing our standard MessageCallback interface, which is used pervasively across, both client and server code. Let's begin with server side component that receives messages:

He we declare an extremely simple service. The @Service annotation provides a convenient, meta-data based way of having the bus auto-discover and deploy the service.

Sending Messages with the Server BusIn the following example we extend our server side component to reply with a message when the callback method is invoked. It will create a message and address it to the subject ' HelloWorldClient ':

The above example shows a service which sends a message in response to receiving a message. Here's what's going on:

Receiving Messages on the Client Bus/ Client ServicesMessages can be received asynchronously and arbitriraily by declaring callback services within the client bus. As ErraiBus maintains an open COMET channel at all times, these messages are delivered in real time to the client as they are sent. This provides built-in push messaging for all client services.

In the above example, we declare a new client service called "BroadcastReceiver" which can now accept both local messages and remote messages from the server bus. The service will be available in the client to receive messages as long the client bus is and the service is not explicitly de-registered.

ConversationsConversations are message exchanges which are between a single client and a service. They are a fundmentally important concept in ErraiBus, since by default, a message will be broadcast to all client services listening on a particular channel.

When you create a reply with an incoming message, you ensure that the message you are sending back is received by the same client which sent the incoming message. A simple example:

Note that the only difference between the example in the previous section (2.4) and this is the use of the createConversation()}}method with {{MessageBuilder .

Asynchronous messaging necessitates the need for asynchronous error handling. Luckily, support for handling errors is built directly into the MessageBuilder API, utilizing the ErrorCallback interface. In the examples shown in previous exceptions, error handing has been glossed over with aubiquitous usage of the noErrorHandling() method while building messaging. We chose to require the explicit use of such a method to remind developers of the fact that they are responsible for their own error handling, requiring you to explicitly make the decision to forego handling potential errors.

As a general rule, you should always handle your errors . It will lead to faster and quicker identification of problems with your applications if you have error handlers, and generally help you build more robust code.

The addition of error handling at first may put off developers as it makes code more verbose and less-readable. This is nothing that some good practice can't fix. In fact, you may find cases where the same error handler can appropriately be shared between multiple different calls.

The error handler is required to return a boolean value. This is to indicate whether or not Errai should perform the default error handling actions it would normally take during a failure. You will almost always want to return true here, unless you are trying to explicitly surpress some undesirably activity by Errai, such as automatic subject-termination in conversations. But this is almost never the case.

Errai further provides a subject to subscribe to for handling global errors on the client (such as a disconnected server bus or an invalid response code) that occur outside a regular application message exchange. Subscribing to this subject is useful to detect errors early (e.g. due to failing heartbeat requests). A use case that comes to mind here is activating your application's offline mode.

It is possible to contruct a message and a default response handler as part of the MessageBuilder API. It should be noted, that multiple replies will not be possible and will result an exception if attempted. Using this aspect of the API is very useful for doing simple psuedo-synchronous conversive things.

You can do this by specifying a MessageCallback using the repliesTo() method in the MessageBuilder API after specifying the error handling of the message.

See the next section on how to build conversational services that can respond to such messages.

Broadcasting messages to all clients listening on a specific subject is quite simple and involves nothing more than forgoing use of the reply API. For instance:

If sent from the server, all clients currently connected, who are listening to the subject "MessageListener" will receive the message. It's as simple as that.

Communication from one client to another client is not directly possible within the bus federation, by design. This isn't to say that it's not possible. But one client cannot see a service within the federation of another client. We institute this limitation as a matter of basic security. But many software engineers will likely find the prospects of such communication appealing, so this section will provide some basic pointers on how to go about accomplishing it.

Relay ServicesThe essential architectural thing you'll need to do is create a relay service that runs on the server. Since a service advertised on the server is visible to all clients and all clients are visible to the server, you might already see where we're going with this.

By creating a service on the server which accepts messages from clients, you can create a simple protocol on-top of the bus to enable quasi peer-to-peer communication. (We say quasi, because it still needs to be routed through the server)

While you can probably imagine simply creating a broadcast-like service which accepts a message from one client and broadcasts it to the rest of the world, it may be less clear how to go about routing from one particular client to another particular client, so we'll focus on that problem. This is covered in Section 2.10, “Message Routing Information”

In some applications, it may be necessary or desirable to delay transmission of, or continually stream data to a remote client or group of clients (or from a client to the server). In cases like this, you can utilize the replyRepeating() , replyDelayed() , sendRepeating() and sendDelayed() methods in the MessageBuilder .

Delayed TasksSending a task with a delay is straight forward. Simply utilize the appropriate method (either replyDelayed() or sendDelayed() ).

or

A repeating task is sent using one of the MessageBuilder's repeatXXX() methods. The task will repeat indefinitely until cancelled (see next section).

The above example sends a message very 1 second with a message part called "time" , containing a formatted time string. Note the use of the withProvided() method; a provided message part is calculated at the time of transmission as opposed to when the message is constructed.

Cancelling an Asynchronous TaskA delayed or repeating task can be cancelled by calling the cancel() method of the AsyncTask instance which is returned when creating a task. Reference to the AsyncTask object can be retained and cancelled by any other thread.

It is possible for the sender to infer, to whatever conversational service it is calling, what subject it would like the reply to go to. This is accomplished by utilizing the standard MessageParts.ReplyTo message part. Using this methodology for building conversations is generally encouraged.

Consider the following client side code:

And the conversational code on the server (for service ObjectService ):

In the above examples, assuming that the latter example is inside a service called " ObjectService " and is referencing the incoming message that was sent in the former example, the message created will automatically reference the ReplyTo subject that was provided by the sender, and send the message back to the subject desired by the client on the client that sent the message.

Every message that is sent between a local and remote (or server and client) buses contain session routing information. This information is used by the bus to determine what outbound queues to use to deliver the message to, so they will reach their intended recipients. It is possible to manually specify this information to indicate to the bus, where you want a specific message to go.

The utility class org.jboss.errai.bus.server.util.ServerBusUtils contains a utility method for extracting the String-based SessionID which is used to identify the message queue associated with any particular client. You may use this method to extract the SessionID from a message so that you may use it for routing. For example:

The SessionID can then be stored in a medium, say a Map, to cross-reference specific users or whatever identifier you wish to allow one client to obtain a reference to the specific SessionID of another client. In which case, you can then provide the SessionID as a MessagePart to indicate to the bus where you want the message to go.

By providing the SessionID part in the message, the bus will see this and use it for routing the message to the relevant queue.

Now you're routing from client-to-client!

It may be tempting however, to try and include destination SessionIDs at the client level, assuming that this will make the infrastructure simpler. But this will not achieve the desired results, as the bus treats SessionIDs as transient. Meaning, the SessionID information is not ever transmitted from bus-to-bus, and therefore is only directly relevant to the proximate bus.

The ErraiBus maintains it's own seperate session management on-top of the regular HTTP session management. While the queue sessions are tied to, and dependant on HTTP sessions for the most part (meaning they die when HTTP sessions die), they provide extra layers of session tracking to make dealing with complex applications built on Errai easier.