package com.foxinmy.weixin4j.startup; 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 io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import com.foxinmy.weixin4j.dispatcher.BeanFactory; import com.foxinmy.weixin4j.dispatcher.DefaultMessageMatcher; import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher; import com.foxinmy.weixin4j.dispatcher.WeixinMessageKey; import com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher; 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.socket.WeixinServerInitializer; import com.foxinmy.weixin4j.util.AesToken; /** * 微信netty服务启动程序 * * @className WeixinServerBootstrap * @author jinyu(foxinmy@gmail.com) * @date 2014年10月12日 * @since JDK 1.6 * @see com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher * @see com.foxinmy.weixin4j.handler.WeixinMessageHandler * @see com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor * @see com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher * @see com.foxinmy.weixin4j.dispatcher.BeanFactory */ public final class WeixinServerBootstrap { private final InternalLogger logger = InternalLoggerFactory .getInstance(getClass()); /** * boss线程数,默认设置为cpu的核数 */ public final static int DEFAULT_BOSSTHREADS; /** * worker线程数,默认设置为DEFAULT_BOSSTHREADS * 4 */ public final static int DEFAULT_WORKERTHREADS; /** * 服务启动的默认端口 */ public final static int DEFAULT_SERVERPORT = 30000; /** * 消息分发器 */ private WeixinMessageDispatcher messageDispatcher; /** * 消息处理器 */ private List<WeixinMessageHandler> messageHandlerList; /** * 消息拦截器 */ private List<WeixinMessageInterceptor> messageInterceptorList; /** * aes and token * */ private final Map<String, AesToken> aesTokenMap; private WeixinServerInitializer wechatInitializer; static { DEFAULT_BOSSTHREADS = Runtime.getRuntime().availableProcessors(); DEFAULT_WORKERTHREADS = DEFAULT_BOSSTHREADS * 4; } /** * * 明文模式 * * @param token * 开发者token * */ public WeixinServerBootstrap(String token) { this("", token, null); } /** * 明文模式 & 兼容模式 & 密文模式 * <dl> * <font * color="red">值得注意的是:企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数</font> * </dl> * * @param weixinId * 公众号的应用ID(appid/corpid) 密文&兼容模式下需要填写 * * @param token * 开发者填写的token 无论哪种模式都需要填写 * @param aesKey * 消息加密的密钥 密文&兼容模式下需要填写 */ public WeixinServerBootstrap(String weixinId, String token, String aesKey) { this(new AesToken(weixinId, token, aesKey)); } /** * 多个公众号的支持 <dt>值得注意的是: * <dl> * <font color="red">1).企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数</font> * </dl> * <dl> * <font * color="red">2).非明文模式下需要在服务器URL后面加多一个`weixin_id=对应的appid/corpid`的参数</font> * </dl> * * @param aesTokens * 多个公众号 * @return */ public WeixinServerBootstrap(AesToken... aesToken) { this(new DefaultMessageMatcher(), aesToken); } /** * 多个公众号的支持 <dt>值得注意的是: * <dl> * <font color="red">1).企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数</font> * </dl> * <dl> * <font * color="red">2).非明文模式下需要在服务器URL后面加多一个`weixin_id=对应的appid/corpid`的参数</font> * </dl> * * @param messageMatcher * 消息匹配器 * @param aesTokens * 公众号信息 * @return */ public WeixinServerBootstrap(WeixinMessageMatcher messageMatcher, AesToken... aesTokens) { if (messageMatcher == null) { throw new IllegalArgumentException("MessageMatcher not be null"); } if (aesTokens == null) { throw new IllegalArgumentException("AesToken not be null"); } this.aesTokenMap = new HashMap<String, AesToken>(); for (AesToken aesToken : aesTokens) { this.aesTokenMap.put(aesToken.getWeixinId(), aesToken); } this.aesTokenMap.put("", aesTokens[0]); this.messageHandlerList = new ArrayList<WeixinMessageHandler>(); this.messageInterceptorList = new ArrayList<WeixinMessageInterceptor>(); this.messageDispatcher = new WeixinMessageDispatcher(messageMatcher); } /** * 默认端口(30000)启动服务 * */ public void startup() throws WeixinException { startup(DEFAULT_SERVERPORT); } /** * 指定端口启动服务 * */ public void startup(int serverPort) throws WeixinException { startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort); } /** * 接受参数启动服务 * * @param bossThreads * boss线程数 * @param workerThreads * worker线程数 * @param serverPort * 服务启动端口 * @return * @throws WeixinException */ public void startup(int bossThreads, int workerThreads, final int serverPort) throws WeixinException { messageDispatcher.setMessageHandlerList(messageHandlerList); messageDispatcher.setMessageInterceptorList(messageInterceptorList); EventLoopGroup bossGroup = new NioEventLoopGroup(bossThreads); EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads); try { wechatInitializer = new WeixinServerInitializer(aesTokenMap, messageDispatcher); ServerBootstrap b = new ServerBootstrap(); b.option(ChannelOption.SO_BACKLOG, 1024); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler()) .childHandler(wechatInitializer); Channel ch = b.bind(serverPort) .addListener(new FutureListener<Void>() { @Override public void operationComplete(Future<Void> future) throws Exception { if (future.isSuccess()) { logger.info("weixin4j server startup OK:{}", serverPort); } else { logger.info("weixin4j server startup FAIL:{}", serverPort); } } }).sync().channel(); ch.closeFuture().sync(); } catch (InterruptedException e) { throw new WeixinException("netty server startup FAIL", e); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } /** * 添加一个或者多个消息处理器 * * @param messageHandler * 消息处理器 * @return */ public WeixinServerBootstrap addHandler( WeixinMessageHandler... messageHandler) { if (messageHandler == null) { throw new IllegalArgumentException("messageHandler not be null"); } messageHandlerList.addAll(Arrays.asList(messageHandler)); return this; } /** * 插入一个或多个消息拦截器 * * @param messageInterceptor * 消息拦截器 * @return */ public WeixinServerBootstrap addInterceptor( WeixinMessageInterceptor... messageInterceptor) { if (messageInterceptor == null) { throw new IllegalArgumentException("messageInterceptor not be null"); } messageInterceptorList.addAll(Arrays.asList(messageInterceptor)); return this; } /** * 按照包名去添加消息处理器 * * @param messageHandlerPackages * 消息处理器所在的包名 * @return */ public WeixinServerBootstrap handlerPackagesToScan( String... messageHandlerPackages) { if (messageHandlerPackages == null) { throw new IllegalArgumentException( "messageHandlerPackages not be null"); } messageDispatcher.setMessageHandlerPackages(messageHandlerPackages); return this; } /** * 按照包名去添加消息拦截器 * * @param messageInterceptorPackages * 消息拦截器所在的包名 * @return */ public WeixinServerBootstrap interceptorPackagesToScan( String... messageInterceptorPackages) { if (messageInterceptorPackages == null) { throw new IllegalArgumentException( "messageInterceptorPackages not be null"); } messageDispatcher .setMessageInterceptorPackages(messageInterceptorPackages); return this; } /** * 声明处理器跟拦截器类实例化的构造工厂,否则通过Class.newInstance的方式构造 * * @param beanFactory * Bean构造工厂 * @return */ public WeixinServerBootstrap resolveBeanFactory(BeanFactory beanFactory) { messageDispatcher.setBeanFactory(beanFactory); return this; } /** * 注册消息类型 * * @param messageKey * 消息key * @param messageClass * 消息类 * @return */ public WeixinServerBootstrap registMessageClass( WeixinMessageKey messageKey, Class<? extends WeixinMessage> messageClass) { messageDispatcher.registMessageClass(messageKey, messageClass); return this; } /** * 打开总是响应开关,如未匹配到MessageHandler时回复空白消息 */ public WeixinServerBootstrap openAlwaysResponse() { messageDispatcher.openAlwaysResponse(); return this; } /** * aesTokenMap 最好是线程安全的 * * @param aesToken * @return */ public int addAesToken(AesToken aesToken) { return wechatInitializer.addAesToken(aesToken); } public final static String VERSION = "1.1.8"; }