A Quick Tour of Errai 2.0
The first beta of Errai 2.0, 2.0.Beta1, is now available! You can download the release in a
zipfile, or–even simpler–use
one of our archetypes to get started right away.
In this post, I will walk you through all the bits and pieces of the framework as it stands today, and I will wrap up with a peek at the future. But first, let's look at where and why we started this project, and what motivated us to take it where it is today.
The Past
Errai started with the vision that, as browsers become
more and more capable computing platforms, the complexity of codebases we write for the browser will grow. Although the ECMAScript language has matured significantly over the years in terms of features, uniform behaviour across browsers, and speed of execution, it still remains true to its roots as a scripting language: loosely typed, little support for encapsulation,
poor support for immutable types, and no direct linguistic support for namespaces. Perhaps most importantly, ECMAScript is a slippery target for refactoring and navigation tools.
The idea behind the Errai Framework is that we (Java developers) have already learned how to develop large code bases, managing their complexity with an array of tools and practices that are simply not available to us in other environments:
- Completely safe refactorings such as rename, move, extract to method, and more
- Typesafe code completion with popup API documentation
- The ability to create truly immutable objects
- Test-driven development using JUnit
- IDEs that highlight dead code, unused methods, and unsafe casts
- Static analysis with FindBugs, CheckStyle, and PMD
- Advanced step-by-step debugging tools
Errai's vision is that we can and should apply these advantages to the increasingly intricate codebases we write for the client side of today's modern web applications.
The Present
With Errai 2.0, we're taking a big step toward the realization of Errai's vision: to provide a uniform, familiar, and well-tooled programming model across client and server. Yes, Errai 1.x provided a uniform programming model across client and server, providing its own publish/subscribe bus API and its own RPC API. Those APIs remain in place, but we now think of them as lower-level enablers. With Errai 2.0, we've begun implementing Java EE 6 APIs on top of the base APIs in Errai 2.0. So far, we support CDI in the client, as well as a JAX-RS client API.
High-level APIs in Errai 2.0
CDI in the Browser
With CDI in the browser, not only can you @Inject dependencies into your beans, but you can also @Observe events that occur both locally (on the client) as well as remotely (on the server.) Naturally, the inverse is true: events fired on the client are observable on the server. To be clear, all of the following are possible:
- Client-local communication (fire and observe events within the browser)
- Server-local communication (this is just traditional CDI eventing)
- Server-to-client communication (Errai pushes server events to the client)
- Client-to-server communication (Errai pushes events from the client to the server)
Underlying this feature is the good old Errai Bus, which will ensure that events are only routed across the wire if there are actually observers interested in that event on the other end of the connection. No HTTP traffic for local events.
Here's what it looks like:
Server Side
@ApplicationScoped
public class TickerService {
@Inject
private Event<Tick> tickEvent;
private void sendTick() {
tickEvent.fire(new Tick());
}
}
Client Side
@EntryPoint
public class TickerClient {
public void tickHappened(@Observes Tick tick) {
// update the UI with the new data
}}
JAX-RS Client
New in Errai 2.0 is the JAX-RS client. This is a true JAX-RS client framework. It does not route messages over the Errai Bus; it makes direct AJAX (XMLHTTPRequest) calls to the JAX-RS resource methods on the server side. Of course, all the object marshalling is taken care of on both sides, just like with bus messaging.
Sticking with the theme of "familiar," you simply implement your server-side resources with standard JAX-RS annotations, and on the client, you use exactly the same code as you would use for Errai RPC calls. A great new feature with no new APIs!
Here's a taste:
Server Side
@Path("customers")
public interface CustomerService {
@GET
@Produces("application/json")
public List<Customer> listAllCustomers();
}
public class CustomerServiceImpl implements CustomerService {
public List<Customer> listAllCustomers() {
List<Customer> customers = CustomerDAO.getAll();
Collections.sort(customers);
return customers;
}
}
Client Side
@EntryPoint
public class CustomerClient {
@Inject
private Caller<CustomerService> customerService;
private void populateCustomersTable() {
RemoteCallback<List<Customer>> listCallback =
new RemoteCallback<List<Customer>>() {
public void callback(List<Customer> customers) {
addCustomersToTable(customers);
}
};
customerService.call(listCallback).listAllCustomers();
}
}
Errai RPC
Errai RPC remains an important high-level API in Errai 2.0. And since it's identical to the JAX-RS client API at the call site, you are free to switch between Errai Bus and RESTful communication simply be re-annotating your server-side code. The client remains untouched. Here's how it looks (notice that the client code is identical to the JAX-RS example):
Server Side
@Remote
public interface CustomerService {
public List<Customer> listAllCustomers();
}
@ApplicationScoped
@Service
public class CustomerServiceImpl implements CustomerService {
@Override public List<Customer> listAllCustomers() {
List<Customer> customers = CustomerDAO.getAll();
Collections.sort(customers);
}
Client Side
public class CustomerClient {
private Caller<CustomerService> customerService;
private void populateCustomersTable() {
RemoteCallback<List<Customer>> listCallback =
new RemoteCallback<List<Customer>>() {
public void callback(List<Customer> customers) {
addCustomersToTable(customers);
customerService.call(listCallback).listAllCustomers();
And like Errai CDI, client and server code are interchangeable. You could swap these client and server code snippets, or put both on the client, or both on the server, and they would sill be able to communicate with each other.
To try Errai RPC yourself, clone our
Errai Kitchen Sink quickstart (note this quickstart also uses JPA, JAX-RS, CDI, and Bean Validation, and it's only set up to work in
JBoss AS 7 at the moment.)
Lower-level APIs in Errai 2.0
Errai Bus and MessageBuilder
If you're familiar with Errai from the 1.x days, you'll already be familiar with the Errai Bus. The bus remains central to the workings of most Errai high-level features, including CDI remote eventing and Errai RPC (JAX-RS is the exception; it's independent of the bus.)
Using the low-level Errai Bus API, you can send messages on a particular subject and register/deregister callbacks that receive messages on a particular subject. Naturally, the API for doing this on the client or the server is the same.
MessageBuilder is a fluent API for constructing (and optionally transmitting) Errai Bus messages. Although we're now leading with the higher-level APIs, MessageBuilder remains available as a convenient way to construct Errai Bus messages "by hand." Of course, the above-mentioned high-level communication features are actually using the MessageBuilder API on your behalf.
Server Side
@Service
public class HelloWorldService implements MessageCallback {
public void callback(Message message) {
MessageBuilder.createConversation(message)
.subjectProvided()
.withValue("Hello, World! The server's time is now " + new Date() + ".")
.done().reply();
}
}
Client Side
@EntryPoint
public class HelloWorldClient extends VerticalPanel {
@Inject
private MessageBus bus;
void sendMessage() {
MessageBuilder.createMessage()
.toSubject("HelloWorldService")
.withValue("Hello, There!")
.done()
.repliesTo(new MessageCallback() {
public void callback(Message message) {
responseLabel.setText(
message.get(String.class, MessageParts.Value));
}
})
.sendNowWith(bus);
}
}
Enabling Technologies
Errai-codegen
Because we use the GWT compiler to bring all this great stuff to the browser, and because GWT eschews runtime reflection in favour of up-front code generation, most Errai features are centred around generating Java source code to feed into GWT at compile time. Because we're such believers in the value of static type checking, we felt compelled to do something a little more structured than just spitting strings into a file and hoping that the result was a well-formed Java source file. Enter Errai's code generator.
Errai-codegen is a fluent API for generating Java source code. It provides static type checking and variable scope checking as it goes, and its promise to you is that it will not allow the creation of a class that can't compile. It also does a really nice job of formatting its output.
Do you generate Java code in any of your projects? Feel free to try out our code generator and let us know what you think. We'd love to hear from you!
Server Side (typically runs at compile time)
Context ctx = Context.create();
String s = StatementBuilder
.create()
.declareVariable("n", Integer.class, 10)
.generate(ctx);
assertEquals("Integer n = 10;", s);
VariableReference n = ctx.getVariable("n");
assertEquals("n", n.getName());
assertEquals(MetaClassFactory.get(Integer.class), n.getType());
assertEquals(LiteralFactory.getLiteral(10), n.getValue());
Errai MarshallingEvery object sent across the Errai Bus, as well as the JSON representations sent and received by your JAX-RS resources, is represented as a JSON object. Errai provides a robust marshalling framework that aims to provide maximum flexibility while imposing minimum markup on your code.
Errai's Marshalling framework includes all of the following techniques, which you can mix and match as you like, based on where you place your annotations:
- Private/default/protected/public field read & write
- Setter and Getter property access
- Provide all properties to the constructor - my favourite because it allows for marshalling properly immutable types
- Out-of-the-box support for marshalling all JDK classes that are GWT translatable
- Custom marshallers for those really hard-to-reach classes :)
Errai IoC
Although Errai supports dependency injection via CDI, CDI remains an optional module for now. If you choose not to use it, you can still use @Inject annotations to obtain references to Errai-provided dependencies such as Sender<?> and Caller<?>. And of course, this works the same in client code and server code.
The Future
Of course, we have our eyes on the horizon beyond Errai 2.0. The theme of our longer-term plans is to continue providing Java EE 6 functionality within the client, while providing a practical framework to keep your app running offline, when the server is unavailable.
JPA with Sync
For starters, you probably already have JPA annotations on your model objects. We plan to breathe life into them on the client side. Errai's JPA module will allow you to store and query your model objects locally on the client for speedy access and for storage and retrieval when the web server can't be contacted. We also plan to add features that will make data synchronization as painless as possible when the server does come back.
Shadow Services
Speaking of offline, I've already mentioned a couple of times that Errai Bus can deliver messages locally as well as remotely. When the server goes away, the local bus remains fully operational in the webpage. This means you can already create services within the client, and the calling code doesn't look any different: the bus itself makes the routing decisions.
We plan to take full advantage of this behaviour by having the client-side bus automatically deliver messages to local "shadow" services when the remote ones are unavailable. Combined with client-side JPA, we hope this will make for a really slick programming model for webapps that keep on tickin' when the server is unreachable.
JBoss Forge
We love Forge, and we can't wait to start using it to build Errai projects. Forge's existing JPA scaffolding will already help you quite a bit with an Errai app, but we can extend that further to make the necessary objects marshallable, and maybe even generate some UiBinder templates. Exciting times ahead!
And maybe you have some ideas! What other parts of the EE 6 suite of APIs do you wish you could use in client-side code? What tooling would make this even better? Come to
our discussion forum and let's talk!