JBoss.orgCommunity Documentation
Use the Errai Forge Addon Add Errai Features command and select Errai IOC to follow along with this section.
Checkout the Manual Setup Section for instructions on how to manually add Errai IOC to your project.
The core Errai IOC module implements the JSR-330 Dependency Injection specification for in-client component wiring.
Dependency injection (DI) allows for cleaner and more modular code, by permitting the implementation of decoupled and type-safe components. By using DI, components do not need to be aware of the implementation of provided services. Instead, they merely declare a contract with the container, which in turn provides instances of the services that component depends on.
Errai only scans the contents of classpath locations (JARs and directories) that have a file called
ErraiApp.properties at their root. If dependency injection is not working for you, double-check that you have an ErraiApp.properties
in every JAR and directory that contains classes Errai should know about.
A simple example:
public class MyLittleClass {
private final TimeService timeService;
@Inject
public MyLittleClass(TimeService timeService) {
this.timeService = timeService;
}
public void printTime() {
System.out.println(this.timeService.getTime());
}
}
In this example, we create a simple class which declares a dependency using @Inject
for the interface TimeService
. In this particular case, we use constructor injection to establish the contract between the container and the component. We can similarly use field injection to the same effect:
public class MyLittleClass {
@Inject
private TimeService timeService;
public void printTime() {
System.out.println(this.timeService.getTime());
}
}
In order to inject TimeService
, you must annotate it with @ApplicationScoped
or the Errai DI container will not acknowledge the type as a bean.
@ApplicationScoped
public class TimeService {
}
Although field injection results in less code, a major disadvantage is that you cannot create immutable classes using the pattern, since the container must first call the default, no-argument constructor, and then iterate through its injection tasks, which leaves the potential albeit remote that the object could be left in an partially or improperly initialized state. The advantage of constructor injection is that fields can be immutable (final), and invariance rules applied at construction time, leading to earlier failures, and the guarantee of consistent state.
In contrast to Gin, the Errai IOC container does not provide a programmatic way of creating and configuring injectors. Instead, container-level binding rules are defined by implementing a Provider
, which is scanned for and auto-discovered by the container.
A Provider
is essentially a factory which produces type instances within in the container, and defers instantiation responsibility for the provided type to the provider implementation. Top-level providers use the standard javax.inject.Provider<T>
interface.
Types made available as top-level providers will be available for injection in any managed component within the container.
Out of the box, Errai IOC implements these default top-level providers, all defined in the org.jboss.errai.ioc.client.api.builtin
package:
CallerProvider
: Makes RPC Caller<T>
objects available for injection.DisposerProvider
: Makes Errai IoC Disposer<T>
objects available for injection.InitBallotProvider
: Makes instances of InitBallot
available for injection.IOCBeanManagerProvider
: Makes Errai’s client-side bean manager, ClientBeanManager
, available for injection.MessageBusProvider
: Makes Errai’s client-side MessageBus
singleton available for injection.RequestDispatcherProvider
: Makes an instance of the RequestDispatcher
available for injection.RootPanelProvider
: Makes GWT’s RootPanel
singleton injectable.SenderProvider
: Makes MessageBus Sender<T>
objects available for injection.Implementing a Provider
is relatively straight-forward. Consider the following two classes:
TimeService.java
public interface TimeService {
public String getTime();
}
TimeServiceProvider.java
@IOCProvider
@Singleton
public class TimeServiceProvider implements Provider<TimeService> {
@Override
public TimeService get() {
return new TimeService() {
public String getTime() {
return "It's midnight somewhere!";
}
};
}
}
If you are familiar with Guice, this is semantically identical to configuring an injector like so:
Guice.createInjector(new AbstractModule() {
public void configure() {
bind(TimeService.class).toProvider(TimeServiceProvider.class);
}
}).getInstance(MyApp.class);
As shown in the above example code, the annotation @IOCProvider
is used to denote top-level providers.
The classpath will be searched for all annotated providers at compile time.
Top-level providers are regular beans, so they can inject dependencies particularly from other top-level providers as necessary.
By default, Errai uses Google Guice to wire server-side components. 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 in Service classes, extension components, and session providers.
Alternatively, supports CDI based wiring of server-side components. See the chapter on Errai CDI for more information.
Out of the box, the IOC container supports three bean scopes, @Dependent
, @Singleton
and @EntryPoint
. The singleton and entry-point scopes are roughly the same semantics.
In Errai IOC, all client types are valid bean types if they are default constructable or can have construction dependencies satisfied. These unqualified beans belong to the dependent pseudo-scope. See: Dependent Psuedo-Scope from CDI Documentation
Additionally, beans may be qualified as @ApplicationScoped
, @Singleton
or @EntryPoint
. Although @ApplicationScoped
and @Singleton
are supported for completeness and conformance, within the client they effectively result in behavior that is identical.
Example 3.1. Example dependent scoped bean
public void MyDependentScopedBean {
private final Date createdDate;
public MyDependentScopedBean {
createdDate = new Date();
}
}
Example 3.2. Example ApplicationScoped bean
@ApplicationScoped
public void MyClientBean {
@Inject MyDependentScopedBean bean;
// ... //
}
As is mentioned in the bean manager documentation, only beans that are explicitly scoped will be made available to the bean manager for lookup. So while it is not necessary for regular injection, you must annotate your dependent scoped beans with @Dependent
if you wish to dynamically lookup these beans at runtime.
As Errai IOC provides a container-based approach to client development, support for Errai services are exposed to the container so they may be injected and used throughout your application where appropriate. This section covers those services.
The org.jboss.errai.bus.server.annotations.Service
annotation is used for binding service endpoints to the bus. Within the Errai IOC container you can annotate services and have them published to the bus on the client (or on the server) in a very straight-forward manner:
Example 3.3. A simple message receiving service
@Service
public class MyService implements MessageCallback {
public void callback(Message message) {
// ... //
}
}
As with server-side use of the annotation, if a service name is not explicitly specified, the underlying class name or field name being annotated will be used as the service name.
The org.jboss.errai.bus.server.api.Local
annotation is used in conjunction with the @Service
annotation to advertise a service only for visibility on the local bus and thus, cannot receive messages across the wire for the service.
Example 3.4. A local only service
@Service @Local
public class MyLocalService implements MessageCallback {
public void callback(Message message) {
// ... //
}
}
Services which are registered with ErraiBus via the bean manager through use of the @Service
annotation, have de-registration hooks tied implicitly to the destruction of the bean. Thus, destruction of the bean implies that these associated services are to be dereferenced.
The IOC container, by default, provides a set of default injectable bean types. They range from basic services, to injectable proxies for RPC. This section covers the facilities available out-of-the-box.
The type org.jboss.errai.bus.client.framework.MessageBus
is globally injectable into any bean. Injecting this type will provide the instance of the active message bus running in the client.
The type org.jboss.errai.bus.client.framework.RequestDispatcher
is globally injectable into any bean. Injecting this type will provide a RequestDispatcher
instance capable of delivering any messages provided to it, to the MessageBus
.
The type org.jboss.errai.common.client.api.Caller<?>
is a globally injectable RPC proxy. RPC proxies may be provided by various components. For example, JAX-RS or Errai RPC. The proxy itself is agnostic to the underlying RPC mechanism and is qualified by it’s type parameterization.
For example:
Example 3.7. An example Caller<?> proxy
public void MyClientBean {
@Inject
private Caller<MyRpcInterface> rpcCaller;
// ... ///
@EventHandler("button")
public void onButtonClick(ClickHandler handler) {
rpcCaller.call(new RemoteCallback<Void>() {
public void callback(Void void) {
// put code here that should execute after RPC response arrives
}
).callSomeMethod();
}
}
The above code shows the injection of a proxy for the RPC remote interface, MyRpcInterface
. For more information on defining RPC proxies see Remote Procedure Calls (RPC).
The org.jboss.errai.ioc.support.bus.client.Sender<?>
interface is the lower-level counterpart to the Caller<?>
interface described above. You can inject a Sender
to send low-level ErraiBus messages directly to subscribers on any subject.
For example:
@Inject
@ToSubject("ListCapitializationService")
Sender<List<String>> listSender;
// ... ///
@EventHandler("button")
public void onButtonClick(ClickHandler handler) {
List<String> myListOfStrings = getSelectedCitiesFromForm();
listSender.send(myListOfStrings, new MessageCallback() {
public void callback(Message reply) {
// do stuff with reply
}
);
}
The Sender.send()
method is overloaded. The variant demonstrated above takes a value and a MessageCallback to reply receive a reply (assuming the subscriber sends a conversational reply). The following variants are available:
send(T)
send(T, ErrorCallback)
send(T, MessageCallback)
send(T, MessageCallback, ErrorCallback)
The reply-to service can also be specified declaratively using the @ReplyTo
annotation. This allows the app to receive conversational replies even when using the send()
variants that do not take a MessageCallback
:
@Inject
@ToSubject("ListCapitializationService")
@ReplyTo("ClientListService")
Sender<List<String>> listSender;
// ... ///
@EventHandler("button")
public void onButtonClick(ClickHandler handler) {
List<String> myListOfStrings = getSelectedCitiesFromForm();
listSender.send(myListOfStrings);
}
@Singleton
@Service
public static class ClientListService implements MessageCallback {
@Override
public void callback(Message message) {
// do stuff with message
}
}
These Sender<?>
features are just convenient wrappers around the full-featured programmatic ErraiBus API. See Messaging API Basics and Conversations for full information about low-level ErraiBus communication.
A problem commonly associated with building large applications in the browser is ensuring that things happen in the proper order when code starts executing. Errai IOC provides you tools which permit you to ensure things happen before initialization, and forcing things to happen after initialization of all of the Errai services.
In order to prevent initialization of the bus and it’s services so that you can do necessary configuration, especially if you are writing extensions to the Errai framework itself, you can create an implicit startup dependency on your bean by injecting an org.jboss.errai.ioc.client.api.InitBallot<?>
.
Example 3.8. Using an InitBallot to Control Startup
@Singleton
public class MyClientBean {
@Inject InitBallot<MyClientBean> ballot;
@PostConstruct
public void doStuff() {
// ... do some work ...
ballot.voteForInit();
}
}
Sending RPC calls to the server from inside constructors and @PostConstruct
methods in Errai is not always reliable due to the fact that the bus and RPC proxies initialize asynchronously with the rest of the application. Therefore it is often desirable to have such things happen in a post-initialization task, which is exposed in the ClientMessageBus
API. However, it is much cleaner to use the @AfterInitialization
annotation on one of your bean methods.
Example 3.9. Using @AfterInitialization to do something after startup
@Singleton
public class MyClientBean {
@AfterInitialization
public void doStuffAfterInit() {
// ... do some work ...
}
}
For some very large applications it is possible for initialization to timeout in Development Mode because of deferred code generation. If you experience this problem, you can adjust the initialization timeout value by setting the erraiInitTimeout
variable in your GWT Host Page to a value in milliseconds.
The @Timed
annotation allows scheduling method executions on managed client-side beans. Timers are automatically scoped to the scope of the corresponding managed bean and participate in the same lifecycle (see Bean Lifecycle for details).
In the following example the updateTime
method is invoked repeatedly every second.
@Timed(type = TimerType.REPEATING, interval = 1, timeUnit = TimeUnit.SECONDS)
private void updateTime() {
timeWidget.setTime(System.currentTimeMillis);
}
For delayed one-time execution of methods type = TimerType.DELAYED
can be used instead.
It may be necessary at times to manually obtain instances of beans managed by Errai IOC from outside the container managed scope or creating a hard dependency from your bean. Errai IOC provides a simple client-side bean manager for handling these scenarios: org.jboss.errai.ioc.client.container.ClientBeanManager
.
As you might expect, you can inject a bean manager instance into any of your managed beans. If you use Errai IOC in its default mode you will need to inject the synchronous bean manager (org.jboss.errai.ioc.client.container.SyncBeanManager
).
If you have asynchronous IOC mode enabled simply inject the asynchronous bean manager (org.jboss.errai.ioc.client.container.async.AsyncBeanManager
) instead. Asynchronous IOC brings support for code splitting. That means that any bean annotated with @LoadAsync
can be compiled into a separate JavaScript file that’s downloaded when the bean is first needed on the client. @LoadAsync
also allows to specify a fragment name using a class literal. Using GWT 2.6.0 or higher, all types with the same fragment name will be part of the same JavaScript file.
Example 3.10. Injecting the client-side bean manager
public MyManagedBean {
@Inject SyncBeanManager manager;
// class body
}
If you need to access the bean manager outside a managed bean, such as in a unit test, you can access it by calling org.jboss.errai.ioc.client.container.IOC.getBeanManager()
Looking up beans can be done through the use of the lookupBeans()
method. Here’s a basic example:
Example 3.11. Example lookup of a bean
public MyManagedBean {
@Inject SyncBeanManager manager;
public void lookupBean() {
IOCBeanDef<SimpleBean> bean = manager.lookupBean(SimpleBean.class);
if (bean != null) {
// get the instance of the bean
SimpleBean inst = bean.getInstance();
}
}
}
In this example we lookup a bean class named SimpleBean
. This example will succeed assuming that SimpleBean
is unambiguous. If the bean is ambiguous and requires qualification, you can do a qualified lookup like so:
Example 3.12. Looking up beans with qualifiers
MyQualifier qual = new MyQualifier() {
public annotationType() {
return MyQualifier.class;
}
}
MyOtherQualifier qual2 = new MyOtherQualifier() {
public annotationType() {
return MyOtherQualifier.class;
}
}
// pass qualifiers to ClientBeanManager.lookupBeans
IOCBeanDef<SimpleBean> bean = beanManager.lookupBean(SimpleBean.class, qual, qual2);
In this example we manually construct instances of qualifier annotations in order to pass it to the bean manager for lookup. This is a necessary step since there’s currently no support for annotation literals in Errai client code.
It may be desirable to have multiple matching dependencies for a given injection point with the ability to specify which implementation to use at runtime. For instance, you may have different versions of your application which target different browsers or capabilities of the browser. Using alternatives allows you to share common interfaces among your beans, while still using dependency injection, by exporting consideration of what implementation to use to the container’s configuration.
Consider the following example:
@Singleton @Alternative
public class MobileView implements View {
// ... //
}
and
@Singleton @Alternative
public class DesktopView implements View {
// ... //
In our controller logic we in turn inject the View
interface:
@EntryPoint
public class MyApp {
@Inject
View view;
// ... //
}
This code is unaware of the implementation of View
, which maintains good separation of concerns. However, this of course creates an ambiguous dependency on the View
interface as it has two matching subtypes in this case. Thus, we must configure the container to specify which alternative to use. Also note, that the beans in both cases have been annotated with javax.enterprise.inject.Alternative
.
In your ErraiApp.properties
for the module, you can simply specify which active alternative should be used:
errai.ioc.enabled.alternatives=org.foo.MobileView
You can specify multiple alternative classes by white space separating them:
errai.ioc.enabled.alternatives=org.foo.MobileView \ org.foo.HTML5Orientation \ org.foo.MobileStorage
You can only have one enabled alternative for a matching set of alternatives, otherwise you will get ambiguous resolution errors from the container.
Similar to alternatives, but specifically designed for testing scenarios, you can replace beans with mocks at runtime for the purposes of running unit tests. This is accomplished simply by annotating a bean with the org.jboss.errai.ioc.client.api.TestMock
annotation. Doing so will prioritize consideration of the bean over any other matching beans while running unit tests.
Consider the following:
@ApplicationScoped
public class UserManagementImpl implements UserManagement {
public List<User> listUsers() {
// do user listy things!
}
}
You can specify a mock implementation of this class by implementing its common parent type (UserManagement
) and annotating that class with the @TestMock
annotation inside your test package like so:
@TestMock @ApplicationScoped
public class MockUserManagementImpl implements UserManagement {
public List<User> listUsers() {
// return only a test user.
return Collections.singletonList(TestUser.INSTANCE);
}
}
In this case, the container will replace the UserManagementImpl
with the MockUserManagementImpl
automatically when running the unit tests.
The @TestMock
annotation can also be used to specify alternative providers during test execution. For example, it can be used to mock a Caller<T>
. Callers
are used to invoke RPC or JAX-RS endpoints. During tests you might want to replace theses callers with mock implementations. For details on providers see Container Wiring.
@TestMock @IOCProvider
public class MockedHappyServiceCallerProvider implements ContextualTypeProvider<Caller<HappyService>> {
@Override
public Caller<HappyService> provide(Class<?>[] typeargs, Annotation[] qualifiers) {
return new Caller<HappyService>() {
...
}
}
All beans managed by the Errai IOC container support the @PostConstruct
and @PreDestroy
annotations.
Beans which have methods annotated with @PostConstruct
are guaranteed to have those methods called before the bean is put into service, and only after all dependencies within its graph has been satisfied.
Beans are also guaranteed to have their @PreDestroy
annotated methods called before they are destroyed by the bean manager.
This cannot be guaranteed when the browser DOM is destroyed prematurely due to: closing the browser window; closing a tab; refreshing the page, etc.
Beans under management of Errai IOC, of any scope, can be explicitly destroyed through the client bean manager. Destruction of a managed bean is accomplished by passing a reference to the destroyBean()
method of the bean manager.
Example 3.13. Destruction of bean
public MyManagedBean {
@Inject SyncBeanManager manager;
public void createABeanThenDestroyIt() {
// get a new bean.
SimpleBean bean = manager.lookupBean(SimpleBean.class).getInstance();
bean.sendMessage("Sorry, I need to dispose of you now");
// destroy the bean!
manager.destroyBean(bean);
}
}
When the bean manager "destroys" the bean, any pre-destroy methods the bean declares are called, it is taken out of service and no longer tracked by the bean manager. If there are references on the bean by other objects, the bean will continue to be accessible to those objects.
Container managed resources that are dependent on the bean such as bus service endpoints or CDI event observers will also be automatically destroyed when the bean is destroyed.
Another important consideration is the rule, "all beans created together are destroyed together." Consider the following example:
Example 3.14. SimpleBean.class
@Dependent
public class SimpleBean {
@Inject @New AnotherBean anotherBean;
public AnotherBean getAnotherBean() {
return anotherBean;
}
@PreDestroy
private void cleanUp() {
// do some cleanup tasks
}
}
Example 3.15. Destroying bean from subgraph
public MyManagedBean {
@Inject SyncBeanManager manager;
public void createABeanThenDestroyIt() {
// get a new bean.
SimpleBean bean = manager.lookupBean(SimpleBean.class).getInstance();
// destroy the AnotherBean reference from inside the bean
manager.destroyBean(bean.getAnotherBean());
}
}
In this example we pass the instance of AnotherBean
, created as a dependency of SimpleBean
, to the bean manager for destruction. Because this bean was created at the same time as its parent, its destruction will also result in the destruction of SimpleBean
; thus, this action will result in the @PreDestroy
cleanUp()
method of SimpleBean
being invoked.
Another way which beans can be destroyed is through the use of the injectable org.jboss.errai.ioc.client.api.Disposer<T>
class. The class provides a straight forward way of disposing of bean type.
For instance:
Example 3.16. Destroying bean with disposer
public MyManagedBean {
@Inject @New SimpleBean myNewSimpleBean;
@Inject Disposer<SimpleBean> simpleBeanDisposer;
public void destroyMyBean() {
simpleBeanDisposer.dispose(myNewSimpleBean);
}
}