1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
|
---
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/docs/v8/framework/articles/VaadinCDI.html[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
<<../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
<<../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
<<../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
<<../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
<<../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
<<../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
<<../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
<<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++#".
+
You can navigate to a 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
<<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
<<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")))
|