JBoss.orgCommunity Documentation
CDI (Contexts and Dependency Injection) is the Jave EE standard (JSR-299) for handling dependency injection. In addition to dependency injection, the standard encompasses component lifecycle, application configuration, call-interception and a decoupled, type-safe eventing specification.
The Errai CDI extension implements a subset of the specification for use inside of client-side applications within Errai, as well as additional capabilities such as distributed eventing.
Errai CDI does not currently implement all life cycles specified in JSR-299 or interceptors. These deficiencies may be addressed in future versions.
Errai CDI is implemented as an extension on top of the Errai IOC Framework (see Chapter 3, Dependency Injection ), which itself implements JSR-330. Inclusion of the CDI module your GWT project will result in the extensions automatically being loaded and made available to your application.
Errai CDI only scans the contents of classpath locations (JARs and directories) that have
a file called
ErraiApp.properties
at their root. If CDI features such as dependency injection, event observation, and
@PostConstruct
are not working for your classes, double-check that you have an
ErraiApp.properties
at the root of every JAR and directory tree that contains classes Errai should know about.
Beans that are deployed to a CDI container will automatically be registered with Errai and exposed to your GWT client application. So, you can use Errai to communicate between your GWT client components and your CDI backend beans.
Errai CDI based applications use the same annotation-driven programming model as server-side CDI components, with some notable limitations. Many of these limitations will be addressed in future releases.
There is no support for CDI interceptors in the client. Although this is planned in a future release.
Passivating scopes are not supported.
The JSR-299 SPI is not supported for client side code. Although writing extensions for the client side container is possible via the Errai IOC Extensions API.
The
@Typed
annotation is unsupported.
The
@Interceptor
annotation is unsupported.
The
@Decorator
annotation is unsupported.
The CDI container in Errai is built around the Errai IOC module , and thus is a superset of the existing functionality in Errai IOC. Thus, all features and APIs documented in Errai IOC are accessible and usable with this Errai CDI programming model.
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.
Example 4.1. 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:
Example 4.2. 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);
}
}
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.
Example 4.3. 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:
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.
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:
Example 4.5. 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:
Example 4.6. 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;
}
}
Example 4.7. 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:
Example 4.8. 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);
}
}
Producer methods and fields act as sources of objects to be injected. They are useful when additional control over object creation is needed before injections can take place e.g. when you need to make a decision at runtime before an object can be created and injected.
Example 4.9. App.java
@EntryPoint
public class App {
...
@Produces @Supported
private MyBaseWidget createWidget() {
return (Canvas.isSupported()) ? new MyHtml5Widget() : new MyDefaultWidget();
}
}
Example 4.10. MyComposite.java
@ApplicationScoped
public class MyComposite extends Composite {
@Inject @Supported
private MyBaseWidget widget;
...
}
Producers can also be scoped themselves. By default, producer methods are dependent-scoped, meaning they get called every time an injection for their provided type is requested. If a producer method is scoped
@Singleton
for instance, the method will only be called once, and the bean manager will inject the instance from the first invokation of the producer into every matching injection point.
Example 4.11. Singleton producer
public class App {
...
@Produces @Singleton
private MyBean produceMyBean() {
return new MyBean();
}
}
For more information on CDI producers, see the CDI specification and the WELD reference documentation .
As an alternative to using the bean manager to dynamically create beans, this can be accomplished in a type-safe way by injecting a
javax.enterprise.inject.Instance<T>
.
For instance, assume you have a dependent-scoped bean
Bar
and consider the following:
public class Foo {
@Inject Instance<Bar> barInstance;
public void pingNewBar() {
Bar bar = barInstance.get();
bar.ping();
}
}
In this example, calling
barInstance.get()
returns a new instance of the dependent-scoped bean
Bar
.
If you do not care about the deployment details for now and just want to get started take a look at the Quickstart Guide .
The CDI integration is a plugin to the Errai core framework and represents a CDI portable extension. Which means it is discovered 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 in Development Mode 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 development 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 Development Mode and packaging our application for deployment.
In development 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>
Consequently, when starting Development Mode from within your IDE the following program argument has to be provided: -server org.jboss.errai.cdi.server.gwt.JettyLauncher
JettyLauncher uses different class loaders to load classes that belongs to the web application, the Jetty server, and the Java standard library itself. In the majority of cases, you can simply put all dependencies into your web application's
WEB-INF/lib
folder. However, there are cases where putting a dependency in
WEB-INF/lib
will cause troubles such as
ClassCastException
when same class is also loaded by a different classloader. To mitigate this problem, JettyLauncher can be instructed that certain classes (or packages) shall be loaded only by the system class loader. To do so, set the Java system property
jetty.custom.sys.classes
when launching Dev Mode.
For example, when using gwt-maven-plugin:
<extraJvmArgs>-Djetty.custom.sys.classes=bitronix;javax.transaction</extraJvmArgs>
Once this is set up correctly, we can bootstrap the CDI container through a servlet listener:
<web-app>
...
<listener>
<listener-class>org.jboss.errai.container.CDIServletStateListener</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 development 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 .
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
).