123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999 |
- ---
- title: Vaadin CDI Add-on
- order: 17
- layout: page
- ---
-
- [[advanced.cdi]]
- = Vaadin CDI Add-on
-
- ((("Contexts and Dependency Injection", id="term.advanced.cdi.cdilong", range="startofrange")))
-
-
- ((("CDI", id="term.advanced.cdi.cdi", range="startofrange")))
-
-
- ((("Vaadin CDI Add-on", id="term.advanced.cdi.cdiaddon", range="startofrange")))
-
-
- Vaadin CDI add-on makes it easier to use contexts and dependency injection (CDI)
- in Vaadin applications. CDI is a Java EE feature especially targeted for web
- applications, which have well-defined contextual scopes, such as sessions,
- views, requests, and so forth. The lifecycle of objects, such as beans, can be
- managed by binding their lifecycles to such contexts. Vaadin CDI enables these
- features with two additional kinds of Vaadin-specific contextual scopes: UIs and
- navigation views.
-
- To learn more about Vaadin CDI, the link:[Vaadin CDI Tutorial] gives a hands-on
- introduction. The source code of the CDI Tutorial demo is available for browsing
- or cloning at https://github.com/vaadin/cdi-tutorial.
-
- [[advanced.cdi.cdi]]
- == CDI Overview
-
- Contexts and dependency injection, defined in the JSR-299 standard, is a Java EE
- feature that, through a set of services, helps in improving application
- architecture by decoupling the management of service object lifecycles from
- client objects using them. The lifecycle of objects stored in a CDI container is
- defined by a context. The managed objects or beans are accessed using dependency
- injection.
-
- CDI builds on the Java concept of beans, but with somewhat different definition
- and requirements.
-
- Regarding general CDI topics, such as use of qualifiers, interceptors,
- decorators, event notifications, and other CDI features, we refer you to CDI
- documentation.
-
- ifdef::web[]
- * link:http://jaxenter.com/tutorial-introduction-to-cdi-contexts-and-dependency-injection-for-java-ee-jsr-299-104536.html[Introduction
- to CDI]. Pete Muir and Mark Struberg, JAXenter.
-
- * link:http://docs.jboss.org/weld/reference/latest/en-US/html_single/[Weld - CDI
- Reference Implementation]
-
- * link:http://cdi-spec.org/[CDI Specification]
-
- * link:https://vaadin.com/wiki?p_p_id=36&p_p_lifecycle=0&p_p_state=normal&p_p_mode=view&p_p_col_id=row-1&p_p_col_pos=1&p_p_col_count=3&p_r_p_185834411_title=Vaadin+CDI&p_r_p_185834411_nodeName=vaadin.com+wiki&_36_struts_action=%2Fwiki%2Fview[Vaadin
- CDI Tutorial]
-
- endif::web[]
-
- [[advanced.cdi.cdi.injection]]
- === Dependency Injection
-
- __Dependency injection__ is a way to pass dependencies (service objects) to
- dependent objects (clients) by injecting them in member variables or initializer
- parameters, instead of managing the lifecycle in the clients or passing them
- explicitly as parameters. In CDI, injection of a service object to a client is
- specified by the [classname]#@Inject# annotation.
-
- For example, if we have a UI view that depends on user data, we could inject the
- data in the client as follows:
-
-
- [source, java]
- ----
- public class MainView extends CustomComponent implements View {
- @Inject
- User user;
-
- ...
- @Override
- public void enter(ViewChangeEvent event) {
- greeting.setValue("Hello, " + user.getName());
- }
- }
- ----
-
- In addition to injecting managed beans with the annotation, you can query for
- them from the bean manager.
-
-
- [[advanced.cdi.cdi.contexts]]
- === Contexts and Scopes
-
- __Contexts__ in CDI are services that manage the lifecycle of objects and handle
- their injection. Generally speaking, a context is a situation in which an
- instance is used with a unique identity. Such objects are essentially
- "singletons" in the context. While conventional singletons are application-wide,
- objects managed by a CDI container can be "singletons" in a more narrow
- __scope__: a user session, a particular UI instance associated with the session,
- a view within the UI, or even just a single request. Such a context defines the
- lifecycle of the object: its creation, use, and finally its destruction.
-
- As a very typical example in a web application, you would have a user data
- object associated with a user session.
-
-
- [source, java]
- ----
- @SessionScoped
- public class User {
- private String name;
-
- public void setName(String name) {this.name = name;}
- public String getName() {return name;}
- }
- ----
-
- Now, when you need to refer to the user, you can use CDI injection to inject the
- session-scoped "singleton" to a member variable or a constructor parameter.
-
-
- [source, java]
- ----
- public class MainView extends CustomComponent implements View {
- @Inject
- User user;
-
- ...
-
- @Override
- public void enter(ViewChangeEvent event) {
- greeting.setValue("Hello, " + user.getName());
- }
- }
- ----
-
-
-
- [[advanced.cdi.installation]]
- == Installing Vaadin CDI Add-on
-
- Vaadin CDI requires a Java EE 7 compatible servlet container, such as Glassfish
- or Apache TomEE Web Profile, as mentioned for the reference toolchain in
- <<dummy/../../../framework/getting-started/getting-started-environment#getting-started.environment,"Setting
- up the Development Environment">>.
-
- To install the Vaadin CDI add-on, either define it as an Ivy or Maven dependency
- or download it from the Vaadin Directory add-on page at
- <<,vaadin.com/directory#addon/vaadin-cdi>>. See
- <<dummy/../../../framework/addons/addons-overview.asciidoc#addons.overview,"Using
- Vaadin Add-ons">> for general instructions for installing and using Vaadin
- add-ons.
-
- The Ivy dependency is as follows:
-
- [subs="normal"]
- ----
- <dependency org="com.vaadin" name="vaadin-cdi"
- rev="[replaceable]#latest.release#"/>
- ----
- The Maven dependency is as follows:
-
- [subs="normal"]
- ----
- <dependency>
- <groupId>com.vaadin</groupId>
- <artifactId>vaadin-cdi</artifactId>
- <version>[replaceable]#LATEST#</version>
- </dependency>
- <dependency>
- <groupId>javax.enterprise</groupId>
- <artifactId>cdi-api</artifactId>
- <version>[replaceable]#1.2#</version>
- </dependency>
- ----
-
- [[advanced.cdi.peparing]]
- == Preparing Application for CDI
-
- A Vaadin application that uses CDI must have a file named [filename]#beans.xml#
- in the [filename]#WEB-INF# directory. The file can be completely empty (it has
- content only in certain limited situations), but it must be present.
-
- The application should not have a servlet extending [classname]#VaadinServlet#,
- as Vaadin servlet has its own [classname]#VaadinCDIServlet# that is deployed
- automatically. If you need multiple servlets or need to customize the Vaadin CDI
- servlet, see <<advanced.cdi.deployment>>.
-
-
- [[advanced.cdi.cdiui]]
- == Injecting a UI with [classname]#@CDIUI#
-
- ((("[classname]#@CDIUI#", id="term.advanced.cdi.cdiui", range="startofrange")))
-
-
- Vaadin CDI offers an easier way to instantiate UIs and to define the URL mapping
- for them than the usual ways described in
- <<dummy/../../../framework/application/application-environment#application.environment,"Deploying
- an Application">>. To define a UI class that should be instantiated for a given
- URL, you simply need to annotate the class with [classname]#@CDIUI#. It takes an
- optional URL path as parameter.
-
-
- [source, java]
- ----
- @CDIUI("myniceui")
- @Theme("valo")
- public class MyNiceUI extends UI {
- ...
- ----
-
- Giving empty UI path maps the UI to the root of the application context.
-
-
- [source, java]
- ----
- @CDIUI("")
- ----
-
- If the optional UI path is not given, the path is determined automatically from
- the class name by removing a possible "-UI" suffix in the class name, making it
- lower-case, and for capitalized letters, a hyphen is added. For example, a UI
- with class name [classname]#MyNiceUI# would have path [literal]#++my-nice++#.
- The URL consists of the server address, application context, and the UI path.
- For example, when running a Vaadin application in a development workstation, you
- would have URL such as http://localhost:8080/myproject/my-nice.
-
- UI path mappings are reported in the server log during deployment.
-
- See <<advanced.cdi.deployment>> for how to handle servlet URL mapping of CDI UIs
- when working with multiple servlets in the same web application.
-
- (((range="endofrange", startref="term.advanced.cdi.cdiui")))
-
- [[advanced.cdi.scopes]]
- == Scopes
-
- ((("CDI", "scopes", id="term.advanced.cdi.scopes", range="startofrange")))
-
-
- As in programming languages, where a variable name refers to a unique object
- within the scope of the variable, a CDI scope is a context in which an object
- has unique identity. In CDI, objects to be injected are identified by their type
- and any qualifiers they may have. The scope can be defined as an annotation to
- the service class as follows:
-
-
- [source, java]
- ----
- @SessionScoped
- public class User {
- ...
- ----
-
- CDI defines a number of scopes. Note that the standard CDI scopes are defined
- under the [package]#javax.enterprise.context# package and Vaadin CDI scopes
- under [package]#com.vaadin.cdi#, while JSF scopes are defined in
- [package]#javax.faces.bean#.
-
- [[advanced.cdi.scopes.ui]]
- === UI Scope
-
- UI-scoped beans are uniquely identified within a UI instance, that is, a browser
- window or tab.
-
- Vaadin CDI provides two annotations for the UI scope, differing in how they
- enable proxies, as explained later.
-
- [classname]#@UIScoped#([package]#com.vaadin.cdi#):: ((("[classname]#@UIScoped#", id="term.advanced.cdi.scopes.uiscoped", range="startofrange")))
-
-
- +
- Injection with this annotation will create a direct reference to the bean rather
- than a proxy. There are some limitations when not using proxies. Circular
- references (injecting A to B and B to A) will not work, and neither do CDI
- interceptors and decorators.
-
- (((range="endofrange", startref="term.advanced.cdi.scopes.uiscoped")))
-
- [classname]#@NormalUIScoped#([package]#com.vaadin.cdi#):: As [classname]#@UIScoped#, but injecting a managed bean having this annotation
- injects a proxy for the bean instead of a direct reference. This is the normal
- behaviour with CDI, as many CDI features utilize the proxy.
-
-
-
- Defining a CDI view (annotated with [classname]#@CDIView# as described later) as
- [classname]#@UIScoped# makes the view retain the same instance when the user
- navigates away and back to the view.
-
-
- [[advanced.cdi.scopes.view]]
- === View Scopes
-
- The lifecycle of a view-scoped bean starts when the user navigates to a view
- referring to the object and ends when the user navigates out of the view (or
- when the UI is closed or expires).
-
- Vaadin CDI provides two annotations for the view scope, differing in how they
- enable proxies, as explained later.
-
- [classname]#@ViewScoped#([package]#com.vaadin.cdi#):: Injection with this annotation will create a direct reference to the bean rather
- than a proxy. There are some limitations when not using proxies. Circular
- references (injecting A to B and B to A) will not work, and neither do CDI
- interceptors and decorators.
-
- [classname]#@NormalViewScoped#([package]#com.vaadin.cdi#):: As [classname]#@NormalScoped#, except that injecting with this annotation will
- create a proxy for the contextual instance rather than provide the contextual
- instance itself. See the explanation of proxies below.
-
-
-
-
- [[advanced.cdi.scopes.cdi]]
- === Standard CDI Scopes
-
- [classname]#@ApplicationScoped#:: ((("[classname]#@ApplicationScoped#", id="term.advanced.cdi.scopes.applicationscoped", range="startofrange")))
-
-
- +
- Application-scoped beans are shared by all servlets in the web application, and
- are essentially equal to singletons.//TODO This is just a guess - is it
- true?
- Note that referencing application-scoped beans is not thread-safe and access
- must be synchronized.
-
- (((range="endofrange", startref="term.advanced.cdi.scopes.applicationscoped")))
-
- [classname]#@SessionScoped#:: ((("[classname]#@SessionScoped#", id="term.advanced.cdi.scopes.sessionscoped", range="startofrange")))
-
-
- +
- The lifecycle and visibility of session-scoped beans is bound to a HTTP or user
- session, which in Vaadin applications is associated with the
- [classname]#VaadinSession# (see
- <<dummy/../../../framework/application/application-lifecycle#application.lifecycle.session,"User
- Session">>). This is a very typical scope to store user data, as is done in many
- examples in this section, or database connections. The lifecycle of
- session-scoped beans starts when a user opens the page for a UI in the browser,
- and ends when the session expires after the last UI in the session is closed.
-
- (((range="endofrange", startref="term.advanced.cdi.scopes.sessionscoped")))
-
-
-
- [[advanced.cdi.scopes.proxies]]
- === Proxies vs Direct References
-
- CDI uses proxy objects to enable many of the CDI features, by hooking into
- message-passing from client to service beans. Under the hood, a proxy is an
- instance of an automatically generated class that extends the proxied bean type,
- so communicating through a proxy occurs transparently, as it has the same
- polymorphic type as the actual bean. Whether proxying is enabled or not is
- defined in the scope: CDI scopes are either __normal scopes__, which can be
- proxied, or __pseudoscopes__, which use direct references to injected beans.
-
- The proxying mechanism creates some requirements for injecting objects in normal
- scope:
-
- * The objects may not be primitive types or arrays
-
- * The bean class must not be final
-
- * The bean class must not have final methods
-
-
- Beans annotated with [classname]#@UIScoped# or [classname]#@ViewScoped# use a
- pseudoscope, and are therefore injected with direct references to the bean
- instances, while [classname]#@NormalUIScoped# and [classname]#@NormalViewScoped#
- beans will use a proxy for communicating with the beans.
-
- When using proxies, be aware that it is not guaranteed that the
- [methodname]#hashCode()# or [methodname]#equals()# will match when comparing a
- proxy to its underlying instance. It is imperative to be aware of this when, for
- example, adding proxies to a [interfacename]#Collection#.
-
- You should avoid using normal scopes with Vaadin components, as proxies may not
- work correctly within the Vaadin framework. If Vaadin CDI plugin detects such
- use, it displays a warning such as the following:
-
-
- ----
- INFO: The following Vaadin components are injected
- into normal scoped contexts:
- @NormalUIScoped org.example.User
- This approach uses proxy objects and has not been
- extensively tested with the framework. Please report
- any unexpected behavior. Switching to a pseudo-scoped
- context may also resolve potential issues.
- ----
-
-
- (((range="endofrange", startref="term.advanced.cdi.scopes")))
-
- [[advanced.cdi.deployment]]
- == Deploying CDI UIs and Servlets
-
- Vaadin CDI hooks into Vaadin framework by using a special
- [classname]#VaadinCDIServlet#. As described earlier, you do not need to map an
- URL path to a UI, as it is handled by Vaadin CDI. However, in the following, we
- go through some cases where you need to customize the servlet or use CDI with
- non-CDI servlets and UIs in a web application.
-
- [[advanced.cdi.deployment.urlmapping]]
- === Defining Servlet Root with [classname]#@URLMapping#
-
- CDI UIs are managed by a CDI servlet ( [classname]#VaadinCDIServlet#), which is
- by default mapped to the root of the application context. For example, if the
- name of a CDI UI is " [literal]#++my-cdi++#" and application context is
- [literal]#++/myproject++#, the UI would by default have URL "
- [literal]#++/myproject/my-cdi++#". If you do not want to have the servlet mapped
- to context root, you can use the [classname]#@URLMapping# annotation to map all
- CDI UIs to a sub-path. The annotation must be given to only one CDI UI, usually
- the one with the default ("") path.
-
- For example, if we have a root UI and another:
-
-
- [source, java]
- ----
- @CDIUI("") // At CDI servlet root
- @URLMapping("mycdiuis") // Define CDI Servlet root
- public class MyCDIRootUI extends UI {...}
-
- @CDIUI("another")
- public class AnotherUI extends UI {...}
- ----
-
- These two UIs would have URLs /myproject/mycdiuis and
- /myproject/mycdiuis/another, respectively.
-
- You can also map the CDI servlet to another URL in servlet definition in
- [filename]#web.xml#, as described the following.
-
-
- [[advanced.cdi.servlets.mixing]]
- === Mixing With Other Servlets
-
- The [classname]#VaadinCDIServlet# is normally used as the default servlet, but
- if you have other servlets in the application, such as for non-CDI UIs, you need
- to define the CDI servlet explicitly in the [filename]#web.xml#. You can map the
- servlet to any URL path, but perhaps typically, you define it as the default
- servlet as follows, and map the other servlets to other URL paths:
-
- [subs="normal"]
- ----
- <web-app>
- ...
-
- <servlet>
- <servlet-name>Default</servlet-name>
- <servlet-class>
- com.vaadin.cdi.internal.VaadinCDIServlet
- </servlet-class>
- </servlet>
-
- <servlet-mapping>
- <servlet-name>Default</servlet-name>
- <url-pattern>[replaceable]#/mycdiuis/*#</url-pattern>
- </servlet-mapping>
-
- <servlet-mapping>
- <servlet-name>Default</servlet-name>
- <url-pattern>/VAADIN/*</url-pattern>
- </servlet-mapping>
- </web-app>
- ----
- With such a setting, paths to CDI UIs would have base path
- [filename]#/myapp/mycdiuis#, to which the (optional) UI path would be appended.
- The [filename]#/VAADIN/*# only needs to be mapped to the servlet if there are no
- other Vaadin servlets.
-
-
- [[advanced.cdi.servlets.custom]]
- === Custom Servlets
-
- When customizing the Vaadin servlet, as outlined in
- <<dummy/../../../framework/application/application-lifecycle#application.lifecycle.servlet-service,"Vaadin
- Servlet, Portlet, and Service">>, you simply need to extend
- [classname]#com.vaadin.cdi.internal.VaadinCDIServlet# instead of
- [classname]#com.vaadin.servlet.VaadinServlet#.
-
- The custom servlet must not have [classname]#@WebServlet# annotation or
- [classname]#@VaadinServletConfiguration#, as you would normally with a Vaadin
- servlet, as described in
- <<dummy/../../../framework/application/application-environment#application.environment,"Deploying
- an Application">>.
-
-
-
- ifdef::web[]
- [[advanced.cdi.navigation]]
- == View Navigation
-
- Vaadin CDI extends the navigation framework in Vaadin, described in
- <<dummy/../../../framework/advanced/advanced-navigator#advanced.navigator,"Navigating
- in an Application">>. It manages CDI views with a special view provider and
- enables view scoping.
-
- [[advanced.cdi.navigation.ui]]
- === Preparing the UI
-
- You can define navigation for any single-component container, as described in
- <<dummy/../../../framework/advanced/advanced-navigator#advanced.navigator.navigating,"Setting
- Up for Navigation">>, but typically you set up navigation for the entire UI
- content. To use Vaadin CDI views, you need to inject a
- [classname]#CDIViewProvider# in the UI and add it as a provider for the
- navigator.
-
-
- [source, java]
- ----
- @CDIUI("mycdiui")
- public class MyCDIUI extends UI {
- @Inject
- CDIViewProvider viewProvider;
-
- @Override
- protected void init(VaadinRequest request) {
- Navigator navigator = new Navigator(this, this);
- navigator.addProvider(viewProvider);
-
- // Navigate to start view
- navigator.navigateTo("");
- }
- }
- ----
-
-
- [[advanced.cdi.navigation.view]]
- === The View
-
- A view managed by Vaadin CDI only needs to have the [classname]#@CDIView#
- annotation.
-
-
- [source, java]
- ----
- @CDIView("main")
- public class MainView extends CustomComponent implements View {
- ...
- ----
-
- The annotation can have the following optional paramers:
-
- value (optional):: Specifies the view name by which it can be accessed programmatically and by the
- URI fragment.
-
-
- +
- [source, java]
- ----
- @CDIView("main")
- ----
- +
- If other optional parameters are given, the value must be given by the named
- [parameter]#value# parameter.
-
- +
- If the view name is not given, it is derived from the class name by removing a
- possible "View" suffix, making it lower case, and using a dash ("-") to separate
- words originally denoted by capital letters. Thereby, a view class such as
- [classname]#MyFunnyView# would have name " [literal]#++my-funny++#".
-
- supportsParameters:: Specifies whether view parameters can be passed to the view as a suffix to the
- name in the navigation state, that is, in the form of
- [literal]#++viewname+viewparameters++#. The view name is merely a prefix and
- there is no separator nor format for the parameters, but those are left for the
- view to handle. The parameter support mode is disabled by default.
-
-
- +
- [source, java]
- ----
- @CDIView(value="myview", supportsParameters=true)
- ----
- +
- You could then navigate to the state with a URI fragment such as
- [literal]#++#!myview/someparameter++# or programmatically with:
-
-
- +
- [source, java]
- ----
- getUI().getNavigator().navigateTo("myview/someparameter");
- ----
- +
- The [methodname]#enter()# method of the view gets the URI fragment as parameter
- as is and can interpret it in any application-defined way.
-
- +
- Note that in this mode, matching a navigation state to a view is done by the
- prefix of the fragment! Thereby, no other views may start with the name of the
- view as prefix. For example, if the view name is " [literal]#++main++#", you
- must not have a view named " [literal]#++maintenance++#".
-
- uis:: If the application has multiple UIs that use [classname]#CDIViewProvider#, you
- can use this parameter to specify which UIs can show the view.
-
-
- +
- [source, java]
- ----
- @CDIView(value="myview", uis={MyCDIUI.class})
- ----
- +
- If the list contains [parameter]#UI.class#, the view is available to all UIs.
-
-
- +
- [source, java]
- ----
- @CDIView(value="myview", uis={UI.class})
- ----
-
-
- In the following, we have a login view that accesses a session-scoped user
- object. Here, we use a constant to define the view name, so that we can use the
- constant when navigating to it.
-
-
- [source, java]
- ----
- @CDIView(LoginView.VIEWNAME)
- public class LoginView extends CustomComponent
- implements View {
- public final static String VIEWNAME = "";
-
- // Here we inject to the constructor and actually do
- // not store the injected object to use it later
- @Inject
- public LoginView(User user) {
- VerticalLayout layout = new VerticalLayout();
-
- // An input field for editing injected data
- BeanItem<User> item = new BeanItem<User>(user);
- TextField username = new TextField("User name",
- item.getItemProperty("name"));
- username.setNullRepresentation("");
- layout.addComponent(username);
-
- // Login button (authentication omitted) / Java 8
- layout.addComponent(new Button("Login", e ->
- getUI().getNavigator().
- navigateTo(MainView.VIEWNAME)));
-
- setCompositionRoot(layout);
- }
-
- @Override
- public void enter(ViewChangeEvent event) {}
- }
- ----
-
- You could now navigate to the view from any other view in the UI with:
-
-
- [source, java]
- ----
- getUI().getNavigator().navigateTo(LoginView.VIEWNAME);
- ----
-
-
- endif::web[]
-
- ifdef::web[]
- [[advanced.cdi.events]]
- == CDI Events
-
- ((("CDI", "events", id="term.advanced.cdi.events", range="startofrange")))
-
-
- CDI events can be used for many purposes in Vaadin applications, such as passing
- messages between different parts of a view, between views, between UIs, or
- between users. Some cases require special consideration, such as when
- communicating between UIs and how injected components should be scoped.
-
- [[advanced.cdi.events.intro]]
- === Observing Events
-
- Let us consider a case where changes in one part of the UI (or view) require
- updating other parts of the UI. This is typical in master-detail views, for
- updating the master view after editing details, or when handling input from a
- sub-window. While you can handle such a situation with a custom call-back
- listener, CDI event mechanism simplifies the task.
-
- Let us consider the following simple UI containing two panels. The input panel
- will send events, which are received by other parts of the UI, in this case a
- display panel. The panels need to be injected to enable CDI event passing in
- them.
-
-
- [source, java]
- ----
- @CDIUI("cdievents")
- @Theme("valo")
- public class CDIEventUI extends UI {
- @Inject
- InputPanel inputPanel;
-
- @Inject
- DisplayPanel displayPanel;
-
- @Override
- protected void init(VaadinRequest request) {
- Layout content =
- new HorizontalLayout(inputPanel, displayPanel);
- setContent(content);
- }
- }
- ----
-
- Now, let us look closer at the sending panel. To send messages, it needs to
- inject a [classname]#javax.enterprise.event.Event# object. As we are injecting
- the event to a component class, we need to specify the full package name to
- avoid confusion with Vaadin [classname]#Component.Event#.
-
-
- [source, java]
- ----
- class InputPanel extends Panel {
- @Inject
- private javax.enterprise.event.Event<MyEvent> event;
-
- public InputPanel() {
- super("Input");
-
- TextField editor = new TextField();
- Button save = new Button("Save", e -> // Java 8
- event.fire(new MyEvent(editor.getValue())));
-
- setContent(new VerticalLayout(editor, save));
- }
- }
- ----
-
- Firing an event is done with the [methodname]#fire()# method on the injected
- event object. In our example, the event is as follows:
-
-
- [source, java]
- ----
- public class MyEvent implements Serializable {
- private String text;
-
- public MyEvent(String text) {
- this.text = text;
- }
-
- public String getName() {
- return text;
- }
- }
- ----
-
- The event is received by any method (in an injected object) marked by
- [classname]#@Observes# annotation for the event parameter to observe the event
- type.
-
-
- [source, java]
- ----
- @UIScoped
- class DisplayPanel extends Panel {
- Label display = new Label("-nothing to display-");
-
- public DisplayPanel() {
- super("Display");
- setContent(display);
- }
-
- void myEventObserver(@Observes MyEvent event) {
- display.setValue("Observed: " + event.getName());
- }
- }
- ----
-
- Such a component that observes events from other components must be scoped to
- the UI or view, as otherwise it will be request-scoped and a new instance is
- created for receiving each event.
-
- The UI with interaction is shown in <<figure.advanced.cdi.events.intro>>.
-
- [[figure.advanced.cdi.events.intro]]
- .Observing CDI Events
- image::img/cdi-events-observing.png[]
-
- Any injection qualifiers defined for the event object in the sender are matched
- in the observers, which feature we will use later to avoid receiving unwanted
- events.
-
-
- [[advanced.cdi.events.broadcasting]]
- === Communicating Between UIs
-
- ((("broadcasting", id="term.advanced.cdi.events.broadcasting", range="startofrange")))
-
-
- CDI events are not propagated to inactive contexts, and only the context of the
- currently processed UI is active. Further, as explained in
- <<dummy/../../../framework/advanced/advanced-push#advanced.push.running,"Accessing
- UI from Another Thread">>, other Vaadin UIs may not be accessed without proper
- synchronization, as their requests are processed concurrently in different
- server threads. Therefore, you need to pass the events through an
- application-scoped messaging service and synchronize the access to other UIs by
- using the [methodname]#access()# method.
-
- In
- <<dummy/../../../framework/advanced/advanced-push#advanced.push.pusharound,"Broadcasting
- to Other Users">> we looked into how to pass messages to all other UIs using a
- broadcasting service. In that example, we used static variables and methods to
- store references and to access the service. With CDI, we can let the context
- manage its lifecycle, access it by injection, and pass messages by CDI events.
- By scoping the messaging service to application, we essentially make it a
- singleton.
-
-
- [source, java]
- ----
- @ApplicationScoped
- public class CDIBroadcaster implements Serializable {
- ----
-
- As we can not let CDI deliver the messages, the messaging service needs to keep
- book of the messaging clients (UIs) waiting to receive messages.
-
-
- [source, java]
- ----
- private Collection<UI> uis = new HashSet<UI>();
-
- public synchronized void register(UI listener) {
- uis.add(listener);
- }
-
- public synchronized void unregister(UI listener) {
- uis.remove(listener);
- }
- ----
-
- The main logic of the messaging service is to observe messages and fire them in
- the recipient UIs. As we are broadcasting to all UIs here, we again use an
- executor service to execute the code. To lock on the session when accessing the
- UIs, we use the [methodname]#access()# method.
-
-
- [source, java]
- ----
- // Inject event to be fired
- @Inject
- private javax.enterprise.event.Event<BroadcastMessage>
- messageEvent;
-
- ExecutorService executorService =
- Executors.newSingleThreadExecutor();
-
- // Observe messages (only from clients)
- @SuppressWarnings("unused")
- private synchronized void observeMessage(
- @Observes @OriginalSender
- final BroadcastMessage message) {
- for (final UI listener: uis)
- executorService.execute(() ->
- listener.access(()->
- messageEvent.fire(message)));
- }
- }
- ----
-
- Here we use a [classname]#@OriginalSender# qualifier to receive events only from
- a client (original sender), not from the messaging service itself, which would
- cause an infinite event loop. The qualifier is defined as follows:
-
- ((("CDI", "qualifiers")))
-
- [source, java]
- ----
- @Qualifier
- @Retention(RUNTIME)
- @Target({PARAMETER, FIELD})
- public @interface OriginalSender {}
- ----
-
- The message type is a simple POJO as follows:
-
-
- [source, java]
- ----
- public class BroadcastMessage {
- private String text;
- private Object sender; // For checking if sent by self
-
- ... constructor, getters, and setters ...
- }
- ----
-
- Let us take a look at the UI class, which manages both the messaging service and
- the client components. The UI just needs to register itself in the messaging
- service and build the UI, including the UI components doing messaging. We could,
- of course, do that also at view level.
-
- ((("[classname]#@Push#")))
-
- [source, java]
- ----
- @CDIUI("cdichat")
- @Push
- public class CDIChatUI extends UI {
- @Inject
- CDIBroadcaster broadcaster;
-
- @Inject
- ChatBox chatbox;
-
- @Override
- protected void init(VaadinRequest request) {
- setContent(chatbox);
-
- // Register to receive broadcasts
- broadcaster.register(this);
- }
-
- // Must also unregister when the UI expires or is closed
- @Override
- public void detach() {
- broadcaster.unregister(this);
- super.detach();
- }
- }
- ----
-
- Now for an actual messaging client, we look at the chat box component. Most of
- the UI code is omitted from the example. As noted earlier, the component
- receiving events must be scoped to the UI, to avoid creation of invalid
- instances.
-
- ((("[classname]#@UIScoped#")))
-
- [source, java]
- ----
- @UIScoped
- class ChatBox extends CustomComponent {
- VerticalLayout messages = new VerticalLayout();
-
- public ChatBox(CDIChatUI cdiChatUI) {
- ... build the composite ...
-
- TextField input = new TextField();
-
- Button send = new Button("Send", e -> { // Java 8
- // Broadcast the input
- broadcast(input.getValue());
- addMessage(input.getValue()); // Add to self
- });
- ...
- }
-
- @Inject
- @OriginalSender
- private javax.enterprise.event.Event<BroadcastMessage>
- messageEvent;
-
- // Sends a message
- private void broadcast(String msg) {
- messageEvent.fire(new BroadcastMessage(msg, this));
- }
-
- // Receives messages
- @SuppressWarnings("unused")
- private void observeMessage(
- @Observes BroadcastMessage event) {
- if (event.getSender() != this)
- addMessage(event.getText());
- }
-
- private void addMessage(String msg) {
- messages.addComponent(new Label(msg));
- }
- }
- ----
-
- ((("CDI", "qualifiers")))
- Note that the client object is completely unaware of the fact that the messages
- are delivered through a messaging service; we have successfully decoupled the
- messaging logic required by Vaadin UIs from the component. Only the requirement
- for using the event qualifier remains (notice that its use is not checked at
- compile time).
-
- (((range="endofrange", startref="term.advanced.cdi.events.broadcasting")))
-
- (((range="endofrange", startref="term.advanced.cdi.events")))
- endif::web[]
-
- (((range="endofrange", startref="term.advanced.cdi.cdilong")))
- (((range="endofrange", startref="term.advanced.cdi.cdi")))
- (((range="endofrange", startref="term.advanced.cdi.cdiaddon")))
-
|