summaryrefslogtreecommitdiffstats
path: root/documentation/articles/IIInjectionAndScopes.asciidoc
blob: f8aa692bd83444e98828840aa5f7a18eef0f9acd (plain)
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
---
title: Injection And Scopes
order: 48
layout: page
---

[[ii-injection-and-scopes]]
II - Injection and scopes
-------------------------

In this tutorial we'll take a closer look at the @CDIUI annotation and
use CDI to inject some beans to our application.

[[cdiui]]
@CDIUI
~~~~~~

The @CDIUI annotation is the way in which you let the Vaadin CDI plugin
know which UI's should be accessible to the user and how they should be
mapped. It accepts one optional String parameter indicating the UI path.
If an explicit path is not provided the class name of the UI will be
used to construct a pathname by the following convention: any trailing
"UI" will be truncated and camelcase will be converted to hyphenated
lowercase. Some examples of the convention:

....
HelloWorldUI → hello-world
ExampleUI → example
VisualEditor → visual-editor
....

Passing an empty String as the path will cause the UI to be mapped to
the root of the deployment. Most single UI applications will probably
want to do this.

[[injecting-beans]]
Injecting beans
~~~~~~~~~~~~~~~

Now that the UI itself has been injected, we can use the @Inject
annotation to further inject beans to it. Let's create something for us
to actually inject.

We'll define the following interface, and an implementation for it.

[source,java]
....
package com.vaadin.cdi.tutorial;

public interface Greeting {
  public String getText();
}
....

[source,java]
....
package com.vaadin.cdi.tutorial;

import java.io.Serializable;

public class SimpleGreetingImpl implements Greeting, Serializable {

  @Override
  public String getText() {
    return "Hello, World!";
  }
}
....

So far so good, now we'll inject it into our UI. You'll need to add the
CDI API as a dependency in your pom.xml. (Group id: javax.enterprise,
artefact id: cdi-api, version: 1.2)

[source,java]
....
package com.vaadin.cdi.tutorial;

import javax.inject.Inject;

import com.vaadin.annotations.Theme;
import com.vaadin.cdi.CDIUI;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

@SuppressWarnings("serial")
@CDIUI("")
@Theme("valo")
public class HelloWorldUI extends UI {

  @Inject
  private Greeting greeting;

  @Override
  protected void init(VaadinRequest request) {
    final VerticalLayout layout = new VerticalLayout();
    layout.setMargin(true);
    setContent(layout);

    Button button = new Button("Click Me");
    button.addClickListener(new Button.ClickListener() {
      public void buttonClick(ClickEvent event) {
        layout.addComponent(new Label(greeting.getText()));
      }
    });
    layout.addComponent(button);
  }
}
....

Let's run that and see.

image:img/hello-world.png[Injection seems to be working]

So far so good. Suppose we want to say hello to the user by name. We'll
create a class to store our user data in. For now it's a simple POJO
just for storing the name in a single string.

[source,java]
....
package com.vaadin.cdi.tutorial;

import java.io.Serializable;

public class UserInfo implements Serializable {
  private String name;

  public UserInfo() {
    this.name = "stranger";
  }

  public UserInfo(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}
....

We'll inject that to the UI and assign the user some name during
initialization (for now)

[source,java]
....
@Inject
private UserInfo user;

@Override
protected void init(VaadinRequest request) {
  ...
  user.setName("Ernest");
}
....

Then we'll create a new implementation of the Greeting and inject the
user there as well.

[source,java]
....
package com.vaadin.cdi.tutorial;

import javax.inject.Inject;

public class UserGreetingImpl implements Greeting {

  @Inject
  private UserInfo user;

  @Override
  public String getText() {
    return "Hello, " + user.getName() + "!";
  }
}
....

Now, it would be easy to think that that's all you need but we're not
quite there. There are two issues with this that need to be addressed.
The first one will become immediately obvious when you try to deploy the
application. The deployment will fail as the injection in HelloWorldUI
is ambiguous, that is CDI doesn't know which implementation of Greeting
we want. +
There are three annotations that are useful in situations like this:
@Default, @Alternative and @Specializes. Giving a bean any of these
annotations will affect it's preference order when the CDI container is
looking for which implementation to inject. Unless otherwise specified,
beans will be considered to have the @Default annotation. Beans with the
@Alternative annotation will only be injected if that particular bean is
named in the beans.xml file (TODO: add link). Beans with the
@Specializes annotation will be considered a drop-in replacement for
it's superclass, and will be used over any implementations it extends. +
In our case, we'll want to give SimpleGreetingImpl the @Default
annotation and UserGreetingImpl the @Alternative annotation.

[source,java]
....
@Default
public class SimpleGreetingImpl implements Greeting {
....

[source,java]
....
@Alternative
public class UserGreetingImpl implements Greeting {
....

After that the application could actually be deployed. To tell CDI we'll
want to use our alternative implementation we need to create the
beans.xml in our WEB-INF folder, and add the following declaration to
it:

[source,xml]
....
<beans>
  <alternatives>
    <class>com.vaadin.cdi.tutorial.UserGreetingImpl</class>
  </alternatives>
</beans>
....

Let's try that out:

image:img/hello-stranger.png[Something's not right]

Better, but not quite there yet. We're getting the wrong username,
despite the fact that we set the username to "Earnest" in the UI. The
problem here is the scope of the bean. If you don't specify a scope for
your bean either in the bean class itself or at the injection point,
it'll default to using the dependent scope. Every time you inject a
dependent bean you'll get a new instance, which is clearly not what we
want here. Let's go back to our UserInfo class and assign it an explicit
scope.

[source,java]
....
import com.vaadin.cdi.UIScoped;

@UIScoped
public class UserInfo {
...
....

The @UIScoped annotation is specific to Vaadin CDI. Anything injected
with that annotation will get the same instance while within the same
UI. Load a different UI and you'll get a different instance. If the
session expires or the UI is closed the instances will be cleaned up. +
Let's see if it worked.

image:img/hello-earnest.png[Something IS right]

Looks like we're making progress.