1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.statemachine;
21
22 import java.lang.reflect.InvocationHandler;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Proxy;
25
26 import org.apache.mina.statemachine.context.SingletonStateContextLookup;
27 import org.apache.mina.statemachine.context.StateContext;
28 import org.apache.mina.statemachine.context.StateContextLookup;
29 import org.apache.mina.statemachine.event.DefaultEventFactory;
30 import org.apache.mina.statemachine.event.Event;
31 import org.apache.mina.statemachine.event.EventArgumentsInterceptor;
32 import org.apache.mina.statemachine.event.EventFactory;
33 import org.apache.mina.statemachine.event.UnhandledEventException;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37
38
39
40
41
42
43 public class StateMachineProxyBuilder {
44 private static final Logger LOGGER = LoggerFactory.getLogger(StateMachineProxyBuilder.class);
45
46 private static final Object[] EMPTY_ARGUMENTS = new Object[0];
47
48 private StateContextLookup contextLookup = new SingletonStateContextLookup();
49
50 private EventFactory eventFactory = new DefaultEventFactory();
51
52 private EventArgumentsInterceptor interceptor = null;
53
54 private boolean ignoreUnhandledEvents = false;
55
56 private boolean ignoreStateContextLookupFailure = false;
57
58 private String name = null;
59
60
61
62
63
64 private ClassLoader defaultCl = null;
65
66
67
68
69 public StateMachineProxyBuilder() {
70 }
71
72
73
74
75
76
77
78
79
80 public StateMachineProxyBuilder setName(String name) {
81 this.name = name;
82 return this;
83 }
84
85
86
87
88
89
90
91
92 public StateMachineProxyBuilder setStateContextLookup(StateContextLookup contextLookup) {
93 this.contextLookup = contextLookup;
94 return this;
95 }
96
97
98
99
100
101
102
103
104 public StateMachineProxyBuilder setEventFactory(EventFactory eventFactory) {
105 this.eventFactory = eventFactory;
106 return this;
107 }
108
109
110
111
112
113
114
115
116 public StateMachineProxyBuilder setEventArgumentsInterceptor(EventArgumentsInterceptor interceptor) {
117 this.interceptor = interceptor;
118 return this;
119 }
120
121
122
123
124
125
126
127
128
129 public StateMachineProxyBuilder setIgnoreUnhandledEvents(boolean b) {
130 this.ignoreUnhandledEvents = b;
131 return this;
132 }
133
134
135
136
137
138
139
140
141
142 public StateMachineProxyBuilder setIgnoreStateContextLookupFailure(boolean b) {
143 this.ignoreStateContextLookupFailure = b;
144 return this;
145 }
146
147
148
149
150
151
152
153
154
155 public StateMachineProxyBuilder setClassLoader(ClassLoader cl) {
156 this.defaultCl = cl;
157 return this;
158 }
159
160
161
162
163
164
165
166
167
168
169
170 @SuppressWarnings("unchecked")
171 public <T> T create(Class<T> iface, StateMachine sm) {
172 return (T) create(new Class[] { iface }, sm);
173 }
174
175
176
177
178
179
180
181
182
183
184 public Object create(Class<?>[] ifaces, StateMachine sm) {
185 ClassLoader cl = defaultCl;
186 if (cl == null) {
187 cl = Thread.currentThread().getContextClassLoader();
188 }
189
190 InvocationHandler handler = new MethodInvocationHandler(sm, contextLookup, interceptor, eventFactory,
191 ignoreUnhandledEvents, ignoreStateContextLookupFailure, name);
192
193 return Proxy.newProxyInstance(cl, ifaces, handler);
194 }
195
196 private static class MethodInvocationHandler implements InvocationHandler {
197 private final StateMachine sm;
198
199 private final StateContextLookup contextLookup;
200
201 private final EventArgumentsInterceptor interceptor;
202
203 private final EventFactory eventFactory;
204
205 private final boolean ignoreUnhandledEvents;
206
207 private final boolean ignoreStateContextLookupFailure;
208
209 private final String name;
210
211 public MethodInvocationHandler(StateMachine sm, StateContextLookup contextLookup,
212 EventArgumentsInterceptor interceptor, EventFactory eventFactory, boolean ignoreUnhandledEvents,
213 boolean ignoreStateContextLookupFailure, String name) {
214
215 this.contextLookup = contextLookup;
216 this.sm = sm;
217 this.interceptor = interceptor;
218 this.eventFactory = eventFactory;
219 this.ignoreUnhandledEvents = ignoreUnhandledEvents;
220 this.ignoreStateContextLookupFailure = ignoreStateContextLookupFailure;
221 this.name = name;
222 }
223
224 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
225 if ("hashCode".equals(method.getName()) && args == null) {
226 return Integer.valueOf(System.identityHashCode(proxy));
227 }
228
229 if ("equals".equals(method.getName()) && args.length == 1) {
230 return Boolean.valueOf(proxy == args[0]);
231 }
232
233 if ("toString".equals(method.getName()) && args == null) {
234 return (name != null ? name : proxy.getClass().getName()) + "@"
235 + Integer.toHexString(System.identityHashCode(proxy));
236 }
237
238 if (LOGGER.isDebugEnabled()) {
239 LOGGER.debug("Method invoked: " + method);
240 }
241
242 args = args == null ? EMPTY_ARGUMENTS : args;
243
244 if (interceptor != null) {
245 args = interceptor.modify(args);
246 }
247
248 StateContext context = contextLookup.lookup(args);
249
250 if (context == null) {
251 if (ignoreStateContextLookupFailure) {
252 return null;
253 }
254
255 throw new IllegalStateException("Cannot determine state context for method invocation: " + method);
256 }
257
258 Event event = eventFactory.create(context, method, args);
259
260 try {
261 sm.handle(event);
262 } catch (UnhandledEventException uee) {
263 if (!ignoreUnhandledEvents) {
264 throw uee;
265 }
266 }
267
268 return null;
269 }
270 }
271 }