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: https://anonsvn.jboss.org/repos/errai/trunk/
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://www.jboss.org/index.html?module=bb&op=viewforum&f=295
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 will start off with some solid code examples and describe the different messaging patterns that are employed by ErraiBus. In later sections, we'll delve into the different percularities of the API, implications, and more complex cases.
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.
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 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 MessageBus private MessageBus bus = ErraiBus.get(); 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(bus); // (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 of the MessageBus
On the client side a reference to the bus is obtained through the singleton ErraiBus.get(). On the server side however it's slightly different. Here the Guice dependency injection provides us with a bus reference, as we can see in the next example.
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 MessageBus bus; @Inject public HelloWorldService(MessageBus bus) { this.bus = bus; } 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(bus); // (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 MessageBus.
If a client needs to receive messages that are "pushed" from the server side it needs to register a listener on a certain subject.
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"); } }); [...] }
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 { private MessageBus bus; @Inject public HelloWorldService(MessageBus bus) { this.bus = bus; } 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() .sendNowWith(bus); }); } }
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 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(bus);
And the conversational code on the server (for service ObjectService):
MessageBuilder.createConversation(message) .subjectProvided().signalling() .with("Records", records) .noErrorHandling().sendNowWith(bus);
In the above examples, assuming that the latter example is
inside a service called "ObjectService" and is referencing the
incoming message that was send 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().sendNowWith(bus);
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 queue's to use to deliver the message to 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 ClientId 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(bus);
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!
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.
ErraiBus provides facility to making asynchronous RPC calls
on-top of the bus archhitecture without the need to explicitly declare
services or any specific mapping or boilerplate code. This method of
communicating with the server is straight-forward and utilizes the
simple RemoteCall
API.
Remote procedure calls can be be performed against against
service class which has been annoted with the
@Service
annotation and the accompanying method
which is being called has been annotated with the
@Endpoint
annotation, and the method has public
access.
@Service public class QueryService { private Map<String, String[]> dataMap; public QueryService() { setupMap(); } private void setupMap() { dataMap = new HashMap<String, String[]>(); dataMap.put("beer", new String[]{"Heineken", "Budweiser", "Hoogaarden"}); dataMap.put("fruit", new String[]{"Apples", "Oranges", "Grapes"}); dataMap.put("animals", new String[]{"Monkeys", "Giraffes", "Lions"}); } @Endpoint public String[] getQuery(String queryString) { return dataMap.get(queryString.toLowerCase()); } }
Notice the @Endpoint
annotation on the
getQuery()
method in the above figure. In this
example, the method simply accepts a single String parameter and
returns an array of Strings. The method can accept and return any
serializable types that have been exposed to the bus within the rules
of serializability as laid out in the section on Object
Serialization.
Also note that in this example, the service class is not
required to implement the MessageCallback
interface and is a simple POJO class.
Calls to RPC endpoints are made using the RemoteCall API which is very similar to the MessageBuilder API, with a few key differences. Here is some code for calling the RPC endpoint we defined in section 3.1.
MessageBuilder.createCall() .call("QueryService") // (1) .endpoint("getQuery", queryBox.getText()) // (2) .respondTo(String[].class, new RemoteCallback<String[]>() { // (3) public void callback(String[] resultsString) { if (resultsString == null) { resultsString = new String[]{"No results."}; } /** * Build an HTML unordered list based on the results. */ StringBuffer buf = new StringBuffer("<ul>"); for (String result : resultsString) { buf.append("<li>").append(result).append("</li>"); } results.setHTML(buf.append("</ul>").toString()); } }) .noErrorHandling() .sendNowWith(bus); // (4)
In this example we (1) send a call to the
"QueryService"
class with (2) the endpoint
"getQuery"
, which is the name of the method which
we annotated, along with the parameter we which to pass to the method.
Note that you can send as many parameters as you want as the
endpoint()
method accepts varargs
parameter.
Then (3) we declare a
RemoteCallback
to handle the response back from
the server. We declare the response type to be that of
String[]
to correlate with the return type of the
endpoint on the server and fill in handling code for the response. In
this case we output an HTML unordered list manually.
Finally (4) we send the call.
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 the java bean convention
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.
Note: The details in this section are currently subject to change. Please consult this section if you are working off pre-release versions of ErraiBus.
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.
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 an 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 adapter 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 adapter must be defined, and any user must be authenticated before the bus will deliver any messages from the client to any service.
The ErraiApp.properties is a marker file. It does not contain any configuration, but it is required that it be present in any module you wish Errai to scan for deployable extensions and services. It should be located as the top-level directory of any JAR or Directory-based module.
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. 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>
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.
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.0.0 \ -DarchetypeRepository=http://repository.jboss.com/maven2
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.