decorators.xml
7.04 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
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ ]>
<chapter id="decorators">
<title>Decorators</title>
<para>
Interceptors are a powerful way to capture and separate concerns which are <emphasis>orthogonal</emphasis> to the
application (and type system). Any interceptor is able to intercept invocations of any Java type. This makes them
perfect for solving technical concerns such as transaction management, security and call logging. However, by
nature, interceptors are unaware of the actual semantics of the events they intercept. Thus, interceptors aren't
an appropriate tool for separating business-related concerns.
</para>
<para>
The reverse is true of <emphasis>decorators</emphasis>. A decorator intercepts invocations only for a certain Java
interface, and is therefore aware of all the semantics attached to that interface. Since decorators directly
implement operations with business semantics, it makes them the perfect tool for modeling some kinds of business
concerns. It also means that a decorator doesn't have the generality of an interceptor. Decorators aren't able to
solve technical concerns that cut across many disparate types. Interceptors and decorators, though similar in many
ways, are complementary. Let's look at some cases where decorators fit the bill.
</para>
<para>Suppose we have an interface that represents accounts:</para>
<programlisting role="JAVA"><![CDATA[public interface Account {
public BigDecimal getBalance();
public User getOwner();
public void withdraw(BigDecimal amount);
public void deposit(BigDecimal amount);
}]]></programlisting>
<para>
Several different beans in our system implement the <literal>Account</literal> interface. However, we have a
common legal requirement that; for any kind of account, large transactions must be recorded by the system in a
special log. This is a perfect job for a decorator.
</para>
<para>
A decorator is a bean (possibly even an abstract class) that implements the type it decorates and is annotated
<literal>@Decorator</literal>.
</para>
<programlisting role="JAVA"><![CDATA[@Decorator
public abstract class LargeTransactionDecorator
implements Account {
...
}]]></programlisting>
<para>
The decorator implements the methods of the decorated type that it wants to intercept.
</para>
<programlisting role="JAVA"><![CDATA[@Decorator
public abstract class LargeTransactionDecorator
implements Account {
@Inject @Delegate @Any Account account;
@PersistenceContext EntityManager em;
public void withdraw(BigDecimal amount) {
...
}
public void deposit(BigDecimal amount);
...
}
}]]></programlisting>
<para>
Unlike other beans, a decorator may be an abstract class. Therefore, if there's nothing special the decorator
needs to do for a particular method of the decorated interface, you don't need to implement that method.
</para>
<para>
Interceptors for a method are called before decorators that apply to the method.
</para>
<section>
<title>Delegate object</title>
<para>
Decorators have a special injection point, called the <emphasis>delegate injection point</emphasis>, with the same
type as the beans they decorate, and the annotation <literal>@Delegate</literal>. There must be exactly one delegate
injection point, which can be a constructor parameter, initializer method parameter or injected field.
</para>
<programlisting role="JAVA"><![CDATA[@Decorator
public abstract class LargeTransactionDecorator
implements Account {
@Inject @Delegate @Any Account account;
...
}]]></programlisting>
<para>A decorator is bound to any bean which:</para>
<itemizedlist>
<listitem>
<para>has the type of the delegate injection point as a bean type, and</para>
</listitem>
<listitem>
<para>has all qualifiers that are declared at the delegate injection point.</para>
</listitem>
</itemizedlist>
<para>
This delegate injection point specifies that the decorator is bound to all beans that implement
<literal>Account</literal>:
</para>
<programlisting role="JAVA"><![CDATA[@Inject @Delegate @Any Account account;]]></programlisting>
<para>
A delegate injection point may specify any number of qualifier annotations. The decorator will only be
bound to beans with the same qualifiers.
</para>
<programlisting role="JAVA"><![CDATA[@Inject @Delegate @Foreign Account account;]]></programlisting>
<para>
The decorator may invoke the delegate object, which has much the same effect as calling
<literal>InvocationContext.proceed()</literal> from an interceptor. The main difference is that the decorator
can invoke <emphasis>any</emphasis> business method on the delegate object.
</para>
<programlisting role="JAVA"><![CDATA[@Decorator
public abstract class LargeTransactionDecorator
implements Account {
@Inject @Delegate @Any Account account;
@PersistenceContext EntityManager em;
public void withdraw(BigDecimal amount) {
account.withdraw(amount);
if ( amount.compareTo(LARGE_AMOUNT)>0 ) {
em.persist( new LoggedWithdrawl(amount) );
}
}
public void deposit(BigDecimal amount);
account.deposit(amount);
if ( amount.compareTo(LARGE_AMOUNT)>0 ) {
em.persist( new LoggedDeposit(amount) );
}
}
}]]></programlisting>
</section>
<section>
<title>Enabling decorators</title>
<para>
By default, all decorators are disabled. We need to <emphasis>enable</emphasis> our decorator 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">
<decorators>
<class>org.mycompany.myapp.LargeTransactionDecorator</class>
</decorators>
</beans>]]></programlisting>
<para>
This declaration serves the same purpose for decorators that the <literal><interceptors></literal>
declaration serves for interceptors:
</para>
<itemizedlist>
<listitem>
<para>
it enables us to specify a total ordering for all decorators in our system, ensuring deterministic
behavior, and
</para>
</listitem>
<listitem>
<para>it lets us enable or disable decorator classes at deployment time.</para>
</listitem>
</itemizedlist>
</section>
<!--
vim:et:ts=3:sw=3:tw=120
-->
</chapter>