interceptors.xml
12 KB
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
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ ]>
<chapter id="interceptors">
<title>Interceptors</title>
<para>
Interceptor functionality is defined in the Java Interceptors specification. CDI enhances this functionality with
a more sophisticated, semantic, annotation-based approach to binding interceptors to beans.
</para>
<para>
The Interceptors specification defines two kinds of interception points:
</para>
<itemizedlist>
<listitem>
<para>business method interception, and</para>
</listitem>
<listitem>
<para>lifecycle callback interception.</para>
</listitem>
</itemizedlist>
<para>
In addition, the EJB specification defines timeout method interception.
</para>
<para>
A <emphasis>business method interceptor</emphasis> applies to invocations of methods of the bean by clients of the
bean:
</para>
<programlisting role="JAVA"><![CDATA[public class TransactionInterceptor {
@AroundInvoke
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}]]></programlisting>
<para>
A <emphasis>lifecycle callback interceptor</emphasis> applies to invocations of lifecycle callbacks by the
container:
</para>
<programlisting role="JAVA"><![CDATA[public class DependencyInjectionInterceptor {
@PostConstruct
public void injectDependencies(InvocationContext ctx) { ... }
}]]></programlisting>
<para>
An interceptor class may intercept both lifecycle callbacks and business methods.
</para>
<para>
A <emphasis>timeout method interceptor</emphasis> applies to invocations of EJB timeout methods by the
container:
</para>
<programlisting role="JAVA"><![CDATA[public class TimeoutInterceptor {
@AroundTimeout
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}]]></programlisting>
<section>
<title>Interceptor bindings</title>
<para>
Suppose we want to declare that some of our beans are transactional. The first thing we need is an
<emphasis>interceptor binding type</emphasis> to specify exactly which beans we're interested in:
</para>
<programlisting role="JAVA"><![CDATA[@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}]]></programlisting>
<para>
Now we can easily specify that our <literal>ShoppingCart</literal> is a transactional object:
</para>
<programlisting role="JAVA"><![CDATA[@Transactional
public class ShoppingCart { ... }]]></programlisting>
<para>
Or, if we prefer, we can specify that just one method is transactional:
</para>
<programlisting role="JAVA"><![CDATA[public class ShoppingCart {
@Transactional public void checkout() { ... }
}]]></programlisting>
</section>
<section>
<title>Implementing interceptors</title>
<para>
That's great, but somewhere along the line we're going to have to actually implement the interceptor that
provides this transaction management aspect. All we need to do is create a standard interceptor, and annotate
it <literal>@Interceptor</literal> and <literal>@Transactional</literal>.
</para>
<programlisting role="JAVA"><![CDATA[@Transactional @Interceptor
public class TransactionInterceptor {
@AroundInvoke
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}]]></programlisting>
<para>
Interceptors can take advantage of dependency injection:
</para>
<programlisting role="JAVA"><![CDATA[@Transactional @Interceptor
public class TransactionInterceptor {
@Resource UserTransaction transaction;
@AroundInvoke
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}]]></programlisting>
<para>
Multiple interceptors may use the same interceptor binding type.
</para>
</section>
<section>
<title>Enabling interceptors</title>
<para>
By default, all interceptors are disabled. We need to <emphasis>enable</emphasis> our interceptor in the
<literal>beans.xml</literal> descriptor of a bean archive. This activation only applies to the beans in
that archive.
</para>
<programlisting role="XML"><![CDATA[<beans
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<interceptors>
<class>org.mycompany.myapp.TransactionInterceptor</class>
</interceptors>
</beans>]]></programlisting>
<para>Whoah! Why the angle bracket stew?</para>
<para>
Well, having the XML declaration is actually a <emphasis>good thing</emphasis>. It solves two problems:
</para>
<itemizedlist>
<listitem>
<para>
it enables us to specify a total ordering for all the interceptors in our system, ensuring deterministic
behavior, and
</para>
</listitem>
<listitem>
<para>it lets us enable or disable interceptor classes at deployment time.</para>
</listitem>
</itemizedlist>
<para>
For example, we could specify that our security interceptor runs before our transaction interceptor.
</para>
<programlisting role="XML"><![CDATA[<beans
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<interceptors>
<class>org.mycompany.myapp.SecurityInterceptor</class>
<class>org.mycompany.myapp.TransactionInterceptor</class>
</interceptors>
</beans>]]></programlisting>
<para>Or we could turn them both off in our test environment by simply not mentioning them in
<literal>beans.xml</literal>! Ah, so simple.</para>
</section>
<section>
<title>Interceptor bindings with members</title>
<para>
Suppose we want to add some extra information to our <literal>@Transactional</literal> annotation:
</para>
<programlisting role="JAVA"><![CDATA[@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {
boolean requiresNew() default false;
}]]></programlisting>
<para>
CDI will use the value of <literal>requiresNew</literal> to choose between two different interceptors,
<literal>TransactionInterceptor</literal> and <literal>RequiresNewTransactionInterceptor</literal>.
</para>
<programlisting role="JAVA"><![CDATA[@Transactional(requiresNew = true) @Interceptor
public class RequiresNewTransactionInterceptor {
@AroundInvoke
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}]]></programlisting>
<para>
Now we can use <literal>RequiresNewTransactionInterceptor</literal> like this:
</para>
<programlisting role="JAVA"><![CDATA[@Transactional(requiresNew = true)
public class ShoppingCart { ... }]]></programlisting>
<para>
But what if we only have one interceptor and we want the container to ignore the value of
<literal>requiresNew</literal> when binding interceptors? Perhaps this information is only useful for the
interceptor implementation. We can use the <literal>@Nonbinding</literal> annotation:
</para>
<programlisting role="JAVA"><![CDATA[@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Secure {
@Nonbinding String[] rolesAllowed() default {};
}]]></programlisting>
</section>
<section>
<title>Multiple interceptor binding annotations</title>
<para>
Usually we use combinations of interceptor bindings types to bind multiple interceptors to a bean. For example,
the following declaration would be used to bind <literal>TransactionInterceptor</literal> and
<literal>SecurityInterceptor</literal> to the same bean:
</para>
<programlisting role="JAVA"><![CDATA[@Secure(rolesAllowed="admin") @Transactional
public class ShoppingCart { ... }]]></programlisting>
<para>
However, in very complex cases, an interceptor itself may specify some combination of interceptor binding
types:
</para>
<programlisting role="JAVA"><![CDATA[@Transactional @Secure @Interceptor
public class TransactionalSecureInterceptor { ... }]]></programlisting>
<para>
Then this interceptor could be bound to the <literal>checkout()</literal> method using any one of the following
combinations:
</para>
<programlisting role="JAVA"><![CDATA[public class ShoppingCart {
@Transactional @Secure public void checkout() { ... }
}]]></programlisting>
<programlisting role="JAVA"><![CDATA[@Secure
public class ShoppingCart {
@Transactional public void checkout() { ... }
}]]></programlisting>
<programlisting role="JAVA"><![CDATA[@Transactional
public class ShoppingCart {
@Secure public void checkout() { ... }
}]]></programlisting>
<programlisting role="JAVA"><![CDATA[@Transactional @Secure
public class ShoppingCart {
public void checkout() { ... }
}]]></programlisting>
</section>
<section>
<title>Interceptor binding type inheritance</title>
<para>
One limitation of the Java language support for annotations is the lack of annotation inheritance. Really,
annotations should have reuse built in, to allow this kind of thing to work:
</para>
<programlisting role="JAVA"><![CDATA[public @interface Action extends Transactional, Secure { ... }]]></programlisting>
<para>
Well, fortunately, CDI works around this missing feature of Java. We may annotate one interceptor binding type
with other interceptor binding types (termed a <emphasis>meta-annotation</emphasis>). The interceptor bindings
are transitive — any bean with the first interceptor binding inherits the interceptor bindings declared as
meta-annotations.
</para>
<programlisting role="JAVA"><![CDATA[@Transactional @Secure
@InterceptorBinding
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }]]></programlisting>
<para>
Now, any bean annotated <literal>@Action</literal> will be bound to both
<literal>TransactionInterceptor</literal> and <literal>SecurityInterceptor</literal>. (And even
<literal>TransactionalSecureInterceptor</literal>, if it exists.)
</para>
</section>
<section>
<title>Use of <literal>@Interceptors</literal></title>
<para>The <literal>@Interceptors</literal> annotation defined by the interceptor specification (and used by the
managed bean and EJB specifications) is still supported in CDI.</para>
<programlisting role="JAVA"><![CDATA[@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class})
public class ShoppingCart {
public void checkout() { ... }
}]]></programlisting>
<para>However, this approach suffers the following drawbacks:</para>
<itemizedlist>
<listitem>
<para>
the interceptor implementation is hardcoded in business code,
</para>
</listitem>
<listitem>
<para>
interceptors may not be easily disabled at deployment time, and
</para>
</listitem>
<listitem>
<para>
the interceptor ordering is non-global — it is determined by the order in which interceptors are
listed at the class level.
</para>
</listitem>
</itemizedlist>
<para>Therefore, we recommend the use of CDI-style interceptor bindings.</para>
</section>
<!--
vim:et:ts=3:sw=3:tw=120
-->
</chapter>