Any CDI managed component may produce and consume events. This allows beans to interact in a completely decoupled fashion. Beans consume events by registering for a particular event type and optional qualifiers. 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 and using the same API, GWT clients can produce events that are consumed by a server-side observer.
Let's take a look at an example.
FraudClient.java
public class FraudClient extends LayoutPanel {
@Inject
private Event<AccountActivity> event; (1)
private HTML responsePanel;
public FraudClient() {
super(new BoxLayout(BoxLayout.Orientation.VERTICAL));
}
@PostConstruct
public void buildUI() {
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 @Detected Fraud fraudEvent) { (2)
responsePanel.setText("Fraud detected: " + fraudEvent.getTimestamp());
}
}
Two things are noteworthy in this example:
-
Injection of an Event dispatcher proxy
-
Creation of an Observer method for a particular event type
The event dispatcher is responsible for sending 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 corresponding 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 client-side observer method for a particular event type. In case an event is fired on the server this method will be invoked with an event instance of type you declared.
To complete the example, let's look at the corresponding server-side CDI bean:
AccountService.java
@ApplicationScoped
public class AccountService {
@Inject @Detected
private Event<Fraud> event;
public void watchActivity(@Observes AccountActivity activity) {
Fraud fraud = new Fraud(System.currentTimeMillis());
event.fire(fraud);
}
}
Conversational events
A server can address a single client in response to an event annotating event types as @Conversational. Consider a service that responds to a subscription event.
SubscriptionService.java
@ApplicationScoped
public class SubscriptionService {
@Inject
private Event<Documents> welcomeEvent;
public void onSubscription(@Observes Subscription subscription) {
Document docs = createWelcomePackage(subscription);
welcomeEvent.fire(docs);
}
}
And the Document class would be annotated like so:
Document.java
@Conversational @Portable
public class Document {
// code here
}
As such, when Document events are fired, they will be limited in scope to the initiating conversational contents – which are implicitly inferred by the caller. So only the client which fired the Subscription event will receive the fired Document event.
Client-Server Event Example
A key feature of the Errai CDI framework is the ability to federate the CDI eventing bus between the client and the server. This permits the observation of server produced events on the client, and vice-versa.
Example server code:
MyServerBean.java
@ApplicationScoped
public class MyServerBean {
@Inject
Event<MyResponseEvent> myResponseEvent;
public void myClientObserver(@Observes MyRequestEvent event) {
MyResponseEvent response;
if (event.isThankYou()) {
// aww, that's nice!
response = new MyResponseEvent("Well, you're welcome!");
}
else {
// how rude!
response = new MyResponseEvent("What? Nobody says 'thank you' anymore?");
}
myResponseEvent.fire(response);
}
}
Domain-model:
MyRequestEvent.java
@Portable
public class MyRequestEvent {
private boolean thankYou;
public MyRequestEvent(boolean thankYou) {
setThankYou(thankYou);
}
public void setThankYou(boolean thankYou) {
this.thankYou = thankYou;
}
public boolean isThankYou() {
return thankYou;
}
}
MyResponseEvent.java
@Portable
public class MyResponseEvent {
private String message;
public MyRequestEvent(String message) {
setMessage(message);
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
Client application logic:
MyClientBean.java
@EntryPoint
public class MyClientBean {
@Inject
Event<MyRequestEvent> requestEvent;
public void myResponseObserver(@Observes MyResponseEvent event) {
Window.alert("Server replied: " + event.getMessage());
}
@PostConstruct
public void init() {
Button thankYou = new Button("Say Thank You!");
thankYou.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
requestEvent.fire(new MyRequestEvent(true));
}
}
Button nothing = new Button("Say nothing!");
nothing.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
requestEvent.fire(new MyRequestEvent(false));
}
}
VerticalPanel vPanel = new VerticalPanel();
vPanel.add(thankYou);
vPanel.add(nothing);
RootPanel.get().add(vPanel);
}
}