weixin4j-server:完成消息分发器、消息处理器、消息拦截器的骨架

This commit is contained in:
jinyu 2015-05-08 08:52:21 +08:00
parent 3f5bfe9e71
commit 48533e05fd
40 changed files with 1597 additions and 307 deletions

View File

@ -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**: 完成消息分发器、消息处理器、消息拦截器的骨架

View File

@ -20,4 +20,8 @@
* 2015-05-07
+ 完成基本骨架
+ 完成基本骨架
* 2015-05-08
+ 完成消息分发器、消息处理器、消息拦截器的骨架

View File

@ -1,4 +1,4 @@
package com.foxinmy.weixin4j.model;
package com.foxinmy.weixin4j.bean;
import java.io.Serializable;

View File

@ -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;
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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 {
}
}

View File

@ -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;
}

View File

@ -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
+ "]";
}
}

View File

@ -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
+ "]";
}
}

View File

@ -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;
}
}

View File

@ -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="

View File

@ -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";
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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()));
}
}

View File

@ -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";
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}

View File

@ -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

View File

@ -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&timestamp=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&timestamp=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&timestamp=1415980001&nonce=1803216865&encrypt_type=aes&msg_signature=c0d38e9ca00548f7142627ec2908a4fe8481025e";

View File

@ -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();

View File

@ -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";

View File

@ -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();

View File

@ -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 {
}
}

View File

@ -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);
}
}