JBoss.orgCommunity Documentation

Errai User Guide


1. Introduction
1.1. What is it
1.2. License and EULA
1.3. Downloads
1.4. Sources
1.5. Reporting problems
2. Installation
2.1. Required software
2.2. Distribution Package
3. Errai Bus
3.1. What is Errai Bus?
3.2. Messaging
3.2.1. Messaging Overview
3.2.2. MessageBuilder API
3.2.3. Sending Messages with the Client Bus
3.2.4. Recieving Messages on the Server Bus / Server Services
3.2.5. Sending Messages with the Server Bus
3.2.6. Receiving Messages on the Client Bus/ Client Services
3.2.7. Conversations
3.2.8. Broadcasting
3.2.9. Client-to-Client Communication
3.2.10. Handling Errors
3.2.11. Asynchronous Message Tasks
3.3. Remote Procedure Calls (RPC)
3.3.1. Creating RPC services
3.3.2. Making calls
3.4. Queue Sessions
3.4.1. Session Scopes
3.4.2. Lifecycle
3.5. Serialization
3.5.1. Serialization of external types
3.6. Wiring server side components
3.7. Bus configuration
3.7.1. web.xml and app server configuration
3.7.2. ErraiService.properties
3.7.3. ErraiApp.properties
3.7.4. Dispatchers
3.7.5. Servlet Implementations
3.8. Debugging Errai Applications
4. Errai Workspaces
4.1. What is workspaces?
4.1.1. Basic concepts
4.1.2. Sandbox
4.2. Workspace API
4.2.1. Declaring and implementing tools
4.2.2. Authorization and access to tools
4.2.3. Common coding pattern
4.3. Core features
4.3.1. Workspace Registry
4.3.2. Security
4.3.3. Authentication and Authorization
4.3.4. History management and perma links
4.3.5. Preferences
4.3.6. Logging
4.4. Building a workspaces application
4.4.1. Check the prerequisites
4.4.2. Setup a build environent
4.4.3. Verify the application configuration
4.4.4. Launch the hosted mode and start developing
5. Appendix A: Quickstart
5.1. Using the maven archetype
6. Appendix B: Development Proxy
6.1. Proxied access to external containers

Errai requires a JDK version 5 or higher and depends on Apache Maven to build and run the examples, and for leverging the quickstart utilities.

Launching maven the first time

Please note, that when launching maven the first time on your machine, it will fetch all dependecies from a central repository. This may take a while, because it includes downloading large binaries like GWT SDK. However subsequent builds are not required to go through this step and will be much faster.

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

In 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'.

    public class HelloWorld implements EntryPoint {

        // Get an instance of the RequestDispatcher
        private RequestDispatcher dispatcher = ErraiBus.getDispatcher();

        public void onModuleLoad() {
            Button button = new Button("Send message");

            button.addClickHandler(new ClickHandler() {

            public void onClick(ClickEvent event) {
                // Send a message to the 'HelloWorldService'.
                MessageBuilder.createMessage()
                    .toSubject("HelloWorldService")             // (1)
                    .signalling()                               // (2)
                    .noErrorHandling()                          // (3)
                    .sendNowWith(dispatcher);                   // (4)
            });

            [...]
         }
    }

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:

In 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':

    @Service
    public class HelloWorldService implements MessageCallback {

        private RequestDispatcher dispatcher;

        @Inject                                         
        public HelloWorldService(RequestDispatcher disaptcher) {
            dispatcher = dispatcher;
        }

        public void callback(CommandMessage message) {
                // Send a message to the 'HelloWorldClient'.
                MessageBuilder.createMessage()
                    .toSubject("HelloWorldClient")              // (1)
                    .signalling()                               // (2)
                    .with("text", "Hi There")                   // (3)
                    .noErrorHandling()                          // (4)
                    .sendNowWith(dispatcher);                   // (5)
            });
        }
    }   

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

Conversations 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 conversation 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:

   @Service
    public class HelloWorldService implements MessageCallback {
        public void callback(CommandMessage message) {
                // Send a message to the 'HelloWorldClient' on the client that sent us the
                // the message.
                MessageBuilder.createConversation(message)
                    .toSubject("HelloWorldClient")
                    .signalling()
                    .with("text", "Hi There! We're having a conversation!")
                    .noErrorHandling().reply();
            });
        }
    }

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.

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.

The 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 particualr client, so we'll focus on that problem.

Message Routing Information

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:

...
public void callback(Message message) {
    String sessionId = ServerBusUtils.getSessionId(message);

    // Record this sessionId somewhere.
    ...
}
...

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.

MessageBuilder.createMessage()
    .toSubject("ClientMessageListener")
    .signalling()
    .with(MessageParts.SessionID, sessionId)
    .with("Message", "We're relaying a message!")
    .noErrorHandling().sendNowWith(dispatcher);

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 treates 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.

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 a ubiquitous 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.

MessageBuilder.createMessage()                                                   
        .toSubject("HelloWorldService")                                          
        .signalling()                                                            
        .with("msg", "Hi there!")                                                
        .errorsHandledBy(new ErrorCallback() {                                   
            public boolean error(Message message, Throwable throwable) {         
                throwable.printStackTrace();                                     
                return true;                                                    
            }                                                                    
        })                                                                       
        .sendNowWith(dispatcher);                                                       

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.

ErrorCallback error = new ErrorCallback() {
    public boolean error(Message message, Throwable throwable) {         
        throwable.printStackTrace();                                     
        return true;                                                    
    } 
}

MessageBuilder.createMessage()                                                   
        .toSubject("HelloWorldService")                                          
        .signalling()                                                            
        .with("msg", "Hi there!")                                                
        .errorsHandledBy(error)                                                                       
        .sendNowWith(dispatcher);   

A little nicer.

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

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.

ErraiBus supports a high-level RPC layer to make typical client-server RPC communication easy on top of the bus. While it is possible to use ErraiBus without ever using this API, you may find it to be a more useful and concise approach to exposing services to the clients.

Please Note that this API has changed since version 1.0

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.

This section contains information on configuring the server-side bus.

The ErraiService.properties file contains basic configuration for the bus itself.

Example Configuration:

##
## Request dispatcher implementation (default is SimpleDispatcher)
##
#errai.dispatcher_implementation=org.jboss.errai.bus.server.SimpleDispatcher
errai.dispatcher_implementation=org.jboss.errai.bus.server.AsyncDispatcher


#
## Worker pool size.  This is the number of threads the asynchronous worker pool should provide for processing
## incoming messages. This option is only valid when using the AsyncDispatcher implementation.
##
errai.async.thread_pool_size=5

##
## Worker timeout (in seconds).  This defines the time that a single asychronous process may run, before the worker pool
## terminates it and reclaims the thread.   This option is only valid when using the AsyncDispatcher implementation.
##
errai.async.worker.timeout=5

##
## Specify the Authentication/Authorization Adapter to use
##
#errai.authentication_adapter=org.jboss.errai.persistence.server.security.HibernateAuthenticationAdapter
#errai.authentication_adapter=org.jboss.errai.bus.server.security.auth.JAASAdapter

##
## This property indicates whether or not authentication is required for all communication with the bus.  Set this
## to 'true' if all access to your application should be secure.
##
#errai.require_authentication_for_all=true



Errai has many several different implementations for HTTP traffic to and from the bus. We provide a universally-compatible blocking implementation that provides fully synchronous communication to/from the server-side bus. Where this introduces scalability problems, we have implemented many webserver-specific implementations that take advantage of the various proprietary APIs to provide true asynchrony.

These inlcuded implementations are packaged at: org.jboss.errai.bus.server.servlet

Errai includes a bus monitoring application, which allows you to monitor all of the message exchange activity on the bus in order to help track down any potential problems It allows you to inspect individual messages to examine their state and structure.

To utilize the bus monitor, you'll need to include the errai-tools package as part of your application's dependencies. When you run your application in development mode, you will simply need to add the following JVM options to your run configuration in order to launch the monitor: -Derrai.tools.bus_monitor_attach=true


The monitor provides you a real-time perspective on what's going on inside the bus. The left side of the main screen lists the services that are currently available, and the right side is the service-explorer, which will show details about the service.

To see what's going on with a specific service, simply double-click on the service or highlight the service, then click "Monitor Service...". This will bring up the service activity monitor.


The service activity monitor will display a list of all the messages that were transmitted on the bus since the monitor became active. You do not need to actually have each specific monitor window open in order to actively monitor the bus activity. All activity on the bus is recorded.

The monitor allows you select individual messages, an view their individual parts. Clicking on a message part will bring up the object inspector, which will allow you to explore the state of any objects contained within the message, not unlike the object inspectors provided by debuggers in your favorite IDE. This can be a powerful tool for looking under the covers of your application.

ErraiWorkspaces provides a fully managed working UI enviroment for which to deploy your console and tooling. Put more succinctly: we provide you the places. All you need to do is put stuff there.

Workspaces goes beyond simple API. It's not just another GWT module. It was designed to get you started quickly, without getting in your way. This includes the environment to build, deploy and test your tools. We've decided to use maven to provide these capabilities. It allows you to quickly get started (see Maven archetype), but more importantly, maven repositories are used to share tool implementations across workspace assemblies. This way you can easily combine your own and 3rd party tools as part of a custom workspace compilation.

Note

The GWT SDK is already part of the sandbox bootstrap. You get everything to get going within minutes: GWT, Errai and 3rd party libraries.

To begin with, we'll look at some examples how to declare tool sets and launch them in a workspace.

It's common to use the WidgetProvider API in a way that GWT 2.0 code splitting can kick in. This allows true lazy loading of tools upon demand, including all .js and resources necessary to run your tools:

@LoadTool(name = "Group Tasks", group = "Tasks")
public class OpenTasksModule implements WidgetProvider
{
    static OpenTasksView instance = null;

    public void provideWidget(final ProvisioningCallback callback)
    {
        GWT.runAsync(
            new RunAsyncCallback()
            {
              public void onFailure(Throwable err)
              {
                GWT.log("Failed to load tool", err);
              }

              public void onSuccess()
              {
                if (instance == null) {
                  instance = new OpenTasksView();  // your tool here
                }
                callback.onSuccess(instance);
              }
            }

        );
    }
}

The workspace itself doesn't require any attention apart from the common API that is decribed in the chapter "Workspace API". Still, in some cases you may need to go beyond the out-of-the-box functionlity. In this chapter we are going to explain the building blocks used in the workspace implementation and how they can be used to replace or extend the default workspace behaviour.

The workspace itself doesn't provide an authentication component. Instead it relies on the Authentication provided by errai bus. When the workspace is loaded two thing happen: The workspace checks if authentication is required and, after authentication if needed, loads the tool sets.

The bus component in charge is the SecurityService (org.jboss.errai.bus.client.security.SecurityService). It is available through workspace Registry. For people extending the workspace functionality it offers two intersting options:

  • Obtain access to the authentication context (username, roles, etc)

  • Deferred workspace assembly

The later is important of you want to provide your own way of authenticating users, but still use the role based authorization build into workspaces.

Note

Don't mess with the authentication unless you now what you are doing. In many cases it might be better to extend the server side authentication adapters instead of overriding the client behaviour. However for the sake of completeness, this mechanism should be mentioned here.

The workspace allows you to store simple key-value pairs as preferences. Like with other common API, you obtain the Preferences through the workspace Registry:

Preferences prefs = Registry.get(Preferences.class);
prefs.set("default_sort_order", "ASC");
            

The default implementation uses a cookie based approach, but it's easy to replace that one with a custom implementation in the module decriptor:


   <module>
    [...]
    <replace-with class="org.jboss.errai.workspaces.client.framework.CookiePreferences">
           <when-type-is class="org.jboss.errai.workspaces.client.framework.Preferences"/>
       </replace-with>
    </module>

Workspaces inherits GWT Log. It ships with an embedded log message console that records log output, besides the default logging to the browser error console and the GWT hosted mode window. To use the internal logging mechanism, simply write messages to the Logger:

import com.allen_sauer.gwt.log.client.Log;
 [...]

Log.warn("some log message");
Log.error("message", throwable);
                
            

In this section we go through the steps necessary to create a workspace application. As mentioned before, workspaces is more then just another GWT library. It's intended to get you started quickly. This includes GWT installation, creating a build environment and prepapring your IDE.

The most simple (and least error prone) way to get started is using the maven archetype that we ship as part of errai. Simply follow the instructions in the quickstart section and return here when you are done.

Once you've created a project using the maven archetype, make yourself familiar with the directory structure:

Laika:gwt-app hbraun$ lstree
| pom.xml    
|-src
|---main
|-----java
|-------foo
|---------bar
|-----------client
|-----------server
|-war
|---WEB-INF

It basically follows the default GWT guidelines for breaking up code between client and server. Simply put tools (@LoadToolSet) into the client subpackage and services (@Service) into the server subpackages. Make sure you are familiar with the Workspace API before you continue.

If you followed the steps above, and did build your application stub using the maven archetype, then you should be launch the GWT hosted mode with the following commands:

  mvn gwt:run

 (alternatively)   
  mvn gwt:debug

Launching maven the first time

Please note, that when launching maven the first time on your machine, it will fetch all dependecies from a central repository. This may take a while, because it includes downloading large binaries like GWT SDK. However subsequent builds are not required to go through this step and will be much faster.

If you have taken a look at the examples that ship with the distribution, you realized that we use maven to build them. The way it is setup, the maven build has several benefits:

In order to get you going quickly, we've provided a project archetype, that allows you to create a project skeleton similiar to the one we use for building the examples. It's based on the maven archetype plugin [1] and needs to be invoked from the command line:

    mvn archetype:generate \
        -DarchetypeGroupId=org.jboss.errai \
        -DarchetypeArtifactId=sandbox-archetype \
        -DarchetypeVersion=1.1-SNAPSHOT \
        -DarchetypeRepository=https://repository.jboss.org/nexus/content/groups/public/
            

When invoking the archetype build it ask you about the maven groupId, artifactId and package name your GWT application should use:

    Define value for groupId: : foo.bar
    Define value for artifactId: : gwt-app
    Define value for version:  1.0-SNAPSHOT: :
    Define value for package:  foo.bar: : foo.bar.ui
    Confirm properties configuration:
    groupId: foo.bar
    artifactId: gwt-app
    version: 1.0-SNAPSHOT
    package: foo.bar.ui
    Y: : Y
            

What will be created for you, is a maven build structure, including references to the GWT SDK and the Errai dependencies necessary to launch a simple application:

    Laika:test hbraun$ cd gwt-app/
    Laika:gwt-app hbraun$ lstree
    |-src
    |---main
    |-----java
    |-------foo
    |---------bar
    |-----------client
    |-----------server
    |-war
    |---WEB-INF
            

In order launch the GWT hosted mode, change into the project directory and type:

    mvn gwt:run
            

The default project includes both a HelloWorld client (GWT), and a HelloWorld service.



[1] http://maven.apache.org/plugins/maven-archetype-plugin/

Usually GWT developement happens in hosted mode and then, later on, the GWT app is turned into a webapp (*.war) that can be deployed on a target container (app server, servlet engine). This works quiet well for closed systems that don't depend on additional resources the target container provides. A typical resource would be a DataSource for access to a relational database.

Instead of pulling these resources into the hosted mode servlet engine (jetty, read-only JNDI) or creating mock objects for any resources that cannot be run in hosted mode, we offer you a much more simple way to work with external resources: Simply proxy all requests that occur in hosted mode to an external target container:


The proxy is implemented a yet another servlet that you need to add to the web.xml that's being sed in hosted mode:

<servlet>
    <servlet-name>erraiProxy</servlet-name>
    <description>Errai Proxy</description>
    <servlet-class>org.jboss.errai.tools.proxy.XmlHttpProxyServlet</servlet-class>
        <init-param>
            <param-name>config.name</param-name>
            <param-value>errai-proxy.json</param-value>      (1)
        </init-param>
        <load-on-startup>1</load-on-startup>
</servlet>
               
<servlet-mapping>
    <servlet-name>erraiProxy</servlet-name>
    <url-pattern>/app/proxy/*</url-pattern>                  (2)
</servlet-mapping>
           

The web.xml proxy declaration contains two notable elements: A reference to the proxy configuration file and a URL pattern, where the proxy can found. While the later shouldn't be changed (the bus bootstraps on this URL), you need to change the proxy config according to your needs:

 {"xhp": {
    "version": "1.1",
    "services": [
        {"id": "default",
         "url":"http://127.0.0.1:8080/my-gwt-app/in.erraiBus",
         "passthrough":true
        },
    ]
  }
}

You would need to change the host, port and webcontext ('my-gwt-app' in this case) to reflect the location of the external container. 'passthrough' simply means that any request to 'proxy/in.erraiBus' will go to 'container/my-gwt-app/in.erraiBus'. This already indicates that you need to have the server side part of your GWT application, already running on the target container. The most simple way to achieve this, is to build a the complete webapp, deploy it and ignore the UI parts that may be available on the server.