removed WeixinException class
This commit is contained in:
parent
4c0f96fcfe
commit
3c92d75276
@ -66,16 +66,12 @@ public class Weixin4jServerStartupWithThread implements ApplicationContextAware
|
|||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
bootstrap = new WeixinServerBootstrap(aesToken) // 指定开发者token信息。
|
||||||
bootstrap = new WeixinServerBootstrap(aesToken) // 指定开发者token信息。
|
.handlerPackagesToScan(handlerPackage) // 扫描处理消息的包。
|
||||||
.handlerPackagesToScan(handlerPackage) // 扫描处理消息的包。
|
.resolveBeanFactory(new SpringBeanFactory(applicationContext)) // 声明处理消息类由Spring容器去实例化。
|
||||||
.resolveBeanFactory(new SpringBeanFactory(applicationContext)) // 声明处理消息类由Spring容器去实例化。
|
.addHandler(DebugMessageHandler.global) // 当没有匹配到消息处理时输出调试信息,开发环境打开。
|
||||||
.addHandler(DebugMessageHandler.global) // 当没有匹配到消息处理时输出调试信息,开发环境打开。
|
.openAlwaysResponse(); // 当没有匹配到消息处理时输出空白回复(公众号不会出现「该公众号无法提供服务的提示」),正式环境打开。
|
||||||
.openAlwaysResponse(); // 当没有匹配到消息处理时输出空白回复(公众号不会出现「该公众号无法提供服务的提示」),正式环境打开。
|
bootstrap.startup(port); // 绑定服务的端口号,即对外暴露(微信服务器URL地址)的服务端口。
|
||||||
bootstrap.startup(port); // 绑定服务的端口号,即对外暴露(微信服务器URL地址)的服务端口。
|
|
||||||
} catch (WeixinException e) {
|
|
||||||
InternalLoggerFactory.getInstance(getClass()).error("weixin4j server startup:FAIL", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.example.server.handler;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||||
import com.foxinmy.weixin4j.qy.chat.WeixinChatMessage;
|
import com.foxinmy.weixin4j.qy.chat.WeixinChatMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
@ -12,22 +11,19 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
|
|
||||||
public class ChatMessageHandler implements WeixinMessageHandler {
|
public class ChatMessageHandler implements WeixinMessageHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandle(WeixinRequest request, WeixinMessage message,
|
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
Set<String> nodeNames) throws WeixinException {
|
return nodeNames.contains("PackageId");
|
||||||
return nodeNames.contains("PackageId");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeixinResponse doHandle(WeixinRequest request,
|
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
WeixinMessage message, Set<String> nodeNames)
|
WeixinChatMessage chatMessage = null; // 转换为实体
|
||||||
throws WeixinException {
|
return BlankResponse.global;
|
||||||
WeixinChatMessage chatMessage = null; // 转换为实体
|
}
|
||||||
return BlankResponse.global;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int weight() {
|
public int weight() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,8 @@ import com.foxinmy.weixin4j.response.TextResponse;
|
|||||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义处理消息
|
* 自定义处理消息
|
||||||
|
*
|
||||||
* @className CustomMessageHandler
|
* @className CustomMessageHandler
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2017年1月19日
|
* @date 2017年1月19日
|
||||||
@ -19,22 +20,19 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
*/
|
*/
|
||||||
public class CustomMessageHandler implements WeixinMessageHandler {
|
public class CustomMessageHandler implements WeixinMessageHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandle(WeixinRequest request, WeixinMessage message,
|
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
Set<String> nodeNames) throws WeixinException {
|
// 消息来源某个用户
|
||||||
// 消息来源某个用户
|
return message.getFromUserName().equals("xxx");
|
||||||
return message.getFromUserName().equals("xxx");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeixinResponse doHandle(WeixinRequest request,
|
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
WeixinMessage message, Set<String> nodeNames)
|
return new TextResponse("是你,是你,还是你。");
|
||||||
throws WeixinException {
|
}
|
||||||
return new TextResponse("是你,是你,还是你。");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int weight() {
|
public int weight() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.example.server.handler;
|
|||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.message.TextMessage;
|
import com.foxinmy.weixin4j.message.TextMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
import com.foxinmy.weixin4j.response.TextResponse;
|
import com.foxinmy.weixin4j.response.TextResponse;
|
||||||
@ -10,7 +9,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 输入 hello 回复 world
|
* 输入 hello 回复 world
|
||||||
*
|
*
|
||||||
* @className HelloMessageHandler
|
* @className HelloMessageHandler
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年12月27日
|
* @date 2015年12月27日
|
||||||
@ -19,30 +18,28 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
@Component
|
@Component
|
||||||
public class HelloMessageHandler extends TextMessageHandler {
|
public class HelloMessageHandler extends TextMessageHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandle0(WeixinRequest request, TextMessage message)
|
public boolean canHandle0(WeixinRequest request, TextMessage message) {
|
||||||
throws WeixinException {
|
/**
|
||||||
/**
|
* 用户输入hello时
|
||||||
* 用户输入hello时
|
*/
|
||||||
*/
|
return message.getContent().equalsIgnoreCase("hello");
|
||||||
return message.getContent().equalsIgnoreCase("hello");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeixinResponse doHandle0(WeixinRequest request, TextMessage message)
|
public WeixinResponse doHandle0(WeixinRequest request, TextMessage message) {
|
||||||
throws WeixinException {
|
/**
|
||||||
/**
|
* 返回用户「world」文本
|
||||||
* 返回用户「world」文本
|
*/
|
||||||
*/
|
return new TextResponse("world");
|
||||||
return new TextResponse("world");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 因为HelloMessageHandler和TextMessageHandler都会匹配到文本消息
|
* 因为HelloMessageHandler和TextMessageHandler都会匹配到文本消息
|
||||||
* 所以这里需要提高下权重(大于TextMessageHandler就行了) > TextMessageHandler
|
* 所以这里需要提高下权重(大于TextMessageHandler就行了) > TextMessageHandler
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int weight() {
|
public int weight() {
|
||||||
return super.weight() + 1;
|
return super.weight() + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.example.server.handler;
|
|||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.MessageHandlerAdapter;
|
import com.foxinmy.weixin4j.handler.MessageHandlerAdapter;
|
||||||
import com.foxinmy.weixin4j.mp.event.ScribeEventMessage;
|
import com.foxinmy.weixin4j.mp.event.ScribeEventMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
@ -11,19 +10,17 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理关注消息
|
* 处理关注消息
|
||||||
*
|
*
|
||||||
* @className SubscribeMessageHandler
|
* @className SubscribeMessageHandler
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年12月3日
|
* @date 2015年12月3日
|
||||||
* @since JDK 1.6
|
* @since JDK 1.6
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class SubscribeMessageHandler extends
|
public class SubscribeMessageHandler extends MessageHandlerAdapter<ScribeEventMessage> {
|
||||||
MessageHandlerAdapter<ScribeEventMessage> {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeixinResponse doHandle0(WeixinRequest request, ScribeEventMessage message)
|
public WeixinResponse doHandle0(WeixinRequest request, ScribeEventMessage message) {
|
||||||
throws WeixinException {
|
return new TextResponse("欢迎关注~");
|
||||||
return new TextResponse("欢迎关注~");
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 文本消息处理
|
* 文本消息处理
|
||||||
*
|
*
|
||||||
* @className TextMessageHandler
|
* @className TextMessageHandler
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年11月18日
|
* @date 2015年11月18日
|
||||||
@ -19,9 +19,8 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class TextMessageHandler extends MessageHandlerAdapter<TextMessage> {
|
public class TextMessageHandler extends MessageHandlerAdapter<TextMessage> {
|
||||||
@Override
|
@Override
|
||||||
public WeixinResponse doHandle0(WeixinRequest request, TextMessage message)
|
public WeixinResponse doHandle0(WeixinRequest request, TextMessage message) {
|
||||||
throws WeixinException {
|
return new TextResponse("收到了文本消息");
|
||||||
return new TextResponse("收到了文本消息");
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.example.server.handler;
|
|||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.MessageHandlerAdapter;
|
import com.foxinmy.weixin4j.handler.MessageHandlerAdapter;
|
||||||
import com.foxinmy.weixin4j.message.VoiceMessage;
|
import com.foxinmy.weixin4j.message.VoiceMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
@ -11,7 +10,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 只处理语音消息
|
* 只处理语音消息
|
||||||
*
|
*
|
||||||
* @className VoiceMessageHandler
|
* @className VoiceMessageHandler
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年11月18日
|
* @date 2015年11月18日
|
||||||
@ -20,12 +19,11 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
@Component
|
@Component
|
||||||
public class VoiceMessageHandler extends MessageHandlerAdapter<VoiceMessage> {
|
public class VoiceMessageHandler extends MessageHandlerAdapter<VoiceMessage> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeixinResponse doHandle0(WeixinRequest request, VoiceMessage message)
|
public WeixinResponse doHandle0(WeixinRequest request, VoiceMessage message) {
|
||||||
throws WeixinException {
|
/**
|
||||||
/**
|
* 返回一段文字给用户
|
||||||
* 返回一段文字给用户
|
*/
|
||||||
*/
|
return new TextResponse("你讲了一句话");
|
||||||
return new TextResponse("你讲了一句话");
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,9 @@ package com.foxinmy.weixin4j.dispatcher;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bean构造
|
* Bean构造
|
||||||
*
|
*
|
||||||
* @className BeanFactory
|
* @className BeanFactory
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月7日
|
* @date 2015年5月7日
|
||||||
@ -14,11 +12,11 @@ import com.foxinmy.weixin4j.exception.WeixinException;
|
|||||||
* @see
|
* @see
|
||||||
*/
|
*/
|
||||||
public interface BeanFactory {
|
public interface BeanFactory {
|
||||||
Object getBean(String name) throws WeixinException;
|
Object getBean(String name);
|
||||||
|
|
||||||
<T> T getBean(Class<T> clazz) throws WeixinException;
|
<T> T getBean(Class<T> clazz);
|
||||||
|
|
||||||
<T> T getBean(String name, Class<T> clazz) throws WeixinException;
|
<T> T getBean(String name, Class<T> clazz);
|
||||||
|
|
||||||
<T> Map<String, T> getBeans(Class<T> clazz) throws WeixinException;
|
<T> Map<String, T> getBeans(Class<T> clazz);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,18 @@
|
|||||||
package com.foxinmy.weixin4j.dispatcher;
|
package com.foxinmy.weixin4j.dispatcher;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||||
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
|
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信消息的处理执行
|
* 微信消息的处理执行
|
||||||
*
|
*
|
||||||
* @className MessageHandlerExecutor
|
* @className MessageHandlerExecutor
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月7日
|
* @date 2015年5月7日
|
||||||
@ -55,7 +54,7 @@ public class MessageHandlerExecutor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行预拦截动作
|
* 执行预拦截动作
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求信息
|
* 微信请求信息
|
||||||
* @param message
|
* @param message
|
||||||
@ -63,8 +62,7 @@ public class MessageHandlerExecutor {
|
|||||||
* @return true则继续执行往下执行
|
* @return true则继续执行往下执行
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public boolean applyPreHandle(WeixinRequest request, WeixinMessage message)
|
public boolean applyPreHandle(WeixinRequest request, WeixinMessage message){
|
||||||
throws WeixinException {
|
|
||||||
if (messageInterceptors != null) {
|
if (messageInterceptors != null) {
|
||||||
for (int i = 0; i < messageInterceptors.length; i++) {
|
for (int i = 0; i < messageInterceptors.length; i++) {
|
||||||
WeixinMessageInterceptor interceptor = messageInterceptors[i];
|
WeixinMessageInterceptor interceptor = messageInterceptors[i];
|
||||||
@ -81,7 +79,7 @@ public class MessageHandlerExecutor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* MessageHandler处理玩请求后的动作
|
* MessageHandler处理玩请求后的动作
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param response
|
* @param response
|
||||||
@ -91,7 +89,7 @@ public class MessageHandlerExecutor {
|
|||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public void applyPostHandle(WeixinRequest request, WeixinResponse response,
|
public void applyPostHandle(WeixinRequest request, WeixinResponse response,
|
||||||
WeixinMessage message) throws WeixinException {
|
WeixinMessage message){
|
||||||
if (messageInterceptors == null) {
|
if (messageInterceptors == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -104,7 +102,7 @@ public class MessageHandlerExecutor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 全部执行完毕后触发
|
* 全部执行完毕后触发
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param response
|
* @param response
|
||||||
@ -117,7 +115,7 @@ public class MessageHandlerExecutor {
|
|||||||
*/
|
*/
|
||||||
public void triggerAfterCompletion(WeixinRequest request,
|
public void triggerAfterCompletion(WeixinRequest request,
|
||||||
WeixinResponse response, WeixinMessage message, Exception exception)
|
WeixinResponse response, WeixinMessage message, Exception exception)
|
||||||
throws WeixinException {
|
{
|
||||||
if (messageInterceptors == null) {
|
if (messageInterceptors == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -126,7 +124,7 @@ public class MessageHandlerExecutor {
|
|||||||
try {
|
try {
|
||||||
interceptor.afterCompletion(context, request, response,
|
interceptor.afterCompletion(context, request, response,
|
||||||
message, messageHandler, exception);
|
message, messageHandler, exception);
|
||||||
} catch (WeixinException e) {
|
} catch (Exception e) {
|
||||||
logger.error(
|
logger.error(
|
||||||
"MessageInterceptor.afterCompletion threw exception", e);
|
"MessageInterceptor.afterCompletion threw exception", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,6 @@
|
|||||||
package com.foxinmy.weixin4j.dispatcher;
|
package com.foxinmy.weixin4j.dispatcher;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
|
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
@ -26,7 +20,6 @@ import javax.xml.bind.Unmarshaller;
|
|||||||
import javax.xml.transform.Source;
|
import javax.xml.transform.Source;
|
||||||
import javax.xml.transform.stream.StreamSource;
|
import javax.xml.transform.stream.StreamSource;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||||
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
|
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
@ -39,9 +32,16 @@ import com.foxinmy.weixin4j.util.HttpUtil;
|
|||||||
import com.foxinmy.weixin4j.util.ServerToolkits;
|
import com.foxinmy.weixin4j.util.ServerToolkits;
|
||||||
import com.foxinmy.weixin4j.xml.MessageTransferHandler;
|
import com.foxinmy.weixin4j.xml.MessageTransferHandler;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFutureListener;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信消息分发器
|
* 微信消息分发器
|
||||||
*
|
*
|
||||||
* @className WeixinMessageDispatcher
|
* @className WeixinMessageDispatcher
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月7日
|
* @date 2015年5月7日
|
||||||
@ -54,438 +54,372 @@ import com.foxinmy.weixin4j.xml.MessageTransferHandler;
|
|||||||
*/
|
*/
|
||||||
public class WeixinMessageDispatcher {
|
public class WeixinMessageDispatcher {
|
||||||
|
|
||||||
private final InternalLogger logger = InternalLoggerFactory
|
private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
|
||||||
.getInstance(getClass());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息处理器
|
* 消息处理器
|
||||||
*/
|
*/
|
||||||
private List<WeixinMessageHandler> messageHandlerList;
|
private List<WeixinMessageHandler> messageHandlerList;
|
||||||
private WeixinMessageHandler[] messageHandlers;
|
private WeixinMessageHandler[] messageHandlers;
|
||||||
/**
|
/**
|
||||||
* 消息处理器所在的包
|
* 消息处理器所在的包
|
||||||
*/
|
*/
|
||||||
private String[] messageHandlerPackages;
|
private String[] messageHandlerPackages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息拦截器
|
* 消息拦截器
|
||||||
*/
|
*/
|
||||||
private List<WeixinMessageInterceptor> messageInterceptorList;
|
private List<WeixinMessageInterceptor> messageInterceptorList;
|
||||||
private WeixinMessageInterceptor[] messageInterceptors;
|
private WeixinMessageInterceptor[] messageInterceptors;
|
||||||
/**
|
/**
|
||||||
* 消息拦截器所在的包
|
* 消息拦截器所在的包
|
||||||
*/
|
*/
|
||||||
private String[] messageInterceptorPackages;
|
private String[] messageInterceptorPackages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bean构造
|
* Bean构造
|
||||||
*/
|
*/
|
||||||
private BeanFactory beanFactory;
|
private BeanFactory beanFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息匹配
|
* 消息匹配
|
||||||
*/
|
*/
|
||||||
private WeixinMessageMatcher messageMatcher;
|
private WeixinMessageMatcher messageMatcher;
|
||||||
/**
|
/**
|
||||||
* 消息转换
|
* 消息转换
|
||||||
*/
|
*/
|
||||||
private Map<Class<? extends WeixinMessage>, Unmarshaller> messageUnmarshaller;
|
private Map<Class<? extends WeixinMessage>, Unmarshaller> messageUnmarshaller;
|
||||||
/**
|
/**
|
||||||
* 是否总是响应请求,如未匹配到MessageHandler时回复空白消息
|
* 是否总是响应请求,如未匹配到MessageHandler时回复空白消息
|
||||||
*/
|
*/
|
||||||
private boolean alwaysResponse;
|
private boolean alwaysResponse;
|
||||||
|
|
||||||
public WeixinMessageDispatcher() {
|
public WeixinMessageDispatcher() {
|
||||||
this(new DefaultMessageMatcher());
|
this(new DefaultMessageMatcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
public WeixinMessageDispatcher(WeixinMessageMatcher messageMatcher) {
|
public WeixinMessageDispatcher(WeixinMessageMatcher messageMatcher) {
|
||||||
this.messageMatcher = messageMatcher;
|
this.messageMatcher = messageMatcher;
|
||||||
this.messageUnmarshaller = new ConcurrentHashMap<Class<? extends WeixinMessage>, Unmarshaller>();
|
this.messageUnmarshaller = new ConcurrentHashMap<Class<? extends WeixinMessage>, Unmarshaller>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对消息进行一系列的处理,包括 拦截、匹配、分发等动作
|
* 对消息进行一系列的处理,包括 拦截、匹配、分发等动作
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* 上下文环境
|
* 上下文环境
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param messageTransfer
|
* @param messageTransfer
|
||||||
* 微信消息
|
* 微信消息 @
|
||||||
* @throws WeixinException
|
*/
|
||||||
*/
|
public void doDispatch(final ChannelHandlerContext context, final WeixinRequest request) {
|
||||||
public void doDispatch(final ChannelHandlerContext context,
|
WeixinMessageTransfer messageTransfer = MessageTransferHandler.parser(request);
|
||||||
final WeixinRequest request) throws WeixinException {
|
context.channel().attr(ServerToolkits.MESSAGE_TRANSFER_KEY).set(messageTransfer);
|
||||||
WeixinMessageTransfer messageTransfer = MessageTransferHandler
|
WeixinMessageKey messageKey = defineMessageKey(messageTransfer, request);
|
||||||
.parser(request);
|
Class<? extends WeixinMessage> targetClass = messageMatcher.match(messageKey);
|
||||||
context.channel().attr(ServerToolkits.MESSAGE_TRANSFER_KEY)
|
WeixinMessage message = messageRead(request.getOriginalContent(), targetClass);
|
||||||
.set(messageTransfer);
|
logger.info("define '{}' matched '{}'", messageKey, targetClass);
|
||||||
WeixinMessageKey messageKey = defineMessageKey(messageTransfer, request);
|
MessageHandlerExecutor handlerExecutor = getHandlerExecutor(context, request, messageKey, message,
|
||||||
Class<? extends WeixinMessage> targetClass = messageMatcher
|
messageTransfer.getNodeNames());
|
||||||
.match(messageKey);
|
if (handlerExecutor == null || handlerExecutor.getMessageHandler() == null) {
|
||||||
WeixinMessage message = messageRead(request.getOriginalContent(),
|
noHandlerFound(context, request, message);
|
||||||
targetClass);
|
return;
|
||||||
logger.info("define '{}' matched '{}'", messageKey, targetClass);
|
}
|
||||||
MessageHandlerExecutor handlerExecutor = getHandlerExecutor(context,
|
if (!handlerExecutor.applyPreHandle(request, message)) {
|
||||||
request, messageKey, message, messageTransfer.getNodeNames());
|
return;
|
||||||
if (handlerExecutor == null
|
}
|
||||||
|| handlerExecutor.getMessageHandler() == null) {
|
Exception exception = null;
|
||||||
noHandlerFound(context, request, message);
|
WeixinResponse response = null;
|
||||||
return;
|
try {
|
||||||
}
|
response = handlerExecutor.getMessageHandler().doHandle(request, message, messageTransfer.getNodeNames());
|
||||||
if (!handlerExecutor.applyPreHandle(request, message)) {
|
handlerExecutor.applyPostHandle(request, response, message);
|
||||||
return;
|
context.writeAndFlush(response);
|
||||||
}
|
} catch (Exception e) {
|
||||||
Exception exception = null;
|
exception = e;
|
||||||
WeixinResponse response = null;
|
}
|
||||||
try {
|
handlerExecutor.triggerAfterCompletion(request, response, message, exception);
|
||||||
response = handlerExecutor.getMessageHandler().doHandle(request,
|
}
|
||||||
message, messageTransfer.getNodeNames());
|
|
||||||
handlerExecutor.applyPostHandle(request, response, message);
|
|
||||||
context.writeAndFlush(response);
|
|
||||||
} catch (Exception e) {
|
|
||||||
exception = e;
|
|
||||||
}
|
|
||||||
handlerExecutor.triggerAfterCompletion(request, response, message,
|
|
||||||
exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 声明messagekey
|
* 声明messagekey
|
||||||
*
|
*
|
||||||
* @param messageTransfer
|
* @param messageTransfer
|
||||||
* 基础消息
|
* 基础消息
|
||||||
* @param request
|
* @param request
|
||||||
* 请求信息
|
* 请求信息
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected WeixinMessageKey defineMessageKey(
|
protected WeixinMessageKey defineMessageKey(WeixinMessageTransfer messageTransfer, WeixinRequest request) {
|
||||||
WeixinMessageTransfer messageTransfer, WeixinRequest request) {
|
return new WeixinMessageKey(messageTransfer.getMsgType(), messageTransfer.getEventType(),
|
||||||
return new WeixinMessageKey(messageTransfer.getMsgType(),
|
messageTransfer.getAccountType());
|
||||||
messageTransfer.getEventType(),
|
}
|
||||||
messageTransfer.getAccountType());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 未匹配到handler时触发
|
* 未匹配到handler时触发
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* 上下文环境
|
* 上下文环境
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param message
|
* @param message
|
||||||
* 微信消息
|
* 微信消息
|
||||||
*/
|
*/
|
||||||
protected void noHandlerFound(ChannelHandlerContext context,
|
protected void noHandlerFound(ChannelHandlerContext context, WeixinRequest request, WeixinMessage message) {
|
||||||
WeixinRequest request, WeixinMessage message) {
|
logger.warn("no handler found for {}", request);
|
||||||
logger.warn("no handler found for {}", request);
|
if (alwaysResponse) {
|
||||||
if (alwaysResponse) {
|
context.write(BlankResponse.global);
|
||||||
context.write(BlankResponse.global);
|
} else {
|
||||||
} else {
|
FullHttpResponse response = new DefaultFullHttpResponse(request.getProtocolVersion(), NOT_FOUND);
|
||||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
HttpUtil.resolveHeaders(response);
|
||||||
request.getProtocolVersion(), NOT_FOUND);
|
context.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||||
HttpUtil.resolveHeaders(response);
|
}
|
||||||
context.writeAndFlush(response).addListener(
|
}
|
||||||
ChannelFutureListener.CLOSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MessageHandlerExecutor
|
* MessageHandlerExecutor
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* 上下文环境
|
* 上下文环境
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param messageKey
|
* @param messageKey
|
||||||
* 消息的key
|
* 消息的key
|
||||||
* @param message
|
* @param message
|
||||||
* 微信消息
|
* 微信消息
|
||||||
* @param nodeNames
|
* @param nodeNames
|
||||||
* 节点名称集合
|
* 节点名称集合
|
||||||
* @return MessageHandlerExecutor
|
* @return MessageHandlerExecutor
|
||||||
* @see MessageHandlerExecutor
|
* @see MessageHandlerExecutor @
|
||||||
* @throws WeixinException
|
*/
|
||||||
*/
|
protected MessageHandlerExecutor getHandlerExecutor(ChannelHandlerContext context, WeixinRequest request,
|
||||||
protected MessageHandlerExecutor getHandlerExecutor(
|
WeixinMessageKey messageKey, WeixinMessage message, Set<String> nodeNames) {
|
||||||
ChannelHandlerContext context, WeixinRequest request,
|
WeixinMessageHandler[] messageHandlers = getMessageHandlers();
|
||||||
WeixinMessageKey messageKey, WeixinMessage message,
|
if (messageHandlers == null) {
|
||||||
Set<String> nodeNames) throws WeixinException {
|
return null;
|
||||||
WeixinMessageHandler[] messageHandlers = getMessageHandlers();
|
}
|
||||||
if (messageHandlers == null) {
|
logger.info("resolve message handlers '{}'", this.messageHandlerList);
|
||||||
return null;
|
List<WeixinMessageHandler> matchedMessageHandlers = new ArrayList<WeixinMessageHandler>();
|
||||||
}
|
for (WeixinMessageHandler handler : messageHandlers) {
|
||||||
logger.info("resolve message handlers '{}'", this.messageHandlerList);
|
if (handler.canHandle(request, message, nodeNames)) {
|
||||||
List<WeixinMessageHandler> matchedMessageHandlers = new ArrayList<WeixinMessageHandler>();
|
matchedMessageHandlers.add(handler);
|
||||||
for (WeixinMessageHandler handler : messageHandlers) {
|
}
|
||||||
if (handler.canHandle(request, message, nodeNames)) {
|
}
|
||||||
matchedMessageHandlers.add(handler);
|
if (matchedMessageHandlers.isEmpty()) {
|
||||||
}
|
return null;
|
||||||
}
|
}
|
||||||
if (matchedMessageHandlers.isEmpty()) {
|
Collections.sort(matchedMessageHandlers, new Comparator<WeixinMessageHandler>() {
|
||||||
return null;
|
@Override
|
||||||
}
|
public int compare(WeixinMessageHandler m1, WeixinMessageHandler m2) {
|
||||||
Collections.sort(matchedMessageHandlers,
|
return m2.weight() - m1.weight();
|
||||||
new Comparator<WeixinMessageHandler>() {
|
}
|
||||||
@Override
|
});
|
||||||
public int compare(WeixinMessageHandler m1,
|
logger.info("matched message handlers '{}'", matchedMessageHandlers);
|
||||||
WeixinMessageHandler m2) {
|
return new MessageHandlerExecutor(context, matchedMessageHandlers.get(0), getMessageInterceptors());
|
||||||
return m2.weight() - m1.weight();
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
logger.info("matched message handlers '{}'", matchedMessageHandlers);
|
|
||||||
return new MessageHandlerExecutor(context,
|
|
||||||
matchedMessageHandlers.get(0), getMessageInterceptors());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有的handler
|
* 获取所有的handler
|
||||||
*
|
*
|
||||||
* @return handler集合
|
* @return handler集合
|
||||||
* @see com.foxinmy.weixin4j.handler.WeixinMessageHandler
|
* @see com.foxinmy.weixin4j.handler.WeixinMessageHandler @
|
||||||
* @throws WeixinException
|
*/
|
||||||
*/
|
public WeixinMessageHandler[] getMessageHandlers() {
|
||||||
public WeixinMessageHandler[] getMessageHandlers() throws WeixinException {
|
if (this.messageHandlers == null) {
|
||||||
if (this.messageHandlers == null) {
|
if (messageHandlerPackages != null) {
|
||||||
if (messageHandlerPackages != null) {
|
List<Class<?>> messageHandlerClass = new ArrayList<Class<?>>();
|
||||||
List<Class<?>> messageHandlerClass = new ArrayList<Class<?>>();
|
for (String packageName : messageHandlerPackages) {
|
||||||
for (String packageName : messageHandlerPackages) {
|
messageHandlerClass.addAll(ClassUtil.getClasses(packageName));
|
||||||
messageHandlerClass.addAll(ClassUtil
|
}
|
||||||
.getClasses(packageName));
|
if (beanFactory != null) {
|
||||||
}
|
for (Class<?> clazz : messageHandlerClass) {
|
||||||
if (beanFactory != null) {
|
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
|
||||||
for (Class<?> clazz : messageHandlerClass) {
|
|| !WeixinMessageHandler.class.isAssignableFrom(clazz)) {
|
||||||
if (clazz.isInterface()
|
continue;
|
||||||
|| Modifier.isAbstract(clazz.getModifiers())
|
}
|
||||||
|| !WeixinMessageHandler.class
|
try {
|
||||||
.isAssignableFrom(clazz)) {
|
messageHandlerList.add((WeixinMessageHandler) beanFactory.getBean(clazz));
|
||||||
continue;
|
} catch (RuntimeException ex) { // multiple
|
||||||
}
|
for (Object o : beanFactory.getBeans(clazz).values()) {
|
||||||
try {
|
if (o.getClass() == clazz) {
|
||||||
messageHandlerList
|
messageHandlerList.add((WeixinMessageHandler) o);
|
||||||
.add((WeixinMessageHandler) beanFactory
|
break;
|
||||||
.getBean(clazz));
|
}
|
||||||
} catch (RuntimeException ex) { // multiple
|
}
|
||||||
for (Object o : beanFactory.getBeans(clazz)
|
}
|
||||||
.values()) {
|
}
|
||||||
if (o.getClass() == clazz) {
|
} else {
|
||||||
messageHandlerList
|
for (Class<?> clazz : messageHandlerClass) {
|
||||||
.add((WeixinMessageHandler) o);
|
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
|
||||||
break;
|
|| !WeixinMessageHandler.class.isAssignableFrom(clazz)) {
|
||||||
}
|
continue;
|
||||||
}
|
}
|
||||||
}
|
try {
|
||||||
}
|
Constructor<?> ctor = clazz.getDeclaredConstructor();
|
||||||
} else {
|
ServerToolkits.makeConstructorAccessible(ctor);
|
||||||
for (Class<?> clazz : messageHandlerClass) {
|
messageHandlerList.add((WeixinMessageHandler) ctor.newInstance((Object[]) null));
|
||||||
if (clazz.isInterface()
|
} catch (Exception ex) {
|
||||||
|| Modifier.isAbstract(clazz.getModifiers())
|
throw new RuntimeException(clazz.getName() + " instantiate fail", ex);
|
||||||
|| !WeixinMessageHandler.class
|
}
|
||||||
.isAssignableFrom(clazz)) {
|
}
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
try {
|
if (messageHandlerList != null && !this.messageHandlerList.isEmpty()) {
|
||||||
Constructor<?> ctor = clazz
|
this.messageHandlers = this.messageHandlerList
|
||||||
.getDeclaredConstructor();
|
.toArray(new WeixinMessageHandler[this.messageHandlerList.size()]);
|
||||||
ServerToolkits.makeConstructorAccessible(ctor);
|
}
|
||||||
messageHandlerList.add((WeixinMessageHandler) ctor
|
}
|
||||||
.newInstance((Object[]) null));
|
return this.messageHandlers;
|
||||||
} catch (Exception ex) {
|
}
|
||||||
throw new WeixinException(clazz.getName()
|
|
||||||
+ " instantiate fail", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (messageHandlerList != null
|
|
||||||
&& !this.messageHandlerList.isEmpty()) {
|
|
||||||
this.messageHandlers = this.messageHandlerList
|
|
||||||
.toArray(new WeixinMessageHandler[this.messageHandlerList
|
|
||||||
.size()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.messageHandlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有的interceptor
|
* 获取所有的interceptor
|
||||||
*
|
*
|
||||||
* @return interceptor集合
|
* @return interceptor集合
|
||||||
* @throws WeixinException
|
* @ @see com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor
|
||||||
* @see com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor
|
*/
|
||||||
*/
|
public WeixinMessageInterceptor[] getMessageInterceptors() {
|
||||||
public WeixinMessageInterceptor[] getMessageInterceptors()
|
if (this.messageInterceptors == null) {
|
||||||
throws WeixinException {
|
if (this.messageInterceptorPackages != null) {
|
||||||
if (this.messageInterceptors == null) {
|
List<Class<?>> messageInterceptorClass = new ArrayList<Class<?>>();
|
||||||
if (this.messageInterceptorPackages != null) {
|
for (String packageName : messageInterceptorPackages) {
|
||||||
List<Class<?>> messageInterceptorClass = new ArrayList<Class<?>>();
|
messageInterceptorClass.addAll(ClassUtil.getClasses(packageName));
|
||||||
for (String packageName : messageInterceptorPackages) {
|
}
|
||||||
messageInterceptorClass.addAll(ClassUtil
|
if (beanFactory != null) {
|
||||||
.getClasses(packageName));
|
for (Class<?> clazz : messageInterceptorClass) {
|
||||||
}
|
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
|
||||||
if (beanFactory != null) {
|
|| !WeixinMessageInterceptor.class.isAssignableFrom(clazz)) {
|
||||||
for (Class<?> clazz : messageInterceptorClass) {
|
continue;
|
||||||
if (clazz.isInterface()
|
}
|
||||||
|| Modifier.isAbstract(clazz.getModifiers())
|
try {
|
||||||
|| !WeixinMessageInterceptor.class
|
messageInterceptorList.add((WeixinMessageInterceptor) beanFactory.getBean(clazz));
|
||||||
.isAssignableFrom(clazz)) {
|
} catch (RuntimeException ex) { // multiple
|
||||||
continue;
|
for (Object o : beanFactory.getBeans(clazz).values()) {
|
||||||
}
|
if (o.getClass() == clazz) {
|
||||||
try {
|
messageInterceptorList.add((WeixinMessageInterceptor) o);
|
||||||
messageInterceptorList
|
break;
|
||||||
.add((WeixinMessageInterceptor) beanFactory
|
}
|
||||||
.getBean(clazz));
|
}
|
||||||
} catch (RuntimeException ex) { // multiple
|
}
|
||||||
for (Object o : beanFactory.getBeans(clazz)
|
}
|
||||||
.values()) {
|
} else {
|
||||||
if (o.getClass() == clazz) {
|
for (Class<?> clazz : messageInterceptorClass) {
|
||||||
messageInterceptorList
|
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())
|
||||||
.add((WeixinMessageInterceptor) o);
|
|| !WeixinMessageInterceptor.class.isAssignableFrom(clazz)) {
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
try {
|
||||||
}
|
Constructor<?> ctor = clazz.getDeclaredConstructor();
|
||||||
}
|
ServerToolkits.makeConstructorAccessible(ctor);
|
||||||
} else {
|
messageInterceptorList.add((WeixinMessageInterceptor) ctor.newInstance((Object[]) null));
|
||||||
for (Class<?> clazz : messageInterceptorClass) {
|
} catch (Exception ex) {
|
||||||
if (clazz.isInterface()
|
throw new RuntimeException(clazz.getName() + " instantiate fail", ex);
|
||||||
|| Modifier.isAbstract(clazz.getModifiers())
|
}
|
||||||
|| !WeixinMessageInterceptor.class
|
}
|
||||||
.isAssignableFrom(clazz)) {
|
}
|
||||||
continue;
|
}
|
||||||
}
|
if (this.messageInterceptorList != null && !this.messageInterceptorList.isEmpty()) {
|
||||||
try {
|
Collections.sort(messageInterceptorList, new Comparator<WeixinMessageInterceptor>() {
|
||||||
Constructor<?> ctor = clazz
|
@Override
|
||||||
.getDeclaredConstructor();
|
public int compare(WeixinMessageInterceptor m1, WeixinMessageInterceptor m2) {
|
||||||
ServerToolkits.makeConstructorAccessible(ctor);
|
return m2.weight() - m1.weight();
|
||||||
messageInterceptorList
|
}
|
||||||
.add((WeixinMessageInterceptor) ctor
|
});
|
||||||
.newInstance((Object[]) null));
|
this.messageInterceptors = this.messageInterceptorList
|
||||||
} catch (Exception ex) {
|
.toArray(new WeixinMessageInterceptor[this.messageInterceptorList.size()]);
|
||||||
throw new WeixinException(clazz.getName()
|
}
|
||||||
+ " instantiate fail", ex);
|
}
|
||||||
}
|
logger.info("resolve message interceptors '{}'", this.messageInterceptorList);
|
||||||
}
|
return this.messageInterceptors;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (this.messageInterceptorList != null
|
|
||||||
&& !this.messageInterceptorList.isEmpty()) {
|
|
||||||
Collections.sort(messageInterceptorList,
|
|
||||||
new Comparator<WeixinMessageInterceptor>() {
|
|
||||||
@Override
|
|
||||||
public int compare(WeixinMessageInterceptor m1,
|
|
||||||
WeixinMessageInterceptor m2) {
|
|
||||||
return m2.weight() - m1.weight();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.messageInterceptors = this.messageInterceptorList
|
|
||||||
.toArray(new WeixinMessageInterceptor[this.messageInterceptorList
|
|
||||||
.size()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info("resolve message interceptors '{}'",
|
|
||||||
this.messageInterceptorList);
|
|
||||||
return this.messageInterceptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jaxb读取微信消息
|
* jaxb读取微信消息
|
||||||
*
|
*
|
||||||
* @param message
|
* @param message
|
||||||
* xml消息
|
* xml消息
|
||||||
* @param clazz
|
* @param clazz
|
||||||
* 消息类型
|
* 消息类型
|
||||||
* @return 消息对象
|
* @return 消息对象 @
|
||||||
* @throws WeixinException
|
*/
|
||||||
*/
|
protected <M extends WeixinMessage> M messageRead(String message, Class<M> clazz) {
|
||||||
protected <M extends WeixinMessage> M messageRead(String message,
|
if (clazz == null) {
|
||||||
Class<M> clazz) throws WeixinException {
|
return null;
|
||||||
if (clazz == null) {
|
}
|
||||||
return null;
|
try {
|
||||||
}
|
Source source = new StreamSource(new ByteArrayInputStream(ServerToolkits.getBytesUtf8(message)));
|
||||||
try {
|
JAXBElement<M> jaxbElement = getUnmarshaller(clazz).unmarshal(source, clazz);
|
||||||
Source source = new StreamSource(new ByteArrayInputStream(
|
return jaxbElement.getValue();
|
||||||
ServerToolkits.getBytesUtf8(message)));
|
} catch (JAXBException e) {
|
||||||
JAXBElement<M> jaxbElement = getUnmarshaller(clazz).unmarshal(
|
throw new RuntimeException(e);
|
||||||
source, clazz);
|
}
|
||||||
return jaxbElement.getValue();
|
}
|
||||||
} catch (JAXBException e) {
|
|
||||||
throw new WeixinException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* xml消息转换器
|
* xml消息转换器
|
||||||
*
|
*
|
||||||
* @param clazz
|
* @param clazz
|
||||||
* 消息类型
|
* 消息类型
|
||||||
* @return 消息转换器
|
* @return 消息转换器 @
|
||||||
* @throws WeixinException
|
*/
|
||||||
*/
|
protected Unmarshaller getUnmarshaller(Class<? extends WeixinMessage> clazz) {
|
||||||
protected Unmarshaller getUnmarshaller(Class<? extends WeixinMessage> clazz)
|
Unmarshaller unmarshaller = messageUnmarshaller.get(clazz);
|
||||||
throws WeixinException {
|
if (unmarshaller == null) {
|
||||||
Unmarshaller unmarshaller = messageUnmarshaller.get(clazz);
|
try {
|
||||||
if (unmarshaller == null) {
|
JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
|
||||||
try {
|
unmarshaller = jaxbContext.createUnmarshaller();
|
||||||
JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
|
messageUnmarshaller.put(clazz, unmarshaller);
|
||||||
unmarshaller = jaxbContext.createUnmarshaller();
|
} catch (JAXBException e) {
|
||||||
messageUnmarshaller.put(clazz, unmarshaller);
|
throw new RuntimeException(e);
|
||||||
} catch (JAXBException e) {
|
}
|
||||||
throw new WeixinException(e);
|
}
|
||||||
}
|
return unmarshaller;
|
||||||
}
|
}
|
||||||
return unmarshaller;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMessageHandlerList(
|
public void setMessageHandlerList(List<WeixinMessageHandler> messageHandlerList) {
|
||||||
List<WeixinMessageHandler> messageHandlerList) {
|
this.messageHandlerList = messageHandlerList;
|
||||||
this.messageHandlerList = messageHandlerList;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void setMessageInterceptorList(
|
public void setMessageInterceptorList(List<WeixinMessageInterceptor> messageInterceptorList) {
|
||||||
List<WeixinMessageInterceptor> messageInterceptorList) {
|
this.messageInterceptorList = messageInterceptorList;
|
||||||
this.messageInterceptorList = messageInterceptorList;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getMessageHandlerPackages() {
|
public String[] getMessageHandlerPackages() {
|
||||||
return messageHandlerPackages;
|
return messageHandlerPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getMessageInterceptorPackages() {
|
public String[] getMessageInterceptorPackages() {
|
||||||
return messageInterceptorPackages;
|
return messageInterceptorPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMessageHandlerPackages(String... messageHandlerPackages) {
|
public void setMessageHandlerPackages(String... messageHandlerPackages) {
|
||||||
this.messageHandlerPackages = messageHandlerPackages;
|
this.messageHandlerPackages = messageHandlerPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMessageInterceptorPackages(
|
public void setMessageInterceptorPackages(String... messageInterceptorPackages) {
|
||||||
String... messageInterceptorPackages) {
|
this.messageInterceptorPackages = messageInterceptorPackages;
|
||||||
this.messageInterceptorPackages = messageInterceptorPackages;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public BeanFactory getBeanFactory() {
|
public BeanFactory getBeanFactory() {
|
||||||
return beanFactory;
|
return beanFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBeanFactory(BeanFactory beanFactory) {
|
public void setBeanFactory(BeanFactory beanFactory) {
|
||||||
this.beanFactory = beanFactory;
|
this.beanFactory = beanFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registMessageClass(WeixinMessageKey messageKey,
|
public void registMessageClass(WeixinMessageKey messageKey, Class<? extends WeixinMessage> messageClass) {
|
||||||
Class<? extends WeixinMessage> messageClass) {
|
messageMatcher.regist(messageKey, messageClass);
|
||||||
messageMatcher.regist(messageKey, messageClass);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public WeixinMessageMatcher getMessageMatcher() {
|
public WeixinMessageMatcher getMessageMatcher() {
|
||||||
return this.messageMatcher;
|
return this.messageMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开总是响应开关,如未匹配到MessageHandler时回复空白消息
|
* 打开总是响应开关,如未匹配到MessageHandler时回复空白消息
|
||||||
*/
|
*/
|
||||||
public void openAlwaysResponse() {
|
public void openAlwaysResponse() {
|
||||||
this.alwaysResponse = true;
|
this.alwaysResponse = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,50 +0,0 @@
|
|||||||
package com.foxinmy.weixin4j.exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 微信异常
|
|
||||||
*
|
|
||||||
* @className WeixinException
|
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
|
||||||
* @date 2014年4月10日
|
|
||||||
* @since JDK 1.6
|
|
||||||
* @see
|
|
||||||
*/
|
|
||||||
public class WeixinException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 7148145661883468514L;
|
|
||||||
|
|
||||||
private String errorCode;
|
|
||||||
private String errorMsg;
|
|
||||||
|
|
||||||
public WeixinException(String errorCode, String errorMsg) {
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
this.errorMsg = errorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WeixinException(String errorMsg) {
|
|
||||||
this.errorCode = "-1";
|
|
||||||
this.errorMsg = errorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WeixinException(Exception e) {
|
|
||||||
super(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WeixinException(String errorMsg, Exception e) {
|
|
||||||
super(e);
|
|
||||||
this.errorMsg = errorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getErrorCode() {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getErrorMsg() {
|
|
||||||
return errorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return this.errorCode + "," + this.errorMsg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.handler;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
import com.foxinmy.weixin4j.response.TextResponse;
|
import com.foxinmy.weixin4j.response.TextResponse;
|
||||||
@ -10,7 +9,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 调试消息处理器
|
* 调试消息处理器
|
||||||
*
|
*
|
||||||
* @className DebugMessageHandler
|
* @className DebugMessageHandler
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月17日
|
* @date 2015年5月17日
|
||||||
@ -19,29 +18,27 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
*/
|
*/
|
||||||
public class DebugMessageHandler implements WeixinMessageHandler {
|
public class DebugMessageHandler implements WeixinMessageHandler {
|
||||||
|
|
||||||
public static final DebugMessageHandler global = new DebugMessageHandler();
|
public static final DebugMessageHandler global = new DebugMessageHandler();
|
||||||
|
|
||||||
private DebugMessageHandler() {
|
private DebugMessageHandler() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandle(WeixinRequest request, WeixinMessage message,
|
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
Set<String> nodeNames) throws WeixinException {
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message,
|
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
Set<String> nodeNames) throws WeixinException {
|
String content = message == null
|
||||||
String content = message == null ? request.getOriginalContent()
|
? request.getOriginalContent().replaceAll("\\!\\[CDATA\\[", "").replaceAll("\\]\\]", "")
|
||||||
.replaceAll("\\!\\[CDATA\\[", "").replaceAll("\\]\\]", "")
|
: message.toString();
|
||||||
: message.toString();
|
return new TextResponse(content);
|
||||||
return new TextResponse(content);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int weight() {
|
public int weight() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.handler;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||||
@ -10,7 +9,7 @@ import com.foxinmy.weixin4j.util.ClassUtil;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息适配器:对于特定的消息类型进行适配,如text文本、voice语音消息
|
* 消息适配器:对于特定的消息类型进行适配,如text文本、voice语音消息
|
||||||
*
|
*
|
||||||
* @className MessageHandlerAdapter
|
* @className MessageHandlerAdapter
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月17日
|
* @date 2015年5月17日
|
||||||
@ -18,56 +17,48 @@ import com.foxinmy.weixin4j.util.ClassUtil;
|
|||||||
* @see com.foxinmy.weixin4j.request.WeixinMessage
|
* @see com.foxinmy.weixin4j.request.WeixinMessage
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public abstract class MessageHandlerAdapter<M extends WeixinMessage> implements
|
public abstract class MessageHandlerAdapter<M extends WeixinMessage> implements WeixinMessageHandler {
|
||||||
WeixinMessageHandler {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandle(WeixinRequest request, WeixinMessage message,
|
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
Set<String> nodeNames) throws WeixinException {
|
return message != null && message.getClass() == ClassUtil.getGenericType(getClass())
|
||||||
return message != null
|
&& canHandle0(request, (M) message);
|
||||||
&& message.getClass() == ClassUtil.getGenericType(getClass())
|
}
|
||||||
&& canHandle0(request, (M) message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 能否处理请求
|
* 能否处理请求
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param message
|
* @param message
|
||||||
* 微信消息
|
* 微信消息
|
||||||
* @return true则执行doHandler0
|
* @return true则执行doHandler0 @
|
||||||
* @throws WeixinException
|
*/
|
||||||
*/
|
public boolean canHandle0(WeixinRequest request, M message) {
|
||||||
public boolean canHandle0(WeixinRequest request, M message)
|
return true;
|
||||||
throws WeixinException {
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeixinResponse doHandle(WeixinRequest request,
|
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
WeixinMessage message, Set<String> nodeNames)
|
return doHandle0(request, (M) message);
|
||||||
throws WeixinException {
|
}
|
||||||
return doHandle0(request, (M) message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理请求
|
* 处理请求
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param message
|
* @param message
|
||||||
* 微信消息
|
* 微信消息
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public abstract WeixinResponse doHandle0(WeixinRequest request, M message)
|
public abstract WeixinResponse doHandle0(WeixinRequest request, M message);
|
||||||
throws WeixinException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缺省值为1,存在多个匹配到的MessageHandler则比较weight大小
|
* 缺省值为1,存在多个匹配到的MessageHandler则比较weight大小
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int weight() {
|
public int weight() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,13 +3,12 @@ package com.foxinmy.weixin4j.handler;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多个消息类型适配
|
* 多个消息类型适配
|
||||||
*
|
*
|
||||||
* @className MultipleMessageHandlerAdapter
|
* @className MultipleMessageHandlerAdapter
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2016年3月12日
|
* @date 2016年3月12日
|
||||||
@ -18,41 +17,40 @@ import com.foxinmy.weixin4j.request.WeixinRequest;
|
|||||||
*/
|
*/
|
||||||
public abstract class MultipleMessageHandlerAdapter implements WeixinMessageHandler {
|
public abstract class MultipleMessageHandlerAdapter implements WeixinMessageHandler {
|
||||||
|
|
||||||
private final Set<Class<? extends WeixinMessage>> messageClasses;
|
private final Set<Class<? extends WeixinMessage>> messageClasses;
|
||||||
|
|
||||||
public MultipleMessageHandlerAdapter(Class<? extends WeixinMessage>... messageClasses) {
|
public MultipleMessageHandlerAdapter(Class<? extends WeixinMessage>... messageClasses) {
|
||||||
if (messageClasses == null) {
|
if (messageClasses == null) {
|
||||||
throw new IllegalArgumentException("messageClasses not be empty");
|
throw new IllegalArgumentException("messageClasses not be empty");
|
||||||
}
|
}
|
||||||
this.messageClasses = new HashSet<Class<? extends WeixinMessage>>(
|
this.messageClasses = new HashSet<Class<? extends WeixinMessage>>(
|
||||||
Math.max((int) (messageClasses.length / .75f) + 1, 16));
|
Math.max((int) (messageClasses.length / .75f) + 1, 16));
|
||||||
for (Class<? extends WeixinMessage> clazz : messageClasses) {
|
for (Class<? extends WeixinMessage> clazz : messageClasses) {
|
||||||
this.messageClasses.add(clazz);
|
this.messageClasses.add(clazz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames)
|
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
throws WeixinException {
|
return message != null && messageClasses.contains(message.getClass()) && canHandle0(request, message);
|
||||||
return message != null && messageClasses.contains(message.getClass()) && canHandle0(request, message);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 能否处理请求
|
* 能否处理请求
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param message
|
* @param message
|
||||||
* 微信消息
|
* 微信消息
|
||||||
* @return true则执行doHandler
|
* @return true则执行doHandler
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public boolean canHandle0(WeixinRequest request, WeixinMessage message) throws WeixinException {
|
public boolean canHandle0(WeixinRequest request, WeixinMessage message) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int weight() {
|
public int weight() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,14 +2,13 @@ package com.foxinmy.weixin4j.handler;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信消息处理器
|
* 微信消息处理器
|
||||||
*
|
*
|
||||||
* @className WeixinMessageHandler
|
* @className WeixinMessageHandler
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月7日
|
* @date 2015年5月7日
|
||||||
@ -19,38 +18,36 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
*/
|
*/
|
||||||
public interface WeixinMessageHandler {
|
public interface WeixinMessageHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 能否处理请求
|
* 能否处理请求
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param message
|
* @param message
|
||||||
* 微信消息
|
* 微信消息
|
||||||
* @param nodeNames
|
* @param nodeNames
|
||||||
* 节点名称集合
|
* 节点名称集合
|
||||||
* @return true则执行doHandle
|
* @return true则执行doHandle
|
||||||
*/
|
*/
|
||||||
public boolean canHandle(WeixinRequest request, WeixinMessage message,
|
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames);
|
||||||
Set<String> nodeNames) throws WeixinException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理请求
|
* 处理请求
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* 微信请求
|
* 微信请求
|
||||||
* @param message
|
* @param message
|
||||||
* 微信消息
|
* 微信消息
|
||||||
* @param nodeNames
|
* @param nodeNames
|
||||||
* 节点名称集合
|
* 节点名称集合
|
||||||
* @return 回复内容
|
* @return 回复内容
|
||||||
*/
|
*/
|
||||||
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message,
|
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames);
|
||||||
Set<String> nodeNames) throws WeixinException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于匹配到多个MessageHandler时权重降序排列,数字越大优先级越高
|
* 用于匹配到多个MessageHandler时权重降序排列,数字越大优先级越高
|
||||||
*
|
*
|
||||||
* @return 权重
|
* @return 权重
|
||||||
*/
|
*/
|
||||||
public int weight();
|
public int weight();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,47 +1,41 @@
|
|||||||
package com.foxinmy.weixin4j.interceptor;
|
package com.foxinmy.weixin4j.interceptor;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息拦截适配
|
* 消息拦截适配
|
||||||
*
|
*
|
||||||
* @className MessageInterceptorAdapter
|
* @className MessageInterceptorAdapter
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月14日
|
* @date 2015年5月14日
|
||||||
* @since JDK 1.6
|
* @since JDK 1.6
|
||||||
* @see
|
* @see
|
||||||
*/
|
*/
|
||||||
public abstract class MessageInterceptorAdapter implements
|
public abstract class MessageInterceptorAdapter implements WeixinMessageInterceptor {
|
||||||
WeixinMessageInterceptor {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(ChannelHandlerContext context,
|
public boolean preHandle(ChannelHandlerContext context, WeixinRequest request, WeixinMessage message,
|
||||||
WeixinRequest request, WeixinMessage message, WeixinMessageHandler handler)
|
WeixinMessageHandler handler) {
|
||||||
throws WeixinException {
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postHandle(ChannelHandlerContext context,
|
public void postHandle(ChannelHandlerContext context, WeixinRequest request, WeixinResponse response,
|
||||||
WeixinRequest request, WeixinResponse response, WeixinMessage message,
|
WeixinMessage message, WeixinMessageHandler handler) {
|
||||||
WeixinMessageHandler handler) throws WeixinException {
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterCompletion(ChannelHandlerContext context,
|
public void afterCompletion(ChannelHandlerContext context, WeixinRequest request, WeixinResponse response,
|
||||||
WeixinRequest request, WeixinResponse response, WeixinMessage message,
|
WeixinMessage message, WeixinMessageHandler handler, Exception exception) {
|
||||||
WeixinMessageHandler handler, Exception exception)
|
}
|
||||||
throws WeixinException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int weight() {
|
public int weight() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
package com.foxinmy.weixin4j.interceptor;
|
package com.foxinmy.weixin4j.interceptor;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信消息拦截器
|
* 微信消息拦截器
|
||||||
*
|
*
|
||||||
* @className WeixinMessageInterceptor
|
* @className WeixinMessageInterceptor
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月7日
|
* @date 2015年5月7日
|
||||||
@ -21,7 +20,7 @@ public interface WeixinMessageInterceptor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行handler前
|
* 执行handler前
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* 通道环境
|
* 通道环境
|
||||||
* @param request
|
* @param request
|
||||||
@ -34,12 +33,11 @@ public interface WeixinMessageInterceptor {
|
|||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
boolean preHandle(ChannelHandlerContext context, WeixinRequest request,
|
boolean preHandle(ChannelHandlerContext context, WeixinRequest request,
|
||||||
WeixinMessage message, WeixinMessageHandler handler)
|
WeixinMessage message, WeixinMessageHandler handler);
|
||||||
throws WeixinException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行handler后
|
* 执行handler后
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* 通道环境
|
* 通道环境
|
||||||
* @param request
|
* @param request
|
||||||
@ -54,11 +52,11 @@ public interface WeixinMessageInterceptor {
|
|||||||
*/
|
*/
|
||||||
void postHandle(ChannelHandlerContext context, WeixinRequest request,
|
void postHandle(ChannelHandlerContext context, WeixinRequest request,
|
||||||
WeixinResponse response, WeixinMessage message,
|
WeixinResponse response, WeixinMessage message,
|
||||||
WeixinMessageHandler handler) throws WeixinException;
|
WeixinMessageHandler handler);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全部执行后
|
* 全部执行后
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* 通道环境
|
* 通道环境
|
||||||
* @param request
|
* @param request
|
||||||
@ -73,12 +71,11 @@ public interface WeixinMessageInterceptor {
|
|||||||
*/
|
*/
|
||||||
void afterCompletion(ChannelHandlerContext context, WeixinRequest request,
|
void afterCompletion(ChannelHandlerContext context, WeixinRequest request,
|
||||||
WeixinResponse response, WeixinMessage message,
|
WeixinResponse response, WeixinMessage message,
|
||||||
WeixinMessageHandler handler, Exception exception)
|
WeixinMessageHandler handler, Exception exception);
|
||||||
throws WeixinException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于匹配到多个MessageHandler时权重降序排列,数字越大优先级越高
|
* 用于匹配到多个MessageHandler时权重降序排列,数字越大优先级越高
|
||||||
*
|
*
|
||||||
* @return 权重
|
* @return 权重
|
||||||
*/
|
*/
|
||||||
int weight();
|
int weight();
|
||||||
|
|||||||
@ -1,21 +1,20 @@
|
|||||||
package com.foxinmy.weixin4j.socket;
|
package com.foxinmy.weixin4j.socket;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.response.SingleResponse;
|
||||||
|
import com.foxinmy.weixin4j.util.HttpUtil;
|
||||||
|
import com.foxinmy.weixin4j.util.ServerToolkits;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.response.SingleResponse;
|
|
||||||
import com.foxinmy.weixin4j.util.HttpUtil;
|
|
||||||
import com.foxinmy.weixin4j.util.ServerToolkits;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单一回复编码类
|
* 单一回复编码类
|
||||||
*
|
*
|
||||||
* @className SingleResponseEncoder
|
* @className SingleResponseEncoder
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年08月02日
|
* @date 2015年08月02日
|
||||||
@ -23,18 +22,14 @@ import com.foxinmy.weixin4j.util.ServerToolkits;
|
|||||||
* @see com.foxinmy.weixin4j.response.SingleResponse
|
* @see com.foxinmy.weixin4j.response.SingleResponse
|
||||||
*/
|
*/
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public class SingleResponseEncoder extends
|
public class SingleResponseEncoder extends MessageToMessageEncoder<SingleResponse> {
|
||||||
MessageToMessageEncoder<SingleResponse> {
|
|
||||||
|
|
||||||
private final InternalLogger logger = InternalLoggerFactory
|
private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
|
||||||
.getInstance(getClass());
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encode(ChannelHandlerContext ctx, SingleResponse response,
|
protected void encode(ChannelHandlerContext ctx, SingleResponse response, List<Object> out) {
|
||||||
List<Object> out) throws WeixinException {
|
String content = response.toContent();
|
||||||
String content = response.toContent();
|
ctx.writeAndFlush(HttpUtil.createHttpResponse(content, ServerToolkits.CONTENTTYPE$TEXT_PLAIN));
|
||||||
ctx.writeAndFlush(HttpUtil.createHttpResponse(content,
|
logger.info("encode single response:{}", content);
|
||||||
ServerToolkits.CONTENTTYPE$TEXT_PLAIN));
|
}
|
||||||
logger.info("encode single response:{}", content);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -5,7 +5,6 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
import com.foxinmy.weixin4j.type.EncryptType;
|
import com.foxinmy.weixin4j.type.EncryptType;
|
||||||
import com.foxinmy.weixin4j.util.AesToken;
|
import com.foxinmy.weixin4j.util.AesToken;
|
||||||
@ -50,7 +49,7 @@ public class WeixinMessageDecoder extends MessageToMessageDecoder<FullHttpReques
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req, List<Object> out) throws WeixinException {
|
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req, List<Object> out) {
|
||||||
String messageContent = req.content().toString(ServerToolkits.UTF_8);
|
String messageContent = req.content().toString(ServerToolkits.UTF_8);
|
||||||
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.uri(), true);
|
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.uri(), true);
|
||||||
HttpMethod method = req.method();
|
HttpMethod method = req.method();
|
||||||
@ -68,7 +67,7 @@ public class WeixinMessageDecoder extends MessageToMessageDecoder<FullHttpReques
|
|||||||
String encryptContent = null;
|
String encryptContent = null;
|
||||||
if (!ServerToolkits.isBlank(messageContent) && encryptType == EncryptType.AES) {
|
if (!ServerToolkits.isBlank(messageContent) && encryptType == EncryptType.AES) {
|
||||||
if (ServerToolkits.isBlank(aesToken.getAesKey())) {
|
if (ServerToolkits.isBlank(aesToken.getAesKey())) {
|
||||||
throw new WeixinException("EncodingAESKey not be empty in safety(AES) mode");
|
throw new RuntimeException("EncodingAESKey not be empty in safety(AES) mode");
|
||||||
}
|
}
|
||||||
EncryptMessageHandler encryptHandler = EncryptMessageHandler.parser(messageContent);
|
EncryptMessageHandler encryptHandler = EncryptMessageHandler.parser(messageContent);
|
||||||
encryptContent = encryptHandler.getEncryptContent();
|
encryptContent = encryptHandler.getEncryptContent();
|
||||||
|
|||||||
@ -3,6 +3,16 @@ package com.foxinmy.weixin4j.socket;
|
|||||||
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
|
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
|
||||||
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
|
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
|
||||||
import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED;
|
import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
|
||||||
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
|
import com.foxinmy.weixin4j.response.SingleResponse;
|
||||||
|
import com.foxinmy.weixin4j.type.EncryptType;
|
||||||
|
import com.foxinmy.weixin4j.util.AesToken;
|
||||||
|
import com.foxinmy.weixin4j.util.HttpUtil;
|
||||||
|
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||||
|
import com.foxinmy.weixin4j.util.ServerToolkits;
|
||||||
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
@ -13,119 +23,92 @@ import io.netty.handler.codec.http.HttpResponseStatus;
|
|||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
|
||||||
import com.foxinmy.weixin4j.response.SingleResponse;
|
|
||||||
import com.foxinmy.weixin4j.type.EncryptType;
|
|
||||||
import com.foxinmy.weixin4j.util.AesToken;
|
|
||||||
import com.foxinmy.weixin4j.util.HttpUtil;
|
|
||||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
|
||||||
import com.foxinmy.weixin4j.util.ServerToolkits;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信请求处理类
|
* 微信请求处理类
|
||||||
*
|
*
|
||||||
* @className WeixinRequestHandler
|
* @className WeixinRequestHandler
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2014年11月16日
|
* @date 2014年11月16日
|
||||||
* @since JDK 1.6
|
* @since JDK 1.6
|
||||||
* @see com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher
|
* @see com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher
|
||||||
*/
|
*/
|
||||||
public class WeixinRequestHandler extends
|
public class WeixinRequestHandler extends SimpleChannelInboundHandler<WeixinRequest> {
|
||||||
SimpleChannelInboundHandler<WeixinRequest> {
|
private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
|
||||||
private final InternalLogger logger = InternalLoggerFactory
|
|
||||||
.getInstance(getClass());
|
|
||||||
|
|
||||||
private final WeixinMessageDispatcher messageDispatcher;
|
private final WeixinMessageDispatcher messageDispatcher;
|
||||||
|
|
||||||
public WeixinRequestHandler(WeixinMessageDispatcher messageDispatcher) {
|
public WeixinRequestHandler(WeixinMessageDispatcher messageDispatcher) {
|
||||||
this.messageDispatcher = messageDispatcher;
|
this.messageDispatcher = messageDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||||
ctx.flush();
|
ctx.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
ctx.close();
|
ctx.close();
|
||||||
logger.error(cause);
|
logger.error(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void channelRead0(ChannelHandlerContext ctx, WeixinRequest request)
|
protected void channelRead0(ChannelHandlerContext ctx, WeixinRequest request) {
|
||||||
throws WeixinException {
|
AesToken aesToken = request.getAesToken();
|
||||||
AesToken aesToken = request.getAesToken();
|
// 消息字段不完整返回400
|
||||||
// 消息字段不完整返回400
|
if (aesToken == null || (ServerToolkits.isBlank(request.getSignature())
|
||||||
if (aesToken == null
|
&& ServerToolkits.isBlank(request.getMsgSignature()))) {
|
||||||
|| (ServerToolkits.isBlank(request.getSignature()) && ServerToolkits
|
ctx.writeAndFlush(resolveResponse(BAD_REQUEST, request)).addListener(ChannelFutureListener.CLOSE);
|
||||||
.isBlank(request.getMsgSignature()))) {
|
return;
|
||||||
ctx.writeAndFlush(resolveResponse(BAD_REQUEST, request))
|
}
|
||||||
.addListener(ChannelFutureListener.CLOSE);
|
/**
|
||||||
return;
|
* 公众平台:无论Get,Post都带signature参数,当开启aes模式时带msg_signature参数
|
||||||
}
|
* 企业号:无论Get,Post都带msg_signature参数
|
||||||
/**
|
* 一般来说:signature验证url上的参数签名,msg_signature验证消息体签名
|
||||||
* 公众平台:无论Get,Post都带signature参数,当开启aes模式时带msg_signature参数
|
**/
|
||||||
* 企业号:无论Get,Post都带msg_signature参数
|
if (request.getMethod() == HttpMethod.GET) {
|
||||||
* 一般来说:signature验证url上的参数签名,msg_signature验证消息体签名
|
// URL参数签名验证
|
||||||
**/
|
if (!ServerToolkits.isBlank(request.getSignature())
|
||||||
if (request.getMethod() == HttpMethod.GET) {
|
&& MessageUtil.signature(aesToken.getToken(), request.getTimeStamp(), request.getNonce())
|
||||||
// URL参数签名验证
|
.equals(request.getSignature())) {
|
||||||
if (!ServerToolkits.isBlank(request.getSignature())
|
ctx.writeAndFlush(new SingleResponse(request.getEchoStr()));
|
||||||
&& MessageUtil.signature(aesToken.getToken(),
|
return;
|
||||||
request.getTimeStamp(), request.getNonce()).equals(
|
}
|
||||||
request.getSignature())) {
|
// XML消息签名验证
|
||||||
ctx.writeAndFlush(new SingleResponse(request.getEchoStr()));
|
if (!ServerToolkits.isBlank(request.getMsgSignature()) && MessageUtil
|
||||||
return;
|
.signature(aesToken.getToken(), request.getTimeStamp(), request.getNonce(), request.getEchoStr())
|
||||||
}
|
.equals(request.getMsgSignature())) {
|
||||||
// XML消息签名验证
|
ctx.writeAndFlush(
|
||||||
if (!ServerToolkits.isBlank(request.getMsgSignature())
|
new SingleResponse(MessageUtil.aesDecrypt(null, aesToken.getAesKey(), request.getEchoStr())));
|
||||||
&& MessageUtil.signature(aesToken.getToken(),
|
return;
|
||||||
request.getTimeStamp(), request.getNonce(),
|
}
|
||||||
request.getEchoStr()).equals(
|
ctx.writeAndFlush(resolveResponse(FORBIDDEN, request)).addListener(ChannelFutureListener.CLOSE);
|
||||||
request.getMsgSignature())) {
|
return;
|
||||||
ctx.writeAndFlush(new SingleResponse(MessageUtil.aesDecrypt(
|
} else if (request.getMethod() == HttpMethod.POST) {
|
||||||
null, aesToken.getAesKey(), request.getEchoStr())));
|
// URL参数签名验证
|
||||||
return;
|
if (!ServerToolkits.isBlank(request.getSignature())
|
||||||
}
|
&& !MessageUtil.signature(aesToken.getToken(), request.getTimeStamp(), request.getNonce())
|
||||||
ctx.writeAndFlush(resolveResponse(FORBIDDEN, request)).addListener(
|
.equals(request.getSignature())) {
|
||||||
ChannelFutureListener.CLOSE);
|
ctx.writeAndFlush(resolveResponse(FORBIDDEN, request)).addListener(ChannelFutureListener.CLOSE);
|
||||||
return;
|
return;
|
||||||
} else if (request.getMethod() == HttpMethod.POST) {
|
}
|
||||||
// URL参数签名验证
|
// XML消息签名验证
|
||||||
if (!ServerToolkits.isBlank(request.getSignature())
|
if (request.getEncryptType() == EncryptType.AES
|
||||||
&& !MessageUtil.signature(aesToken.getToken(),
|
&& !MessageUtil.signature(aesToken.getToken(), request.getTimeStamp(), request.getNonce(),
|
||||||
request.getTimeStamp(), request.getNonce()).equals(
|
request.getEncryptContent()).equals(request.getMsgSignature())) {
|
||||||
request.getSignature())) {
|
ctx.writeAndFlush(resolveResponse(FORBIDDEN, request)).addListener(ChannelFutureListener.CLOSE);
|
||||||
ctx.writeAndFlush(resolveResponse(FORBIDDEN, request))
|
return;
|
||||||
.addListener(ChannelFutureListener.CLOSE);
|
}
|
||||||
return;
|
} else {
|
||||||
}
|
// 访问其它URL
|
||||||
// XML消息签名验证
|
ctx.writeAndFlush(resolveResponse(METHOD_NOT_ALLOWED, request)).addListener(ChannelFutureListener.CLOSE);
|
||||||
if (request.getEncryptType() == EncryptType.AES
|
return;
|
||||||
&& !MessageUtil.signature(aesToken.getToken(),
|
}
|
||||||
request.getTimeStamp(), request.getNonce(),
|
messageDispatcher.doDispatch(ctx, request);
|
||||||
request.getEncryptContent()).equals(
|
}
|
||||||
request.getMsgSignature())) {
|
|
||||||
ctx.writeAndFlush(resolveResponse(FORBIDDEN, request))
|
|
||||||
.addListener(ChannelFutureListener.CLOSE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 访问其它URL
|
|
||||||
ctx.writeAndFlush(resolveResponse(METHOD_NOT_ALLOWED, request))
|
|
||||||
.addListener(ChannelFutureListener.CLOSE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
messageDispatcher.doDispatch(ctx, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FullHttpResponse resolveResponse(HttpResponseStatus responseStatus,
|
private FullHttpResponse resolveResponse(HttpResponseStatus responseStatus, WeixinRequest request) {
|
||||||
WeixinRequest request) {
|
FullHttpResponse response = new DefaultFullHttpResponse(request.getProtocolVersion(), responseStatus);
|
||||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
HttpUtil.resolveHeaders(response);
|
||||||
request.getProtocolVersion(), responseStatus);
|
return response;
|
||||||
HttpUtil.resolveHeaders(response);
|
}
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,7 @@
|
|||||||
package com.foxinmy.weixin4j.socket;
|
package com.foxinmy.weixin4j.socket;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||||
import com.foxinmy.weixin4j.type.EncryptType;
|
import com.foxinmy.weixin4j.type.EncryptType;
|
||||||
import com.foxinmy.weixin4j.util.AesToken;
|
import com.foxinmy.weixin4j.util.AesToken;
|
||||||
@ -16,6 +9,12 @@ import com.foxinmy.weixin4j.util.HttpUtil;
|
|||||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||||
import com.foxinmy.weixin4j.util.ServerToolkits;
|
import com.foxinmy.weixin4j.util.ServerToolkits;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信回复编码类
|
* 微信回复编码类
|
||||||
*
|
*
|
||||||
@ -23,66 +22,55 @@ import com.foxinmy.weixin4j.util.ServerToolkits;
|
|||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2014年11月13日
|
* @date 2014年11月13日
|
||||||
* @since JDK 1.6
|
* @since JDK 1.6
|
||||||
* @see <a
|
* @see <a href=
|
||||||
* href="http://mp.weixin.qq.com/wiki/0/61c3a8b9d50ac74f18bdf2e54ddfc4e0.html">加密接入指引</a>
|
* "http://mp.weixin.qq.com/wiki/0/61c3a8b9d50ac74f18bdf2e54ddfc4e0.html">加密接入指引</a>
|
||||||
* @see com.foxinmy.weixin4j.response.WeixinResponse
|
* @see com.foxinmy.weixin4j.response.WeixinResponse
|
||||||
*/
|
*/
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public class WeixinResponseEncoder extends
|
public class WeixinResponseEncoder extends MessageToMessageEncoder<WeixinResponse> {
|
||||||
MessageToMessageEncoder<WeixinResponse> {
|
|
||||||
|
|
||||||
protected final InternalLogger logger = InternalLoggerFactory
|
protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
|
||||||
.getInstance(getClass());
|
|
||||||
|
|
||||||
private final String XML_START = "<xml>";
|
private final String XML_START = "<xml>";
|
||||||
// ---------------明文节点
|
// ---------------明文节点
|
||||||
private final String ELEMENT_TOUSERNAME = "<ToUserName><![CDATA[%s]]></ToUserName>";
|
private final String ELEMENT_TOUSERNAME = "<ToUserName><![CDATA[%s]]></ToUserName>";
|
||||||
private final String ELEMENT_FROMUSERNAME = "<FromUserName><![CDATA[%s]]></FromUserName>";
|
private final String ELEMENT_FROMUSERNAME = "<FromUserName><![CDATA[%s]]></FromUserName>";
|
||||||
private final String ELEMENT_CREATETIME = "<CreateTime><![CDATA[%d]]></CreateTime>";
|
private final String ELEMENT_CREATETIME = "<CreateTime><![CDATA[%d]]></CreateTime>";
|
||||||
private final String ELEMENT_MSGTYPE = "<MsgType><![CDATA[%s]]></MsgType>";
|
private final String ELEMENT_MSGTYPE = "<MsgType><![CDATA[%s]]></MsgType>";
|
||||||
// ---------------密文节点
|
// ---------------密文节点
|
||||||
private final String ELEMENT_MSGSIGNATURE = "<MsgSignature><![CDATA[%s]]></MsgSignature>";
|
private final String ELEMENT_MSGSIGNATURE = "<MsgSignature><![CDATA[%s]]></MsgSignature>";
|
||||||
private final String ELEMENT_ENCRYPT = "<Encrypt><![CDATA[%s]]></Encrypt>";
|
private final String ELEMENT_ENCRYPT = "<Encrypt><![CDATA[%s]]></Encrypt>";
|
||||||
private final String ELEMENT_TIMESTAMP = "<TimeStamp><![CDATA[%s]]></TimeStamp>";
|
private final String ELEMENT_TIMESTAMP = "<TimeStamp><![CDATA[%s]]></TimeStamp>";
|
||||||
private final String ELEMENT_NONCE = "<Nonce><![CDATA[%s]]></Nonce>";
|
private final String ELEMENT_NONCE = "<Nonce><![CDATA[%s]]></Nonce>";
|
||||||
private final String XML_END = "</xml>";
|
private final String XML_END = "</xml>";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encode(ChannelHandlerContext ctx, WeixinResponse response,
|
protected void encode(ChannelHandlerContext ctx, WeixinResponse response, List<Object> out) {
|
||||||
List<Object> out) throws WeixinException {
|
WeixinMessageTransfer messageTransfer = ctx.channel().attr(ServerToolkits.MESSAGE_TRANSFER_KEY).get();
|
||||||
WeixinMessageTransfer messageTransfer = ctx.channel()
|
EncryptType encryptType = messageTransfer.getEncryptType();
|
||||||
.attr(ServerToolkits.MESSAGE_TRANSFER_KEY).get();
|
StringBuilder content = new StringBuilder();
|
||||||
EncryptType encryptType = messageTransfer.getEncryptType();
|
content.append(XML_START);
|
||||||
StringBuilder content = new StringBuilder();
|
content.append(String.format(ELEMENT_TOUSERNAME, messageTransfer.getFromUserName()));
|
||||||
content.append(XML_START);
|
content.append(String.format(ELEMENT_FROMUSERNAME, messageTransfer.getToUserName()));
|
||||||
content.append(String.format(ELEMENT_TOUSERNAME,
|
content.append(String.format(ELEMENT_CREATETIME, System.currentTimeMillis() / 1000l));
|
||||||
messageTransfer.getFromUserName()));
|
content.append(String.format(ELEMENT_MSGTYPE, response.getMsgType()));
|
||||||
content.append(String.format(ELEMENT_FROMUSERNAME,
|
content.append(response.toContent());
|
||||||
messageTransfer.getToUserName()));
|
content.append(XML_END);
|
||||||
content.append(String.format(ELEMENT_CREATETIME,
|
if (encryptType == EncryptType.AES) {
|
||||||
System.currentTimeMillis() / 1000l));
|
AesToken aesToken = messageTransfer.getAesToken();
|
||||||
content.append(String.format(ELEMENT_MSGTYPE, response.getMsgType()));
|
String nonce = ServerToolkits.generateRandomString(32);
|
||||||
content.append(response.toContent());
|
String timestamp = Long.toString(System.currentTimeMillis() / 1000l);
|
||||||
content.append(XML_END);
|
String encrtypt = MessageUtil.aesEncrypt(aesToken.getWeixinId(), aesToken.getAesKey(), content.toString());
|
||||||
if (encryptType == EncryptType.AES) {
|
String msgSignature = MessageUtil.signature(aesToken.getToken(), nonce, timestamp, encrtypt);
|
||||||
AesToken aesToken = messageTransfer.getAesToken();
|
content.delete(0, content.length());
|
||||||
String nonce = ServerToolkits.generateRandomString(32);
|
content.append(XML_START);
|
||||||
String timestamp = Long
|
content.append(String.format(ELEMENT_NONCE, nonce));
|
||||||
.toString(System.currentTimeMillis() / 1000l);
|
content.append(String.format(ELEMENT_TIMESTAMP, timestamp));
|
||||||
String encrtypt = MessageUtil.aesEncrypt(aesToken.getWeixinId(),
|
content.append(String.format(ELEMENT_MSGSIGNATURE, msgSignature));
|
||||||
aesToken.getAesKey(), content.toString());
|
content.append(String.format(ELEMENT_ENCRYPT, encrtypt));
|
||||||
String msgSignature = MessageUtil.signature(aesToken.getToken(),
|
content.append(XML_END);
|
||||||
nonce, timestamp, encrtypt);
|
}
|
||||||
content.delete(0, content.length());
|
ctx.writeAndFlush(HttpUtil.createHttpResponse(content.toString(), ServerToolkits.CONTENTTYPE$APPLICATION_XML));
|
||||||
content.append(XML_START);
|
logger.info("{} encode weixin response:{}", encryptType, content);
|
||||||
content.append(String.format(ELEMENT_NONCE, nonce));
|
}
|
||||||
content.append(String.format(ELEMENT_TIMESTAMP, timestamp));
|
|
||||||
content.append(String.format(ELEMENT_MSGSIGNATURE, msgSignature));
|
|
||||||
content.append(String.format(ELEMENT_ENCRYPT, encrtypt));
|
|
||||||
content.append(XML_END);
|
|
||||||
}
|
|
||||||
ctx.writeAndFlush(HttpUtil.createHttpResponse(content.toString(),
|
|
||||||
ServerToolkits.CONTENTTYPE$APPLICATION_XML));
|
|
||||||
logger.info("{} encode weixin response:{}", encryptType, content);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -5,11 +5,10 @@ import java.util.Map;
|
|||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.dispatcher.BeanFactory;
|
import com.foxinmy.weixin4j.dispatcher.BeanFactory;
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用spring容器获取bean
|
* 使用spring容器获取bean
|
||||||
*
|
*
|
||||||
* @className SpringBeanFactory
|
* @className SpringBeanFactory
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年8月11日
|
* @date 2015年8月11日
|
||||||
@ -18,29 +17,29 @@ import com.foxinmy.weixin4j.exception.WeixinException;
|
|||||||
*/
|
*/
|
||||||
public class SpringBeanFactory implements BeanFactory {
|
public class SpringBeanFactory implements BeanFactory {
|
||||||
|
|
||||||
private ApplicationContext context;
|
private ApplicationContext context;
|
||||||
|
|
||||||
public SpringBeanFactory(ApplicationContext context) {
|
public SpringBeanFactory(ApplicationContext context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getBean(String name) throws WeixinException {
|
public Object getBean(String name) {
|
||||||
return context.getBean(name);
|
return context.getBean(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getBean(Class<T> classType) throws WeixinException {
|
public <T> T getBean(Class<T> classType) {
|
||||||
return context.getBean(classType);
|
return context.getBean(classType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getBean(String name, Class<T> classType) throws WeixinException {
|
public <T> T getBean(String name, Class<T> classType) {
|
||||||
return context.getBean(name, classType);
|
return context.getBean(name, classType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Map<String, T> getBeans(Class<T> clazz) throws WeixinException {
|
public <T> Map<String, T> getBeans(Class<T> clazz) {
|
||||||
return context.getBeansOfType(clazz);
|
return context.getBeansOfType(clazz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,7 +11,6 @@ import com.foxinmy.weixin4j.dispatcher.DefaultMessageMatcher;
|
|||||||
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
|
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
|
||||||
import com.foxinmy.weixin4j.dispatcher.WeixinMessageKey;
|
import com.foxinmy.weixin4j.dispatcher.WeixinMessageKey;
|
||||||
import com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher;
|
import com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher;
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||||
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
|
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
|
||||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||||
@ -173,7 +172,7 @@ public final class WeixinServerBootstrap {
|
|||||||
* 默认端口(30000)启动服务
|
* 默认端口(30000)启动服务
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void startup() throws WeixinException {
|
public void startup() {
|
||||||
startup(DEFAULT_SERVERPORT);
|
startup(DEFAULT_SERVERPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +180,7 @@ public final class WeixinServerBootstrap {
|
|||||||
* 指定端口启动服务
|
* 指定端口启动服务
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void startup(int serverPort) throws WeixinException {
|
public void startup(int serverPort) {
|
||||||
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort);
|
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +196,7 @@ public final class WeixinServerBootstrap {
|
|||||||
* @return
|
* @return
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public void startup(int bossThreads, int workerThreads, final int serverPort) throws WeixinException {
|
public void startup(int bossThreads, int workerThreads, final int serverPort) {
|
||||||
messageDispatcher.setMessageHandlerList(messageHandlerList);
|
messageDispatcher.setMessageHandlerList(messageHandlerList);
|
||||||
messageDispatcher.setMessageInterceptorList(messageInterceptorList);
|
messageDispatcher.setMessageInterceptorList(messageInterceptorList);
|
||||||
try {
|
try {
|
||||||
@ -218,7 +217,7 @@ public final class WeixinServerBootstrap {
|
|||||||
}).sync().channel();
|
}).sync().channel();
|
||||||
ch.closeFuture().sync();
|
ch.closeFuture().sync();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new WeixinException("netty server startup FAIL", e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
shutdown();
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,11 +18,9 @@ import java.util.List;
|
|||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对class的获取
|
* 对class的获取
|
||||||
*
|
*
|
||||||
* @className ClassUtil
|
* @className ClassUtil
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2014年10月31日
|
* @date 2014年10月31日
|
||||||
@ -30,189 +28,180 @@ import com.foxinmy.weixin4j.exception.WeixinException;
|
|||||||
* @see
|
* @see
|
||||||
*/
|
*/
|
||||||
public final class ClassUtil {
|
public final class ClassUtil {
|
||||||
private final static String POINT = ".";
|
private final static String POINT = ".";
|
||||||
private final static String CLASS = ".class";
|
private final static String CLASS = ".class";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取某个包下所有的class信息
|
* 获取某个包下所有的class信息
|
||||||
*
|
*
|
||||||
* @param packageName
|
* @param packageName
|
||||||
* 包名
|
* 包名
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static List<Class<?>> getClasses(String packageName)
|
public static List<Class<?>> getClasses(String packageName) {
|
||||||
throws WeixinException {
|
String packageFileName = packageName.replace(POINT, File.separator);
|
||||||
String packageFileName = packageName.replace(POINT, File.separator);
|
URL fullPath = getDefaultClassLoader().getResource(packageFileName);
|
||||||
URL fullPath = getDefaultClassLoader().getResource(packageFileName);
|
String protocol = fullPath.getProtocol();
|
||||||
String protocol = fullPath.getProtocol();
|
if (protocol.equals(ServerToolkits.PROTOCOL_FILE)) {
|
||||||
if (protocol.equals(ServerToolkits.PROTOCOL_FILE)) {
|
try {
|
||||||
try {
|
File dir = new File(fullPath.toURI());
|
||||||
File dir = new File(fullPath.toURI());
|
return findClassesByFile(dir, packageName);
|
||||||
return findClassesByFile(dir, packageName);
|
} catch (URISyntaxException e) {
|
||||||
} catch (URISyntaxException e) {
|
throw new RuntimeException(e);
|
||||||
throw new WeixinException(e);
|
}
|
||||||
}
|
} else if (protocol.equals(ServerToolkits.PROTOCOL_JAR)) {
|
||||||
} else if (protocol.equals(ServerToolkits.PROTOCOL_JAR)) {
|
try {
|
||||||
try {
|
return findClassesByJar(((JarURLConnection) fullPath.openConnection()).getJarFile(), packageName);
|
||||||
return findClassesByJar(
|
} catch (IOException e) {
|
||||||
((JarURLConnection) fullPath.openConnection())
|
throw new RuntimeException(e);
|
||||||
.getJarFile(),
|
}
|
||||||
packageName);
|
}
|
||||||
} catch (IOException e) {
|
return null;
|
||||||
throw new WeixinException(e);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫描目录下所有的class对象
|
* 扫描目录下所有的class对象
|
||||||
*
|
*
|
||||||
* @param dir
|
* @param dir
|
||||||
* 文件目录
|
* 文件目录
|
||||||
* @param packageName
|
* @param packageName
|
||||||
* 包的全限类名
|
* 包的全限类名
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static List<Class<?>> findClassesByFile(File dir, String packageName) {
|
private static List<Class<?>> findClassesByFile(File dir, String packageName) {
|
||||||
List<Class<?>> classes = new ArrayList<Class<?>>();
|
List<Class<?>> classes = new ArrayList<Class<?>>();
|
||||||
File[] files = dir.listFiles(new FilenameFilter() {
|
File[] files = dir.listFiles(new FilenameFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(File file, String name) {
|
public boolean accept(File file, String name) {
|
||||||
return file.isDirectory() || file.getName().endsWith(CLASS);
|
return file.isDirectory() || file.getName().endsWith(CLASS);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
classes.addAll(findClassesByFile(file, packageName + POINT
|
classes.addAll(findClassesByFile(file, packageName + POINT + file.getName()));
|
||||||
+ file.getName()));
|
} else {
|
||||||
} else {
|
try {
|
||||||
try {
|
classes.add(Class.forName(packageName + POINT + file.getName().replace(CLASS, "")));
|
||||||
classes.add(Class.forName(packageName + POINT
|
} catch (ClassNotFoundException e) {
|
||||||
+ file.getName().replace(CLASS, "")));
|
;
|
||||||
} catch (ClassNotFoundException e) {
|
}
|
||||||
;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return classes;
|
||||||
}
|
}
|
||||||
return classes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫描jar包下所有的class对象
|
* 扫描jar包下所有的class对象
|
||||||
*
|
*
|
||||||
* @param jar
|
* @param jar
|
||||||
* jar包对象
|
* jar包对象
|
||||||
* @param packageName
|
* @param packageName
|
||||||
* 包的全限类名
|
* 包的全限类名
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static List<Class<?>> findClassesByJar(JarFile jar,
|
private static List<Class<?>> findClassesByJar(JarFile jar, String packageName) {
|
||||||
String packageName) {
|
List<Class<?>> classes = new ArrayList<Class<?>>();
|
||||||
List<Class<?>> classes = new ArrayList<Class<?>>();
|
Enumeration<JarEntry> jarEntries = jar.entries();
|
||||||
Enumeration<JarEntry> jarEntries = jar.entries();
|
while (jarEntries.hasMoreElements()) {
|
||||||
while (jarEntries.hasMoreElements()) {
|
JarEntry jarEntry = jarEntries.nextElement();
|
||||||
JarEntry jarEntry = jarEntries.nextElement();
|
if (jarEntry.isDirectory()) {
|
||||||
if (jarEntry.isDirectory()) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
String className = jarEntry.getName().replace(File.separator, POINT);
|
||||||
String className = jarEntry.getName()
|
if (!className.startsWith(packageName) || !className.endsWith(CLASS)) {
|
||||||
.replace(File.separator, POINT);
|
continue;
|
||||||
if (!className.startsWith(packageName)
|
}
|
||||||
|| !className.endsWith(CLASS)) {
|
try {
|
||||||
continue;
|
classes.add(Class.forName(className.replace(CLASS, "")));
|
||||||
}
|
} catch (ClassNotFoundException e) {
|
||||||
try {
|
;
|
||||||
classes.add(Class.forName(className.replace(CLASS, "")));
|
}
|
||||||
} catch (ClassNotFoundException e) {
|
}
|
||||||
;
|
return classes;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return classes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object deepClone(Object obj) throws WeixinException {
|
public static Object deepClone(Object obj) {
|
||||||
ByteArrayOutputStream bos = null;
|
ByteArrayOutputStream bos = null;
|
||||||
ObjectOutputStream oos = null;
|
ObjectOutputStream oos = null;
|
||||||
ByteArrayInputStream bis = null;
|
ByteArrayInputStream bis = null;
|
||||||
ObjectInputStream ois = null;
|
ObjectInputStream ois = null;
|
||||||
try {
|
try {
|
||||||
bos = new ByteArrayOutputStream();
|
bos = new ByteArrayOutputStream();
|
||||||
oos = new ObjectOutputStream(bos);
|
oos = new ObjectOutputStream(bos);
|
||||||
oos.writeObject(obj);
|
oos.writeObject(obj);
|
||||||
bis = new ByteArrayInputStream(bos.toByteArray());
|
bis = new ByteArrayInputStream(bos.toByteArray());
|
||||||
ois = new ObjectInputStream(bis);
|
ois = new ObjectInputStream(bis);
|
||||||
return ois.readObject();
|
return ois.readObject();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new WeixinException(e);
|
throw new RuntimeException(e);
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new WeixinException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
if (bos != null) {
|
if (bos != null) {
|
||||||
bos.close();
|
bos.close();
|
||||||
}
|
}
|
||||||
if (oos != null) {
|
if (oos != null) {
|
||||||
oos.close();
|
oos.close();
|
||||||
}
|
}
|
||||||
if (bis != null) {
|
if (bis != null) {
|
||||||
bis.close();
|
bis.close();
|
||||||
}
|
}
|
||||||
if (ois != null) {
|
if (ois != null) {
|
||||||
ois.close();
|
ois.close();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
;// ignore
|
;// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得泛型类型
|
* 获得泛型类型
|
||||||
*
|
*
|
||||||
* @param object
|
* @param object
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static Class<?> getGenericType(Class<?> clazz) {
|
public static Class<?> getGenericType(Class<?> clazz) {
|
||||||
if(clazz == Object.class){
|
if (clazz == Object.class) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Type type = clazz.getGenericSuperclass();
|
Type type = clazz.getGenericSuperclass();
|
||||||
if (type instanceof ParameterizedType) {
|
if (type instanceof ParameterizedType) {
|
||||||
ParameterizedType ptype = ((ParameterizedType) type);
|
ParameterizedType ptype = ((ParameterizedType) type);
|
||||||
Type[] args = ptype.getActualTypeArguments();
|
Type[] args = ptype.getActualTypeArguments();
|
||||||
return (Class<?>) args[0];
|
return (Class<?>) args[0];
|
||||||
}
|
}
|
||||||
return getGenericType(clazz.getSuperclass());
|
return getGenericType(clazz.getSuperclass());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ClassLoader getDefaultClassLoader() {
|
public static ClassLoader getDefaultClassLoader() {
|
||||||
ClassLoader cl = null;
|
ClassLoader cl = null;
|
||||||
try {
|
try {
|
||||||
cl = Thread.currentThread().getContextClassLoader();
|
cl = Thread.currentThread().getContextClassLoader();
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
// Cannot access thread context ClassLoader - falling back...
|
// Cannot access thread context ClassLoader - falling back...
|
||||||
}
|
}
|
||||||
if (cl == null) {
|
if (cl == null) {
|
||||||
// No thread context class loader -> use class loader of this class.
|
// No thread context class loader -> use class loader of this class.
|
||||||
cl = ClassUtil.class.getClassLoader();
|
cl = ClassUtil.class.getClassLoader();
|
||||||
if (cl == null) {
|
if (cl == null) {
|
||||||
// getClassLoader() returning null indicates the bootstrap
|
// getClassLoader() returning null indicates the bootstrap
|
||||||
// ClassLoader
|
// ClassLoader
|
||||||
try {
|
try {
|
||||||
cl = ClassLoader.getSystemClassLoader();
|
cl = ClassLoader.getSystemClassLoader();
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
// Cannot access system ClassLoader - oh well, maybe the
|
// Cannot access system ClassLoader - oh well, maybe the
|
||||||
// caller can live with null...
|
// caller can live with null...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cl;
|
return cl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws WeixinException {
|
public static void main(String[] args) {
|
||||||
System.err.println(getClasses("com.foxinmy.weixin4j.qy.event"));
|
System.err.println(getClasses("com.foxinmy.weixin4j.qy.event"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,11 +7,10 @@ import javax.crypto.spec.IvParameterSpec;
|
|||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.base64.Base64;
|
import com.foxinmy.weixin4j.base64.Base64;
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息工具类
|
* 消息工具类
|
||||||
*
|
*
|
||||||
* @className MessageUtil
|
* @className MessageUtil
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2014年10月31日
|
* @date 2014年10月31日
|
||||||
@ -19,150 +18,137 @@ import com.foxinmy.weixin4j.exception.WeixinException;
|
|||||||
* @see
|
* @see
|
||||||
*/
|
*/
|
||||||
public final class MessageUtil {
|
public final class MessageUtil {
|
||||||
/**
|
/**
|
||||||
* 验证微信签名
|
* 验证微信签名
|
||||||
*
|
*
|
||||||
* @param signature
|
* @param signature
|
||||||
* 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数
|
* 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数
|
||||||
* @return 开发者通过检验signature对请求进行相关校验。若确认此次GET请求来自微信服务器
|
* @return 开发者通过检验signature对请求进行相关校验。若确认此次GET请求来自微信服务器
|
||||||
* 请原样返回echostr参数内容,则接入生效 成为开发者成功,否则接入失败
|
* 请原样返回echostr参数内容,则接入生效 成为开发者成功,否则接入失败
|
||||||
* @see <a
|
* @see <a href=
|
||||||
* href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN">接入指南</a>
|
* "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN">接入指南</a>
|
||||||
*/
|
*/
|
||||||
public static String signature(String... para) {
|
public static String signature(String... para) {
|
||||||
Arrays.sort(para);
|
Arrays.sort(para);
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
for (String str : para) {
|
for (String str : para) {
|
||||||
sb.append(str);
|
sb.append(str);
|
||||||
}
|
}
|
||||||
return ServerToolkits.digestSHA1(sb.toString());
|
return ServerToolkits.digestSHA1(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对xml消息加密
|
* 对xml消息加密
|
||||||
*
|
*
|
||||||
* @param appId
|
* @param appId
|
||||||
* 应用ID
|
* 应用ID
|
||||||
* @param encodingAesKey
|
* @param encodingAesKey
|
||||||
* 加密密钥
|
* 加密密钥
|
||||||
* @param xmlContent
|
* @param xmlContent
|
||||||
* 原始消息体
|
* 原始消息体
|
||||||
* @return aes加密后的消息体
|
* @return aes加密后的消息体
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public static String aesEncrypt(String appId, String encodingAesKey,
|
public static String aesEncrypt(String appId, String encodingAesKey, String xmlContent) {
|
||||||
String xmlContent) throws WeixinException {
|
/**
|
||||||
/**
|
* 其中,msg_encrypt=Base64_Encode(AES_Encrypt [random(16B)+ msg_len(4B) +
|
||||||
* 其中,msg_encrypt=Base64_Encode(AES_Encrypt [random(16B)+ msg_len(4B) +
|
* msg + $AppId])
|
||||||
* msg + $AppId])
|
*
|
||||||
*
|
* random(16B)为16字节的随机字符串;msg_len为msg长度,占4个字节(网络字节序),$AppId为公众账号的AppId
|
||||||
* random(16B)为16字节的随机字符串;msg_len为msg长度,占4个字节(网络字节序),$AppId为公众账号的AppId
|
*/
|
||||||
*/
|
byte[] randomBytes = ServerToolkits.getBytesUtf8(ServerToolkits.generateRandomString(16));
|
||||||
byte[] randomBytes = ServerToolkits.getBytesUtf8(ServerToolkits
|
byte[] xmlBytes = ServerToolkits.getBytesUtf8(xmlContent);
|
||||||
.generateRandomString(16));
|
int xmlLength = xmlBytes.length;
|
||||||
byte[] xmlBytes = ServerToolkits.getBytesUtf8(xmlContent);
|
byte[] orderBytes = new byte[4];
|
||||||
int xmlLength = xmlBytes.length;
|
orderBytes[3] = (byte) (xmlLength & 0xFF);
|
||||||
byte[] orderBytes = new byte[4];
|
orderBytes[2] = (byte) (xmlLength >> 8 & 0xFF);
|
||||||
orderBytes[3] = (byte) (xmlLength & 0xFF);
|
orderBytes[1] = (byte) (xmlLength >> 16 & 0xFF);
|
||||||
orderBytes[2] = (byte) (xmlLength >> 8 & 0xFF);
|
orderBytes[0] = (byte) (xmlLength >> 24 & 0xFF);
|
||||||
orderBytes[1] = (byte) (xmlLength >> 16 & 0xFF);
|
byte[] appidBytes = ServerToolkits.getBytesUtf8(appId);
|
||||||
orderBytes[0] = (byte) (xmlLength >> 24 & 0xFF);
|
|
||||||
byte[] appidBytes = ServerToolkits.getBytesUtf8(appId);
|
|
||||||
|
|
||||||
int byteLength = randomBytes.length + xmlLength + orderBytes.length
|
int byteLength = randomBytes.length + xmlLength + orderBytes.length + appidBytes.length;
|
||||||
+ appidBytes.length;
|
// ... + pad: 使用自定义的填充方式对明文进行补位填充
|
||||||
// ... + pad: 使用自定义的填充方式对明文进行补位填充
|
byte[] padBytes = PKCS7Encoder.encode(byteLength);
|
||||||
byte[] padBytes = PKCS7Encoder.encode(byteLength);
|
// random + endian + xml + appid + pad 获得最终的字节流
|
||||||
// random + endian + xml + appid + pad 获得最终的字节流
|
byte[] unencrypted = new byte[byteLength + padBytes.length];
|
||||||
byte[] unencrypted = new byte[byteLength + padBytes.length];
|
byteLength = 0;
|
||||||
byteLength = 0;
|
// src:源数组;srcPos:源数组要复制的起始位置;dest:目的数组;destPos:目的数组放置的起始位置;length:复制的长度
|
||||||
// src:源数组;srcPos:源数组要复制的起始位置;dest:目的数组;destPos:目的数组放置的起始位置;length:复制的长度
|
System.arraycopy(randomBytes, 0, unencrypted, byteLength, randomBytes.length);
|
||||||
System.arraycopy(randomBytes, 0, unencrypted, byteLength,
|
byteLength += randomBytes.length;
|
||||||
randomBytes.length);
|
System.arraycopy(orderBytes, 0, unencrypted, byteLength, orderBytes.length);
|
||||||
byteLength += randomBytes.length;
|
byteLength += orderBytes.length;
|
||||||
System.arraycopy(orderBytes, 0, unencrypted, byteLength,
|
System.arraycopy(xmlBytes, 0, unencrypted, byteLength, xmlBytes.length);
|
||||||
orderBytes.length);
|
byteLength += xmlBytes.length;
|
||||||
byteLength += orderBytes.length;
|
System.arraycopy(appidBytes, 0, unencrypted, byteLength, appidBytes.length);
|
||||||
System.arraycopy(xmlBytes, 0, unencrypted, byteLength, xmlBytes.length);
|
byteLength += appidBytes.length;
|
||||||
byteLength += xmlBytes.length;
|
System.arraycopy(padBytes, 0, unencrypted, byteLength, padBytes.length);
|
||||||
System.arraycopy(appidBytes, 0, unencrypted, byteLength,
|
try {
|
||||||
appidBytes.length);
|
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");
|
||||||
byteLength += appidBytes.length;
|
// 设置加密模式为AES的CBC模式
|
||||||
System.arraycopy(padBytes, 0, unencrypted, byteLength, padBytes.length);
|
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||||
try {
|
SecretKeySpec keySpec = new SecretKeySpec(aesKey, ServerToolkits.AES);
|
||||||
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");
|
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
|
||||||
// 设置加密模式为AES的CBC模式
|
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
|
||||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
// 加密
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, ServerToolkits.AES);
|
byte[] encrypted = cipher.doFinal(unencrypted);
|
||||||
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
|
// 使用BASE64对加密后的字符串进行编码
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
|
// return Base64.encodeBase64String(encrypted);
|
||||||
// 加密
|
return Base64.encodeBase64String(encrypted);
|
||||||
byte[] encrypted = cipher.doFinal(unencrypted);
|
} catch (Exception e) {
|
||||||
// 使用BASE64对加密后的字符串进行编码
|
throw new RuntimeException("-40006,AES加密失败:" + e.getMessage());
|
||||||
// return Base64.encodeBase64String(encrypted);
|
}
|
||||||
return Base64
|
}
|
||||||
.encodeBase64String(encrypted);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new WeixinException("-40006", "AES加密失败:" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对AES消息解密
|
* 对AES消息解密
|
||||||
*
|
*
|
||||||
* @param appId
|
* @param appId
|
||||||
* @param encodingAesKey
|
* @param encodingAesKey
|
||||||
* aes加密的密钥
|
* aes加密的密钥
|
||||||
* @param encryptContent
|
* @param encryptContent
|
||||||
* 加密的消息体
|
* 加密的消息体
|
||||||
* @return 解密后的字符
|
* @return 解密后的字符
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public static String aesDecrypt(String appId, String encodingAesKey,
|
public static String aesDecrypt(String appId, String encodingAesKey, String encryptContent) {
|
||||||
String encryptContent) throws WeixinException {
|
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");
|
||||||
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");
|
byte[] original;
|
||||||
byte[] original;
|
try {
|
||||||
try {
|
// 设置解密模式为AES的CBC模式
|
||||||
// 设置解密模式为AES的CBC模式
|
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
SecretKeySpec key_spec = new SecretKeySpec(aesKey, ServerToolkits.AES);
|
||||||
SecretKeySpec key_spec = new SecretKeySpec(aesKey, ServerToolkits.AES);
|
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
|
||||||
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey,
|
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
|
||||||
0, 16));
|
// 使用BASE64对密文进行解码
|
||||||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
|
byte[] encrypted = Base64.decodeBase64(encryptContent);
|
||||||
// 使用BASE64对密文进行解码
|
// 解密
|
||||||
byte[] encrypted = Base64.decodeBase64(encryptContent);
|
original = cipher.doFinal(encrypted);
|
||||||
// 解密
|
} catch (Exception e) {
|
||||||
original = cipher.doFinal(encrypted);
|
throw new RuntimeException("-40007,AES解密失败:" + e.getMessage());
|
||||||
} catch (Exception e) {
|
}
|
||||||
throw new WeixinException("-40007", "AES解密失败:" + e.getMessage());
|
String xmlContent, fromAppId;
|
||||||
}
|
try {
|
||||||
String xmlContent, fromAppId;
|
// 去除补位字符
|
||||||
try {
|
byte[] bytes = PKCS7Encoder.decode(original);
|
||||||
// 去除补位字符
|
/**
|
||||||
byte[] bytes = PKCS7Encoder.decode(original);
|
* AES加密的buf由16个字节的随机字符串、4个字节的msg_len(网络字节序)、msg和$AppId组成,
|
||||||
/**
|
* 其中msg_len为msg的长度,$AppId为公众帐号的AppId
|
||||||
* AES加密的buf由16个字节的随机字符串、4个字节的msg_len(网络字节序)、msg和$AppId组成,
|
*/
|
||||||
* 其中msg_len为msg的长度,$AppId为公众帐号的AppId
|
// 获取表示xml长度的字节数组
|
||||||
*/
|
byte[] lengthByte = Arrays.copyOfRange(bytes, 16, 20);
|
||||||
// 获取表示xml长度的字节数组
|
// 获取xml消息主体的长度(byte[]2int)
|
||||||
byte[] lengthByte = Arrays.copyOfRange(bytes, 16, 20);
|
// http://my.oschina.net/u/169390/blog/97495
|
||||||
// 获取xml消息主体的长度(byte[]2int)
|
int xmlLength = lengthByte[3] & 0xff | (lengthByte[2] & 0xff) << 8 | (lengthByte[1] & 0xff) << 16
|
||||||
// http://my.oschina.net/u/169390/blog/97495
|
| (lengthByte[0] & 0xff) << 24;
|
||||||
int xmlLength = lengthByte[3] & 0xff | (lengthByte[2] & 0xff) << 8
|
xmlContent = ServerToolkits.newStringUtf8(Arrays.copyOfRange(bytes, 20, 20 + xmlLength));
|
||||||
| (lengthByte[1] & 0xff) << 16
|
fromAppId = ServerToolkits.newStringUtf8(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length));
|
||||||
| (lengthByte[0] & 0xff) << 24;
|
} catch (Exception e) {
|
||||||
xmlContent = ServerToolkits.newStringUtf8(Arrays.copyOfRange(bytes, 20,
|
throw new RuntimeException("-40008,xml内容不合法:" + e.getMessage());
|
||||||
20 + xmlLength));
|
}
|
||||||
fromAppId = ServerToolkits.newStringUtf8(Arrays.copyOfRange(bytes,
|
// 校验appId是否一致
|
||||||
20 + xmlLength, bytes.length));
|
if (appId != null && !fromAppId.trim().equals(appId)) {
|
||||||
} catch (Exception e) {
|
throw new RuntimeException("-40005,校验AppID失败,expect " + appId + ",but actual is " + fromAppId);
|
||||||
throw new WeixinException("-40008", "xml内容不合法:" + e.getMessage());
|
}
|
||||||
}
|
return xmlContent;
|
||||||
// 校验appId是否一致
|
}
|
||||||
if (appId != null && !fromAppId.trim().equals(appId)) {
|
|
||||||
throw new WeixinException("-40005", "校验AppID失败,expect " + appId
|
|
||||||
+ ",but actual is " + fromAppId);
|
|
||||||
}
|
|
||||||
return xmlContent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.server.ext;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||||
import com.foxinmy.weixin4j.qy.suite.SuiteEventType;
|
import com.foxinmy.weixin4j.qy.suite.SuiteEventType;
|
||||||
import com.foxinmy.weixin4j.qy.suite.SuiteMessage;
|
import com.foxinmy.weixin4j.qy.suite.SuiteMessage;
|
||||||
@ -13,7 +12,7 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 企业号套件消息处理
|
* 企业号套件消息处理
|
||||||
*
|
*
|
||||||
* @className SuiteMessageHandler
|
* @className SuiteMessageHandler
|
||||||
* @author jy
|
* @author jy
|
||||||
* @date 2015年6月25日
|
* @date 2015年6月25日
|
||||||
@ -21,29 +20,27 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
*/
|
*/
|
||||||
public class SuiteMessageHandler implements WeixinMessageHandler {
|
public class SuiteMessageHandler implements WeixinMessageHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames)
|
public boolean canHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
throws WeixinException {
|
return nodeNames.contains("suiteid");
|
||||||
return nodeNames.contains("suiteid");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames)
|
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
throws WeixinException {
|
SuiteMessage suiteMessage = null; // 转换为 SuiteMessage
|
||||||
SuiteMessage suiteMessage = null; // 转换为 SuiteMessage
|
SuiteEventType eventType = suiteMessage.getFormatEventType();
|
||||||
SuiteEventType eventType = suiteMessage.getFormatEventType();
|
if (eventType == SuiteEventType.suite_ticket) {
|
||||||
if (eventType == SuiteEventType.suite_ticket) {
|
// do something
|
||||||
// do something
|
} else if (eventType == SuiteEventType.change_auth) {
|
||||||
} else if (eventType == SuiteEventType.change_auth) {
|
// do something
|
||||||
// do something
|
} else if (eventType == SuiteEventType.cancel_auth) {
|
||||||
} else if (eventType == SuiteEventType.cancel_auth) {
|
// do something
|
||||||
// do something
|
}
|
||||||
}
|
return BlankResponse.global;
|
||||||
return BlankResponse.global;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int weight() {
|
public int weight() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
package com.foxinmy.weixin4j.server.test;
|
package com.foxinmy.weixin4j.server.test;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.dispatcher.BeanFactory;
|
import com.foxinmy.weixin4j.dispatcher.BeanFactory;
|
||||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
|
||||||
import com.foxinmy.weixin4j.handler.DebugMessageHandler;
|
import com.foxinmy.weixin4j.handler.DebugMessageHandler;
|
||||||
import com.foxinmy.weixin4j.handler.MessageHandlerAdapter;
|
import com.foxinmy.weixin4j.handler.MessageHandlerAdapter;
|
||||||
import com.foxinmy.weixin4j.handler.MultipleMessageHandlerAdapter;
|
import com.foxinmy.weixin4j.handler.MultipleMessageHandlerAdapter;
|
||||||
@ -23,6 +20,8 @@ import com.foxinmy.weixin4j.response.WeixinResponse;
|
|||||||
import com.foxinmy.weixin4j.spring.SpringBeanFactory;
|
import com.foxinmy.weixin4j.spring.SpringBeanFactory;
|
||||||
import com.foxinmy.weixin4j.startup.WeixinServerBootstrap;
|
import com.foxinmy.weixin4j.startup.WeixinServerBootstrap;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务启动测试类
|
* 服务启动测试类
|
||||||
*
|
*
|
||||||
@ -34,139 +33,123 @@ import com.foxinmy.weixin4j.startup.WeixinServerBootstrap;
|
|||||||
*/
|
*/
|
||||||
public class MessageServerStartup {
|
public class MessageServerStartup {
|
||||||
|
|
||||||
// 公众号ID
|
// 公众号ID
|
||||||
final String weixinId = "wx4ab8f8de58159a57";
|
final String weixinId = "wx4ab8f8de58159a57";
|
||||||
// 开发者token
|
// 开发者token
|
||||||
final String token = "weixin4j";
|
final String token = "weixin4j";
|
||||||
// AES密钥(安全模式)
|
// AES密钥(安全模式)
|
||||||
final String aesKey = "";
|
final String aesKey = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调试输出用户发来的消息
|
* 调试输出用户发来的消息
|
||||||
*
|
*
|
||||||
* @throws WeixinException
|
*/
|
||||||
*/
|
public void test1() {
|
||||||
public void test1() throws WeixinException {
|
// 明文模式
|
||||||
// 明文模式
|
new WeixinServerBootstrap(token).addHandler(DebugMessageHandler.global).startup();
|
||||||
new WeixinServerBootstrap(token).addHandler(DebugMessageHandler.global)
|
// 密文模式
|
||||||
.startup();
|
// new WeixinServerBootstrap(weixinId, token, aesKey).addHandler(
|
||||||
// 密文模式
|
// DebugMessageHandler.global).startup();
|
||||||
// new WeixinServerBootstrap(weixinId, token, aesKey).addHandler(
|
}
|
||||||
// DebugMessageHandler.global).startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 针对特定消息类型
|
* 针对特定消息类型
|
||||||
*
|
*
|
||||||
* @throws WeixinException
|
*/
|
||||||
*/
|
public void test2() {
|
||||||
public void test2() throws WeixinException {
|
// 针对文本消息回复
|
||||||
// 针对文本消息回复
|
WeixinMessageHandler textMessageHandler = new MessageHandlerAdapter<TextMessage>() {
|
||||||
WeixinMessageHandler textMessageHandler = new MessageHandlerAdapter<TextMessage>() {
|
@Override
|
||||||
@Override
|
public WeixinResponse doHandle0(WeixinRequest request, TextMessage message) {
|
||||||
public WeixinResponse doHandle0(WeixinRequest request,
|
return new TextResponse("HelloWorld!");
|
||||||
TextMessage message) throws WeixinException {
|
}
|
||||||
return new TextResponse("HelloWorld!");
|
};
|
||||||
}
|
// 针对语音消息回复
|
||||||
};
|
WeixinMessageHandler voiceMessageHandler = new MessageHandlerAdapter<VoiceMessage>() {
|
||||||
// 针对语音消息回复
|
@Override
|
||||||
WeixinMessageHandler voiceMessageHandler = new MessageHandlerAdapter<VoiceMessage>() {
|
public WeixinResponse doHandle0(WeixinRequest request, VoiceMessage message) {
|
||||||
@Override
|
return new TextResponse("HelloWorld!");
|
||||||
public WeixinResponse doHandle0(WeixinRequest request,
|
}
|
||||||
VoiceMessage message) throws WeixinException {
|
};
|
||||||
return new TextResponse("HelloWorld!");
|
// 当消息类型为文本(text)或者语音时回复「HelloWorld」, 否则回复调试消息
|
||||||
}
|
new WeixinServerBootstrap(weixinId, token, aesKey)
|
||||||
};
|
.addHandler(textMessageHandler, voiceMessageHandler, DebugMessageHandler.global).startup();
|
||||||
// 当消息类型为文本(text)或者语音时回复「HelloWorld」, 否则回复调试消息
|
}
|
||||||
new WeixinServerBootstrap(weixinId, token, aesKey).addHandler(
|
|
||||||
textMessageHandler, voiceMessageHandler,
|
|
||||||
DebugMessageHandler.global).startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多种消息类型处理
|
* 多种消息类型处理
|
||||||
*
|
*
|
||||||
* @throws WeixinException
|
*/
|
||||||
*/
|
public void test3() {
|
||||||
public void test3() throws WeixinException {
|
@SuppressWarnings("unchecked")
|
||||||
@SuppressWarnings("unchecked")
|
MultipleMessageHandlerAdapter messageHandler = new MultipleMessageHandlerAdapter(ScanEventMessage.class,
|
||||||
MultipleMessageHandlerAdapter messageHandler = new MultipleMessageHandlerAdapter(
|
TextMessage.class) {
|
||||||
ScanEventMessage.class, TextMessage.class) {
|
@Override
|
||||||
@Override
|
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message, Set<String> nodeNames) {
|
||||||
public WeixinResponse doHandle(WeixinRequest request,
|
return new TextResponse("处理了扫描和文字消息");
|
||||||
WeixinMessage message, Set<String> nodeNames)
|
}
|
||||||
throws WeixinException {
|
};
|
||||||
return new TextResponse("处理了扫描和文字消息");
|
new WeixinServerBootstrap(token).addHandler(messageHandler, DebugMessageHandler.global).startup();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
new WeixinServerBootstrap(token).addHandler(messageHandler,
|
|
||||||
DebugMessageHandler.global).startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫描包添加handler
|
* 扫描包添加handler
|
||||||
*
|
*
|
||||||
* @throws WeixinException
|
* @
|
||||||
*/
|
*/
|
||||||
public void test4() throws WeixinException {
|
public void test4() {
|
||||||
// handler处理所在的包名(子包也会扫描)
|
// handler处理所在的包名(子包也会扫描)
|
||||||
String packageToScan = "com.foxinmy.weixin4j.handler";
|
String packageToScan = "com.foxinmy.weixin4j.handler";
|
||||||
// handler默认使用 Class.newInstance
|
// handler默认使用 Class.newInstance
|
||||||
// 方式实例化,如果handler中含有service等类需要注入,可以声明一个BeanFactory,如SpringBeanFactory
|
// 方式实例化,如果handler中含有service等类需要注入,可以声明一个BeanFactory,如SpringBeanFactory
|
||||||
ApplicationContext applicationContext = null; // spring容器
|
ApplicationContext applicationContext = null; // spring容器
|
||||||
BeanFactory beanFactory = new SpringBeanFactory(applicationContext);
|
BeanFactory beanFactory = new SpringBeanFactory(applicationContext);
|
||||||
new WeixinServerBootstrap(token).handlerPackagesToScan(packageToScan)
|
new WeixinServerBootstrap(token).handlerPackagesToScan(packageToScan).openAlwaysResponse()
|
||||||
.openAlwaysResponse().resolveBeanFactory(beanFactory).startup();
|
.resolveBeanFactory(beanFactory).startup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拦截器应用
|
* 拦截器应用
|
||||||
*
|
*
|
||||||
* @throws WeixinException
|
* @
|
||||||
*/
|
*/
|
||||||
public void test5() throws WeixinException {
|
public void test5() {
|
||||||
// 拦截所有请求
|
// 拦截所有请求
|
||||||
WeixinMessageInterceptor interceptor = new WeixinMessageInterceptor() {
|
WeixinMessageInterceptor interceptor = new WeixinMessageInterceptor() {
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(ChannelHandlerContext context,
|
public boolean preHandle(ChannelHandlerContext context, WeixinRequest request, WeixinMessage message,
|
||||||
WeixinRequest request, WeixinMessage message,
|
WeixinMessageHandler handler) {
|
||||||
WeixinMessageHandler handler) throws WeixinException {
|
context.writeAndFlush(new TextResponse("所有消息被拦截了!"));
|
||||||
context.writeAndFlush(new TextResponse("所有消息被拦截了!"));
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postHandle(ChannelHandlerContext context,
|
public void postHandle(ChannelHandlerContext context, WeixinRequest request, WeixinResponse response,
|
||||||
WeixinRequest request, WeixinResponse response,
|
WeixinMessage message, WeixinMessageHandler handler) {
|
||||||
WeixinMessage message, WeixinMessageHandler handler)
|
System.err.println("preHandle返回为true,执行handler后");
|
||||||
throws WeixinException {
|
}
|
||||||
System.err.println("preHandle返回为true,执行handler后");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterCompletion(ChannelHandlerContext context,
|
public void afterCompletion(ChannelHandlerContext context, WeixinRequest request, WeixinResponse response,
|
||||||
WeixinRequest request, WeixinResponse response,
|
WeixinMessage message, WeixinMessageHandler handler, Exception exception) {
|
||||||
WeixinMessage message, WeixinMessageHandler handler,
|
System.err.println("请求处理完毕");
|
||||||
Exception exception) throws WeixinException {
|
}
|
||||||
System.err.println("请求处理完毕");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int weight() {
|
public int weight() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
new WeixinServerBootstrap(token).addInterceptor(interceptor)
|
new WeixinServerBootstrap(token).addInterceptor(interceptor).openAlwaysResponse().startup();
|
||||||
.openAlwaysResponse().startup();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main方法入口
|
* main方法入口
|
||||||
*
|
*
|
||||||
* @param args
|
* @param args
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
new MessageServerStartup().test1();
|
new MessageServerStartup().test1();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user