diff --git a/CHANGE.md b/CHANGE.md index e35d6293..c4d6149f 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -267,4 +267,12 @@ * 2015-04-30 - + 新增`media_id`和`view_limited`两种菜单类型 \ No newline at end of file + + **weixin4j-mp**: 新增`media_id`和`view_limited`两种菜单类型 + +* 2015-05-07 + + +**weixin4j-server**: 完成基本骨架 + +* 2015-05-08 + + +**weixin4j-server**: 完成消息分发器、消息处理器、消息拦截器的骨架 \ No newline at end of file diff --git a/weixin4j-server/CHANGE.md b/weixin4j-server/CHANGE.md index f7744eef..2eb108bb 100644 --- a/weixin4j-server/CHANGE.md +++ b/weixin4j-server/CHANGE.md @@ -20,4 +20,8 @@ * 2015-05-07 - + 完成基本骨架 \ No newline at end of file + + 完成基本骨架 + +* 2015-05-08 + + + 完成消息分发器、消息处理器、消息拦截器的骨架 \ No newline at end of file diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/model/AesToken.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/bean/AesToken.java similarity index 95% rename from weixin4j-server/src/main/java/com/foxinmy/weixin4j/model/AesToken.java rename to weixin4j-server/src/main/java/com/foxinmy/weixin4j/bean/AesToken.java index 262dd3fe..2de56014 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/model/AesToken.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/bean/AesToken.java @@ -1,4 +1,4 @@ -package com.foxinmy.weixin4j.model; +package com.foxinmy.weixin4j.bean; import java.io.Serializable; diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/bean/BeanFactory.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/bean/BeanFactory.java new file mode 100644 index 00000000..146628e4 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/bean/BeanFactory.java @@ -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 getBean(Class clazz) throws WeixinException; + + T getBean(String name, Class clazz) throws WeixinException; +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/dispatcher/MessageHandlerExecutor.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/dispatcher/MessageHandlerExecutor.java new file mode 100644 index 00000000..76674941 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/dispatcher/MessageHandlerExecutor.java @@ -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); + } + } + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/dispatcher/WeixinMessageDispatcher.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/dispatcher/WeixinMessageDispatcher.java new file mode 100644 index 00000000..a70fd2fd --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/dispatcher/WeixinMessageDispatcher.java @@ -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 messageHandlerList; + private WeixinMessageHandler[] messageHandlers; + /** + * 消息处理器所在的包 + */ + private String[] messageHandlerPackages; + + /** + * 消息拦截器 + */ + private List 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> messageHandlerClass = new LinkedList>(); + 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> messageInterceptorClass = new LinkedList>(); + 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 messageHandlerList) { + this.messageHandlerList = messageHandlerList; + } + + public void setMessageInterceptorList( + List 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; + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java new file mode 100644 index 00000000..d138fb76 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java @@ -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; + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/BlankMessageHandler.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/BlankMessageHandler.java new file mode 100644 index 00000000..59bba354 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/BlankMessageHandler.java @@ -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; + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/DebugMessageHandler.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/DebugMessageHandler.java new file mode 100644 index 00000000..25410587 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/DebugMessageHandler.java @@ -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)); + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/MessageHandlerAdapter.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/MessageHandlerAdapter.java new file mode 100644 index 00000000..73b4baed --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/MessageHandlerAdapter.java @@ -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; + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/WeixinMessageHandler.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/WeixinMessageHandler.java new file mode 100644 index 00000000..49e2edfc --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/handler/WeixinMessageHandler.java @@ -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; +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/interceptor/MessageInterceptorAdapter.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/interceptor/MessageInterceptorAdapter.java new file mode 100644 index 00000000..2fd758e2 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/interceptor/MessageInterceptorAdapter.java @@ -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 { + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/interceptor/WeixinMessageInterceptor.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/interceptor/WeixinMessageInterceptor.java new file mode 100644 index 00000000..b81f654f --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/interceptor/WeixinMessageInterceptor.java @@ -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; +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/message/ResponseMessage.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/message/ResponseMessage.java deleted file mode 100644 index 1d674927..00000000 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/message/ResponseMessage.java +++ /dev/null @@ -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(""); - xmlContent.append(String.format( - "", - message.getFromUserName())); - xmlContent.append(String.format( - "", - message.getToUserName())); - xmlContent.append(String.format( - "", - System.currentTimeMillis() / 1000l)); - xmlContent.append(String.format("", - response.getMsgType())); - xmlContent.append(response.toContent()); - xmlContent.append(""); - return xmlContent.toString(); - } - - @Override - public String toString() { - return "ResponseMessage [message=" + message + ", response=" + response - + "]"; - } -} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/request/ResponseMessage.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/request/ResponseMessage.java new file mode 100644 index 00000000..291940e2 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/request/ResponseMessage.java @@ -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(""); + xmlContent.append(String.format( + "", + message.getFromUserName())); + xmlContent.append(String.format( + "", + message.getToUserName())); + xmlContent.append(String.format( + "", + System.currentTimeMillis() / 1000l)); + xmlContent + .append(String.format("", + response.getMsgType())); + xmlContent.append(response.toContent()); + xmlContent.append(""); + return xmlContent.toString(); + } + } + + @Override + public String toString() { + return "ResponseMessage [message=" + message + ", response=" + response + + "]"; + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/message/WeixinMessage.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/request/WeixinMessage.java similarity index 69% rename from weixin4j-server/src/main/java/com/foxinmy/weixin4j/message/WeixinMessage.java rename to weixin4j-server/src/main/java/com/foxinmy/weixin4j/request/WeixinMessage.java index 6071a6be..33ccbaba 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/message/WeixinMessage.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/request/WeixinMessage.java @@ -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; } } diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/message/HttpWeixinMessage.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/request/WeixinRequest.java similarity index 92% rename from weixin4j-server/src/main/java/com/foxinmy/weixin4j/message/HttpWeixinMessage.java rename to weixin4j-server/src/main/java/com/foxinmy/weixin4j/request/WeixinRequest.java index c35dcd38..b236f2ce 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/message/HttpWeixinMessage.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/request/WeixinRequest.java @@ -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=" diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/BlankResponse.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/BlankResponse.java index f172bacc..8d159e50 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/BlankResponse.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/BlankResponse.java @@ -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"; } - } diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/DebugResponse.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/DebugResponse.java deleted file mode 100644 index 3cc3d89f..00000000 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/DebugResponse.java +++ /dev/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; - } -} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/WeixinResponse.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/WeixinResponse.java index 20b72c53..189496b7 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/WeixinResponse.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/response/WeixinResponse.java @@ -15,7 +15,6 @@ package com.foxinmy.weixin4j.response; * @see NewsResponse * @see TransferCustomerResponse * @see BlankResponse - * @see DebugResponse * @see 订阅号、服务号的被动响应消息 * @see { - 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); - } - } -} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/README.md b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/README.md similarity index 100% rename from weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/README.md rename to weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/README.md diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/WeixinMessageDecoder.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinMessageDecoder.java similarity index 90% rename from weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/WeixinMessageDecoder.java rename to weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinMessageDecoder.java index 9366faf1..e626e8c2 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/WeixinMessageDecoder.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinMessageDecoder.java @@ -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 out) throws RuntimeException { + List 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); diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/WeixinMessageEncoder.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinMessageEncoder.java similarity index 88% rename from weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/WeixinMessageEncoder.java rename to weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinMessageEncoder.java index fd9777b1..c8e046b2 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/WeixinMessageEncoder.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinMessageEncoder.java @@ -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 out) throws RuntimeException { + List 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); diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinMessageHandler.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinMessageHandler.java new file mode 100644 index 00000000..2e928056 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinMessageHandler.java @@ -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 { + 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); + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/WeixinServerInitializer.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinServerInitializer.java similarity index 52% rename from weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/WeixinServerInitializer.java rename to weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinServerInitializer.java index 30b69db0..803f5321 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/server/WeixinServerInitializer.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/socket/WeixinServerInitializer.java @@ -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 { 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)); } } diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/startup/WeixinServerBootstrap.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/startup/WeixinServerBootstrap.java index 702e2d9f..6695b868 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/startup/WeixinServerBootstrap.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/startup/WeixinServerBootstrap.java @@ -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 messageHandlerList; + /** + * 消息拦截器 + */ + private List 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(); + this.messageInterceptorList = new LinkedList(); + 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; + } +} \ No newline at end of file diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/Base64.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/Base64.java index 4d4cba7c..88421e93 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/Base64.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/Base64.java @@ -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) { diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/ClassUtil.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/ClassUtil.java new file mode 100644 index 00000000..2fba1be9 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/ClassUtil.java @@ -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> 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> findClassesByFile(File dir, String packageName) { + List> classes = new LinkedList>(); + 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> findClassesByJar(JarFile jar, + String packageName) { + List> classes = new LinkedList>(); + Enumeration 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())); + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/Consts.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/Consts.java index 07842477..8a034261 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/Consts.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/Consts.java @@ -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"; } diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/MessageUtil.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/MessageUtil.java index 8add46cc..99d636c3 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/MessageUtil.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/MessageUtil.java @@ -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; } diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/ReflectionUtil.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/ReflectionUtil.java new file mode 100644 index 00000000..8ca4c4f0 --- /dev/null +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/ReflectionUtil.java @@ -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); + } + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java index dd7f7e69..bf4ace71 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java @@ -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()); + } } diff --git a/weixin4j-server/src/main/startup.sh b/weixin4j-server/src/main/startup.sh index cd2f91fb..781e9bcf 100644 --- a/weixin4j-server/src/main/startup.sh +++ b/weixin4j-server/src/main/startup.sh @@ -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 diff --git a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/AesMsgTest.java b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/EncryptMessageTest.java similarity index 88% rename from weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/AesMsgTest.java rename to weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/EncryptMessageTest.java index 16d7f53e..c63e55dd 100644 --- a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/AesMsgTest.java +++ b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/EncryptMessageTest.java @@ -5,9 +5,23 @@ import java.io.IOException; import org.junit.Assert; import org.junit.Test; -public class AesMsgTest extends MessagePush { +/** + * 消息验证测试 + * + * @className EncryptMessageTest + * @author jy + * @date 2015年5月8日 + * @since JDK 1.7 + * @see + */ +public class EncryptMessageTest extends MessagePush { StringBuilder xmlSb = new StringBuilder(); + /** + * 验证是否服务器配置是否正常 + * + * @throws IOException + */ @Test public void testValidate() throws IOException { String echostr = "2143641595566077626"; @@ -18,6 +32,11 @@ public class AesMsgTest extends MessagePush { Assert.assertEquals(echostr, response); } + /** + * 验证明文模式 + * + * @throws IOException + */ @Test public void testType1() throws IOException { String para = "?signature=6dd806a20a314723e78bc58742a1b98a7adfd151×tamp=1415979366&nonce=1865915590"; @@ -35,6 +54,11 @@ public class AesMsgTest extends MessagePush { System.err.println(response); } + /** + * 验证兼容模式 + * + * @throws IOException + */ @Test public void testType2() throws IOException { String para = "/?signature=b4343f727d6e9b1072f6f72d28b0d0cf38986dce×tamp=1430926116&nonce=1801492986&encrypt_type=aes&msg_signature=af1868ffe3058db89643c6c546e49cd40d717ac9"; @@ -53,6 +77,11 @@ public class AesMsgTest extends MessagePush { System.err.println(response); } + /** + * 验证密文模式 + * + * @throws IOException + */ @Test public void testType3() throws IOException { String para = "?signature=ad05f836772d1cbba1ff2edb7be4b9c9fb3a43d5×tamp=1415980001&nonce=1803216865&encrypt_type=aes&msg_signature=c0d38e9ca00548f7142627ec2908a4fe8481025e"; diff --git a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/EventMsgTest.java b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/EventMessageRequestTest.java similarity index 96% rename from weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/EventMsgTest.java rename to weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/EventMessageRequestTest.java index d2bff32f..a0bd9bf1 100644 --- a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/EventMsgTest.java +++ b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/EventMessageRequestTest.java @@ -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(); diff --git a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessagePush.java b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessagePush.java index e8f4c416..c5b89219 100644 --- a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessagePush.java +++ b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessagePush.java @@ -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"; diff --git a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/InMsgTest.java b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessageRequestTest.java similarity index 96% rename from weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/InMsgTest.java rename to weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessageRequestTest.java index 52a4992d..d8a398b0 100644 --- a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/InMsgTest.java +++ b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessageRequestTest.java @@ -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(); diff --git a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessageServerStarup.java b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessageServerStarup.java new file mode 100644 index 00000000..28fc0ae5 --- /dev/null +++ b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/MessageServerStarup.java @@ -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 { + + } +} diff --git a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/ServerStarup.java b/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/ServerStarup.java deleted file mode 100644 index b616c2bf..00000000 --- a/weixin4j-server/src/test/java/com/foxinmy/weixin4j/server/test/ServerStarup.java +++ /dev/null @@ -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); - } -}