In the first part of this series, we'll focus on the CDI Event API and see how it integrates with Errai Bus.
Some knowledge about Errai Bus and GWT is recommended before reading any further. If you don't know about the Bus and it's purpose, then checkout the project page first.
Creating a CDI event consumer
To begin with, we'll create a simple CDI component that listens on paticular event types:
@ApplicationScoped
public class EchoService
{
@Inject
BeanManager beanManager;
public void echo(@Observes EchoRequest message)
{
String replyText = message.getMessageText() + " Response";
beanManager.fireEvent(
new EchoResponse(replyText),
new ErraiBusQualifier()
);
}
}
In this example the
echo(...)
operation will be invoked on any occurance of an event type EchoRequest
. It will create an EchoResponse
event type and dispatch it through the BeanManager
.A simple GWT client
Now that we've got our server side component implementation, it's time to look at the GWT client. The client component basically needs to do two things:
- Fire events that will ultimately invoke our server side component
- Listen to events that are send in response
Create an event producer
We keep it simple and fire event when a button is clicked:
Button b = new Button("Send", new ClickHandler()
{
public void onClick(ClickEvent clickEvent)
{
CommandMessage.create(WeldCommands.WELD_EVENT)
.toSubject("weldDispatcher")
.set(WeldProtocol.TYPE, EchoRequest.class.getName())
.set(WeldProtocol.OBJECT_REF,
new EchoRequest("Message from client"))
.sendNowWith(bus);
}
});
Register a listener for response messages:
MessageBus.subscribe("echoClient", // local endpoint
new MessageCallback()
{
public void callback(CommandMessage message)
{
[...]
}
}
);
In this case, we've create a listener that will receive messages that are send to the subject "echoClient".
Great, now we have a GWT event producer that sends messages through Errai Bus and we've created a CDI event consumer that listens on the internal CDI event subsystem. But how do we introduce them to each other?
Bridge between Errai Bus and the CDI Event Subsystem
If you carefully followed the example, you might have realized the event producer sends messages to a subject called
"weldDispatcher"
. Actually this is an Errai server side service consuming messages send from a GWT client. That means any message put on the Bus that addresses this particular subject will be directed to a service that is registered to this subject:
@Service("weldDispatcher")
@ApplicationScoped
public class WeldDispatcher implements MessageCallback
{
[...]
// Invoked by Errai
public void callback(CommandMessage message)
{
try
{
switch (WeldCommands.valueOf(message.getCommandType()))
{
case WELD_EVENT:
String type =
message.get(String.class, WeldProtocol.TYPE);
Class clazz =
getClass().getClassLoader().loadClass(type);
Object o = message.get(clazz, WeldProtocol.OBJECT_REF);
beanManager.fireEvent(o);
break;
default:
throw new IllegalArgumentException(
"Unknown command type "+message.getCommandType()
);
}
}
catch (Exception e)
{
throw new RuntimeException(
"Failed to dispatch Weld Event", e
);
}
}
// Invoked by Weld Event producer
public void sendMessage(@Observes @ErraiBus Object message)
{
String subject = null;
ErraiBus busAnnotation =
message.getClass().getAnnotation(ErraiBus.class);
if(busAnnotation!=null)
subject = busAnnotation.subject();
assert subject!=null : "Missing subject declaration";
// put on the bus
CommandMessage.create(WeldCommands.WELD_EVENT)
.toSubject(subject)
.set(WeldProtocol.TYPE, message.getClass().getName())
.set(WeldProtocol.OBJECT_REF, message)
.sendNowWith(erraiBus);
}
[...]
}
What's happening here?
The WeldDispatcher implementation acts a bridge between the Errai Bus and the CDI event subsystem. It does listen on messages in both directions and fowards the event message to the corresponding subsystem: either Errai Bus or CDI.
Receiving messages send from the client
When a messages is send to the
"weldDispatcher"
subject the callback(CommandMessage message)
operation will be invoked. It umarshalls the the event type and forwards it to CDI using the BeanManager
API:
String type = message.get(String.class, WeldProtocol.TYPE);
Class clazz = getClass().getClassLoader().loadClass(type);
Object o = message.get(clazz, WeldProtocol.OBJECT_REF);
beanManager.fireEvent(o);
Forwarding events produced on the server side
The CDI component that we've shown in the beginning of this article, fires
EchoResponse
events in response to it's invocation. These events are caught by a CDI ObserverMethod (sendMessage(@Observes @ErraiBus Object message)
) that extracts the recipient subject, creates an Errai message and put's it on the bus:
CommandMessage.create(WeldCommands.WELD_EVENT)
.toSubject(subject)
.set(WeldProtocol.TYPE, message.getClass().getName())
.set(WeldProtocol.OBJECT_REF, message)
.sendNowWith(erraiBus);
Wrap up
That's it for today. To sum up, here's what we've shown in this example:
- We've created a simple example that uses GWT on the client side.
- It does leverage Errai Bus for integrating with the server side
- We've used a CDI component on the server side to back our application
- All communication is done asynchronously, using a bridge between Errai Bus and the CDI event subsystem
Stay tuned. More articles on Errai and CDI integration are following.
Update
More recent examples can be found here:
http://anonsvn.jboss.org/repos/errai/projects/weld-integration/trunk/