Vaadin Portlet CDI Support
Note
| Vaadin Portlet CDI features are not supported for Liferay 7. See Dependency Injection in Liferay if you need dependency injection in your project. |
The Vaadin Portlet CDI (Contexts and Dependency Injection) extension allows development of Portlet 3.0 (JSR 362) portlets with Vaadin components and services as managed beans. With the Vaadin Portlet CDI extension, managed beans can be injected into any Vaadin component with the application container managing the initialization and destruction (lifecycle) of beans automatically.
As a Developer, When Would I Want to Use CDI for My Vaadin Portlet Application?
The primary use cases for Vaadin Portlet CDI are:
-
If the business layer or other parts of your Portlet application is already using CDI, and you want to leverage CDI in your UI layer as well.
-
You want to use portlet scopes, such as
@PortletRequestScoped
and@PortletSessionScoped
, for portlet-specific lifecycle management of your UI or business objects. -
You want to access portlet predefined beans, as specified by JSR 362.
Note
| This tutorial assumes you are building Vaadin portlets for the Apache Pluto portal, which uses Tomcat and Weld as the application and CDI containers. Currently, Vaadin Portlet CDI has not been tested with other CDI implementations. |
How Do I Use the Vaadin CDI Portlet extension?
Taking the CDI extension into use in your Vaadin Portlet project requires the following steps:
-
Add the
vaadin-portlet-cdi
dependency -
Add an empty
beans.xml
configuration file -
Use the
VaadinCdiPortlet
class as the base class for your portlet -
Add
@Inject
, scope annotations and other CDI features in your code
The remainder of this chapter goes through each of these steps in detail.
Note
|
The Vaadin CDI Portlet extension differs from the Vaadin CDI add-on (Using Vaadin with CDI) for servlet applications.
In particular, the scopes introduced by the Vaadin CDI add-on (@VaadinServiceScoped , @VaadinSessionScoped , @UIScoped , @RouteScoped ) should not be used for portlets, since the session and service concepts differ slightly, and there is no routing.
Other than this, the standard CDI features work in exactly the same way.
|
Note
| This chapter is not a tutorial on CDI in general. For resources on CDI, visit https://www.cdi-spec.org/, and for using CDI with Vaadin servlet applications, see Using Vaadin with CDI. |
Add the vaadin-portlet-cdi Dependency
To use CDI with Vaadin Portlets, the vaadin-portlet-cdi
artifact, must be added as a dependency.
Add the following to your portlet project’s pom.xml
:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-portlet-cdi</artifactId>
<version>${vaadin.portlet.version}</version>
</dependency>
In multi-portlet projects, the value of vaadin.portlet.version
property should match the version of the vaadin.portlet
dependency used.
Add an Empty beans.xml Configuration File
The second step is to add the file /src/main/webapp/WEB-INF/beans.xml
to your Vaadin Portlet application with the following contents:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
This file enables CDI for your portlet and ensures that the application container will scan your projects for bean annotations and injection points.
For a quick overview of bean.xml
, read "What is bean.xml and why do I need it?" in CDI frequently asked questions.
Change Portlet Class
The first step in enabling CDI is to replace the base class of your Vaadin Portlet (VaadinPortlet
) with VaadinCdiPortlet
.
For example, updating BookListPortlet
:
// old, non-CDI portlet:
public class NormalBookListPortlet extends VaadinPortlet<BookListView> {
// ... contents
}
// new, CDI portlet:
public class CdiBookListVaadinPortlet extends CdiVaadinPortlet<BookListView> {
// ... contents
}
After completing these initial steps, rebuild and deploy to ensure that everything is working as before. You are now ready to start using CDI features in your Vaadin Portlet application.
Scopes and Injection Points
The CDI framework allows injection of objects with a well-defined, container-managed lifecycle (called managed beans) into any class (including Vaadin components) by simply annotating the corresponding field with @Inject
.
An injection point may get populated by an already existing bean or a new instance may be created, depending on the context of the object containing the bean (the bean client in CDI lingo).
For example, the same instance of a bean with a longer lifecycle (such as @PortletSessionRequestScoped
beans) is injected into multiple different beans with a shorter lifecycle (such as @PortletRequestScoped
beans).
Injecting Vaadin Components
The root Vaadin component of your Vaadin Portlet application is the one passed as a type parameter to the CdiVaadinPortlet
when sub-classing it (BookListView
in this example).
One instance of this component is created for each browser window and each instance of the portlet in this window, and it may be destroyed on the server side when the window is closed.
Beans injected into the root component that are not annotated with an explicit scope (corresponding to the implicit @Dependent
scope) will share this lifecycle.
This makes the default scope appropriate for objects that you want to be exclusively owned by the root component, such as child components.
For example:
public class BookListView extends Div {
@Inject
private BookGrid bookGrid;
// ...
@PostConstruct
private void init() {
add(bookGrid);
// ...
}
}
public class BookGrid extends Grid<Book> {
@PostConstruct
private void init() {
// ...
}
}
In this example, the child component of type BookGrid
is injected into the root component BookListView
.
The default scope also guarantees that each injection point will get its own instance.
Note
|
When using injection, make sure all concrete classes being injected have the default (no-arguments) constructor.
Initialization code that depends on bean resolution should be placed in a @PostConstruct -annotated method, not in the constructor.
|
Note
| Be aware that before a managed bean is resolved, the injected field may be populated by a proxy object. If you are storing managed beans in a collection or perform other operations where the identity of the object may be of significance, make sure to perform them after the actual object has been resolved. |
Session and Request Scope and Predefined Beans
JSR 362 defines the scopes @PortletSessionScoped
and @PortletRequestScoped
for beans that follow the portlet session and request lifecycle, respectively.
Use @PortletSessionScoped
when you want a bean that is persistent throughout the portlet session.
Use @PortletRequestScoped
for beans that should live only through the portlet request.
JSR 362 also defines a number of predefined beans that can be injected into your portlets. These predefined beans allow easy access to portlet state information, such as current request, current mode, window state, and more. For example, you could inject these into a Vaadin Portlet component:
public class MyPortletComponent extends Div {
@Inject
private PortletRequest portletRequest;
@Inject
private WindowState windowState;
@Inject
private PortletMode portletMode;
// ...
}
As these beans are request-scoped, the values of the fields will be updated for each portlet request.
In the example case, the type of each field determines which predefined bean gets injected.
Some predefined beans are of a basic type, in which case a qualifier is required to identify the desired bean.
For instance, the following portlet view injects the portlet namespace and window identifier (both of type String
):
public class MyPortletView extends Div {
@Inject
@Namespace
private String namespace;
@Inject
@WindowId
private String windowId;
// ...
}
For a full list of predefined beans and qualifiers, see Chapter 20 of JSR 362 (https://jcp.org/en/jsr/detail?id=362).
I18Provider
I18NProvider
is Vaadin’s mechanism for introducing a localization object into your application.
You can have your own I18NProvider
subclass automatically instantiated as a managed bean when using Vaadin Portlet CDI.
This requires adding just two annotations to the class definition:
@VaadinServiceEnabled
@ApplicationScoped
public static class I18N implements I18NProvider {
@PostConstruct
public void init() {
// ...
}
@Override
public List<Locale> getProvidedLocales() {
// ...
}
@Override
public String getTranslation(String key, Locale locale, Object... params) {
// ...
}
}
As with Vaadin CDI for servlet applications, the class also needs to be annotated with @VaadinServiceEnabled
for the Vaadin service to pick it up.
Unlike Vaadin CDI, use @ApplicationScoped
instead of @VaadinServiceScoped
.
The scope ensures that there will be only one I18NProvider
shared among all the views of your portlet application.
C1FFFF77-8BC2-45CC-8F29-69B3B22ACA15