weixin4j-server:完成消息分发器、消息处理器、消息拦截器的骨架
This commit is contained in:
parent
3f5bfe9e71
commit
48533e05fd
10
CHANGE.md
10
CHANGE.md
@ -267,4 +267,12 @@
|
||||
|
||||
* 2015-04-30
|
||||
|
||||
+ 新增`media_id`和`view_limited`两种菜单类型
|
||||
+ **weixin4j-mp**: 新增`media_id`和`view_limited`两种菜单类型
|
||||
|
||||
* 2015-05-07
|
||||
|
||||
+**weixin4j-server**: 完成基本骨架
|
||||
|
||||
* 2015-05-08
|
||||
|
||||
+**weixin4j-server**: 完成消息分发器、消息处理器、消息拦截器的骨架
|
||||
@ -20,4 +20,8 @@
|
||||
|
||||
* 2015-05-07
|
||||
|
||||
+ 完成基本骨架
|
||||
+ 完成基本骨架
|
||||
|
||||
* 2015-05-08
|
||||
|
||||
+ 完成消息分发器、消息处理器、消息拦截器的骨架
|
||||
@ -1,4 +1,4 @@
|
||||
package com.foxinmy.weixin4j.model;
|
||||
package com.foxinmy.weixin4j.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package com.foxinmy.weixin4j.bean;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
|
||||
/**
|
||||
* Bean构造
|
||||
*
|
||||
* @className BeanFactory
|
||||
* @author jy
|
||||
* @date 2015年5月7日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public interface BeanFactory {
|
||||
Object getBean(String name) throws WeixinException;
|
||||
|
||||
<T> T getBean(Class<T> clazz) throws WeixinException;
|
||||
|
||||
<T> T getBean(String name, Class<T> clazz) throws WeixinException;
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
package com.foxinmy.weixin4j.dispatcher;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
|
||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
|
||||
/**
|
||||
* 微信消息分发器
|
||||
*
|
||||
* @className MessageHandlerExecutor
|
||||
* @author jy
|
||||
* @date 2015年5月7日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class MessageHandlerExecutor {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
/**
|
||||
* 消息处理器
|
||||
*/
|
||||
private final WeixinMessageHandler messageHandler;
|
||||
|
||||
/**
|
||||
* 消息拦截器
|
||||
*/
|
||||
private final WeixinMessageInterceptor[] messageInterceptors;
|
||||
|
||||
private int interceptorIndex = -1;
|
||||
|
||||
public MessageHandlerExecutor(WeixinMessageHandler messageHandler,
|
||||
WeixinMessageInterceptor[] messageInterceptors) {
|
||||
this.messageHandler = messageHandler;
|
||||
this.messageInterceptors = messageInterceptors;
|
||||
}
|
||||
|
||||
public WeixinMessageHandler getMessageHandler() {
|
||||
return messageHandler;
|
||||
}
|
||||
|
||||
public boolean applyPreHandle(ChannelHandlerContext ctx,
|
||||
WeixinRequest request, WeixinMessage message)
|
||||
throws WeixinException {
|
||||
if (messageInterceptors != null) {
|
||||
for (int i = 0; i < messageInterceptors.length; i++) {
|
||||
WeixinMessageInterceptor interceptor = messageInterceptors[i];
|
||||
if (!interceptor.preHandle(ctx, request, message,
|
||||
messageHandler)) {
|
||||
triggerAfterCompletion(ctx, request, message, null);
|
||||
return false;
|
||||
}
|
||||
this.interceptorIndex = i;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void applyPostHandle(ChannelHandlerContext ctx,
|
||||
WeixinRequest request, WeixinResponse response,
|
||||
WeixinMessage message) throws WeixinException {
|
||||
if (messageInterceptors == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = messageInterceptors.length - 1; i >= 0; i--) {
|
||||
WeixinMessageInterceptor interceptor = messageInterceptors[i];
|
||||
interceptor.postHandle(ctx, request, response, message,
|
||||
messageHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public void triggerAfterCompletion(ChannelHandlerContext ctx,
|
||||
WeixinRequest request, WeixinMessage message, WeixinException ex)
|
||||
throws WeixinException {
|
||||
if (messageInterceptors == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = this.interceptorIndex; i >= 0; i--) {
|
||||
WeixinMessageInterceptor interceptor = messageInterceptors[i];
|
||||
try {
|
||||
interceptor.afterCompletion(ctx, request, message,
|
||||
messageHandler, ex);
|
||||
} catch (WeixinException e) {
|
||||
logger.error(
|
||||
"MessageInterceptor.afterCompletion threw exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,241 @@
|
||||
package com.foxinmy.weixin4j.dispatcher;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.bean.BeanFactory;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
|
||||
import com.foxinmy.weixin4j.request.ResponseMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
import com.foxinmy.weixin4j.type.EncryptType;
|
||||
import com.foxinmy.weixin4j.util.ClassUtil;
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
import com.foxinmy.weixin4j.util.HttpUtil;
|
||||
|
||||
/**
|
||||
* 微信消息分发器
|
||||
*
|
||||
* @className WeixinMessageDispatcher
|
||||
* @author jy
|
||||
* @date 2015年5月7日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class WeixinMessageDispatcher {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* 消息处理器
|
||||
*/
|
||||
private List<WeixinMessageHandler> messageHandlerList;
|
||||
private WeixinMessageHandler[] messageHandlers;
|
||||
/**
|
||||
* 消息处理器所在的包
|
||||
*/
|
||||
private String[] messageHandlerPackages;
|
||||
|
||||
/**
|
||||
* 消息拦截器
|
||||
*/
|
||||
private List<WeixinMessageInterceptor> messageInterceptorList;
|
||||
private WeixinMessageInterceptor[] messageInterceptors;
|
||||
/**
|
||||
* 消息拦截器所在的包
|
||||
*/
|
||||
private String[] messageInterceptorPackages;
|
||||
|
||||
/**
|
||||
* Bean构造
|
||||
*/
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
public WeixinMessageDispatcher() {
|
||||
}
|
||||
|
||||
public void doDispatch(final ChannelHandlerContext context,
|
||||
final WeixinRequest request, final WeixinMessage message)
|
||||
throws WeixinException {
|
||||
MessageHandlerExecutor handlerExecutor = getHandlerExecutor(request,
|
||||
message);
|
||||
if (handlerExecutor == null
|
||||
|| handlerExecutor.getMessageHandler() == null) {
|
||||
noHandlerFound(context, request, message);
|
||||
return;
|
||||
}
|
||||
if (!handlerExecutor.applyPreHandle(context, request, message)) {
|
||||
return;
|
||||
}
|
||||
WeixinException dispatchException = null;
|
||||
try {
|
||||
WeixinResponse _response = handlerExecutor.getMessageHandler()
|
||||
.doHandle(request, message);
|
||||
log.info(
|
||||
"\n=================message response=================\n{}",
|
||||
_response);
|
||||
handlerExecutor.applyPostHandle(context, request, _response,
|
||||
message);
|
||||
ResponseMessage response = new ResponseMessage(message, _response);
|
||||
if (request.getEncryptType() == EncryptType.RAW) {
|
||||
context.write(HttpUtil.createWeixinMessageResponse(
|
||||
response.toXml(), Consts.CONTENTTYPE$APPLICATION_XML));
|
||||
} else {
|
||||
context.write(response);
|
||||
}
|
||||
} catch (WeixinException e) {
|
||||
dispatchException = e;
|
||||
}
|
||||
handlerExecutor.triggerAfterCompletion(context, request, message,
|
||||
dispatchException);
|
||||
}
|
||||
|
||||
protected void noHandlerFound(ChannelHandlerContext ctx,
|
||||
WeixinRequest request, WeixinMessage message) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.NOT_FOUND));
|
||||
}
|
||||
|
||||
protected MessageHandlerExecutor getHandlerExecutor(WeixinRequest request,
|
||||
WeixinMessage message) throws WeixinException {
|
||||
WeixinMessageHandler messageHandler = null;
|
||||
WeixinMessageHandler[] messageHandlers = getMessageHandlers();
|
||||
if (messageHandlers == null) {
|
||||
return null;
|
||||
}
|
||||
for (WeixinMessageHandler handler : messageHandlers) {
|
||||
if (handler.canHandle(request, message)) {
|
||||
messageHandler = handler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new MessageHandlerExecutor(messageHandler, messageInterceptors);
|
||||
}
|
||||
|
||||
public WeixinMessageHandler[] getMessageHandlers() throws WeixinException {
|
||||
if (this.messageHandlers == null) {
|
||||
if (messageHandlerPackages != null) {
|
||||
List<Class<?>> messageHandlerClass = new LinkedList<Class<?>>();
|
||||
for (String packageName : messageHandlerPackages) {
|
||||
messageHandlerClass.addAll(ClassUtil
|
||||
.getClasses(packageName));
|
||||
}
|
||||
if (beanFactory != null) {
|
||||
for (Class<?> clazz : messageHandlerClass) {
|
||||
messageHandlerList
|
||||
.add((WeixinMessageHandler) beanFactory
|
||||
.getBean(clazz));
|
||||
}
|
||||
} else {
|
||||
for (Class<?> clazz : messageHandlerClass) {
|
||||
try {
|
||||
messageHandlerList.add((WeixinMessageHandler) clazz
|
||||
.newInstance());
|
||||
} catch (InstantiationException ex) {
|
||||
throw new WeixinException(clazz.getName()
|
||||
+ " Is it an abstract class?", ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new WeixinException(clazz.getName()
|
||||
+ " Is the constructor accessible?", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (messageHandlerList != null
|
||||
&& !this.messageHandlerList.isEmpty()) {
|
||||
this.messageHandlers = this.messageHandlerList
|
||||
.toArray(new WeixinMessageHandler[this.messageHandlerList
|
||||
.size()]);
|
||||
}
|
||||
}
|
||||
return this.messageHandlers;
|
||||
|
||||
}
|
||||
|
||||
public WeixinMessageInterceptor[] getMessageInterceptors()
|
||||
throws WeixinException {
|
||||
if (this.messageInterceptors == null) {
|
||||
if (this.messageInterceptorList != null) {
|
||||
List<Class<?>> messageInterceptorClass = new LinkedList<Class<?>>();
|
||||
for (String packageName : messageInterceptorPackages) {
|
||||
messageInterceptorClass.addAll(ClassUtil
|
||||
.getClasses(packageName));
|
||||
}
|
||||
if (beanFactory != null) {
|
||||
for (Class<?> clazz : messageInterceptorClass) {
|
||||
messageInterceptorList
|
||||
.add((WeixinMessageInterceptor) beanFactory
|
||||
.getBean(clazz));
|
||||
}
|
||||
} else {
|
||||
for (Class<?> clazz : messageInterceptorClass) {
|
||||
try {
|
||||
messageInterceptorList
|
||||
.add((WeixinMessageInterceptor) clazz
|
||||
.newInstance());
|
||||
} catch (InstantiationException ex) {
|
||||
throw new WeixinException(clazz.getName()
|
||||
+ " Is it an abstract class?", ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new WeixinException(clazz.getName()
|
||||
+ " Is the constructor accessible?", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.messageInterceptorList != null
|
||||
&& !this.messageInterceptorList.isEmpty()) {
|
||||
this.messageInterceptors = this.messageInterceptorList
|
||||
.toArray(new WeixinMessageInterceptor[this.messageInterceptorList
|
||||
.size()]);
|
||||
}
|
||||
}
|
||||
return this.messageInterceptors;
|
||||
}
|
||||
|
||||
public void setMessageHandlerList(
|
||||
List<WeixinMessageHandler> messageHandlerList) {
|
||||
this.messageHandlerList = messageHandlerList;
|
||||
}
|
||||
|
||||
public void setMessageInterceptorList(
|
||||
List<WeixinMessageInterceptor> messageInterceptorList) {
|
||||
this.messageInterceptorList = messageInterceptorList;
|
||||
}
|
||||
|
||||
public String[] getMessageHandlerPackages() {
|
||||
return messageHandlerPackages;
|
||||
}
|
||||
|
||||
public String[] getMessageInterceptorPackages() {
|
||||
return messageInterceptorPackages;
|
||||
}
|
||||
|
||||
public void setMessageHandlerPackages(String... messageHandlerPackages) {
|
||||
this.messageHandlerPackages = messageHandlerPackages;
|
||||
}
|
||||
|
||||
public void setMessageInterceptorPackages(
|
||||
String... messageInterceptorPackages) {
|
||||
this.messageInterceptorPackages = messageInterceptorPackages;
|
||||
}
|
||||
|
||||
public BeanFactory getBeanFactory() {
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.foxinmy.weixin4j.exception;
|
||||
|
||||
/**
|
||||
* 调用微信接口抛出的异常
|
||||
*
|
||||
* @className WeixinException
|
||||
* @author jy.hu
|
||||
* @date 2014年4月10日
|
||||
* @since JDK 1.7
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.foxinmy.weixin4j.handler;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.response.BlankResponse;
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
|
||||
public class BlankMessageHandler extends MessageHandlerAdapter {
|
||||
|
||||
public final static BlankMessageHandler global = new BlankMessageHandler();
|
||||
|
||||
private BlankMessageHandler() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message)
|
||||
throws WeixinException {
|
||||
return BlankResponse.global;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.foxinmy.weixin4j.handler;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.response.TextResponse;
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
|
||||
public class DebugMessageHandler extends MessageHandlerAdapter {
|
||||
|
||||
public static final DebugMessageHandler global = new DebugMessageHandler();
|
||||
|
||||
private DebugMessageHandler(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message)
|
||||
throws WeixinException {
|
||||
return new TextResponse(String.format("%s,%s", request,
|
||||
message));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.foxinmy.weixin4j.handler;
|
||||
|
||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
|
||||
public abstract class MessageHandlerAdapter implements WeixinMessageHandler {
|
||||
|
||||
@Override
|
||||
public boolean canHandle(WeixinRequest request, WeixinMessage message) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package com.foxinmy.weixin4j.handler;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
|
||||
/**
|
||||
* 微信消息处理器
|
||||
*
|
||||
* @className WeixinMessageHandler
|
||||
* @author jy
|
||||
* @date 2015年5月7日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public interface WeixinMessageHandler {
|
||||
|
||||
/**
|
||||
* 能否处理请求
|
||||
*
|
||||
* @param request
|
||||
* 微信请求
|
||||
* @param message
|
||||
* 微信消息
|
||||
* @return
|
||||
*/
|
||||
public boolean canHandle(WeixinRequest request, WeixinMessage message);
|
||||
|
||||
/**
|
||||
* 处理请求
|
||||
*
|
||||
* @param request
|
||||
* 微信请求
|
||||
* @param message
|
||||
* 微信消息
|
||||
* @return
|
||||
*/
|
||||
public WeixinResponse doHandle(WeixinRequest request, WeixinMessage message)
|
||||
throws WeixinException;
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
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.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
|
||||
public abstract class MessageInterceptorAdapter implements
|
||||
WeixinMessageInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(ChannelHandlerContext ctx, WeixinRequest request,
|
||||
WeixinMessage message, WeixinMessageHandler handler)
|
||||
throws WeixinException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postHandle(ChannelHandlerContext ctx, WeixinRequest request,
|
||||
WeixinResponse response, WeixinMessage message,
|
||||
WeixinMessageHandler handler) throws WeixinException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(ChannelHandlerContext ctx,
|
||||
WeixinRequest request, WeixinMessage message,
|
||||
WeixinMessageHandler handler, WeixinException exception)
|
||||
throws WeixinException {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
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.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
|
||||
/**
|
||||
* 微信消息拦截器
|
||||
*
|
||||
* @className WeixinMessageInterceptor
|
||||
* @author jy
|
||||
* @date 2015年5月7日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public interface WeixinMessageInterceptor {
|
||||
|
||||
/**
|
||||
* 执行handler前
|
||||
*
|
||||
* @param ctx
|
||||
* 通道环境
|
||||
* @param request
|
||||
* 微信请求
|
||||
* @param message
|
||||
* 微信消息
|
||||
* @param handler
|
||||
* 消息处理器
|
||||
* @return
|
||||
* @throws WeixinException
|
||||
*/
|
||||
boolean preHandle(ChannelHandlerContext ctx, WeixinRequest request,
|
||||
WeixinMessage message, WeixinMessageHandler handler)
|
||||
throws WeixinException;
|
||||
|
||||
/**
|
||||
* 执行handler后
|
||||
*
|
||||
* @param ctx
|
||||
* 通道环境
|
||||
* @param request
|
||||
* 微信请求
|
||||
* @param response
|
||||
* 微信响应
|
||||
* @param message
|
||||
* 微信消息
|
||||
* @param handler
|
||||
* 消息处理器
|
||||
* @throws WeixinException
|
||||
*/
|
||||
void postHandle(ChannelHandlerContext ctx, WeixinRequest request,
|
||||
WeixinResponse response, WeixinMessage message,
|
||||
WeixinMessageHandler handler) throws WeixinException;
|
||||
|
||||
/**
|
||||
* 全部执行后
|
||||
*
|
||||
* @param ctx
|
||||
* 通道环境
|
||||
* @param request
|
||||
* 微信请求
|
||||
* @param message
|
||||
* 微信消息
|
||||
* @param handler
|
||||
* 消息处理器
|
||||
* @param exception
|
||||
* 执行异常
|
||||
* @throws WeixinException
|
||||
*/
|
||||
void afterCompletion(ChannelHandlerContext ctx, WeixinRequest request,
|
||||
WeixinMessage message, WeixinMessageHandler handler,
|
||||
WeixinException exception) throws WeixinException;
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
package com.foxinmy.weixin4j.message;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
|
||||
public class ResponseMessage implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2822272237544040042L;
|
||||
|
||||
private WeixinMessage message;
|
||||
private WeixinResponse response;
|
||||
|
||||
public ResponseMessage(WeixinMessage message, WeixinResponse response) {
|
||||
this.message = message;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public WeixinMessage getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public WeixinResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public String toXml() {
|
||||
StringBuilder xmlContent = new StringBuilder();
|
||||
xmlContent.append("<xml>");
|
||||
xmlContent.append(String.format(
|
||||
"<ToUserName><![CDATA[%s]]></ToUserName>",
|
||||
message.getFromUserName()));
|
||||
xmlContent.append(String.format(
|
||||
"<FromUserName><![CDATA[%s]]></FromUserName>",
|
||||
message.getToUserName()));
|
||||
xmlContent.append(String.format(
|
||||
"<CreateTime><![CDATA[%d]]></CreateTime>",
|
||||
System.currentTimeMillis() / 1000l));
|
||||
xmlContent.append(String.format("<MsgType><![CDATA[%s]]></MsgType>",
|
||||
response.getMsgType()));
|
||||
xmlContent.append(response.toContent());
|
||||
xmlContent.append("</xml>");
|
||||
return xmlContent.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResponseMessage [message=" + message + ", response=" + response
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.foxinmy.weixin4j.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.foxinmy.weixin4j.response.BlankResponse;
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
|
||||
public class ResponseMessage implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2822272237544040042L;
|
||||
|
||||
private WeixinMessage message;
|
||||
private WeixinResponse response;
|
||||
|
||||
public ResponseMessage(WeixinMessage message, WeixinResponse response) {
|
||||
this.message = message;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public WeixinMessage getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public WeixinResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public String toXml() {
|
||||
if (response instanceof BlankResponse) {
|
||||
return response.toContent();
|
||||
} else {
|
||||
StringBuilder xmlContent = new StringBuilder();
|
||||
xmlContent.append("<xml>");
|
||||
xmlContent.append(String.format(
|
||||
"<ToUserName><![CDATA[%s]]></ToUserName>",
|
||||
message.getFromUserName()));
|
||||
xmlContent.append(String.format(
|
||||
"<FromUserName><![CDATA[%s]]></FromUserName>",
|
||||
message.getToUserName()));
|
||||
xmlContent.append(String.format(
|
||||
"<CreateTime><![CDATA[%d]]></CreateTime>",
|
||||
System.currentTimeMillis() / 1000l));
|
||||
xmlContent
|
||||
.append(String.format("<MsgType><![CDATA[%s]]></MsgType>",
|
||||
response.getMsgType()));
|
||||
xmlContent.append(response.toContent());
|
||||
xmlContent.append("</xml>");
|
||||
return xmlContent.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResponseMessage [message=" + message + ", response=" + response
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.foxinmy.weixin4j.message;
|
||||
package com.foxinmy.weixin4j.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@ -36,10 +36,6 @@ public class WeixinMessage implements Serializable {
|
||||
*
|
||||
*/
|
||||
private String msgType;
|
||||
/**
|
||||
* 消息ID 可用于排重
|
||||
*/
|
||||
private long msgId;
|
||||
|
||||
public String getToUserName() {
|
||||
return toUserName;
|
||||
@ -77,28 +73,12 @@ public class WeixinMessage implements Serializable {
|
||||
this.msgType = msgType;
|
||||
}
|
||||
|
||||
public long getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
@XmlElement(name = "MsgId")
|
||||
public void setMsgId(long msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof WeixinMessage) {
|
||||
return ((WeixinMessage) obj).getMsgId() == msgId
|
||||
&& ((WeixinMessage) obj).getCreateTime() == createTime;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//@Override
|
||||
//public abstract boolean equals(Object obj) ;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return " toUserName=" + toUserName + ", fromUserName=" + fromUserName
|
||||
+ ", createTime=" + createTime + ", msgType=" + msgType
|
||||
+ ", msgId=" + msgId;
|
||||
+ ", createTime=" + createTime + ", msgType=" + msgType;
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,19 @@
|
||||
package com.foxinmy.weixin4j.message;
|
||||
package com.foxinmy.weixin4j.request;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.foxinmy.weixin4j.type.EncryptType;
|
||||
|
||||
/**
|
||||
* 请求消息
|
||||
* 微信请求
|
||||
*
|
||||
* @className HttpWeixinMessage
|
||||
* @className WeixinRequest
|
||||
* @author jy
|
||||
* @date 2015年3月29日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class HttpWeixinMessage implements Serializable {
|
||||
public class WeixinRequest implements Serializable, Cloneable {
|
||||
|
||||
private static final long serialVersionUID = -9157395300510879866L;
|
||||
|
||||
@ -133,7 +133,7 @@ public class HttpWeixinMessage implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HttpWeixinMessage [encryptContent=" + encryptContent
|
||||
return "WeixinRequest [encryptContent=" + encryptContent
|
||||
+ ", encryptType=" + encryptType + ", echoStr=" + echoStr
|
||||
+ ", timeStamp=" + timeStamp + ", nonce=" + nonce
|
||||
+ ", signature=" + signature + ", originalContent="
|
||||
@ -1,17 +1,29 @@
|
||||
package com.foxinmy.weixin4j.response;
|
||||
|
||||
/**
|
||||
* 空白回复
|
||||
*
|
||||
* @className BlankResponse
|
||||
* @author jy
|
||||
* @date 2015年5月7日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class BlankResponse implements WeixinResponse {
|
||||
|
||||
public static final BlankResponse global = new BlankResponse();
|
||||
|
||||
private BlankResponse(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMsgType() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return "blank";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toContent() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return "success";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
package com.foxinmy.weixin4j.response;
|
||||
|
||||
public class DebugResponse implements WeixinResponse {
|
||||
|
||||
@Override
|
||||
public String getMsgType() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toContent() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,6 @@ package com.foxinmy.weixin4j.response;
|
||||
* @see NewsResponse
|
||||
* @see TransferCustomerResponse
|
||||
* @see BlankResponse
|
||||
* @see DebugResponse
|
||||
* @see <a href=
|
||||
* "http://mp.weixin.qq.com/wiki/9/2c15b20a16019ae613d413e30cac8ea1.html">订阅号、服务号的被动响应消息</a>
|
||||
* @see <a
|
||||
|
||||
@ -1,125 +0,0 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.message.HttpWeixinMessage;
|
||||
import com.foxinmy.weixin4j.message.ResponseMessage;
|
||||
import com.foxinmy.weixin4j.message.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.model.AesToken;
|
||||
import com.foxinmy.weixin4j.response.TextResponse;
|
||||
import com.foxinmy.weixin4j.type.EncryptType;
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
import com.foxinmy.weixin4j.util.HttpUtil;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
|
||||
/**
|
||||
* 微信被动消息处理类
|
||||
*
|
||||
* @className WeixinServerHandler
|
||||
* @author jy
|
||||
* @date 2014年11月16日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class WeixinMessageHandler extends
|
||||
SimpleChannelInboundHandler<HttpWeixinMessage> {
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final AesToken aesToken;
|
||||
private final JAXBContext jaxbContext;
|
||||
|
||||
public WeixinMessageHandler(AesToken aesToken) throws JAXBException {
|
||||
this.aesToken = aesToken;
|
||||
jaxbContext = JAXBContext.newInstance(WeixinMessage.class);
|
||||
}
|
||||
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
log.error("catch the exception:{}", cause.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx,
|
||||
HttpWeixinMessage httpMessage) throws Exception {
|
||||
log.info("\n=================message in=================\n{}",
|
||||
httpMessage);
|
||||
if (httpMessage.getMethod().equals(HttpMethod.GET.name())) {
|
||||
if (MessageUtil.signature(aesToken.getToken(),
|
||||
httpMessage.getTimeStamp(), httpMessage.getNonce()).equals(
|
||||
httpMessage.getSignature())) {
|
||||
ctx.write(HttpUtil.createWeixinMessageResponse(
|
||||
httpMessage.getEchoStr(), Consts.CONTENTTYPE$TEXT_PLAIN));
|
||||
return;
|
||||
}
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.FORBIDDEN));
|
||||
return;
|
||||
} else if (httpMessage.getMethod().equals(HttpMethod.POST.name())) {
|
||||
if (!MessageUtil.signature(aesToken.getToken(),
|
||||
httpMessage.getTimeStamp(), httpMessage.getNonce()).equals(
|
||||
httpMessage.getSignature())) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.FORBIDDEN));
|
||||
return;
|
||||
}
|
||||
if (httpMessage.getEncryptType() == EncryptType.AES) {
|
||||
if (!MessageUtil.signature(aesToken.getToken(),
|
||||
httpMessage.getTimeStamp(), httpMessage.getNonce(),
|
||||
httpMessage.getEncryptContent()).equals(
|
||||
httpMessage.getMsgSignature())) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.FORBIDDEN));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.METHOD_NOT_ALLOWED));
|
||||
return;
|
||||
}
|
||||
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
|
||||
WeixinMessage weixinMessage = (WeixinMessage) jaxbUnmarshaller
|
||||
.unmarshal(new ByteArrayInputStream(httpMessage
|
||||
.getOriginalContent().getBytes(Consts.UTF_8)));
|
||||
/*
|
||||
* if (action == null) { ctx.write(new
|
||||
* DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
* HttpResponseStatus.NOT_FOUND)); return; }
|
||||
*/
|
||||
ResponseMessage response = new ResponseMessage(weixinMessage,
|
||||
new TextResponse("Hello World!"));
|
||||
log.info("\n=================message out=================\n{}",
|
||||
response);
|
||||
/*
|
||||
* if (response == null) {
|
||||
* ctx.write(HttpUtil.createWeixinMessageResponse(Consts.SUCCESS,
|
||||
* Consts.CONTENTTYPE$TEXT_PLAIN)); return; }
|
||||
*/
|
||||
if (httpMessage.getEncryptType() == EncryptType.RAW) {
|
||||
ctx.write(HttpUtil.createWeixinMessageResponse(response.toXml(),
|
||||
Consts.CONTENTTYPE$APPLICATION_XML));
|
||||
} else {
|
||||
ctx.write(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
package com.foxinmy.weixin4j.socket;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
@ -11,8 +11,9 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.message.HttpWeixinMessage;
|
||||
import com.foxinmy.weixin4j.model.AesToken;
|
||||
import com.foxinmy.weixin4j.bean.AesToken;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.type.EncryptType;
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
@ -40,9 +41,9 @@ public class WeixinMessageDecoder extends
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req,
|
||||
List<Object> out) throws RuntimeException {
|
||||
List<Object> out) throws WeixinException {
|
||||
String content = req.content().toString(Consts.UTF_8);
|
||||
HttpWeixinMessage message = new HttpWeixinMessage();
|
||||
WeixinRequest message = new WeixinRequest();
|
||||
message.setMethod(req.getMethod().name());
|
||||
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(),
|
||||
true);
|
||||
@ -73,7 +74,7 @@ public class WeixinMessageDecoder extends
|
||||
if (!content.isEmpty()) {
|
||||
if (message.getEncryptType() == EncryptType.AES) {
|
||||
if (aesToken.getAesKey() == null || aesToken.getAppid() == null) {
|
||||
throw new IllegalArgumentException(
|
||||
throw new WeixinException(
|
||||
"AESEncodingKey or AppId not be null in AES mode");
|
||||
}
|
||||
String encryptContent = EncryptMessageHandler.parser(content);
|
||||
@ -1,4 +1,4 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
package com.foxinmy.weixin4j.socket;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
@ -8,8 +8,9 @@ import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.message.ResponseMessage;
|
||||
import com.foxinmy.weixin4j.model.AesToken;
|
||||
import com.foxinmy.weixin4j.bean.AesToken;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.request.ResponseMessage;
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
import com.foxinmy.weixin4j.util.HttpUtil;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
@ -36,9 +37,9 @@ public class WeixinMessageEncoder extends
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, ResponseMessage response,
|
||||
List<Object> out) throws RuntimeException {
|
||||
List<Object> out) throws WeixinException {
|
||||
if (aesToken.getAesKey() == null || aesToken.getAppid() == null) {
|
||||
throw new IllegalArgumentException(
|
||||
throw new WeixinException(
|
||||
"AESEncodingKey or AppId not be null in AES mode");
|
||||
}
|
||||
String nonce = RandomUtil.generateString(32);
|
||||
@ -0,0 +1,122 @@
|
||||
package com.foxinmy.weixin4j.socket;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.bean.AesToken;
|
||||
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.type.EncryptType;
|
||||
import com.foxinmy.weixin4j.util.ClassUtil;
|
||||
import com.foxinmy.weixin4j.util.Consts;
|
||||
import com.foxinmy.weixin4j.util.HttpUtil;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
|
||||
/**
|
||||
* 微信被动消息处理类
|
||||
*
|
||||
* @className WeixinMessageHandler
|
||||
* @author jy
|
||||
* @date 2014年11月16日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class WeixinMessageHandler extends
|
||||
SimpleChannelInboundHandler<WeixinRequest> {
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final AesToken aesToken;
|
||||
private final WeixinMessageDispatcher messageDispatcher;
|
||||
private final JAXBContext jaxbContext;
|
||||
|
||||
public WeixinMessageHandler(AesToken aesToken,
|
||||
WeixinMessageDispatcher messageDispatcher) throws WeixinException {
|
||||
this.aesToken = aesToken;
|
||||
this.messageDispatcher = messageDispatcher;
|
||||
try {
|
||||
jaxbContext = JAXBContext.newInstance(WeixinMessage.class);
|
||||
} catch (JAXBException e) {
|
||||
throw new WeixinException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
ctx.close();
|
||||
log.error("catch the exception:{}", cause.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, WeixinRequest request)
|
||||
throws WeixinException {
|
||||
log.info("\n=================message request=================\n{}",
|
||||
request);
|
||||
if (request.getMethod().equals(HttpMethod.GET.name())) {
|
||||
if (MessageUtil.signature(aesToken.getToken(),
|
||||
request.getTimeStamp(), request.getNonce()).equals(
|
||||
request.getSignature())) {
|
||||
ctx.write(HttpUtil.createWeixinMessageResponse(
|
||||
request.getEchoStr(), Consts.CONTENTTYPE$TEXT_PLAIN));
|
||||
return;
|
||||
}
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.FORBIDDEN));
|
||||
return;
|
||||
} else if (request.getMethod().equals(HttpMethod.POST.name())) {
|
||||
if (!MessageUtil.signature(aesToken.getToken(),
|
||||
request.getTimeStamp(), request.getNonce()).equals(
|
||||
request.getSignature())) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.FORBIDDEN));
|
||||
return;
|
||||
}
|
||||
if (request.getEncryptType() == EncryptType.AES) {
|
||||
if (!MessageUtil.signature(aesToken.getToken(),
|
||||
request.getTimeStamp(), request.getNonce(),
|
||||
request.getEncryptContent()).equals(
|
||||
request.getMsgSignature())) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.FORBIDDEN));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.METHOD_NOT_ALLOWED));
|
||||
return;
|
||||
}
|
||||
WeixinMessage weixinMessage = null;
|
||||
try {
|
||||
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
|
||||
weixinMessage = (WeixinMessage) jaxbUnmarshaller
|
||||
.unmarshal(new ByteArrayInputStream(request
|
||||
.getOriginalContent().getBytes(Consts.UTF_8)));
|
||||
} catch (JAXBException e) {
|
||||
throw new WeixinException(e);
|
||||
}
|
||||
final WeixinRequest cloneRequest = (WeixinRequest) ClassUtil
|
||||
.deepClone(request);
|
||||
final WeixinMessage cloneMessage = (WeixinMessage) ClassUtil
|
||||
.deepClone(weixinMessage);
|
||||
messageDispatcher.doDispatch(ctx, cloneRequest, cloneMessage);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
package com.foxinmy.weixin4j.socket;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
@ -6,26 +6,31 @@ import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
|
||||
import com.foxinmy.weixin4j.model.AesToken;
|
||||
import com.foxinmy.weixin4j.bean.AesToken;
|
||||
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
|
||||
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private final AesToken aesToken;
|
||||
private final WeixinMessageDispatcher messageDispatcher;
|
||||
|
||||
public WeixinServerInitializer(AesToken aesToken) {
|
||||
public WeixinServerInitializer(AesToken aesToken,
|
||||
WeixinMessageDispatcher messageDispatcher) throws WeixinException {
|
||||
if (aesToken == null) {
|
||||
throw new IllegalArgumentException("AesToken not be null.");
|
||||
throw new WeixinException("AesToken not be null.");
|
||||
}
|
||||
this.aesToken = aesToken;
|
||||
this.messageDispatcher = messageDispatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel channel) throws Exception {
|
||||
protected void initChannel(SocketChannel channel) throws WeixinException {
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||
pipeline.addLast(new WeixinMessageDecoder(aesToken));
|
||||
pipeline.addLast(new WeixinMessageEncoder(aesToken));
|
||||
pipeline.addLast(new WeixinMessageHandler(aesToken));
|
||||
pipeline.addLast(new WeixinMessageHandler(aesToken, messageDispatcher));
|
||||
}
|
||||
}
|
||||
@ -8,8 +8,17 @@ import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
import com.foxinmy.weixin4j.model.AesToken;
|
||||
import com.foxinmy.weixin4j.server.WeixinServerInitializer;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import com.foxinmy.weixin4j.bean.AesToken;
|
||||
import com.foxinmy.weixin4j.bean.BeanFactory;
|
||||
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||
import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor;
|
||||
import com.foxinmy.weixin4j.socket.WeixinServerInitializer;
|
||||
|
||||
/**
|
||||
* 微信netty服务启动程序
|
||||
@ -34,6 +43,25 @@ public final class WeixinServerBootstrap {
|
||||
* 默认服务启动端口
|
||||
*/
|
||||
public final static int DEFAULT_SERVERPORT = 30000;
|
||||
/**
|
||||
* 消息分发器
|
||||
*/
|
||||
private WeixinMessageDispatcher messageDispatcher;
|
||||
|
||||
/**
|
||||
* 消息处理器
|
||||
*/
|
||||
private List<WeixinMessageHandler> messageHandlerList;
|
||||
/**
|
||||
* 消息拦截器
|
||||
*/
|
||||
private List<WeixinMessageInterceptor> messageInterceptorList;
|
||||
|
||||
/**
|
||||
* aes and token
|
||||
*
|
||||
*/
|
||||
private final AesToken aesToken;
|
||||
|
||||
/**
|
||||
* 明文模式
|
||||
@ -41,9 +69,8 @@ public final class WeixinServerBootstrap {
|
||||
* @param token
|
||||
* 开发者填写的token
|
||||
*/
|
||||
public static void startup(String token) {
|
||||
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, DEFAULT_SERVERPORT,
|
||||
new AesToken(token));
|
||||
public WeixinServerBootstrap(String token) {
|
||||
this(new AesToken(token));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,13 +83,41 @@ public final class WeixinServerBootstrap {
|
||||
* @param aesKey
|
||||
* 消息加密的密钥
|
||||
*/
|
||||
public static void startup(String appid, String token, String aesKey) {
|
||||
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, DEFAULT_SERVERPORT,
|
||||
new AesToken(appid, token, aesKey));
|
||||
public WeixinServerBootstrap(String appid, String token, String aesKey) {
|
||||
this(new AesToken(appid, token, aesKey));
|
||||
}
|
||||
|
||||
public static void startup(int bossThreads, int workerThreads,
|
||||
int serverPort, AesToken aesToken) {
|
||||
public WeixinServerBootstrap(AesToken aesToken) {
|
||||
this.aesToken = aesToken;
|
||||
this.messageHandlerList = new LinkedList<WeixinMessageHandler>();
|
||||
this.messageInterceptorList = new LinkedList<WeixinMessageInterceptor>();
|
||||
this.messageDispatcher = new WeixinMessageDispatcher();
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认端口启动服务
|
||||
*
|
||||
*/
|
||||
public void startup() throws WeixinException {
|
||||
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, DEFAULT_SERVERPORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受参数启动服务
|
||||
*
|
||||
* @param bossThreads
|
||||
* boss线程数,一般设置为cpu的核数
|
||||
* @param workerThreads
|
||||
* worker线程数
|
||||
* @param serverPort
|
||||
* 服务启动端口
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public void startup(int bossThreads, int workerThreads, int serverPort)
|
||||
throws WeixinException {
|
||||
messageDispatcher.setMessageHandlerList(messageHandlerList);
|
||||
messageDispatcher.setMessageInterceptorList(messageInterceptorList);
|
||||
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(bossThreads);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
|
||||
try {
|
||||
@ -71,15 +126,74 @@ public final class WeixinServerBootstrap {
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler())
|
||||
.childHandler(new WeixinServerInitializer(aesToken));
|
||||
.childHandler(
|
||||
new WeixinServerInitializer(aesToken,
|
||||
messageDispatcher));
|
||||
Channel ch = b.bind(serverPort).sync().channel();
|
||||
System.err.println("weixin server startup OK:" + serverPort);
|
||||
ch.closeFuture().sync();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (WeixinException e) {
|
||||
throw e;
|
||||
} catch (InterruptedException e) {
|
||||
throw new WeixinException(e);
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WeixinServerBootstrap pushMessageHandler(
|
||||
WeixinMessageHandler... messageHandler) {
|
||||
messageHandlerList.addAll(Arrays.asList(messageHandler));
|
||||
return this;
|
||||
}
|
||||
|
||||
public WeixinServerBootstrap pushFirstMessageHandler(
|
||||
WeixinMessageHandler messageHandler) {
|
||||
messageHandlerList.add(0, messageHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WeixinServerBootstrap pushLastMessageHandler(
|
||||
WeixinMessageHandler messageHandler) {
|
||||
messageHandlerList.add(messageHandlerList.size(), messageHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WeixinServerBootstrap pushMessageInterceptor(
|
||||
WeixinMessageInterceptor... messageInterceptor) {
|
||||
messageInterceptorList.addAll(Arrays.asList(messageInterceptor));
|
||||
return this;
|
||||
}
|
||||
|
||||
public WeixinServerBootstrap pushFirstMessageInterceptor(
|
||||
WeixinMessageInterceptor messageInterceptor) {
|
||||
messageInterceptorList.add(0, messageInterceptor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WeixinServerBootstrap pushLastMessageInterceptor(
|
||||
WeixinMessageInterceptor messageInterceptor) {
|
||||
messageInterceptorList.add(messageInterceptorList.size(),
|
||||
messageInterceptor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WeixinServerBootstrap messageHandlerPackagesToScan(
|
||||
String... messageHandlerPackages) {
|
||||
messageDispatcher.setMessageHandlerPackages(messageHandlerPackages);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WeixinServerBootstrap messageInterceptorPackagesToScan(
|
||||
String... messageInterceptorPackages) {
|
||||
messageDispatcher
|
||||
.setMessageInterceptorPackages(messageInterceptorPackages);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WeixinServerBootstrap resolveBeanFactory(BeanFactory beanFactory) {
|
||||
messageDispatcher.setBeanFactory(beanFactory);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@ -5,29 +5,27 @@ import io.netty.buffer.Unpooled;
|
||||
|
||||
public final class Base64 {
|
||||
|
||||
private static byte[] byteBuf2Array(ByteBuf byteBuf) {
|
||||
if (byteBuf.hasArray()) {
|
||||
return byteBuf.array();
|
||||
} else {
|
||||
byte[] desArray = new byte[byteBuf.readableBytes()];
|
||||
byteBuf.readBytes(desArray);
|
||||
return desArray;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64(final String content) {
|
||||
byte[] data = StringUtil.getBytesUtf8(content);
|
||||
ByteBuf des = io.netty.handler.codec.base64.Base64.decode(Unpooled
|
||||
.copiedBuffer(data));
|
||||
if (des.hasArray()) {
|
||||
return des.array();
|
||||
} else {
|
||||
byte[] desArray = new byte[des.readableBytes()];
|
||||
des.readBytes(desArray);
|
||||
return desArray;
|
||||
}
|
||||
return byteBuf2Array(des);
|
||||
}
|
||||
|
||||
public static byte[] encodeBase64(final byte[] bytes) {
|
||||
ByteBuf des = io.netty.handler.codec.base64.Base64.encode(Unpooled
|
||||
.copiedBuffer(bytes));
|
||||
if (des.hasArray()) {
|
||||
return des.array();
|
||||
} else {
|
||||
byte[] desArray = new byte[des.readableBytes()];
|
||||
des.readBytes(desArray);
|
||||
return desArray;
|
||||
}
|
||||
return byteBuf2Array(des);
|
||||
}
|
||||
|
||||
public static String encodeBase64String(final byte[] bytes) {
|
||||
|
||||
@ -0,0 +1,174 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* 对class的获取
|
||||
*
|
||||
* @className ClassUtil
|
||||
* @author jy
|
||||
* @date 2014年10月31日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public final class ClassUtil {
|
||||
|
||||
/**
|
||||
* 获取某个包下所有的class信息
|
||||
*
|
||||
* @param packageName
|
||||
* 包名
|
||||
* @return
|
||||
*/
|
||||
public static List<Class<?>> getClasses(String packageName)
|
||||
throws RuntimeException {
|
||||
String packageFileName = packageName.replace(".", File.separator);
|
||||
URL fullPath = Thread.currentThread().getContextClassLoader()
|
||||
.getResource(packageFileName);
|
||||
String protocol = fullPath.getProtocol();
|
||||
if (protocol.equals(Consts.PROTOCOL_FILE)) {
|
||||
File dir = new File(fullPath.getPath());
|
||||
return findClassesByFile(dir, packageName);
|
||||
} else if (protocol.equals(Consts.PROTOCOL_JAR)) {
|
||||
try {
|
||||
return findClassesByJar(
|
||||
((JarURLConnection) fullPath.openConnection())
|
||||
.getJarFile(),
|
||||
packageFileName);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"unknow protocol:", protocol));
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录下所有的class对象
|
||||
*
|
||||
* @param dir
|
||||
* 文件目录
|
||||
* @param packageName
|
||||
* 包的全限类名
|
||||
* @return
|
||||
*/
|
||||
private static List<Class<?>> findClassesByFile(File dir, String packageName) {
|
||||
List<Class<?>> classes = new LinkedList<Class<?>>();
|
||||
File[] files = dir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File file, String name) {
|
||||
return file.isDirectory() || file.getName().endsWith(".class");
|
||||
}
|
||||
});
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
classes.addAll(findClassesByFile(file,
|
||||
packageName + "." + file.getName()));
|
||||
} else {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(packageName + "."
|
||||
+ file.getName().replace(".class", ""));
|
||||
if (clazz.isInterface()) {
|
||||
continue;
|
||||
}
|
||||
classes.add(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描jar包下所有的class对象
|
||||
*
|
||||
* @param jar
|
||||
* jar包对象
|
||||
* @param packageName
|
||||
* 包的全限类名
|
||||
* @return
|
||||
*/
|
||||
private static List<Class<?>> findClassesByJar(JarFile jar,
|
||||
String packageName) {
|
||||
List<Class<?>> classes = new LinkedList<Class<?>>();
|
||||
Enumeration<JarEntry> jarEntries = jar.entries();
|
||||
while (jarEntries.hasMoreElements()) {
|
||||
JarEntry jarEntry = jarEntries.nextElement();
|
||||
if (jarEntry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
String entryName = jarEntry.getName();
|
||||
if (!entryName.startsWith(packageName)) {
|
||||
continue;
|
||||
}
|
||||
if (!entryName.endsWith(".class")) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Class<?> clazz = Class.forName(entryName.replaceAll("/", ".")
|
||||
.replace(".class", ""));
|
||||
if (clazz.isInterface()) {
|
||||
continue;
|
||||
}
|
||||
classes.add(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
;
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
public static Object deepClone(Object obj) throws RuntimeException {
|
||||
ByteArrayOutputStream bos = null;
|
||||
ObjectOutputStream oos = null;
|
||||
ByteArrayInputStream bis = null;
|
||||
ObjectInputStream ois = null;
|
||||
try {
|
||||
bos = new ByteArrayOutputStream();
|
||||
oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(obj);
|
||||
bis = new ByteArrayInputStream(bos.toByteArray());
|
||||
ois = new ObjectInputStream(bis);
|
||||
return ois.readObject();
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
try {
|
||||
if (bos != null) {
|
||||
bos.close();
|
||||
}
|
||||
if (oos != null) {
|
||||
oos.close();
|
||||
}
|
||||
if (bis != null) {
|
||||
bis.close();
|
||||
}
|
||||
if (ois != null) {
|
||||
ois.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
;// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.err
|
||||
.println(getClasses(com.foxinmy.weixin4j.handler.WeixinMessageHandler.class
|
||||
.getPackage().getName()));
|
||||
}
|
||||
}
|
||||
@ -26,6 +26,8 @@ public final class Consts {
|
||||
public static final String MD5 = "MD5";
|
||||
public static final String SHA = "SHA";
|
||||
public static final String SHA1 = "SHA-1";
|
||||
public static final String PROTOCOL_FILE = "file";
|
||||
public static final String PROTOCOL_JAR = "jar";
|
||||
public static final String CONTENTTYPE$APPLICATION_XML = "application/xml";
|
||||
public static final String CONTENTTYPE$TEXT_PLAIN = "text/plain";
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
|
||||
/**
|
||||
* 消息工具类
|
||||
*
|
||||
@ -49,7 +51,7 @@ public final class MessageUtil {
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public static String aesEncrypt(String appId, String encodingAesKey,
|
||||
String xmlContent) throws RuntimeException {
|
||||
String xmlContent) throws WeixinException {
|
||||
byte[] randomBytes = StringUtil.getBytesUtf8(RandomUtil
|
||||
.generateString(16));
|
||||
byte[] xmlBytes = StringUtil.getBytesUtf8(xmlContent);
|
||||
@ -92,7 +94,7 @@ public final class MessageUtil {
|
||||
// 使用BASE64对加密后的字符串进行编码
|
||||
return Base64.encodeBase64String(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("-40006,AES加密失败:", e);
|
||||
throw new WeixinException("-40006", "AES加密失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +110,7 @@ public final class MessageUtil {
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public static String aesDecrypt(String appId, String encodingAesKey,
|
||||
String encryptContent) throws RuntimeException {
|
||||
String encryptContent) throws WeixinException {
|
||||
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");
|
||||
byte[] original;
|
||||
try {
|
||||
@ -123,7 +125,7 @@ public final class MessageUtil {
|
||||
// 解密
|
||||
original = cipher.doFinal(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("-40007,AES解密失败", e);
|
||||
throw new WeixinException("-40007", "AES解密失败" + e.getMessage());
|
||||
}
|
||||
String xmlContent, fromAppId;
|
||||
try {
|
||||
@ -141,11 +143,13 @@ public final class MessageUtil {
|
||||
fromAppId = StringUtil.newStringUtf8(Arrays.copyOfRange(bytes,
|
||||
20 + xmlLength, bytes.length));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("-40008,公众平台发送的xml不合法", e);
|
||||
throw new WeixinException("-40008", "公众平台发送的xml不合法"
|
||||
+ e.getMessage());
|
||||
}
|
||||
// 校验appId是否一致
|
||||
if (!fromAppId.trim().equals(appId)) {
|
||||
throw new RuntimeException("-40005,校验AppID失败");
|
||||
throw new WeixinException("-40005", "校验AppID失败,expect " + appId
|
||||
+ ",but actual is " + fromAppId);
|
||||
}
|
||||
return xmlContent;
|
||||
}
|
||||
|
||||
@ -0,0 +1,228 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* @title 反射工具类
|
||||
* @description 提供对类,字段的反射调用
|
||||
* @author jy.hu , 2012-10-26
|
||||
*/
|
||||
public class ReflectionUtil {
|
||||
|
||||
/**
|
||||
* 获取包包名
|
||||
*
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
public static String getPackageName(Object obj) {
|
||||
return obj.getClass().getPackage().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段的泛型参数类型
|
||||
*
|
||||
* @param obj
|
||||
* @param fieldName
|
||||
* @return
|
||||
*/
|
||||
public static Class<?> getFieldGenericType(Object obj, String fieldName) {
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
Type type = field.getGenericType();
|
||||
if (type instanceof ParameterizedType) {
|
||||
return (Class<?>) ((ParameterizedType) type)
|
||||
.getActualTypeArguments()[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用方法
|
||||
*
|
||||
* @param object
|
||||
* 对象
|
||||
*
|
||||
* @param propertyName
|
||||
* 属性名称
|
||||
*/
|
||||
public static Object invokeMethod(Object object, String propertyName) {
|
||||
try {
|
||||
Method getterMethod = object.getClass().getMethod(propertyName);
|
||||
return getterMethod.invoke(object);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object invokeMethod(Object object, String propertyName,
|
||||
Object... args) {
|
||||
try {
|
||||
Method getterMethod = object.getClass().getMethod(propertyName);
|
||||
return getterMethod.invoke(object, args);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用Getter方法
|
||||
*
|
||||
* @param object
|
||||
* 对象
|
||||
*
|
||||
* @param propertyName
|
||||
* 属性名称
|
||||
* @throws SecurityException
|
||||
* @throws NoSuchMethodException
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalArgumentException
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
public static Object invokeGetterMethod(Object object, String propertyName)
|
||||
throws Exception {
|
||||
String getterMethodName = null;
|
||||
Method getterMethod = null;
|
||||
String propertyNa = null;
|
||||
if (propertyName.contains(".")) {
|
||||
propertyNa = StringUtil.substringBefore(propertyName, ".");
|
||||
getterMethodName = "get" + StringUtil.capitalize(propertyNa);
|
||||
getterMethod = object.getClass().getMethod(getterMethodName);
|
||||
return invokeGetterMethod(getterMethod.invoke(object),
|
||||
StringUtil.substringAfter(propertyName, "."));
|
||||
} else {
|
||||
getterMethodName = "get" + StringUtil.capitalize(propertyName);
|
||||
getterMethod = object.getClass().getMethod(getterMethodName);
|
||||
return getterMethod.invoke(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用Setter方法
|
||||
*
|
||||
* @param object
|
||||
* 对象
|
||||
*
|
||||
* @param propertyName
|
||||
* 属性名称
|
||||
*
|
||||
* @param propertyValue
|
||||
* 属性值
|
||||
*/
|
||||
public static void invokeSetterMethod(Object object, String propertyName,
|
||||
Object propertyValue) {
|
||||
Class<?> setterMethodClass = propertyValue.getClass();
|
||||
invokeSetterMethod(object, propertyName, propertyValue,
|
||||
setterMethodClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用Setter方法
|
||||
*
|
||||
* @param object
|
||||
* 对象
|
||||
*
|
||||
* @param propertyName
|
||||
* 属性名称
|
||||
*
|
||||
* @param propertyValue
|
||||
* 属性值
|
||||
*
|
||||
* @param setterMethodClass
|
||||
* 参数类型
|
||||
*/
|
||||
public static void invokeSetterMethod(Object object, String propertyName,
|
||||
Object propertyValue, Class<?> setterMethodClass) {
|
||||
String setterMethodName = "set" + StringUtil.capitalize(propertyName);
|
||||
try {
|
||||
Method setterMethod = object.getClass().getMethod(setterMethodName,
|
||||
setterMethodClass);
|
||||
setterMethod.invoke(object, propertyValue);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象属性值,无视private/protected/getter
|
||||
*
|
||||
* @param object
|
||||
* 对象
|
||||
*
|
||||
* @param fieldName
|
||||
* 属性名称
|
||||
*/
|
||||
public static Object getFieldValue(Object object, String fieldName) {
|
||||
Field field = getAccessibleField(object, fieldName);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Could not find field "
|
||||
+ fieldName);
|
||||
}
|
||||
Object result = null;
|
||||
try {
|
||||
result = field.get(object);
|
||||
} catch (IllegalAccessException e) {
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对象属性值,无视private/protected/setter
|
||||
*
|
||||
* @param object
|
||||
* 对象
|
||||
*
|
||||
* @param fieldName
|
||||
* 属性名称
|
||||
*/
|
||||
public static void setFieldValue(Object object, String fieldName,
|
||||
Object value) {
|
||||
Field field = getAccessibleField(object, fieldName);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("Could not find field "
|
||||
+ fieldName);
|
||||
}
|
||||
try {
|
||||
field.set(object, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 获取字段的类型
|
||||
public static String getFieldType(Object object, String fieldName) {
|
||||
Field field = getAccessibleField(object, fieldName);
|
||||
return field.getType().getSimpleName();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static Field getAccessibleField(final Object object,
|
||||
final String fieldName) {
|
||||
for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass
|
||||
.getSuperclass()) {
|
||||
try {
|
||||
Field field = superClass.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void makeAccessible(Constructor<?> ctor) {
|
||||
if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))
|
||||
&& !ctor.isAccessible()) {
|
||||
ctor.setAccessible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,9 @@ import java.nio.charset.Charset;
|
||||
|
||||
public final class StringUtil {
|
||||
|
||||
public static final String EMPTY = "";
|
||||
public static final int INDEX_NOT_FOUND = -1;
|
||||
|
||||
private static byte[] getBytes(final String content, final Charset charset) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
@ -22,4 +25,66 @@ public final class StringUtil {
|
||||
public static String newStringUtf8(final byte[] bytes) {
|
||||
return newString(bytes, Consts.UTF_8);
|
||||
}
|
||||
|
||||
public static boolean isEmpty(final CharSequence cs) {
|
||||
return cs == null || cs.length() == 0;
|
||||
}
|
||||
|
||||
public static boolean isBlank(final CharSequence cs) {
|
||||
int strLen;
|
||||
if (cs == null || (strLen = cs.length()) == 0) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < strLen; i++) {
|
||||
if (Character.isWhitespace(cs.charAt(i)) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String capitalize(final String str) {
|
||||
int strLen;
|
||||
if (str == null || (strLen = str.length()) == 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
char firstChar = str.charAt(0);
|
||||
if (Character.isTitleCase(firstChar)) {
|
||||
// already capitalized
|
||||
return str;
|
||||
}
|
||||
return new StringBuilder(strLen)
|
||||
.append(Character.toTitleCase(firstChar))
|
||||
.append(str.substring(1)).toString();
|
||||
}
|
||||
|
||||
public static String substringBefore(final String str,
|
||||
final String separator) {
|
||||
if (isEmpty(str) || separator == null) {
|
||||
return str;
|
||||
}
|
||||
if (separator.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
final int pos = str.indexOf(separator);
|
||||
if (pos == INDEX_NOT_FOUND) {
|
||||
return str;
|
||||
}
|
||||
return str.substring(0, pos);
|
||||
}
|
||||
|
||||
public static String substringAfter(final String str, final String separator) {
|
||||
if (isEmpty(str)) {
|
||||
return str;
|
||||
}
|
||||
if (separator == null) {
|
||||
return EMPTY;
|
||||
}
|
||||
final int pos = str.indexOf(separator);
|
||||
if (pos == INDEX_NOT_FOUND) {
|
||||
return EMPTY;
|
||||
}
|
||||
return str.substring(pos + separator.length());
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ JAVA_HOME="/usr/local/java/"
|
||||
RUNNING_USER=root
|
||||
|
||||
#Run home
|
||||
APP_HOME="/usr/local/weixin/weixin-mp-server"
|
||||
APP_HOME="/usr/local/weixin/weixin-server"
|
||||
|
||||
#main class
|
||||
APP_MAINCLASS=com.foxinmy.weixin4j.startup.WeixinServerBootstrap
|
||||
|
||||
@ -5,9 +5,23 @@ import java.io.IOException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AesMsgTest extends MessagePush {
|
||||
/**
|
||||
* 消息验证测试
|
||||
*
|
||||
* @className EncryptMessageTest
|
||||
* @author jy
|
||||
* @date 2015年5月8日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class EncryptMessageTest extends MessagePush {
|
||||
StringBuilder xmlSb = new StringBuilder();
|
||||
|
||||
/**
|
||||
* 验证是否服务器配置是否正常
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testValidate() throws IOException {
|
||||
String echostr = "2143641595566077626";
|
||||
@ -18,6 +32,11 @@ public class AesMsgTest extends MessagePush {
|
||||
Assert.assertEquals(echostr, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证明文模式
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testType1() throws IOException {
|
||||
String para = "?signature=6dd806a20a314723e78bc58742a1b98a7adfd151×tamp=1415979366&nonce=1865915590";
|
||||
@ -35,6 +54,11 @@ public class AesMsgTest extends MessagePush {
|
||||
System.err.println(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证兼容模式
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testType2() throws IOException {
|
||||
String para = "/?signature=b4343f727d6e9b1072f6f72d28b0d0cf38986dce×tamp=1430926116&nonce=1801492986&encrypt_type=aes&msg_signature=af1868ffe3058db89643c6c546e49cd40d717ac9";
|
||||
@ -53,6 +77,11 @@ public class AesMsgTest extends MessagePush {
|
||||
System.err.println(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密文模式
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testType3() throws IOException {
|
||||
String para = "?signature=ad05f836772d1cbba1ff2edb7be4b9c9fb3a43d5×tamp=1415980001&nonce=1803216865&encrypt_type=aes&msg_signature=c0d38e9ca00548f7142627ec2908a4fe8481025e";
|
||||
@ -12,7 +12,7 @@ import org.junit.Test;
|
||||
* @date 2014年3月24日
|
||||
* @since JDK 1.7
|
||||
*/
|
||||
public class EventMsgTest extends MessagePush {
|
||||
public class EventMessageRequestTest extends MessagePush {
|
||||
|
||||
private StringBuilder xmlSb = new StringBuilder();
|
||||
|
||||
@ -14,6 +14,15 @@ import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
/**
|
||||
* 发送消息请求到服务器
|
||||
*
|
||||
* @className MessagePush
|
||||
* @author jy
|
||||
* @date 2015年5月8日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class MessagePush {
|
||||
|
||||
private final String server = "http://localhost:30000";
|
||||
|
||||
@ -12,7 +12,7 @@ import org.junit.Test;
|
||||
* @date 2014年3月24日
|
||||
* @since JDK 1.7
|
||||
*/
|
||||
public class InMsgTest extends MessagePush {
|
||||
public class MessageRequestTest extends MessagePush {
|
||||
|
||||
private StringBuilder xmlSb = new StringBuilder();
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
package com.foxinmy.weixin4j.server.test;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.handler.DebugMessageHandler;
|
||||
import com.foxinmy.weixin4j.handler.WeixinMessageHandler;
|
||||
import com.foxinmy.weixin4j.request.WeixinMessage;
|
||||
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||
import com.foxinmy.weixin4j.response.TextResponse;
|
||||
import com.foxinmy.weixin4j.response.WeixinResponse;
|
||||
import com.foxinmy.weixin4j.startup.WeixinServerBootstrap;
|
||||
|
||||
/**
|
||||
* 服务启动
|
||||
*
|
||||
* @className ServerStarup
|
||||
* @author jy
|
||||
* @date 2015年5月7日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class MessageServerStarup {
|
||||
|
||||
final String appid = "appid";
|
||||
final String token = "token";
|
||||
final String aesKey = "aeskey";
|
||||
|
||||
/**
|
||||
* 明文模式
|
||||
*
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public void test1() throws WeixinException {
|
||||
// 所有请求都回复调试的文本消息
|
||||
new WeixinServerBootstrap(token).pushMessageHandler(
|
||||
DebugMessageHandler.global).startup();
|
||||
}
|
||||
|
||||
/**
|
||||
* 密文模式
|
||||
*
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public void test2() throws WeixinException {
|
||||
// 所有请求都回复调试的文本消息
|
||||
new WeixinServerBootstrap(appid, token, aesKey).pushMessageHandler(
|
||||
DebugMessageHandler.global).startup();
|
||||
}
|
||||
|
||||
/**
|
||||
* 针对特定消息回复
|
||||
*
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public void test3() throws WeixinException {
|
||||
// 回复文本消息
|
||||
WeixinMessageHandler messageHandler = new WeixinMessageHandler() {
|
||||
@Override
|
||||
public WeixinResponse doHandle(WeixinRequest request,
|
||||
WeixinMessage message) throws WeixinException {
|
||||
return new TextResponse("HelloWorld!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(WeixinRequest request,
|
||||
WeixinMessage message) {
|
||||
return message.getMsgType().equals("text");
|
||||
}
|
||||
};
|
||||
// 当消息类型为文本(text)时回复「HelloWorld」, 否则回复调试消息
|
||||
new WeixinServerBootstrap(appid, token, aesKey).pushMessageHandler(
|
||||
messageHandler, DebugMessageHandler.global).startup();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws WeixinException {
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
package com.foxinmy.weixin4j.server.test;
|
||||
|
||||
import com.foxinmy.weixin4j.startup.WeixinServerBootstrap;
|
||||
|
||||
/**
|
||||
* 服务启动
|
||||
*
|
||||
* @className ServerStarup
|
||||
* @author jy
|
||||
* @date 2015年5月7日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class ServerStarup {
|
||||
public static void main(String[] args) {
|
||||
String appid = "";
|
||||
String token = "";
|
||||
String aesKey = "";
|
||||
WeixinServerBootstrap.startup(appid, token, aesKey);
|
||||
//WeixinServerBootstrap.startup(token);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user