View Javadoc
1   package com.zone.weixin4j.dispatcher;
2   
3   import com.zone.weixin4j.annotation.WxMessageHandler;
4   import com.zone.weixin4j.annotation.WxMessageInterceptor;
5   import com.zone.weixin4j.exception.HttpResponseException;
6   import com.zone.weixin4j.exception.MessageInterceptorException;
7   import com.zone.weixin4j.exception.WeixinException;
8   import com.zone.weixin4j.handler.DebugMessageHandler;
9   import com.zone.weixin4j.handler.WeixinMessageHandler;
10  import com.zone.weixin4j.interceptor.WeixinMessageInterceptor;
11  import com.zone.weixin4j.request.WeixinMessage;
12  import com.zone.weixin4j.request.WeixinRequest;
13  import com.zone.weixin4j.response.BlankResponse;
14  import com.zone.weixin4j.response.WeixinResponse;
15  import com.zone.weixin4j.service.WeiXin4jContextAware;
16  import com.zone.weixin4j.service.context.WeiXin4jContextAwareImpl;
17  import com.zone.weixin4j.socket.WeixinMessageTransfer;
18  import com.zone.weixin4j.util.ServerToolkits;
19  import com.zone.weixin4j.xml.MessageTransferHandler;
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  import org.springframework.beans.factory.annotation.Autowired;
23  import org.springframework.context.annotation.DependsOn;
24  import org.springframework.stereotype.Component;
25  
26  import javax.annotation.PostConstruct;
27  import javax.xml.bind.JAXBContext;
28  import javax.xml.bind.JAXBElement;
29  import javax.xml.bind.JAXBException;
30  import javax.xml.bind.Unmarshaller;
31  import javax.xml.transform.Source;
32  import javax.xml.transform.stream.StreamSource;
33  import java.io.ByteArrayInputStream;
34  import java.util.*;
35  import java.util.concurrent.ConcurrentHashMap;
36  
37  /**
38   * 微信消息分发器
39   *
40   * @author jinyu(foxinmy@gmail.com)
41   * @className WeixinMessageDispatcher
42   * @date 2015年5月7日
43   * @updateBy Yz(174975857@qq.com)
44   * @since JDK 1.6
45   */
46  @Component
47  @DependsOn({"weiXin4jContextAware"})
48  public class WeixinMessageDispatcher {
49  
50      @Autowired
51      private WeiXin4jContextAware weiXin4jContextAware;
52  
53      private final Log logger = LogFactory.getLog(getClass());
54  
55      /**
56       * 消息处理器
57       */
58      private List<WeixinMessageHandler> messageHandlerList = new ArrayList<WeixinMessageHandler>();
59      private WeixinMessageHandler[] messageHandlers;
60  
61      /**
62       * 消息拦截器
63       */
64      private List<WeixinMessageInterceptor> messageInterceptorList = new ArrayList<WeixinMessageInterceptor>();
65      private WeixinMessageInterceptor[] messageInterceptors;
66  
67      /**
68       * 消息匹配
69       */
70      private WeixinMessageMatcher messageMatcher;
71      /**
72       * 消息转换
73       */
74      private Map<Class<? extends WeixinMessage>, Unmarshaller> messageUnmarshaller;
75      /**
76       * 是否总是响应请求,如未匹配到MessageHandler时回复空白消息
77       */
78      private boolean alwaysResponse;
79  
80      public WeixinMessageDispatcher() {
81          this(new DefaultMessageMatcher());
82      }
83  
84      public WeixinMessageDispatcher(WeixinMessageMatcher messageMatcher) {
85          this.messageMatcher = messageMatcher;
86          this.messageUnmarshaller = new ConcurrentHashMap<Class<? extends WeixinMessage>, Unmarshaller>();
87      }
88  
89      @PostConstruct
90      public void init() {
91          try {
92              this.getMessageHandlers();
93              this.getMessageInterceptors();
94              if (weiXin4jContextAware.isOpenAlwaysResponse()) {
95                  this.openAlwaysResponse();
96              }
97              if (weiXin4jContextAware.isUseDebugMessageHandler()) {
98                  if(null == messageHandlerList){
99                      messageHandlerList = new ArrayList<WeixinMessageHandler>();
100                     messageHandlerList.add(DebugMessageHandler.global);
101                 }
102             }
103             this.messageMatcher = weiXin4jContextAware.getWeixinMessageMatcher() == null ? new DefaultMessageMatcher() : weiXin4jContextAware.getWeixinMessageMatcher();
104             this.messageUnmarshaller = new ConcurrentHashMap<Class<? extends WeixinMessage>, Unmarshaller>();
105         } catch (Exception e) {
106             e.printStackTrace();
107         }
108     }
109 
110     /**
111      * 对消息进行一系列的处理,包括 拦截、匹配、分发等动作
112      *
113      * @param request 微信请求
114      * @throws WeixinException
115      */
116     public WeixinResponse doDispatch(final WeixinRequest request) throws WeixinException, HttpResponseException, MessageInterceptorException {
117         WeixinMessageTransfer messageTransfer = MessageTransferHandler.parser(request);
118         WeiXin4jContextAwareImpl.getWeixinMessageTransfer().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(String.format("define %s matched %s", messageKey, targetClass));
123         MessageHandlerExecutor handlerExecutor = getHandlerExecutor(request, messageKey, message, messageTransfer.getNodeNames());
124         if (handlerExecutor == null || handlerExecutor.getMessageHandler() == null) {
125             return noHandlerFound(request, message);
126         }
127         if (!handlerExecutor.applyPreHandle(request, message)) {
128             throw new MessageInterceptorException(" Interceptor Not Accept !! ");
129         }
130         Exception exception = null;
131         WeixinResponse response = null;
132         try {
133             response = handlerExecutor.getMessageHandler().doHandle(request, message, messageTransfer.getNodeNames());
134             handlerExecutor.applyPostHandle(request, response, message);
135         } catch (Exception e) {
136             exception = e;
137         }
138         handlerExecutor.triggerAfterCompletion(request, response, message, exception);
139         return response;
140     }
141 
142     /**
143      * 声明messagekey
144      *
145      * @param messageTransfer 基础消息
146      * @param request         请求信息
147      * @return
148      */
149     protected WeixinMessageKey defineMessageKey(
150             WeixinMessageTransfer messageTransfer, WeixinRequest request) {
151         return new WeixinMessageKey(messageTransfer.getMsgType(),
152                 messageTransfer.getEventType(),
153                 messageTransfer.getAccountType());
154     }
155 
156     /**
157      * 未匹配到handler时触发
158      *
159      * @param request 微信请求
160      * @param message 微信消息
161      */
162     protected WeixinResponse noHandlerFound(WeixinRequest request, WeixinMessage message) throws HttpResponseException {
163         logger.warn(String.format("no handler found for %s", request));
164         if (alwaysResponse) {
165             return BlankResponse.global;
166         } else {
167             throw new HttpResponseException(HttpResponseException.HttpResponseStatus.NOT_FOUND);
168         }
169     }
170 
171     /**
172      * MessageHandlerExecutor
173      *
174      * @param request    微信请求
175      * @param messageKey 消息的key
176      * @param message    微信消息
177      * @param nodeNames  节点名称集合
178      * @return MessageHandlerExecutor
179      * @throws WeixinException
180      * @see MessageHandlerExecutor
181      */
182     protected MessageHandlerExecutor getHandlerExecutor(
183             WeixinRequest request,
184             WeixinMessageKey messageKey, WeixinMessage message,
185             Set<String> nodeNames) throws WeixinException {
186         WeixinMessageHandler[] messageHandlers = getMessageHandlers();
187         if (messageHandlers == null) {
188             return null;
189         }
190         logger.info(String.format("resolve message handlers %s", this.messageHandlerList));
191         List<WeixinMessageHandler> matchedMessageHandlers = new ArrayList<WeixinMessageHandler>();
192         for (WeixinMessageHandler handler : messageHandlers) {
193             if (handler.canHandle(request, message, nodeNames)) {
194                 matchedMessageHandlers.add(handler);
195             }
196         }
197         if (matchedMessageHandlers.isEmpty()) {
198             return null;
199         }
200         Collections.sort(matchedMessageHandlers,
201                 new Comparator<WeixinMessageHandler>() {
202                     @Override
203                     public int compare(WeixinMessageHandler m1,
204                                        WeixinMessageHandler m2) {
205                         return m2.weight() - m1.weight();
206                     }
207                 });
208         logger.info(String.format("matched message handlers %s", matchedMessageHandlers));
209         return new MessageHandlerExecutor(matchedMessageHandlers.get(0), getMessageInterceptors());
210     }
211 
212     /**
213      * 获取所有的handler
214      *
215      * @return handler集合
216      * @throws WeixinException
217      * @see com.zone.weixin4j.handler.WeixinMessageHandler
218      */
219     public WeixinMessageHandler[] getMessageHandlers() throws WeixinException {
220         if (this.messageHandlers == null) {
221             String[] beanNamesForAnnotation = this.weiXin4jContextAware.getApplicationContext().getBeanNamesForAnnotation(WxMessageHandler.class);
222             for (String str : beanNamesForAnnotation) {
223                 Object bean = this.weiXin4jContextAware.getApplicationContext().getBean(str);
224                 if (bean instanceof WeixinMessageHandler) {
225                     this.messageHandlerList.add((WeixinMessageHandler) this.weiXin4jContextAware.getApplicationContext().getBean(str));
226                 }
227             }
228         }
229         if (messageHandlerList != null && !this.messageHandlerList.isEmpty()) {
230             this.messageHandlers = this.messageHandlerList.toArray(new WeixinMessageHandler[this.messageHandlerList.size()]);
231         }
232         return this.messageHandlers;
233     }
234 
235     /**
236      * 获取所有的interceptor
237      *
238      * @return interceptor集合
239      * @throws WeixinException
240      */
241     public WeixinMessageInterceptor[] getMessageInterceptors()
242             throws WeixinException {
243         if (this.messageInterceptors == null) {
244             String[] beanNamesForAnnotation = this.weiXin4jContextAware.getApplicationContext().getBeanNamesForAnnotation(WxMessageInterceptor.class);
245             for (String str : beanNamesForAnnotation) {
246                 Object bean = this.weiXin4jContextAware.getApplicationContext().getBean(str);
247                 if (bean instanceof WeixinMessageInterceptor) {
248                     this.messageInterceptorList.add((WeixinMessageInterceptor) this.weiXin4jContextAware.getApplicationContext().getBean(str));
249                 }
250             }
251         }
252         if (this.messageInterceptorList != null && !this.messageInterceptorList.isEmpty()) {
253             Collections.sort(messageInterceptorList,
254                     new Comparator<WeixinMessageInterceptor>() {
255                         @Override
256                         public int compare(WeixinMessageInterceptor m1, WeixinMessageInterceptor m2) {
257                             return m2.weight() - m1.weight();
258                         }
259                     });
260             this.messageInterceptors = this.messageInterceptorList.toArray(new WeixinMessageInterceptor[this.messageInterceptorList.size()]);
261         }
262         logger.info(String.format("resolve message interceptors %s", this.messageInterceptorList));
263         return this.messageInterceptors;
264     }
265 
266     /**
267      * jaxb读取微信消息
268      *
269      * @param message xml消息
270      * @param clazz   消息类型
271      * @return 消息对象
272      * @throws WeixinException
273      */
274     protected <M extends WeixinMessage> M messageRead(String message,
275                                                       Class<M> clazz) throws WeixinException {
276         if (clazz == null) {
277             return null;
278         }
279         try {
280             Source source = new StreamSource(new ByteArrayInputStream(
281                     ServerToolkits.getBytesUtf8(message)));
282             JAXBElement<M> jaxbElement = getUnmarshaller(clazz).unmarshal(
283                     source, clazz);
284             return jaxbElement.getValue();
285         } catch (JAXBException e) {
286             throw new WeixinException(e);
287         }
288     }
289 
290     /**
291      * xml消息转换器
292      *
293      * @param clazz 消息类型
294      * @return 消息转换器
295      * @throws WeixinException
296      */
297     protected Unmarshaller getUnmarshaller(Class<? extends WeixinMessage> clazz)
298             throws WeixinException {
299         Unmarshaller unmarshaller = messageUnmarshaller.get(clazz);
300         if (unmarshaller == null) {
301             try {
302                 JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
303                 unmarshaller = jaxbContext.createUnmarshaller();
304                 messageUnmarshaller.put(clazz, unmarshaller);
305             } catch (JAXBException e) {
306                 throw new WeixinException(e);
307             }
308         }
309         return unmarshaller;
310     }
311 
312     public void setMessageHandlerList(
313             List<WeixinMessageHandler> messageHandlerList) {
314         this.messageHandlerList = messageHandlerList;
315     }
316 
317     public void setMessageInterceptorList(
318             List<WeixinMessageInterceptor> messageInterceptorList) {
319         this.messageInterceptorList = messageInterceptorList;
320     }
321 
322     public void registMessageClass(WeixinMessageKey messageKey,
323                                    Class<? extends WeixinMessage> messageClass) {
324         messageMatcher.regist(messageKey, messageClass);
325     }
326 
327     public WeixinMessageMatcher getMessageMatcher() {
328         return this.messageMatcher;
329     }
330 
331     /**
332      * 打开总是响应开关,如未匹配到MessageHandler时回复空白消息
333      */
334     public void openAlwaysResponse() {
335         this.alwaysResponse = true;
336     }
337 }