weixin4j-server:完成消息分发器、消息处理器、消息拦截器的骨架
This commit is contained in:
parent
3f5bfe9e71
commit
48533e05fd
10
CHANGE.md
10
CHANGE.md
@ -267,4 +267,12 @@
|
|||||||
|
|
||||||
* 2015-04-30
|
* 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-07
|
||||||
|
|
||||||
+ 完成基本骨架
|
+ 完成基本骨架
|
||||||
|
|
||||||
|
* 2015-05-08
|
||||||
|
|
||||||
|
+ 完成消息分发器、消息处理器、消息拦截器的骨架
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.foxinmy.weixin4j.model;
|
package com.foxinmy.weixin4j.bean;
|
||||||
|
|
||||||
import java.io.Serializable;
|
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;
|
import java.io.Serializable;
|
||||||
|
|
||||||
@ -36,10 +36,6 @@ public class WeixinMessage implements Serializable {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private String msgType;
|
private String msgType;
|
||||||
/**
|
|
||||||
* 消息ID 可用于排重
|
|
||||||
*/
|
|
||||||
private long msgId;
|
|
||||||
|
|
||||||
public String getToUserName() {
|
public String getToUserName() {
|
||||||
return toUserName;
|
return toUserName;
|
||||||
@ -77,28 +73,12 @@ public class WeixinMessage implements Serializable {
|
|||||||
this.msgType = msgType;
|
this.msgType = msgType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMsgId() {
|
//@Override
|
||||||
return msgId;
|
//public abstract boolean equals(Object obj) ;
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return " toUserName=" + toUserName + ", fromUserName=" + fromUserName
|
return " toUserName=" + toUserName + ", fromUserName=" + fromUserName
|
||||||
+ ", createTime=" + createTime + ", msgType=" + msgType
|
+ ", createTime=" + createTime + ", msgType=" + msgType;
|
||||||
+ ", msgId=" + msgId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,19 +1,19 @@
|
|||||||
package com.foxinmy.weixin4j.message;
|
package com.foxinmy.weixin4j.request;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.type.EncryptType;
|
import com.foxinmy.weixin4j.type.EncryptType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求消息
|
* 微信请求
|
||||||
*
|
*
|
||||||
* @className HttpWeixinMessage
|
* @className WeixinRequest
|
||||||
* @author jy
|
* @author jy
|
||||||
* @date 2015年3月29日
|
* @date 2015年3月29日
|
||||||
* @since JDK 1.7
|
* @since JDK 1.7
|
||||||
* @see
|
* @see
|
||||||
*/
|
*/
|
||||||
public class HttpWeixinMessage implements Serializable {
|
public class WeixinRequest implements Serializable, Cloneable {
|
||||||
|
|
||||||
private static final long serialVersionUID = -9157395300510879866L;
|
private static final long serialVersionUID = -9157395300510879866L;
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ public class HttpWeixinMessage implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "HttpWeixinMessage [encryptContent=" + encryptContent
|
return "WeixinRequest [encryptContent=" + encryptContent
|
||||||
+ ", encryptType=" + encryptType + ", echoStr=" + echoStr
|
+ ", encryptType=" + encryptType + ", echoStr=" + echoStr
|
||||||
+ ", timeStamp=" + timeStamp + ", nonce=" + nonce
|
+ ", timeStamp=" + timeStamp + ", nonce=" + nonce
|
||||||
+ ", signature=" + signature + ", originalContent="
|
+ ", signature=" + signature + ", originalContent="
|
||||||
@ -1,17 +1,29 @@
|
|||||||
package com.foxinmy.weixin4j.response;
|
package com.foxinmy.weixin4j.response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空白回复
|
||||||
|
*
|
||||||
|
* @className BlankResponse
|
||||||
|
* @author jy
|
||||||
|
* @date 2015年5月7日
|
||||||
|
* @since JDK 1.7
|
||||||
|
* @see
|
||||||
|
*/
|
||||||
public class BlankResponse implements WeixinResponse {
|
public class BlankResponse implements WeixinResponse {
|
||||||
|
|
||||||
|
public static final BlankResponse global = new BlankResponse();
|
||||||
|
|
||||||
|
private BlankResponse(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMsgType() {
|
public String getMsgType() {
|
||||||
// TODO Auto-generated method stub
|
return "blank";
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toContent() {
|
public String toContent() {
|
||||||
// TODO Auto-generated method stub
|
return "success";
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 NewsResponse
|
||||||
* @see TransferCustomerResponse
|
* @see TransferCustomerResponse
|
||||||
* @see BlankResponse
|
* @see BlankResponse
|
||||||
* @see DebugResponse
|
|
||||||
* @see <a href=
|
* @see <a href=
|
||||||
* "http://mp.weixin.qq.com/wiki/9/2c15b20a16019ae613d413e30cac8ea1.html">订阅号、服务号的被动响应消息</a>
|
* "http://mp.weixin.qq.com/wiki/9/2c15b20a16019ae613d413e30cac8ea1.html">订阅号、服务号的被动响应消息</a>
|
||||||
* @see <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.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
@ -11,8 +11,9 @@ import java.util.Map;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.message.HttpWeixinMessage;
|
import com.foxinmy.weixin4j.bean.AesToken;
|
||||||
import com.foxinmy.weixin4j.model.AesToken;
|
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||||
|
import com.foxinmy.weixin4j.request.WeixinRequest;
|
||||||
import com.foxinmy.weixin4j.type.EncryptType;
|
import com.foxinmy.weixin4j.type.EncryptType;
|
||||||
import com.foxinmy.weixin4j.util.Consts;
|
import com.foxinmy.weixin4j.util.Consts;
|
||||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||||
@ -40,9 +41,9 @@ public class WeixinMessageDecoder extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req,
|
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);
|
String content = req.content().toString(Consts.UTF_8);
|
||||||
HttpWeixinMessage message = new HttpWeixinMessage();
|
WeixinRequest message = new WeixinRequest();
|
||||||
message.setMethod(req.getMethod().name());
|
message.setMethod(req.getMethod().name());
|
||||||
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(),
|
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(),
|
||||||
true);
|
true);
|
||||||
@ -73,7 +74,7 @@ public class WeixinMessageDecoder extends
|
|||||||
if (!content.isEmpty()) {
|
if (!content.isEmpty()) {
|
||||||
if (message.getEncryptType() == EncryptType.AES) {
|
if (message.getEncryptType() == EncryptType.AES) {
|
||||||
if (aesToken.getAesKey() == null || aesToken.getAppid() == null) {
|
if (aesToken.getAesKey() == null || aesToken.getAppid() == null) {
|
||||||
throw new IllegalArgumentException(
|
throw new WeixinException(
|
||||||
"AESEncodingKey or AppId not be null in AES mode");
|
"AESEncodingKey or AppId not be null in AES mode");
|
||||||
}
|
}
|
||||||
String encryptContent = EncryptMessageHandler.parser(content);
|
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.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||||
@ -8,8 +8,9 @@ import java.util.List;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.message.ResponseMessage;
|
import com.foxinmy.weixin4j.bean.AesToken;
|
||||||
import com.foxinmy.weixin4j.model.AesToken;
|
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||||
|
import com.foxinmy.weixin4j.request.ResponseMessage;
|
||||||
import com.foxinmy.weixin4j.util.Consts;
|
import com.foxinmy.weixin4j.util.Consts;
|
||||||
import com.foxinmy.weixin4j.util.HttpUtil;
|
import com.foxinmy.weixin4j.util.HttpUtil;
|
||||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||||
@ -36,9 +37,9 @@ public class WeixinMessageEncoder extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encode(ChannelHandlerContext ctx, ResponseMessage response,
|
protected void encode(ChannelHandlerContext ctx, ResponseMessage response,
|
||||||
List<Object> out) throws RuntimeException {
|
List<Object> out) throws WeixinException {
|
||||||
if (aesToken.getAesKey() == null || aesToken.getAppid() == null) {
|
if (aesToken.getAesKey() == null || aesToken.getAppid() == null) {
|
||||||
throw new IllegalArgumentException(
|
throw new WeixinException(
|
||||||
"AESEncodingKey or AppId not be null in AES mode");
|
"AESEncodingKey or AppId not be null in AES mode");
|
||||||
}
|
}
|
||||||
String nonce = RandomUtil.generateString(32);
|
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.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
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.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpServerCodec;
|
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> {
|
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||||
|
|
||||||
private final AesToken aesToken;
|
private final AesToken aesToken;
|
||||||
|
private final WeixinMessageDispatcher messageDispatcher;
|
||||||
|
|
||||||
public WeixinServerInitializer(AesToken aesToken) {
|
public WeixinServerInitializer(AesToken aesToken,
|
||||||
|
WeixinMessageDispatcher messageDispatcher) throws WeixinException {
|
||||||
if (aesToken == null) {
|
if (aesToken == null) {
|
||||||
throw new IllegalArgumentException("AesToken not be null.");
|
throw new WeixinException("AesToken not be null.");
|
||||||
}
|
}
|
||||||
this.aesToken = aesToken;
|
this.aesToken = aesToken;
|
||||||
|
this.messageDispatcher = messageDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(SocketChannel channel) throws Exception {
|
protected void initChannel(SocketChannel channel) throws WeixinException {
|
||||||
ChannelPipeline pipeline = channel.pipeline();
|
ChannelPipeline pipeline = channel.pipeline();
|
||||||
pipeline.addLast(new HttpServerCodec());
|
pipeline.addLast(new HttpServerCodec());
|
||||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||||
pipeline.addLast(new WeixinMessageDecoder(aesToken));
|
pipeline.addLast(new WeixinMessageDecoder(aesToken));
|
||||||
pipeline.addLast(new WeixinMessageEncoder(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.channel.socket.nio.NioServerSocketChannel;
|
||||||
import io.netty.handler.logging.LoggingHandler;
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.model.AesToken;
|
import java.util.Arrays;
|
||||||
import com.foxinmy.weixin4j.server.WeixinServerInitializer;
|
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服务启动程序
|
* 微信netty服务启动程序
|
||||||
@ -34,6 +43,25 @@ public final class WeixinServerBootstrap {
|
|||||||
* 默认服务启动端口
|
* 默认服务启动端口
|
||||||
*/
|
*/
|
||||||
public final static int DEFAULT_SERVERPORT = 30000;
|
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
|
* @param token
|
||||||
* 开发者填写的token
|
* 开发者填写的token
|
||||||
*/
|
*/
|
||||||
public static void startup(String token) {
|
public WeixinServerBootstrap(String token) {
|
||||||
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, DEFAULT_SERVERPORT,
|
this(new AesToken(token));
|
||||||
new AesToken(token));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,13 +83,41 @@ public final class WeixinServerBootstrap {
|
|||||||
* @param aesKey
|
* @param aesKey
|
||||||
* 消息加密的密钥
|
* 消息加密的密钥
|
||||||
*/
|
*/
|
||||||
public static void startup(String appid, String token, String aesKey) {
|
public WeixinServerBootstrap(String appid, String token, String aesKey) {
|
||||||
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, DEFAULT_SERVERPORT,
|
this(new AesToken(appid, token, aesKey));
|
||||||
new AesToken(appid, token, aesKey));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void startup(int bossThreads, int workerThreads,
|
public WeixinServerBootstrap(AesToken aesToken) {
|
||||||
int serverPort, 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 bossGroup = new NioEventLoopGroup(bossThreads);
|
||||||
EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
|
EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
|
||||||
try {
|
try {
|
||||||
@ -71,15 +126,74 @@ public final class WeixinServerBootstrap {
|
|||||||
b.group(bossGroup, workerGroup)
|
b.group(bossGroup, workerGroup)
|
||||||
.channel(NioServerSocketChannel.class)
|
.channel(NioServerSocketChannel.class)
|
||||||
.handler(new LoggingHandler())
|
.handler(new LoggingHandler())
|
||||||
.childHandler(new WeixinServerInitializer(aesToken));
|
.childHandler(
|
||||||
|
new WeixinServerInitializer(aesToken,
|
||||||
|
messageDispatcher));
|
||||||
Channel ch = b.bind(serverPort).sync().channel();
|
Channel ch = b.bind(serverPort).sync().channel();
|
||||||
System.err.println("weixin server startup OK:" + serverPort);
|
System.err.println("weixin server startup OK:" + serverPort);
|
||||||
ch.closeFuture().sync();
|
ch.closeFuture().sync();
|
||||||
} catch (Exception e) {
|
} catch (WeixinException e) {
|
||||||
e.printStackTrace();
|
throw e;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new WeixinException(e);
|
||||||
} finally {
|
} finally {
|
||||||
bossGroup.shutdownGracefully();
|
bossGroup.shutdownGracefully();
|
||||||
workerGroup.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 {
|
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) {
|
public static byte[] decodeBase64(final String content) {
|
||||||
byte[] data = StringUtil.getBytesUtf8(content);
|
byte[] data = StringUtil.getBytesUtf8(content);
|
||||||
ByteBuf des = io.netty.handler.codec.base64.Base64.decode(Unpooled
|
ByteBuf des = io.netty.handler.codec.base64.Base64.decode(Unpooled
|
||||||
.copiedBuffer(data));
|
.copiedBuffer(data));
|
||||||
if (des.hasArray()) {
|
return byteBuf2Array(des);
|
||||||
return des.array();
|
|
||||||
} else {
|
|
||||||
byte[] desArray = new byte[des.readableBytes()];
|
|
||||||
des.readBytes(desArray);
|
|
||||||
return desArray;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] encodeBase64(final byte[] bytes) {
|
public static byte[] encodeBase64(final byte[] bytes) {
|
||||||
ByteBuf des = io.netty.handler.codec.base64.Base64.encode(Unpooled
|
ByteBuf des = io.netty.handler.codec.base64.Base64.encode(Unpooled
|
||||||
.copiedBuffer(bytes));
|
.copiedBuffer(bytes));
|
||||||
if (des.hasArray()) {
|
return byteBuf2Array(des);
|
||||||
return des.array();
|
|
||||||
} else {
|
|
||||||
byte[] desArray = new byte[des.readableBytes()];
|
|
||||||
des.readBytes(desArray);
|
|
||||||
return desArray;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String encodeBase64String(final byte[] bytes) {
|
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 MD5 = "MD5";
|
||||||
public static final String SHA = "SHA";
|
public static final String SHA = "SHA";
|
||||||
public static final String SHA1 = "SHA-1";
|
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$APPLICATION_XML = "application/xml";
|
||||||
public static final String CONTENTTYPE$TEXT_PLAIN = "text/plain";
|
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.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息工具类
|
* 消息工具类
|
||||||
*
|
*
|
||||||
@ -49,7 +51,7 @@ public final class MessageUtil {
|
|||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public static String aesEncrypt(String appId, String encodingAesKey,
|
public static String aesEncrypt(String appId, String encodingAesKey,
|
||||||
String xmlContent) throws RuntimeException {
|
String xmlContent) throws WeixinException {
|
||||||
byte[] randomBytes = StringUtil.getBytesUtf8(RandomUtil
|
byte[] randomBytes = StringUtil.getBytesUtf8(RandomUtil
|
||||||
.generateString(16));
|
.generateString(16));
|
||||||
byte[] xmlBytes = StringUtil.getBytesUtf8(xmlContent);
|
byte[] xmlBytes = StringUtil.getBytesUtf8(xmlContent);
|
||||||
@ -92,7 +94,7 @@ public final class MessageUtil {
|
|||||||
// 使用BASE64对加密后的字符串进行编码
|
// 使用BASE64对加密后的字符串进行编码
|
||||||
return Base64.encodeBase64String(encrypted);
|
return Base64.encodeBase64String(encrypted);
|
||||||
} catch (Exception e) {
|
} 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
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public static String aesDecrypt(String appId, String encodingAesKey,
|
public static String aesDecrypt(String appId, String encodingAesKey,
|
||||||
String encryptContent) throws RuntimeException {
|
String encryptContent) throws WeixinException {
|
||||||
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");
|
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "=");
|
||||||
byte[] original;
|
byte[] original;
|
||||||
try {
|
try {
|
||||||
@ -123,7 +125,7 @@ public final class MessageUtil {
|
|||||||
// 解密
|
// 解密
|
||||||
original = cipher.doFinal(encrypted);
|
original = cipher.doFinal(encrypted);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("-40007,AES解密失败", e);
|
throw new WeixinException("-40007", "AES解密失败" + e.getMessage());
|
||||||
}
|
}
|
||||||
String xmlContent, fromAppId;
|
String xmlContent, fromAppId;
|
||||||
try {
|
try {
|
||||||
@ -141,11 +143,13 @@ public final class MessageUtil {
|
|||||||
fromAppId = StringUtil.newStringUtf8(Arrays.copyOfRange(bytes,
|
fromAppId = StringUtil.newStringUtf8(Arrays.copyOfRange(bytes,
|
||||||
20 + xmlLength, bytes.length));
|
20 + xmlLength, bytes.length));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("-40008,公众平台发送的xml不合法", e);
|
throw new WeixinException("-40008", "公众平台发送的xml不合法"
|
||||||
|
+ e.getMessage());
|
||||||
}
|
}
|
||||||
// 校验appId是否一致
|
// 校验appId是否一致
|
||||||
if (!fromAppId.trim().equals(appId)) {
|
if (!fromAppId.trim().equals(appId)) {
|
||||||
throw new RuntimeException("-40005,校验AppID失败");
|
throw new WeixinException("-40005", "校验AppID失败,expect " + appId
|
||||||
|
+ ",but actual is " + fromAppId);
|
||||||
}
|
}
|
||||||
return xmlContent;
|
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 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) {
|
private static byte[] getBytes(final String content, final Charset charset) {
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -22,4 +25,66 @@ public final class StringUtil {
|
|||||||
public static String newStringUtf8(final byte[] bytes) {
|
public static String newStringUtf8(final byte[] bytes) {
|
||||||
return newString(bytes, Consts.UTF_8);
|
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
|
RUNNING_USER=root
|
||||||
|
|
||||||
#Run home
|
#Run home
|
||||||
APP_HOME="/usr/local/weixin/weixin-mp-server"
|
APP_HOME="/usr/local/weixin/weixin-server"
|
||||||
|
|
||||||
#main class
|
#main class
|
||||||
APP_MAINCLASS=com.foxinmy.weixin4j.startup.WeixinServerBootstrap
|
APP_MAINCLASS=com.foxinmy.weixin4j.startup.WeixinServerBootstrap
|
||||||
|
|||||||
@ -5,9 +5,23 @@ import java.io.IOException;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
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();
|
StringBuilder xmlSb = new StringBuilder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证是否服务器配置是否正常
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testValidate() throws IOException {
|
public void testValidate() throws IOException {
|
||||||
String echostr = "2143641595566077626";
|
String echostr = "2143641595566077626";
|
||||||
@ -18,6 +32,11 @@ public class AesMsgTest extends MessagePush {
|
|||||||
Assert.assertEquals(echostr, response);
|
Assert.assertEquals(echostr, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证明文模式
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testType1() throws IOException {
|
public void testType1() throws IOException {
|
||||||
String para = "?signature=6dd806a20a314723e78bc58742a1b98a7adfd151×tamp=1415979366&nonce=1865915590";
|
String para = "?signature=6dd806a20a314723e78bc58742a1b98a7adfd151×tamp=1415979366&nonce=1865915590";
|
||||||
@ -35,6 +54,11 @@ public class AesMsgTest extends MessagePush {
|
|||||||
System.err.println(response);
|
System.err.println(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证兼容模式
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testType2() throws IOException {
|
public void testType2() throws IOException {
|
||||||
String para = "/?signature=b4343f727d6e9b1072f6f72d28b0d0cf38986dce×tamp=1430926116&nonce=1801492986&encrypt_type=aes&msg_signature=af1868ffe3058db89643c6c546e49cd40d717ac9";
|
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);
|
System.err.println(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证密文模式
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testType3() throws IOException {
|
public void testType3() throws IOException {
|
||||||
String para = "?signature=ad05f836772d1cbba1ff2edb7be4b9c9fb3a43d5×tamp=1415980001&nonce=1803216865&encrypt_type=aes&msg_signature=c0d38e9ca00548f7142627ec2908a4fe8481025e";
|
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日
|
* @date 2014年3月24日
|
||||||
* @since JDK 1.7
|
* @since JDK 1.7
|
||||||
*/
|
*/
|
||||||
public class EventMsgTest extends MessagePush {
|
public class EventMessageRequestTest extends MessagePush {
|
||||||
|
|
||||||
private StringBuilder xmlSb = new StringBuilder();
|
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.impl.client.DefaultHttpClient;
|
||||||
import org.apache.http.util.EntityUtils;
|
import org.apache.http.util.EntityUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息请求到服务器
|
||||||
|
*
|
||||||
|
* @className MessagePush
|
||||||
|
* @author jy
|
||||||
|
* @date 2015年5月8日
|
||||||
|
* @since JDK 1.7
|
||||||
|
* @see
|
||||||
|
*/
|
||||||
public class MessagePush {
|
public class MessagePush {
|
||||||
|
|
||||||
private final String server = "http://localhost:30000";
|
private final String server = "http://localhost:30000";
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import org.junit.Test;
|
|||||||
* @date 2014年3月24日
|
* @date 2014年3月24日
|
||||||
* @since JDK 1.7
|
* @since JDK 1.7
|
||||||
*/
|
*/
|
||||||
public class InMsgTest extends MessagePush {
|
public class MessageRequestTest extends MessagePush {
|
||||||
|
|
||||||
private StringBuilder xmlSb = new StringBuilder();
|
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