Docs

Documentation versions (currently viewingVaadin 14)

You are viewing documentation for an older Vaadin version. View latest documentation

Vaadin Portlet CDI Support

Instructions on adding CDI support to your Vaadin Portlet projects running in non-Liferay containers
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:

  1. Add the vaadin-portlet-cdi dependency

  2. Add an empty beans.xml configuration file

  3. Use the VaadinCdiPortlet class as the base class for your portlet

  4. 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