引入netty服务模块并支持消息分发

This commit is contained in:
jy.hu 2014-10-27 11:18:18 +08:00
parent 0cc69cff01
commit 083147c270
48 changed files with 956 additions and 0 deletions

View File

@ -0,0 +1,3 @@
config.properties包含了app跟接口url信息
WeixinProxy类为接口调用入口

View File

@ -0,0 +1,53 @@
package com.foxinmy.weixin4j.action;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.dom4j.DocumentException;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.util.MessageUtil;
import com.foxinmy.weixin4j.xml.XStream;
/**
* 继承的类需实现execute(M inMessage)
*
* @className AbstractAction
* @author jy
* @date 2014年10月12日
* @since JDK 1.7
* @see
*/
public abstract class AbstractAction<M extends BaseMessage> implements
WeixinAction {
public abstract String execute(M inMessage);
@SuppressWarnings("unchecked")
@Override
public String execute(String msg) throws DocumentException {
BaseMessage message = MessageUtil.xml2msg(msg);
if (message == null) {
Class<M> messageClass = getGenericType();
XStream xstream = new XStream();
xstream.ignoreUnknownElements();
xstream.autodetectAnnotations(true);
xstream.processAnnotations(messageClass);
xstream.alias("xml", messageClass);
return execute(xstream.fromXML(msg, messageClass));
}
return execute((M) message);
}
@SuppressWarnings("unchecked")
private Class<M> getGenericType() {
Class<M> clazz = null;
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType ptype = ((ParameterizedType) type);
Type[] args = ptype.getActualTypeArguments();
clazz = (Class<M>) args[0];
}
return clazz;
}
}

View File

@ -0,0 +1,26 @@
package com.foxinmy.weixin4j.action;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
/**
* 标注
* @className Action
* @author jy
* @date 2014年10月12日
* @since JDK 1.7
* @see
*/
public @interface Action {
MessageType msgType();
EventType[] eventType() default {};
}

View File

@ -0,0 +1,23 @@
package com.foxinmy.weixin4j.action;
import com.foxinmy.weixin4j.msg.BaseMessage;
/**
* 返回空白消息
*
* @className BlankAction
* @author jy.hu
* @date 2014年10月2日
* @since JDK 1.7
* @see
*/
public abstract class BlankAction<M extends BaseMessage> extends
AbstractAction<M> {
private final String BLANK = "";
@Override
public String execute(M message) {
return BLANK;
}
}

View File

@ -0,0 +1,23 @@
package com.foxinmy.weixin4j.action;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.msg.TextMessage;
/**
* 显示调试信息
*
* @className DebugAction
* @author jy
* @date 2014年10月8日
* @since JDK 1.7
* @see
*/
public abstract class DebugAction<M extends BaseMessage> extends
AbstractAction<M> {
@Override
public String execute(M message) {
BaseMessage response = new TextMessage(message.toString(), message);
return response.toXml();
}
}

View File

@ -0,0 +1,18 @@
package com.foxinmy.weixin4j.action;
import com.foxinmy.weixin4j.msg.in.ImageMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 图片消息响应
*
* @className ImageAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.in.ImageMessage
*/
@Action(msgType = MessageType.image)
public class ImageAction extends DebugAction<ImageMessage> {
}

View File

@ -0,0 +1,18 @@
package com.foxinmy.weixin4j.action;
import com.foxinmy.weixin4j.msg.in.LinkMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 链接消息响应
*
* @className LinkAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.in.LinkMessage
*/
@Action(msgType = MessageType.link)
public class LinkAction extends DebugAction<LinkMessage> {
}

View File

@ -0,0 +1,18 @@
package com.foxinmy.weixin4j.action;
import com.foxinmy.weixin4j.msg.in.LocationMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 地理位置响应
*
* @className LocationAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.in.LocationMessage
*/
@Action(msgType = MessageType.location)
public class LocationAction extends DebugAction<LocationMessage> {
}

View File

@ -0,0 +1 @@
普通消息类型对应的Action

View File

@ -0,0 +1,47 @@
package com.foxinmy.weixin4j.action;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.util.List;
import java.util.Map;
import org.dom4j.DocumentException;
import com.foxinmy.weixin4j.type.MessageType;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.util.MessageUtil;
/**
* 用于校验消息安全性
*
* @className SignatureAction
* @author jy
* @date 2014年10月24日
* @since JDK 1.7
* @see
*/
@Action(msgType = MessageType.signature)
public class SignatureAction implements WeixinAction {
@Override
public String execute(String uri) throws DocumentException {
String[] paths = uri.split("\\?");
if (paths == null || paths.length < 2) {
return "";
}
QueryStringDecoder queryDecoder = new QueryStringDecoder(paths[1],
false);
Map<String, List<String>> parameters = queryDecoder.parameters();
String echostr = parameters.containsKey("echostr") ? parameters.get(
"echostr").get(0) : null;
String timestamp = parameters.containsKey("timestamp") ? parameters
.get("timestamp").get(0) : null;
String nonce = parameters.containsKey("nonce") ? parameters
.get("nonce").get(0) : null;
String signature = parameters.containsKey("signature") ? parameters
.get("signature").get(0) : null;
String token = ConfigUtil.getValue("app_token");
return MessageUtil.signature(token, echostr, timestamp, nonce,
signature);
}
}

View File

@ -0,0 +1,22 @@
package com.foxinmy.weixin4j.action;
import com.foxinmy.weixin4j.msg.TextMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 文字消息响应
*
* @className TextAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.TextMessage
*/
@Action(msgType = MessageType.text)
public class TextAction extends AbstractAction<TextMessage> {
@Override
public String execute(TextMessage inMessage) {
return new TextMessage("Hello World!", inMessage).toXml();
}
}

View File

@ -0,0 +1,18 @@
package com.foxinmy.weixin4j.action;
import com.foxinmy.weixin4j.msg.in.VideoMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 视频消息响应
*
* @className VideoAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.in.VideoMessage
*/
@Action(msgType = MessageType.video)
public class VideoAction extends DebugAction<VideoMessage> {
}

View File

@ -0,0 +1,18 @@
package com.foxinmy.weixin4j.action;
import com.foxinmy.weixin4j.msg.in.VoiceMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 语音消息响应
*
* @className VoiceAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.in.VoiceMessage
*/
@Action(msgType = MessageType.voice)
public class VoiceAction extends DebugAction<VoiceMessage> {
}

View File

@ -0,0 +1,18 @@
package com.foxinmy.weixin4j.action;
import org.dom4j.DocumentException;
/**
* 消息处理接口
*
* @className Action
* @author jy.hu
* @date 2014年10月2日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.action.AbstractAction
* @see com.foxinmy.weixin4j.action.BlankAction
* @see com.foxinmy.weixin4j.action.DebugAction
*/
public interface WeixinAction {
public String execute(String msg) throws DocumentException;
}

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.LocationEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 上报地理位置后触发
*
* @className LocationAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.LocationEventMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.location })
public class LocationAction extends DebugAction<LocationEventMessage> {
}

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.MassEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 群发消息发送动作完成后触发
*
* @className MassSendAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.MassEventMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.massendjobfinish })
public class MassSendAction extends DebugAction<MassEventMessage> {
}

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 菜单点击click事件时触发
*
* @className MenuClickAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.click })
public class MenuClickAction extends DebugAction<MenuEventMessage> {
}

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.menu.MenuLocationEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 菜单发送地理位置时触发
*
* @className MenuLocationAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuLocationEventMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.location_select })
public class MenuLocationAction extends DebugAction<MenuLocationEventMessage> {
}

View File

@ -0,0 +1,23 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.menu.MenuPhotoEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 菜单发送图片时触发
*
* @className MenuPhotoAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuPhotoEventMessage
*/
@Action(msgType = MessageType.event, eventType = {
EventType.pic_photo_or_album, EventType.pic_sysphoto,
EventType.pic_weixin })
public class MenuPhotoAction extends DebugAction<MenuPhotoEventMessage> {
}

View File

@ -0,0 +1,22 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.menu.MenuScanEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 菜单扫描时触发
*
* @className MenuScanAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuScanEventMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.scancode_push,
EventType.scancode_waitmsg })
public class MenuScanAction extends DebugAction<MenuScanEventMessage> {
}

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 菜单点击view事件时触发
*
* @className MenuViewAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.view })
public class MenuViewAction extends DebugAction<MenuEventMessage> {
}

View File

@ -0,0 +1 @@
事件消息类型对应的Action

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.ScanEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 扫描事件时触发
*
* @className ScanAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.ScanEventMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.scan })
public class ScanAction extends DebugAction<ScanEventMessage> {
}

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.ScribeEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 关注时触发
*
* @className SubscribeAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.ScanEventMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.subscribe })
public class SubscribeAction extends DebugAction<ScribeEventMessage> {
}

View File

@ -0,0 +1,22 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 模板消息发送动作完成时触发
*
* @className TemplateSendAction
* @author jy
* @date 2014年10月10日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.templatesendjobfinish })
public class TemplateSendAction extends
DebugAction<TemplatesendjobfinishMessage> {
}

View File

@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.action.event;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.msg.event.ScribeEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 取消关注时触发
*
* @className UnsubscribeAction
* @author jy
* @date 2014年10月10日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.ScanEventMessage
*/
@Action(msgType = MessageType.event, eventType = { EventType.unsubscribe })
public class UnsubscribeAction extends DebugAction<ScribeEventMessage> {
}

View File

@ -0,0 +1 @@
API的实现

View File

@ -0,0 +1 @@
Token的实现

View File

@ -0,0 +1 @@
基于HttpClient封装的针对微信API的HttpRequest

View File

@ -0,0 +1 @@
事件消息

View File

@ -0,0 +1 @@
底部菜单消息

View File

@ -0,0 +1 @@
微信push过来的消息

View File

@ -0,0 +1 @@
不同的消息类型中的模型

View File

@ -0,0 +1 @@
客服消息

View File

@ -0,0 +1 @@
回复消息

View File

@ -0,0 +1 @@
微信服务netty启动类

View File

@ -0,0 +1,48 @@
package com.foxinmy.weixin4j.server;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.foxinmy.weixin4j.action.Action;
import com.foxinmy.weixin4j.action.WeixinAction;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
import com.foxinmy.weixin4j.util.ClassUtil;
public class WeixinActionMapping {
private final Map<String, WeixinAction> actionMap;
public WeixinActionMapping() {
actionMap = new HashMap<String, WeixinAction>();
Set<Class<?>> weixinActions = ClassUtil.getClasses(WeixinAction.class
.getPackage());
for (Class<?> clazz : weixinActions) {
Action action = clazz.getAnnotation(Action.class);
if (action == null) {
continue;
}
WeixinAction weixinAction = null;
try {
weixinAction = (WeixinAction) clazz.newInstance();
} catch (Exception e) {
continue;
}
MessageType msgType = action.msgType();
EventType[] eventTypes = action.eventType();
if (eventTypes != null && eventTypes.length > 0) {
for (EventType e : eventTypes) {
actionMap.put(
(msgType.name() + "_" + e.name()).toLowerCase(),
weixinAction);
}
continue;
}
actionMap.put(msgType.name().toLowerCase(), weixinAction);
}
}
public WeixinAction getAction(String key) {
return actionMap.get(key.toLowerCase());
}
}

View File

@ -0,0 +1,86 @@
package com.foxinmy.weixin4j.server;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import java.nio.charset.StandardCharsets;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.foxinmy.weixin4j.action.WeixinAction;
import com.foxinmy.weixin4j.util.MessageUtil;
public class WeixinServerHandler extends ChannelInboundHandlerAdapter {
private final Logger log = LoggerFactory.getLogger(getClass());
private final WeixinActionMapping weixinActionMapping;
public WeixinServerHandler(WeixinActionMapping weixinActionMapping) {
this.weixinActionMapping = weixinActionMapping;
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws DocumentException {
if (msg instanceof FullHttpRequest) {
FullHttpRequest req = (FullHttpRequest) msg;
if (HttpHeaders.is100ContinueExpected(req)) {
ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
return;
}
String xmlMsg = req.content().toString(StandardCharsets.UTF_8);
log.info("\n=================message in=================\n{}",
xmlMsg);
String key = MessageUtil.getMappingKey(xmlMsg);
WeixinAction action = weixinActionMapping.getAction(key);
if (action == null) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.NOT_FOUND));
return;
}
String content = action.execute(xmlMsg);
log.info("\n=================message out=================\n{}",
content);
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
OK, Unpooled.copiedBuffer(content, StandardCharsets.UTF_8));
response.headers().set(CONTENT_TYPE, "text/plain;charset=utf-8");
response.headers().set(CONTENT_LENGTH,
response.content().readableBytes());
if (!HttpHeaders.isKeepAlive(req)) {
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
} else {
response.headers().set(CONNECTION, Values.KEEP_ALIVE);
ctx.write(response);
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}

View File

@ -0,0 +1,24 @@
package com.foxinmy.weixin4j.server;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
private final WeixinActionMapping weixinActionMapping;
public WeixinServerInitializer() {
this.weixinActionMapping = new WeixinActionMapping();
}
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new WeixinServerHandler(weixinActionMapping));
}
}

View File

@ -0,0 +1,53 @@
package com.foxinmy.weixin4j.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import java.util.ResourceBundle;
/**
* 微信服务启动程序
*
* @className WeixinBootstrap
* @author jy
* @date 2014年10月12日
* @since JDK 1.7
* @see
*/
public final class WeixinServiceBootstrap {
private final static int port;
private final static int workerThreads;
static {
ResourceBundle config = ResourceBundle
.getBundle("com.foxinmy.weixin4j.server.netty");
port = Integer.parseInt(config.getString("port"));
workerThreads = Integer.parseInt(config.getString("workerThreads"));
}
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
try {
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler())
.childHandler(new WeixinServerInitializer());
Channel ch = b.bind(port).sync().channel();
System.err.println("weixin server startup OK:" + port);
ch.closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

View File

@ -0,0 +1,2 @@
port=8090
workerThreads=20

View File

@ -0,0 +1,5 @@
package com.foxinmy.weixin4j.spider;
public final class MpWeixin {
}

View File

@ -0,0 +1,3 @@
模拟微信公众平台登陆
(模拟登录|启用开发者模式|修改服务器配置|修改回调地址|创建自定义菜单....more)

View File

@ -0,0 +1 @@
消息类型、事件类型等枚举

View File

@ -0,0 +1,56 @@
package com.foxinmy.weixin4j.util;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import com.foxinmy.weixin4j.action.WeixinAction;
public class ClassUtil {
public static Set<Class<?>> getClasses(Package _package) {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
String subPath = _package.getName().replace(".", File.separator);
URL fullPath = classLoader.getResource(subPath);
File dir = new File(fullPath.getPath());
return findClasses(dir, _package.getName());
}
private static Set<Class<?>> findClasses(File dir, String packageName) {
Set<Class<?>> classes = new HashSet<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(findClasses(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;
}
public static void main(String[] args) throws ClassNotFoundException,
IOException, InstantiationException, IllegalAccessException {
System.out.println(getClasses(WeixinAction.class.getPackage()));
}
}

View File

@ -0,0 +1,76 @@
package com.foxinmy.weixin4j.util;
import java.util.Random;
import java.util.UUID;
/**
* 随机码工具类
* @className RandomUtil
* @author jy
* @date 2014年10月22日
* @since JDK 1.7
* @see
*/
public class RandomUtil {
private static final String ALLCHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String LETTERCHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String NUMBERCHAR = "0123456789";
// 返回一个定长的随机字符串(包含数字和大小写字母)
public static String generateString(int length) {
StringBuilder sb = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
}
return sb.toString();
}
// 返回一个定长的随机纯数字字符串(只包含数字)
public static String generateStringByNumberChar(int length) {
StringBuilder sb = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(NUMBERCHAR.charAt(random.nextInt(NUMBERCHAR.length())));
}
return sb.toString();
}
// 返回一个定长的随机纯字母字符串(只包含大小写字母)
public static String generateStringByLetterCharr(int length) {
StringBuilder sb = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(LETTERCHAR.charAt(random.nextInt(LETTERCHAR.length())));
}
return sb.toString();
}
// 返回一个定长的随机纯大写字母字符串(只包含大小写字母)
public static String generateLowerString(int length) {
return generateStringByLetterCharr(length).toLowerCase();
}
// 返回一个定长的随机纯小写字母字符串(只包含大小写字母)
public static String generateUpperString(int length) {
return generateStringByLetterCharr(length).toUpperCase();
}
/**
* 随机获取UUID字符串(无中划线)
*
* @return UUID字符串
*/
public static String getUUID() {
String uuid = UUID.randomUUID().toString();
return uuid.substring(0, 8) + uuid.substring(9, 13)
+ uuid.substring(14, 18) + uuid.substring(19, 23)
+ uuid.substring(24);
}
public static void main(String[] args) {
System.out.println(System.nanoTime());
System.out.println(System.currentTimeMillis());
}
}

View File

@ -0,0 +1 @@
添加<![CDATA[]>支持

View File

@ -0,0 +1,49 @@
package com.foxinmy.weixin4j.test.msg;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ResourceBundle;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import com.foxinmy.weixin4j.exception.WeixinException;
public class MessagePush {
private HttpClient httpClient;
private final HttpPost httpPost;
public MessagePush() {
this.httpClient = new DefaultHttpClient();
ResourceBundle config = ResourceBundle
.getBundle("com.foxinmy.weixin4j.server.netty");
httpPost = new HttpPost();
httpPost.setURI(URI.create(String.format("http://localhost:%s",
Integer.parseInt(config.getString("port")))));
}
public String push(String xml) throws WeixinException, IOException {
httpPost.setEntity(new StringEntity(xml, StandardCharsets.UTF_8));
HttpResponse httpResponse = httpClient.execute(httpPost);
StatusLine statusLine = httpResponse.getStatusLine();
int status = statusLine.getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new WeixinException(status, "request fail");
}
if (status == HttpStatus.SC_MOVED_PERMANENTLY
|| status == HttpStatus.SC_MOVED_TEMPORARILY) {
throw new WeixinException(status, "uri moved");
}
return EntityUtils.toString(httpResponse.getEntity(),
StandardCharsets.UTF_8);
}
}