1 package com.foxinmy.weixin4j.dispatcher;
2
3 import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
4
5 import java.io.ByteArrayInputStream;
6 import java.lang.reflect.Constructor;
7 import java.lang.reflect.Modifier;
8 import java.util.ArrayList;
9 import java.util.Collections;
10 import java.util.Comparator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.concurrent.ConcurrentHashMap;
15
16 import javax.xml.bind.JAXBContext;
17 import javax.xml.bind.JAXBElement;
18 import javax.xml.bind.JAXBException;
19 import javax.xml.bind.Unmarshaller;
20 import javax.xml.transform.Source;
21 import javax.xml.transform.stream.StreamSource;
22
23 import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
24 import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
25 import com.foxinmy.weixin4j.request.WeixinMessage;
26 import com.foxinmy.weixin4j.request.WeixinRequest;
27 import com.foxinmy.weixin4j.response.BlankResponse;
28 import com.foxinmy.weixin4j.response.WeixinResponse;
29 import com.foxinmy.weixin4j.socket.WeixinMessageTransfer;
30 import com.foxinmy.weixin4j.util.ClassUtil;
31 import com.foxinmy.weixin4j.util.HttpUtil;
32 import com.foxinmy.weixin4j.util.ServerToolkits;
33 import com.foxinmy.weixin4j.xml.MessageTransferHandler;
34
35 import io.netty.channel.ChannelFutureListener;
36 import io.netty.channel.ChannelHandlerContext;
37 import io.netty.handler.codec.http.DefaultFullHttpResponse;
38 import io.netty.handler.codec.http.FullHttpResponse;
39 import io.netty.util.internal.logging.InternalLogger;
40 import io.netty.util.internal.logging.InternalLoggerFactory;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 public class WeixinMessageDispatcher {
56
57 private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
58
59
60
61
62 private List<WeixinMessageHandler> messageHandlerList;
63 private WeixinMessageHandler[] messageHandlers;
64
65
66
67 private String[] messageHandlerPackages;
68
69
70
71
72 private List<WeixinMessageInterceptor> messageInterceptorList;
73 private WeixinMessageInterceptor[] messageInterceptors;
74
75
76
77 private String[] messageInterceptorPackages;
78
79
80
81
82 private BeanFactory beanFactory;
83
84
85
86
87 private WeixinMessageMatcher messageMatcher;
88
89
90
91 private Map<Class<? extends WeixinMessage>, Unmarshaller> messageUnmarshaller;
92
93
94
95 private boolean alwaysResponse;
96
97 public WeixinMessageDispatcher() {
98 this(new DefaultMessageMatcher());
99 }
100
101 public WeixinMessageDispatcher(WeixinMessageMatcher messageMatcher) {
102 this.messageMatcher = messageMatcher;
103 this.messageUnmarshaller = new ConcurrentHashMap<Class<? extends WeixinMessage>, Unmarshaller>();
104 }
105
106
107
108
109
110
111
112
113
114
115
116 public void doDispatch(final ChannelHandlerContext context, final WeixinRequest request) {
117 WeixinMessageTransfer messageTransfer = MessageTransferHandler.parser(request);
118 context.channel().attr(ServerToolkits.MESSAGE_TRANSFER_KEY).set(messageTransfer);
119 WeixinMessageKey messageKey = defineMessageKey(messageTransfer, request);
120 Class<? extends WeixinMessage> targetClass = messageMatcher.match(messageKey);
121 WeixinMessage message = messageRead(request.getOriginalContent(), targetClass);
122 logger.info("define '{}' matched '{}'", messageKey, targetClass);
123 MessageHandlerExecutor handlerExecutor = getHandlerExecutor(context, request, messageKey, message,
124 messageTransfer.getNodeNames());
125 if (handlerExecutor == null || handlerExecutor.getMessageHandler() == null) {
126 noHandlerFound(context, request, message);
127 return;
128 }
129 if (!handlerExecutor.applyPreHandle(request, message)) {
130 return;
131 }
132 Exception exception = null;
133 WeixinResponse response = null;
134 try {
135 response = handlerExecutor.getMessageHandler().doHandle(request, message);
136 handlerExecutor.applyPostHandle(request, response, message);
137 context.writeAndFlush(response);
138 } catch (Exception e) {
139 exception = e;
140 }
141 handlerExecutor.triggerAfterCompletion(request, response, message, exception);
142 }
143
144
145
146
147
148
149
150
151
152
153 protected WeixinMessageKey defineMessageKey(WeixinMessageTransfer messageTransfer, WeixinRequest request) {
154 return new WeixinMessageKey(messageTransfer.getMsgType(), messageTransfer.getEventType(),
155 messageTransfer.getAccountType());
156 }
157
158
159
160
161
162
163
164
165
166
167
168 protected void noHandlerFound(ChannelHandlerContext context, WeixinRequest request, WeixinMessage message) {
169 logger.warn("no handler found for {}", request);
170 if (alwaysResponse) {
171 context.write(BlankResponse.global);
172 } else {
173 FullHttpResponse response = new DefaultFullHttpResponse(request.getProtocolVersion(), NOT_FOUND);
174 HttpUtil.resolveHeaders(response);
175 context.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
176 }
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195 protected MessageHandlerExecutor getHandlerExecutor(ChannelHandlerContext context, WeixinRequest request,
196 WeixinMessageKey messageKey, WeixinMessage message, Set<String> nodeNames) {
197 WeixinMessageHandler[] messageHandlers = getMessageHandlers();
198 if (messageHandlers == null) {
199 return null;
200 }
201 logger.info("resolve message handlers '{}'", this.messageHandlerList);
202 List<WeixinMessageHandler> matchedMessageHandlers = new ArrayList<WeixinMessageHandler>();
203 for (WeixinMessageHandler handler : messageHandlers) {
204 if (handler.canHandle(request, message, nodeNames)) {
205 matchedMessageHandlers.add(handler);
206 }
207 }
208 if (matchedMessageHandlers.isEmpty()) {
209 return null;
210 }
211 Collections.sort(matchedMessageHandlers, new Comparator<WeixinMessageHandler>() {
212 @Override
213 public int compare(WeixinMessageHandler m1, WeixinMessageHandler m2) {
214 return m2.weight() - m1.weight();
215 }
216 });
217 logger.info("matched message handlers '{}'", matchedMessageHandlers);
218 return new MessageHandlerExecutor(context, matchedMessageHandlers.get(0), getMessageInterceptors());
219 }
220
221
222
223
224
225
226
227 public WeixinMessageHandler[] getMessageHandlers() {
228 if (this.messageHandlers == null) {
229 if (messageHandlerPackages != null) {
230 List<Class<?>> messageHandlerClass = new ArrayList<Class<?>>();
231 for (String packageName : messageHandlerPackages) {
232 messageHandlerClass.addAll(ClassUtil.getClasses(packageName));
233 }
234 if (beanFactory != null) {
235 for (Class<?> clazz : messageHandlerClass) {
236 if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
237 || !WeixinMessageHandler.class.isAssignableFrom(clazz)) {
238 continue;
239 }
240 try {
241 messageHandlerList.add((WeixinMessageHandler) beanFactory.getBean(clazz));
242 } catch (RuntimeException ex) {
243 for (Object o : beanFactory.getBeans(clazz).values()) {
244 if (o.getClass() == clazz) {
245 messageHandlerList.add((WeixinMessageHandler) o);
246 break;
247 }
248 }
249 }
250 }
251 } else {
252 for (Class<?> clazz : messageHandlerClass) {
253 if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
254 || !WeixinMessageHandler.class.isAssignableFrom(clazz)) {
255 continue;
256 }
257 try {
258 Constructor<?> ctor = clazz.getDeclaredConstructor();
259 ServerToolkits.makeConstructorAccessible(ctor);
260 messageHandlerList.add((WeixinMessageHandler) ctor.newInstance((Object[]) null));
261 } catch (Exception ex) {
262 throw new RuntimeException(clazz.getName() + " instantiate fail", ex);
263 }
264 }
265 }
266 }
267 if (messageHandlerList != null && !this.messageHandlerList.isEmpty()) {
268 this.messageHandlers = this.messageHandlerList
269 .toArray(new WeixinMessageHandler[this.messageHandlerList.size()]);
270 }
271 }
272 return this.messageHandlers;
273 }
274
275
276
277
278
279
280
281 public WeixinMessageInterceptor[] getMessageInterceptors() {
282 if (this.messageInterceptors == null) {
283 if (this.messageInterceptorPackages != null) {
284 List<Class<?>> messageInterceptorClass = new ArrayList<Class<?>>();
285 for (String packageName : messageInterceptorPackages) {
286 messageInterceptorClass.addAll(ClassUtil.getClasses(packageName));
287 }
288 if (beanFactory != null) {
289 for (Class<?> clazz : messageInterceptorClass) {
290 if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
291 || !WeixinMessageInterceptor.class.isAssignableFrom(clazz)) {
292 continue;
293 }
294 try {
295 messageInterceptorList.add((WeixinMessageInterceptor) beanFactory.getBean(clazz));
296 } catch (RuntimeException ex) {
297 for (Object o : beanFactory.getBeans(clazz).values()) {
298 if (o.getClass() == clazz) {
299 messageInterceptorList.add((WeixinMessageInterceptor) o);
300 break;
301 }
302 }
303 }
304 }
305 } else {
306 for (Class<?> clazz : messageInterceptorClass) {
307 if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
308 || !WeixinMessageInterceptor.class.isAssignableFrom(clazz)) {
309 continue;
310 }
311 try {
312 Constructor<?> ctor = clazz.getDeclaredConstructor();
313 ServerToolkits.makeConstructorAccessible(ctor);
314 messageInterceptorList.add((WeixinMessageInterceptor) ctor.newInstance((Object[]) null));
315 } catch (Exception ex) {
316 throw new RuntimeException(clazz.getName() + " instantiate fail", ex);
317 }
318 }
319 }
320 }
321 if (this.messageInterceptorList != null && !this.messageInterceptorList.isEmpty()) {
322 Collections.sort(messageInterceptorList, new Comparator<WeixinMessageInterceptor>() {
323 @Override
324 public int compare(WeixinMessageInterceptor m1, WeixinMessageInterceptor m2) {
325 return m2.weight() - m1.weight();
326 }
327 });
328 this.messageInterceptors = this.messageInterceptorList
329 .toArray(new WeixinMessageInterceptor[this.messageInterceptorList.size()]);
330 }
331 }
332 logger.info("resolve message interceptors '{}'", this.messageInterceptorList);
333 return this.messageInterceptors;
334 }
335
336
337
338
339
340
341
342
343
344
345 protected <M extends WeixinMessage> M messageRead(String message, Class<M> clazz) {
346 if (clazz == null) {
347 return null;
348 }
349 try {
350 Source source = new StreamSource(new ByteArrayInputStream(ServerToolkits.getBytesUtf8(message)));
351 JAXBElement<M> jaxbElement = getUnmarshaller(clazz).unmarshal(source, clazz);
352 return jaxbElement.getValue();
353 } catch (JAXBException e) {
354 throw new RuntimeException(e);
355 }
356 }
357
358
359
360
361
362
363
364
365 protected Unmarshaller getUnmarshaller(Class<? extends WeixinMessage> clazz) {
366 Unmarshaller unmarshaller = messageUnmarshaller.get(clazz);
367 if (unmarshaller == null) {
368 try {
369 JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
370 unmarshaller = jaxbContext.createUnmarshaller();
371 messageUnmarshaller.put(clazz, unmarshaller);
372 } catch (JAXBException e) {
373 throw new RuntimeException(e);
374 }
375 }
376 return unmarshaller;
377 }
378
379 public void setMessageHandlerList(List<WeixinMessageHandler> messageHandlerList) {
380 this.messageHandlerList = messageHandlerList;
381 }
382
383 public void setMessageInterceptorList(List<WeixinMessageInterceptor> messageInterceptorList) {
384 this.messageInterceptorList = messageInterceptorList;
385 }
386
387 public String[] getMessageHandlerPackages() {
388 return messageHandlerPackages;
389 }
390
391 public String[] getMessageInterceptorPackages() {
392 return messageInterceptorPackages;
393 }
394
395 public void setMessageHandlerPackages(String... messageHandlerPackages) {
396 this.messageHandlerPackages = messageHandlerPackages;
397 }
398
399 public void setMessageInterceptorPackages(String... messageInterceptorPackages) {
400 this.messageInterceptorPackages = messageInterceptorPackages;
401 }
402
403 public BeanFactory getBeanFactory() {
404 return beanFactory;
405 }
406
407 public void setBeanFactory(BeanFactory beanFactory) {
408 this.beanFactory = beanFactory;
409 }
410
411 public void registMessageClass(WeixinMessageKey messageKey, Class<? extends WeixinMessage> messageClass) {
412 messageMatcher.regist(messageKey, messageClass);
413 }
414
415 public WeixinMessageMatcher getMessageMatcher() {
416 return this.messageMatcher;
417 }
418
419
420
421
422 public void openAlwaysResponse() {
423 this.alwaysResponse = true;
424 }
425 }