Errai is a GWT-based framework for building rich web applications using next-generation web technologies. Built on-top of ErraiBus, the framework provides a unified federation and RPC infrastructure with true, uniform, asynchronous messaging across the client and server.
Errai is distributed under the terms of the the Apache License, Version 2.0. See the full Apache license text.
The distribution packages can be downloaded from jboss.org http://jboss.org/errai/Downloads.html
The source code for this component can be found in the Errai SVN repository:
If you run into trouble don't hesitate to get in touch with us:
JIRA Issue Tracking: https://jira.jboss.org/jira/browse/ERRAI
User Forum: http://community.jboss.org/en/errai?view=discussions
Mailing List: http://jboss.org/errai/MailingLists.html
IRC: irc://irc.freenode.net/errai
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.
Apache Maven: http://maven.apache.org/download.html
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.
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.
This section covers the core messaging concepts of the ErraiBus messaging framework.
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.
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:
We specify the subject we wish to send a message to. In this
case, "HelloWorldService
".
We indicate that we wish to only signal the service, meaning, that we're not sending a qualifying command to the service. For information on this, read the section on Protocols.
We indicate that we do not want to provide an
ErrorCallback
to deal with errors for this
message.
We transmit the message by providing an instance to the EquestDispatcher
An astute observer will note that access to the
RequestDispatcher
differs within client code and
server code. Because the client code does not run within a
container, access to the RequestDispatcher
and
MessageBus
is statically accessed using the
ErraiBus.get()
and
ErraiBus.getDispatcher()
methods. The server-side
code, conversely, runs inside a dependency container for managing
components. It currently uses Guice as the default container, but
will more generically support the JSR-330 specification in the
future.
Every 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:
@Service public class HelloWorldService implements MessageCallback { public void callback(Message message) { System.out.println("Hello, World!"); } }
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.
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:
We specify the subject we wish to send a message to. In this
case, "HelloWorldClient
". We are sending this
message to all clients which are listening in on this subject. For
information on how to communicate with a single client, see
Section 2.6.
We indicate that we wish to only signal the service, meaning that we're not sending a qualifying command to the service. For information on this, read the section on Protocols.
We add a message part called "text" which contains the value "Hi there".
We indicate that we do not want to provide an
ErrorCallback
to deal with errors for this
message.
We transmit the message by providing an instance of the
RequestDispatcher
.
Messages 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.
public class HelloWorld implements EntryPoint { private MessageBus bus = ErraiBus.get(); public void onModuleLoad() { [...] /** * Declare a local service to receive messages on the subject * "BroadcastReceiver". */ bus.subscribe("BroadcastReceiver", new MessageCallback() { public void callback(CommandMessage message) { /** * When a message arrives, extract the "text" field and * do something with it */ String messageText = message.get(String.class, "text"); } }); [...] }
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
connected and the service is not explicitly de-registered.
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
.
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.
MessageBuilder.createMessage() .toSubject("ConversationalService").signalling() .with("SomeField", someValue) .noErrorHandling() .repliesTo(new MessageCallback() { public void callback(Message message) { System.out.println("I received a response"); } })
See the next section on how to build conversational services that can respond to such messages.
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:
MessageBuilder.createMessage() .toSubject("ObjectService").signalling() .with(MessageParts.ReplyTo, "ClientEndpoint") .noErrorHandling().sendNowWith(dispatcher);
And the conversational code on the server (for service ObjectService):
MessageBuilder.createConversation(message) .subjectProvided().signalling() .with("Records", records) .noErrorHandling().reply();
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.
Broadcasting messages to all clients listening on a specific subject is quite simple and involves nothing more than forgoing use of the conversation API. For instance:
MessageBuilder.createMessage(). .toSubject("MessageListener") .with("Text", "Hello, from your overlords in the cloud") .noErrorHandling().sendGlobalWith(dispatcher);
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.
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
SessionID
s at the client level, assuming that
this will make the infrastructure simpler. But this will not achieve
the desired results, as the bus treates
SessionID
s 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
.
Sending a task with a delay is straight forward. Simply
utilize the appropriate method (either
replyDelayed()
or
sendDelayed()
).
MessageBuilder.createConversation(msg) .toSubject("FunSubject") .signalling() .noErrorHandling() .replyDelayed(TimeUnit.SECONDS, 5); // sends the message after 5 seconds.
or
MessageBuilder.createMessage() .toSubject("FunSubject") .signalling() .noErrorHandling() .sendDelayed(requestDispatcher, TimeUnit.SECONDS, 5); // sends the message after 5 seconds.
A repeating task is sent using one of the MessageBuilder's
repeatXXX()
methods. The task will repeat
indefinitely until cancelled (see next section).
MessageBuilder.createMessage() .toSubject("FunSubject") .signalling() .withProvided("time", new ResourceProvider<String>() { SimpleDateFormat fmt = new SimpleDateFormat("hh:mm:ss"); public String get() { return fmt.format(new Date(System.currentTimeMillis()); } } .noErrorHandling() .sendRepeatingWith(requestDispatcher, TimeUnit.SECONDS, 1); //sends a message every 1 second
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.
A delayed or repeating task can be cancelled by calledin 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.
AsyncTask task = MessageBuilder.createConversation(message) .toSubject("TimeChannel").signalling() .withProvided(TimeServerParts.TimeString, new ResourceProvider<String>() { public String get() { return String.valueOf(System.currentTimeMillis()); } }).defaultErrorHandling().replyRepeating(TimeUnit.MILLISECONDS, 100); ... // cancel the task and interrupt it's thread if necessary. task.cancel(true);
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
RPC services provide a way of creating type-safe mechanisms to make client-to-server calls. Currently, this mechanism only support client-to-server calls, and not vice-versa.
Creating a service is straight forward. It requires the definition of a remote interface, and a service class which implements it. See the following:
@Remote public interface MyRemoteService { public boolean isEveryoneHappy(); }
The @Remote
annotation tells Errai that we'd
like to use this interface as a remote interface. The remote interface
must be part of of the GWT client code. It cannot be part of the
server-side code, since the interface will need to be referenced from
both the client and server side code. That said, the implementation of
a service is relatively to the point:
@Service public class MyRemoteServiceImpl implements MyRemoteService { public boolean isEveryoneHappy() { // blatently lie and say everyone's happy. return true; } }
That's all there is to it. You use the same
@Service
annotation as described in Section 2.4.
The presence of the remote interface tips Errai off as to what you
want to do with the class.
Calling a remote service involves use of the
MessageBuilder
API. Since all messages are
asynchronous, the actual code for calling the remote service involves
the use of a callback, which we use to receive the response from the
remote method. Let's see how it works:
MessageBuilder.createCall(new RemoteCallback<Boolean>() { public void callback(Boolean isHappy) { if (isHappy) Window.alert("Everyone is happy!"); } }, MyRemoteService.class).isEveryoneHappy();
In the above example, we declare a remote callback that receives a Boolean, to correpond to the return value of the method on the server. We also reference the remote interface we are calling, and directly call the method. However, don't be tempted to write code like this:
boolean bool = MessageBuilder.createCall(..., MyRemoteService.class).isEveryoneHappy();
The above code will never return a valid result. In fact, it will always return null, false, or 0 depending on the type. This is due to the fact that the method is dispatched asynchronously, as in, it does not wait for a server response before returning control. The reason we chose to do this, as opposed to emulate the native GWT-approach, which requires the implementation of remote and async interfaces, was purely a function of a tradeoff for simplicity.
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.
One of the things Errai offers is the concept of session and local scopes.
A local scope is scoped to a single browser instance. But not to a single session.
In a browser a local scope would be confined to a tab or a
window within a browser. You can store parameters inside a local
scope just like with a session by using the
LocalContext
helper class.
public class TestService implements MessageCallback { public void callback(final Message message) { // obtain a reference to the local context by referencing the incoming message. LocalContext injectionContext = LocalContext.get(message); // set an attribute. injectionContext.setAttribute("MyAttribute", "Foo"); } }
A session scope is scoped across all instances of the same session. When a session scope is used, any parameters stored will be accessible and visible by all browser instances and tabs.
The SessionContext helper class is used for accessing the session scope.
public class TestService implements MessageCallback { public void callback(final Message message) { // obtain a reference to the session context by referencing the incoming message. SessionContext injectionContext = SessionContext.get(message); // set an attribute. injectionContext.setAttribute("MyAttribute", "Foo"); } }
The lifescyle of a session is bound by the underlying HTTP session. It is also bound by activity thresholds. Clients are required to send heartbeat messages every once in a while to maintain their sessions with the server. If a heartbeat message is not received after a certain period of time, the session is terminated and any resources are deallocated.
Serialization on the ErraiBus supports serialization within the
same scope and limitations as the default GWT RPC serialization rules.
In order to expose your domain objects to the bus so they can be
exported across the bus, you must annotate them with the
org.jboss.errai.bus.server.annotations.ExposeEntity
annotation. The presence of this annotation will cause Errai's GWT
compiler extensions to generate marshall/demarshall stubs for the
annotated objects at compile-time.
For example:
@ExposeEntity public class User implements java.io.Serializable { private int userId; public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } [...] }
All exposed entities must follow Java Bean convensions, and must be in the classpath both at compile-time and at runtime. Compile-time access to the entities is required since the creation of the marshalling/demarshalling proxies involves code generation.
.
It may not be possible to annotate certain types you wish to
expose to the bus for serialization if the entities are located in a
third-party library that you do not maintain. As such, you can
explicitly indicate in the configuration that you would like to have
this entities made available by declaring them in the
ErraiApp.properties
of any module.
errai.bus.serializableTypes=org.foo.Foo \ org.bar.Bar \ org.foobie.Foobie
Currently, ErraiBus uses Google Guice to wire components. However,
we plan on falling back on the JSR-330 Dependency
Injection specification in the near future. When deploying
services on the server-side, it is currently possible to obtain
references to the MessageBus
,
RequestDispatcher
, the
ErraiServiceConfigurator
, and
ErraiService
by declaring them as injection
dependencies in Service classes, extension components, and session
providers.
This section contains information on configuring the server-side bus.
Depending on what application server you are deploying on, you must provide an appropriate servlet implementation if you wish to use true, asynchronous I/O. See section 6.5 for information on the available servlet implementations.
Here's a sample web.xml file:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>ErraiServlet</servlet-name> <servlet-class>org.jboss.errai.bus.server.servlet.DefaultBlockingServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ErraiServlet</servlet-name> <url-pattern>*.erraiBus</url-pattern> </servlet-mapping> <context-param> <param-name>errai.properties</param-name> <param-value>/WEB-INF/errai.properties</param-value> </context-param> <context-param> <param-name>login.config</param-name> <param-value>/WEB-INF/login.config</param-value> </context-param> <context-param> <param-name>users.properties</param-name> <param-value>/WEB-INF/users.properties</param-value> </context-param> </web-app>
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
The errai.dispatcher_implementation
defines, as it's name quite succinctly implies, the dispatcher
implementation to be used by the bus. There are two implementations
which come with Errai out of the box: the
SimpleDispatcher
and the
AsyncDispatcher
. See section on Dispatchers for
more information about the differences between the two.
Specifies the total number of worker threads in the worker pool for handling and delivering messages. Adjusting this value does not have any effect if you are using the SimpleDispatcher.
Specifies the total amount of a time (in seconds) a service has to finish processing an incoming message before the pool interrupts the thread and returns an error. Adjusting this value does not have an effect if you are using the SimpleDispatcher.
Specifies the authentication modelAdapter the bus should use for determining whether calls should be serviced based on authentication and security principles.
Indicates whether or not the bus should always require the use of authentication for all requests inbound for the bus. If this is turned on, an authentication modelAdapter must be defined, and any user must be authenticated before the bus will deliver any messages from the client to any service.
A boolean indicating whether or not the Errai bootstrapper should automatically scan for services.
The ErraiApp.properties acts as a marker file. When it is detected inside a JAR or at the top of any classpath, the subdirectories are scanned for deployable components. As such, all Errai application modules in a project should contain an ErraiApp.properties at the root of all classpaths that you wish to be scanned.
The file can also include explicitly declared serializable types (such as those from third-party code) that cannot be annotated for serialization. (See the section on serialization for more details)
Dispatchers encapsulate the strategy for taking messages that need to be delivered somewhere and seeing that they are delivered to where they need to go. There are two primary implementations that are provided with Errai, depending on your needs.
SimpleDispatcher is basic implementation that provides no asychronous delivery mechanism. Rather, when you configure the Errai to use this implementation, messages are delivered to their endpoints synchronously. The incoming HTTP thread will be held open until the messages are delivered.
While this sounds like it has almost no advantages, especially in terms of scalablity. Using the SimpleDispatcher can be far preferable when you're developing your application, as any errors and stack traces will be far more easily traced and some cloud services may not permit the use of threads in any case.
The AsyncDispatcher provides full asynchronous delivery of messages. When this dispatcher is used, HTTP threads will have control immediately returned upon dispatch of the message. This dispatcher provides far more efficient use of resources in high-load applications, and will significantly decrease memory and thread usage overall.
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
This is a universal, completely servlet spec compliant, Servlet implementation. It provides purely synchronous request handling and should work in virtually any servlet container, unless there are restrictions on putting threads into sleep states.
The Tomcat AIO implementation of our servlet allows Errai to take advantage of Tomcat's event-based AIO APIs to improve scalability and reduce thread usage. The use of this implementation is dependant on the Tomcat container being configured to support AIO using either it's NIO or APR connectors. This servlet will NOT work with the regular HTTP and AJP connectors.
The Jetty implementation leverages Jetty's continuations support, which allows for threadless pausing of port connections. This servlet implementation should work without any special configuration of Jetty.
The JBoss Comet support utilizes the JBoss Web AIO APIs to improve scalability and reduce thread usage. The HTTP, NIO, and AJP connectors are not supported. Use of this implementation requires use of the APR (Apache Portable Runtime).
Support for the comet API in the Grizzy HTTP server (used in Glassfish).
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.
It's good to start with an explanation of the basic workspace concepts and terminlogy that we chose for it.
A workspace, in our term, is basically a collection of tools. Each tool serves a distinct purpose and can represent anything you like. The workspace manages the tools (or tool sets) and access to them. This includes loading and initialization, history, preferences and authorization amongst other things. Usually tools rely on the message bus for inter component communication as well as backend integration. But they are not tight to that. Workspaces makes no assumption about the implementation of your tool, it merely provides the glue to keep everything together.
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.
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.
Tools are declared through the @LoadToolSet annotation. This instructs the workspace to generate all the boiler plate to wire your tools with the workspace. A tool needs to implement the WidgetProvider interface. It's used by the workspace to instruct the tool to create it's UI widget when needed. This widget will then be embedded in the workspace.
@LoadTool(name = "Users", group = "Administration") (1) public class UserManagement implements WidgetProvider { public void provideWidget(final ProvisioningCallback callback) { (2) callback.onSuccess(new UserManagementWidget()); } class UserManagementWidget extends LayoutPanel (3) { [...] } }
Toolset declaration including the name and group this tool belongs to
The WidgetProvider implementation is manadatory.
In this example our widget is LayoutPanel with unknown contents. The implementation is up to you.
Workspaces relies on a authentication scheme similiar to JAAS. It ships with different authentication modules (i.e. Hibernate, JAAS) that enable role based access to tools on the client side and services on the server side.
@LoadTool(name = "Inventory", group = "Administration") (1) @RequireRoles({"admin"}) (2) public class Inventory implements WidgetProvider { public void provideWidget(final ProvisioningCallback callback) (3) { [...] } }
A tool declaration
It restricts access to the role 'admin'
Implements the default WidgetProvider interface which will only be invoked when the principal is authorized to access this tool.
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 registry (org.jboss.errai.workspaces.client.framework.Registry) is a simple all purpose lookup for common workspace API that is available to any workspace component:
AuthenticationContext authenticationContext = Registry.get(SecurityService.class).getAuthenticationContext();
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.
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 LoginClient is the counter part of the SecurityService. It is instructed by the SecurityService to request the credentials upon demand. This typically happens when the workspace is initialized, but might also occur later on, when a particular service requires a different role set, then the one assigned initially. We call this elevation. Similiar to the operating system of your choice that demands administrator priviledges for certain operations.
After the initial authentication (or lack of) the LoginClient instructs the actual workspace to assemble the tool sets.
History is build into the workspace. It's wired to the main navigation and allows to use the browser navigation to switch between previously selected tools. In addition to that, the same mechanism can be used to perma link tools from external applications. I.e. when notifying users, using an email that contains a link back to a particular tool.
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.
You need Apache Maven and a JDK to get started.
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.
Several configuration files drive a workspace build and the final application. Make sure to include all of them when porting the archetype example to a different GWT application:
./src/main/java/ErraiApp.properties ./src/main/java/ErraiService.properties ./src/main/java/my/app/App.gwt.xml
ErraiApp.properties is a simple marker, that is required for the annotation processing to discover your modules. This is especially important when you declare maven dependencies on tool or service implementations. Make sure that any classpath entry (i.e. dependency jar) contains an ErraiApp.properties file. Otherwise errai might not be able to discover your components.
ErraiService.properties is the primary bus configuration. Details can be found in the chapter bus configuration.
App.gwt.xml is your applications module descriptor.
<module rename-to="app"> <inherits name='com.google.gwt.user.User'/> <inherits name="com.google.gwt.uibinder.UiBinder"/> <inherits name="com.google.gwt.resources.Resources" /> <inherits name="org.jboss.errai.common.ErraiCommon"/> <inherits name="org.jboss.errai.bus.ErraiBus"/> <inherits name="org.jboss.errai.workspaces.ErraiWorkspaces" /> <entry-point class='my.app.client.HelloWorldClient'/> </module>
If you are building on workspaces make sure to include it's dependencies common and bus as well.
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
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.
The CDI integration module offers seamless integration of CDI backend components with GWT client applications. Build as an extension to the Errai core framework, it allows you to transparently access CDI beans while harnessing the power of the core framework
Beans that are deployed to a CDI container (i.e. JBoss 6) will automatically be registered with Errai and exposed to your GWT client application. Communication between GWT client components and CDI beans can done in several ways:
RPC style invocations on beans through a typed interface
Access beans in a publish/subscribe manner
Wiring up your GWT application with the CDI event subsystem
When chosing RPC style invocations on beans, you basically rely on a typed java interface the CDI managed bean needs to expose. A GWT client component can then create an invocation proxy based on this interface. For more information see chapter on RPC mechanism
If you chose publish/subscribe then your CDI bean needs to implement the
MessageCallback
interface, as described in
chapter 'Messaging'. Any bean exposed in this way can
accessed through the MessageBuilder
API.
Any CDI managed component may produce and consume events. This allows beans to inertact in a completely decoupled fashion. Beans consume event by registering for a particular event type and qualifier. The Errai CDI extension simply extends this concept into the client tier. A GWT client application can simply register an Observer for a particular event type and thus receive events that are produced on the server side. Likewise GWT clients can produce events that are consumed by a server side component.
Let's take alook at an example.
public class FraudClient extends LayoutPanel { @Inject public Event event; (1) private HTML responsePanel; public FraudClient() { super(new BoxLayout(BoxLayout.Orientation.VERTICAL)); Button button = new Button("Create activity", new ClickHandler() { public void onClick(ClickEvent clickEvent) { event.fire(new AccountActivity()); } }); responsePanel = new HTML(); add(button); add(responsePanel); } public void processFraud(@Observes Fraud fraudEvent) { (2) responsePanel.setText("Fraud detected: " + fraudEvent.getTimestamp()); } }
Two things are notable in this example:
Injection of an event dispatcher proxy
Creation of an Oberserver method for a particular event type
The event dispatcher is responsible to send events created on the client side to the server side event subsystem (CDI container). This means any event that is fired through a dispatcher will eventually be consumed by a CDI managed bean. If there is an Observer registered for it on the server side.
In order to consume events that are created on the server side you need to declare an Observer method for a particular event type. In case an event is fired on the server side this method will be invoked with an event instance of type you declared.
To complete this exercise, let's look at the corrsponding CDI managed bean as well:
@ApplicationScoped public class AccountService { @Inject @Any Event<Outbound> event; public void watchActivity(@Observes @Inbound AccountActivity activity) { Fraud payload = new Fraud(System.currentTimeMillis()); event.fire(new Outbound(payload)); } }
If you take a close look to the server side event example you can see
the usage of @Inbound
and @Outbound
.
This is currently one of the constraints we impose on the event integration.
These qualifiers are used to distinguish communication through the event subsystem
that reaches the client and communication that is restricted to the server side components.
The reason is simple: We don't want every event to be pushed to the GWT client, since there is
a certain overhead invloved (serialization, HTTP traffic, etc).
The CDI integration is a plugin to the Errai core framework and represents a CDI portable extension. Which means it is disovered automatically by both Errai and the CDI container. In order to use it, you first need to understand the different runtime models involved when working GWT, Errai and CDI.
Typically a GWT application lifecycle begins with Hosted Mode development and finally a web application containing the GWT client code will be deployed to a target container (Servlet Engine, Application Server). This is no way different when working with CDI components to back your application.
What's different however is availability of the CDI container across the different runtimes. In GWT hosted mode and in a pure servlet environment you need to provide and bootstrap the CDI environment on your own. While any Java EE 6 Application Server already provides a preconfigured CDI container. To accomodate these differences, we need to do a little trickery when executing the GWT Hosted Mode and packaging our application for deployment.
In Hosted Mode we need to bootstrap the CDI environment on our own and make both Errai and CDI available through JNDI (common denominator across all runtimes). Since GWT uses Jetty, that only supports read only JNDI, we need to replace the default Jetty launcher with a custom one that will setup the JNDI bindings:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>gwt-maven-plugin</artifactId> <version>${gwt.maven}</version> <configuration> [...] <server>org.jboss.errai.cdi.server.gwt.JettyLauncher</server> </configuration> <executions> [...] </executions> </plugin>
Once that is setup correctly we can boostrap the CDI container though servlet listener
<web-app> [...] <listener> <listener-class>org.jboss.errai.container.DevModeCDIBootstrap</listener-class> </listener> <resource-env-ref> <description>Object factory for the CDI Bean Manager</description> <resource-env-ref-name>BeanManager</resource-env-ref-name> <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type> </resource-env-ref> [...] </web-app>
Sounds terribly complicated, no? Don't worry we provide a maven archetype that takes care of all these setup steps and configuration details.
Deployment to servlet engine has basically the same requirements as running in hosted mode. You need to include the servlet listener that bootstraps the CDI container and make sure both Errai and CDI are accessible through JNDI. For Jetty you can re-use the artefacts we ship with the archetype. In case you want to run on tomcat, please consult the Apache Tomcat Documentation However the most simple case, is to use JBoss right away.
We provide integration with the JBoss Application Server,
but the requirements are basically the same for other vendors. When running a GWT client app that leverages
CDI beans on a Java EE 6 application server, CDI is already part of the container and accessible through JNDI
(java:/BeanManager
). The only thing that needs to be done, is to expose Errai through JNDI (java:/Errai
),
which in case of JBoss, is done through the deployment of a service archive (errai-jboss.sar
).
The CDI extension, that is part of the distribution, contains an installer for the Errai-CDI service archive. Please consult the README.txt in the extension directory how to use it.
Laika:errai-<VERSION> hbraun$ lstree |-doc |-examples |-ext |---cdi |-----lib |-----resources |-lib
Since the discovery of service implementations (beans) is delegated to the
CDI container, we need to disable Errai's own service discovery mechanism.
In order to do so, simply turn off the auto-discovery feature in
ErraiService.properties
:
# # Disable service auto discovery # It's the CDI container responsibility # errai.auto_discover_services=false
The different runtime models explained above are all incorporated into the maven archetype. It allows execution in Hosted Mode and supports both packaging for depoyment to a Servlet Engine and the JBoss Application Server.
To begin with we'll create a project layout using a maven build structure, which will provide us with bare bone project, including all dependencies, which can later on be imported in eclipse.
mvn archetype:generate \ -DarchetypeGroupId=org.jboss.errai \ -DarchetypeArtifactId=cdi-archetype \ -DarchetypeVersion=1.1-CR1 \ -DarchetypeRepository=https://repository.jboss.org/nexus/content/groups/public/Customize the build properties according to your needs.
Define value for property 'groupId': : foo.bar Define value for property 'artifactId': : gwt-webapp Define value for property 'version': 1.0-SNAPSHOT: Define value for property 'package': foo.bar: com.foo.bar Confirm properties configuration: groupId: foo.bar artifactId: gwt-webapp version: 1.0-SNAPSHOT package: com.foo.bar Y:The archetype will be created in a directory that equals the artifactId.
Laika:Desktop hbraun$ cd gwt-webapp/ Laika:gwt-webapp hbraun$ ll -rw-r--r-- 1 hbraun staff 13119 Jul 21 16:01 pom.xml drwxr-xr-x 5 hbraun staff 170 Jul 21 16:01 src drwxr-xr-x 6 hbraun staff 204 Jul 21 16:01 war
mvn gwt:run (launch hosted mode) mvn gwt:debug (launch hosted with debug settings) mvn install (package and install the web application)
The setup instuctions for eclipse can be found in the Errai WIKI
By default the archtype does package the web application for Hosted Mode execution
and other servlet environments. If you want to deploy your application to JBoss, you need
todo a clean rebuild using the JBoss profile (-Pjboss6
).
mvn -Pjboss clean install cp target/foobr-jboss6.war $JBOSS_HOME/server/default/deploy
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:
It pulls the GWT from a central repository. No need for manual installation of the SDK
It provides us with the proper dependencies in order to run the examples
IDE setup is greatly simplfied, since most modern IDE's can directly import the maven structure
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-CR2 \ -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.
By default the archtype does package the web application for Hosted Mode execution
and other servlet environments. If you want to deploy your application to JBoss, you need
todo a clean rebuild using the JBoss profile (-Pjboss6
).
mvn -Pjboss clean install cp target/foobar.war $JBOSS_HOME/server/default/deploy
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>
Proxy configuration. See details below.
The proxy url pattern convetion. The bus bootstraps on this URL.
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.