| @@ -45,6 +45,7 @@ | |||||
| <artifactId>flyway-core</artifactId> | <artifactId>flyway-core</artifactId> | ||||
| <version>5.2.4</version> | <version>5.2.4</version> | ||||
| </dependency> | </dependency> | ||||
| </dependencies> | </dependencies> | ||||
| <build> | <build> | ||||
| <plugins> | <plugins> | ||||
| @@ -1,9 +1,13 @@ | |||||
| package com.iformall; | package com.iformall; | ||||
| import com.iformall.annotation.BaseEnableSwagger; | import com.iformall.annotation.BaseEnableSwagger; | ||||
| import com.iformall.haikang.netty.NioSocketServer; | |||||
| import org.mybatis.spring.annotation.MapperScan; | import org.mybatis.spring.annotation.MapperScan; | ||||
| import org.rocketmq.starter.annotation.EnableRocketMQ; | import org.rocketmq.starter.annotation.EnableRocketMQ; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.boot.CommandLineRunner; | |||||
| import org.springframework.boot.SpringApplication; | import org.springframework.boot.SpringApplication; | ||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||
| import org.springframework.context.annotation.Bean; | import org.springframework.context.annotation.Bean; | ||||
| @@ -23,7 +27,7 @@ import org.springframework.scheduling.annotation.EnableAsync; | |||||
| @EnableAsync | @EnableAsync | ||||
| @EnableRocketMQ | @EnableRocketMQ | ||||
| @EnableAspectJAutoProxy(exposeProxy = true) | @EnableAspectJAutoProxy(exposeProxy = true) | ||||
| public class CApplication { | |||||
| public class CApplication implements CommandLineRunner{ | |||||
| @Value("${fm.exception}") | @Value("${fm.exception}") | ||||
| private boolean fmException; | private boolean fmException; | ||||
| @@ -45,6 +49,15 @@ public class CApplication { | |||||
| @Value("${tcp.server.port}") | @Value("${tcp.server.port}") | ||||
| private int tcpServerPort; | private int tcpServerPort; | ||||
| @Autowired | |||||
| private NioSocketServer nioSocketServer; | |||||
| @Override | |||||
| public void run(String... args) throws Exception { | |||||
| nioSocketServer.start(tcpServerPort); | |||||
| } | |||||
| @Bean | @Bean | ||||
| public boolean isFmException() { | public boolean isFmException() { | ||||
| @@ -13,7 +13,7 @@ import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
| @Component | |||||
| //@Component | |||||
| public class TcpClient { | public class TcpClient { | ||||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | private final Logger log = LoggerFactory.getLogger(this.getClass()); | ||||
| @@ -23,7 +23,7 @@ import com.alibaba.fastjson.JSONArray; | |||||
| import com.alibaba.fastjson.JSONObject; | import com.alibaba.fastjson.JSONObject; | ||||
| import com.iformall.haikang.HKPeopleNumberFactory; | import com.iformall.haikang.HKPeopleNumberFactory; | ||||
| @Component | |||||
| //@Component | |||||
| public class TcpServer { | public class TcpServer { | ||||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | private final Logger log = LoggerFactory.getLogger(this.getClass()); | ||||
| @@ -54,26 +54,42 @@ public class TcpServer { | |||||
| } | } | ||||
| private void handleClient(Socket clientSocket) { | private void handleClient(Socket clientSocket) { | ||||
| try ( | |||||
| BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); | |||||
| PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true) | |||||
| ) { | |||||
| BufferedReader in = null; | |||||
| PrintWriter out = null; | |||||
| try { | |||||
| in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); | |||||
| out = new PrintWriter(clientSocket.getOutputStream(), true); | |||||
| String message; | String message; | ||||
| while ((message = in.readLine()) != null) { | while ((message = in.readLine()) != null) { | ||||
| log.info("Received: {}", message); | log.info("Received: {}", message); | ||||
| // | // | ||||
| handleMessage(out, message); | |||||
| //handleMessage(out, message); | |||||
| out.flush(); | |||||
| // Echo back to client | // Echo back to client | ||||
| //out.println("Server: " + message); | //out.println("Server: " + message); | ||||
| } | } | ||||
| } catch (IOException e) { | } catch (IOException e) { | ||||
| log.error("Client connection error: {}", e.getMessage()); | |||||
| log.error("Client connection error: {}", e); | |||||
| } finally { | } finally { | ||||
| try { | |||||
| clientSocket.close(); | |||||
| } catch (IOException e) { | |||||
| log.error("Failed to close socket: {}", e.getMessage()); | |||||
| } | |||||
| // try { | |||||
| // if (null != in) { | |||||
| // in.close(); | |||||
| // } | |||||
| // } catch (Exception e) { | |||||
| // log.error("Failed to close socket: {}", e); | |||||
| // } | |||||
| // try { | |||||
| // if (null != out) { | |||||
| // out.close(); | |||||
| // } | |||||
| // } catch (Exception e) { | |||||
| // log.error("Failed to close socket: {}", e); | |||||
| // } | |||||
| // try { | |||||
| // clientSocket.close(); | |||||
| // } catch (IOException e) { | |||||
| // log.error("Failed to close socket: {}", e); | |||||
| // } | |||||
| } | } | ||||
| } | } | ||||
| @@ -52,6 +52,13 @@ | |||||
| <artifactId>jsoup</artifactId> | <artifactId>jsoup</artifactId> | ||||
| <version>1.10.2</version> | <version>1.10.2</version> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>io.netty</groupId> | |||||
| <artifactId>netty-all</artifactId> | |||||
| <version>4.1.68.Final</version> | |||||
| <!-- 使用适合你项目的版本 --> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| </project> | </project> | ||||
| @@ -0,0 +1,17 @@ | |||||
| package com.iformall.haikang.netty; | |||||
| import java.util.List; | |||||
| import io.netty.buffer.ByteBuf; | |||||
| import io.netty.channel.ChannelHandlerContext; | |||||
| import io.netty.handler.codec.MessageToMessageDecoder; | |||||
| public class HexMessageDecoder extends MessageToMessageDecoder<ByteBuf>{ | |||||
| @Override | |||||
| protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { | |||||
| byte[] bytes = new byte[in.readableBytes()]; | |||||
| in.readBytes(bytes); | |||||
| String hexString = HexUtils.byteArrayToHexString(bytes); | |||||
| out.add(hexString); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,15 @@ | |||||
| package com.iformall.haikang.netty; | |||||
| import io.netty.buffer.ByteBuf; | |||||
| import io.netty.buffer.Unpooled; | |||||
| import io.netty.channel.ChannelHandlerContext; | |||||
| import io.netty.handler.codec.MessageToByteEncoder; | |||||
| public class HexMessageEncoder extends MessageToByteEncoder<String>{ | |||||
| @Override | |||||
| protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception { | |||||
| byte[] bytes = HexUtils.hexStringToByteArray(msg); | |||||
| ByteBuf buffer = Unpooled.wrappedBuffer(bytes); | |||||
| out.writeBytes(buffer); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,93 @@ | |||||
| package com.iformall.haikang.netty; | |||||
| public class HexUtils { | |||||
| public static String byteArrayToHexString(byte[] data) { | |||||
| StringBuilder hexString = new StringBuilder(); | |||||
| for (byte b : data) { | |||||
| hexString.append(String.format("%02X ", b)); | |||||
| } | |||||
| return hexString.toString().trim(); | |||||
| } | |||||
| public static byte[] hexStringToByteArray(String s) { | |||||
| s = s.replaceAll("\\s", ""); // 去除所有空白字符 | |||||
| int len = s.length(); | |||||
| if (len % 2 != 0) { | |||||
| throw new IllegalArgumentException("Invalid hexadecimal string length"); | |||||
| } | |||||
| byte[] data = new byte[len / 2]; | |||||
| for (int i = 0; i < len; i += 2) { | |||||
| data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) | |||||
| + Character.digit(s.charAt(i + 1), 16)); | |||||
| } | |||||
| return data; | |||||
| } | |||||
| /** | |||||
| * 将16进制字符串转换为ASCII字符串 | |||||
| * @param hexString | |||||
| * @return | |||||
| */ | |||||
| public static String hexStringToAscii(String hexString) { | |||||
| byte[] bytes = hexStringToByteArray(hexString); | |||||
| return new String(bytes); | |||||
| } | |||||
| /** | |||||
| * 将16进制字符串转换为十进制整型 | |||||
| * @param hexString | |||||
| * @return | |||||
| */ | |||||
| public static long hexStringToDecimal(String hexString) { | |||||
| hexString = hexString.replaceAll("\\s", ""); // 去除所有空白字符 | |||||
| long decimalValue = 0; | |||||
| for (int i = 0; i < hexString.length(); i++) { | |||||
| char c = hexString.charAt(i); | |||||
| int value = Character.digit(c, 16); // 获取当前字符的10进制值 | |||||
| decimalValue = decimalValue * 16 + value; | |||||
| } | |||||
| return decimalValue; | |||||
| } | |||||
| /** | |||||
| * 将16进制字符串转换为浮点数 | |||||
| * @param hexString | |||||
| * @return | |||||
| */ | |||||
| public static float hexStringToFloat(String hexString) { | |||||
| hexString = hexString.replaceAll("\\s", ""); // 去除所有空白字符 | |||||
| byte[] bytes = hexStringToByteArray(hexString); | |||||
| return bytesToFloat(bytes); | |||||
| } | |||||
| /** | |||||
| * 格式化16进制字符串 | |||||
| * @param hexString | |||||
| * @return | |||||
| */ | |||||
| public static String formatHexString(String hexString) { | |||||
| StringBuilder formattedString = new StringBuilder(); | |||||
| for (int i = 0; i < hexString.length(); i += 2) { | |||||
| if (i > 0) { | |||||
| formattedString.append(" "); | |||||
| } | |||||
| formattedString.append(hexString.substring(i, i + 2)); | |||||
| } | |||||
| return formattedString.toString(); | |||||
| } | |||||
| private static float bytesToFloat(byte[] bytes) { | |||||
| int intBits = 0; | |||||
| for (int i = 0; i < 4; i++) { | |||||
| intBits |= (bytes[i] & 0xFF) << (24 - 8 * i); | |||||
| } | |||||
| return Float.intBitsToFloat(intBits); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,41 @@ | |||||
| package com.iformall.haikang.netty; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import io.netty.channel.ChannelHandlerContext; | |||||
| import io.netty.channel.ChannelInboundHandlerAdapter; | |||||
| public class IpRejectHandler extends ChannelInboundHandlerAdapter{ | |||||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
| // 拒绝连接的IP列表 | |||||
| private static final String[] BLOCKED_IPS = {}; | |||||
| @Override | |||||
| public void channelActive(ChannelHandlerContext ctx) throws Exception { | |||||
| String remoteAddress = ctx.channel().remoteAddress().toString(); | |||||
| if (isBlocked(remoteAddress)) { | |||||
| log.error("netty tcp 拒绝连接: " + remoteAddress); | |||||
| ctx.close(); | |||||
| } else { | |||||
| log.info("netty tcp 允许连接: " + remoteAddress); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void channelInactive(ChannelHandlerContext ctx) throws Exception { | |||||
| String remoteAddress = ctx.channel().remoteAddress().toString(); | |||||
| log.info("netty tcp 设备已断开连接: " + remoteAddress); | |||||
| super.channelInactive(ctx); | |||||
| } | |||||
| private boolean isBlocked(String remoteAddress) { | |||||
| for (String blockedIp : BLOCKED_IPS) { | |||||
| if (remoteAddress.contains(blockedIp)) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,132 @@ | |||||
| package com.iformall.haikang.netty; | |||||
| import java.io.PrintWriter; | |||||
| import java.util.HashMap; | |||||
| import java.util.LinkedHashMap; | |||||
| import java.util.Map; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||||
| import org.springframework.stereotype.Component; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.alibaba.fastjson.JSONArray; | |||||
| import com.alibaba.fastjson.JSONObject; | |||||
| import com.iformall.haikang.HKPeopleNumberFactory; | |||||
| import io.netty.channel.ChannelHandlerContext; | |||||
| import io.netty.channel.SimpleChannelInboundHandler; | |||||
| @Component | |||||
| public class NioSocketHandler extends SimpleChannelInboundHandler<String>{ | |||||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
| private String hexData = ""; | |||||
| private HKPeopleNumberFactory peopleNumberFactory; | |||||
| private final ThreadPoolTaskExecutor threadPoolTaskExecutor; | |||||
| public NioSocketHandler(ThreadPoolTaskExecutor threadPoolTaskExecutor,HKPeopleNumberFactory peopleNumberFactory) { | |||||
| this.threadPoolTaskExecutor = threadPoolTaskExecutor; | |||||
| this.peopleNumberFactory = peopleNumberFactory; | |||||
| } | |||||
| @Override | |||||
| protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { | |||||
| // 异步处理消息 | |||||
| threadPoolTaskExecutor.execute(() -> { | |||||
| // 你的处理逻辑 | |||||
| String clientId = ctx.channel().id().asShortText(); | |||||
| log.info("netty tcp 接收到用户id为"+clientId+"的消息: " + msg); | |||||
| // 应答报文,根据要求应答 | |||||
| String repeak = msg; | |||||
| handleMessage(ctx,msg); | |||||
| // 应答 | |||||
| //ctx.writeAndFlush(repeak); | |||||
| }); | |||||
| } | |||||
| @Override | |||||
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { | |||||
| cause.printStackTrace(); | |||||
| ctx.close(); | |||||
| } | |||||
| //编号2代表需要等待时长,编号3代表当前排队人数,编号4代表当前场内出租车,编号5代表在途出租车 | |||||
| private static final int[] types = new int[] {2,3,4,5}; | |||||
| private void handleMessage(ChannelHandlerContext ctx,String message) { | |||||
| if (StringUtils.isBlank(message)) { | |||||
| return ; | |||||
| } | |||||
| JSONObject mo = JSON.parseObject(message); | |||||
| if (null == mo) { | |||||
| return ; | |||||
| } | |||||
| String func = mo.getString("func"); | |||||
| //心跳 | |||||
| if ("KeepOnline".equals(func)) { | |||||
| Map keepMap = new LinkedHashMap(); | |||||
| String dest = mo.getString("dest"); | |||||
| String source = mo.getString("source"); | |||||
| keepMap.put("dest", source); | |||||
| keepMap.put("source", dest); | |||||
| keepMap.put("func", "AckKeepOnline"); | |||||
| JSONArray datas = new JSONArray(); | |||||
| Map dm = new HashMap(); | |||||
| dm.put("node", "OK"); | |||||
| datas.add(dm); | |||||
| keepMap.put("data", datas); | |||||
| String xintiaoresult = JSON.toJSONString(keepMap); | |||||
| log.debug("haikang keepOnline result:"+xintiaoresult); | |||||
| ctx.writeAndFlush(xintiaoresult); | |||||
| //给灯箱发送 | |||||
| for (int i = 0 ; i < types.length ; i++) { | |||||
| Map dxobj = new LinkedHashMap(); | |||||
| dxobj.put("dest", source); | |||||
| dxobj.put("source", dest); | |||||
| dxobj.put("func", "ControlData"); | |||||
| JSONArray datas1 = new JSONArray(); | |||||
| Map dm1 = new HashMap(); | |||||
| dm1.put("node", generateNode(types[i],source)); | |||||
| datas1.add(dm1); | |||||
| dxobj.put("data", datas1); | |||||
| String dengxiangcontent = JSON.toJSONString(dxobj); | |||||
| log.debug("haikang send result:"+dengxiangcontent); | |||||
| ctx.writeAndFlush(dengxiangcontent); | |||||
| } | |||||
| } | |||||
| } | |||||
| //编号2代表需要等待时长,编号3代表当前排队人数,编号4代表当前场内出租车,编号5代表在途出租车 | |||||
| private String generateNode(int type,String sn) { | |||||
| StringBuffer sb = new StringBuffer("0-5-").append(type).append("-"); | |||||
| int value = 0; | |||||
| switch (type) { | |||||
| case 2: | |||||
| value = 1;//查询等待时长 | |||||
| break; | |||||
| case 3: | |||||
| value = peopleNumberFactory.getPeopleNumber();;//查询当前排队人数 | |||||
| break; | |||||
| case 4: | |||||
| value = 1;//查询场内出租车 | |||||
| break; | |||||
| case 5: | |||||
| value = 1;//查询在途出租车 | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| sb.append(value).append("-0-1-"+sn); | |||||
| return sb.toString(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,55 @@ | |||||
| package com.iformall.haikang.netty; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||||
| import org.springframework.stereotype.Component; | |||||
| import com.iformall.haikang.HKPeopleNumberFactory; | |||||
| import io.netty.channel.ChannelInitializer; | |||||
| import io.netty.channel.ChannelPipeline; | |||||
| import io.netty.channel.socket.SocketChannel; | |||||
| import io.netty.handler.codec.string.StringDecoder; | |||||
| import io.netty.handler.codec.string.StringEncoder; | |||||
| @Component | |||||
| public class NioSocketInitializer extends ChannelInitializer<SocketChannel>{ | |||||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| private ThreadPoolTaskExecutor threadPoolTaskExecutor; | |||||
| @Autowired | |||||
| private HKPeopleNumberFactory peopleNumberFactory; | |||||
| public NioSocketInitializer(ThreadPoolTaskExecutor threadPoolTaskExecutor) { | |||||
| this.threadPoolTaskExecutor = threadPoolTaskExecutor; | |||||
| } | |||||
| @Override | |||||
| protected void initChannel(SocketChannel ch) { | |||||
| ChannelPipeline pipeline = ch.pipeline(); | |||||
| // tcp报文解码器,接收后自动调用解码 | |||||
| //pipeline.addLast(new HexMessageDecoder()); | |||||
| // tcp报文编码器,应答时自动编码 | |||||
| //pipeline.addLast(new HexMessageEncoder()); | |||||
| // 如果socket传输的是字符串类型,请使用StringDecoder和StringEncoder | |||||
| pipeline.addLast(new StringDecoder()); | |||||
| pipeline.addLast(new StringEncoder()); | |||||
| // 初始化并添加你的业务处理类 | |||||
| //ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); | |||||
| //threadPoolTaskExecutor.initialize(); // 初始化线程池 | |||||
| // 使用自定义线程池处理业务逻辑 | |||||
| log.info(">>>>peopleNumberFactory:"+peopleNumberFactory); | |||||
| pipeline.addLast(new NioSocketHandler(threadPoolTaskExecutor,peopleNumberFactory)); | |||||
| // 连接策略 | |||||
| pipeline.addLast(new IpRejectHandler()); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,48 @@ | |||||
| package com.iformall.haikang.netty; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||||
| import org.springframework.stereotype.Component; | |||||
| import io.netty.bootstrap.ServerBootstrap; | |||||
| import io.netty.channel.ChannelFuture; | |||||
| import io.netty.channel.ChannelOption; | |||||
| import io.netty.channel.EventLoopGroup; | |||||
| import io.netty.channel.nio.NioEventLoopGroup; | |||||
| import io.netty.channel.socket.nio.NioServerSocketChannel; | |||||
| @Component | |||||
| public class NioSocketServer { | |||||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| private ThreadPoolTaskExecutor threadPoolTaskExecutor; | |||||
| private EventLoopGroup bossGroup; | |||||
| private EventLoopGroup workerGroup; | |||||
| private ChannelFuture channelFuture; | |||||
| public void start(int port) throws Exception { | |||||
| bossGroup = new NioEventLoopGroup(1); // 主线程组 | |||||
| workerGroup = new NioEventLoopGroup(); // 工作线程组 | |||||
| try { | |||||
| ServerBootstrap b = new ServerBootstrap(); | |||||
| b.group(bossGroup, workerGroup) | |||||
| .channel(NioServerSocketChannel.class) | |||||
| .childHandler(new NioSocketInitializer(threadPoolTaskExecutor)) | |||||
| .option(ChannelOption.SO_BACKLOG, 128) | |||||
| .childOption(ChannelOption.SO_KEEPALIVE, true); | |||||
| channelFuture = b.bind(port).sync(); | |||||
| log.info("netty tcp NIO Socket服务器已启动,监听端口:" + port); | |||||
| channelFuture.channel().closeFuture().sync(); | |||||
| } finally { | |||||
| bossGroup.shutdownGracefully(); | |||||
| workerGroup.shutdownGracefully(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,21 @@ | |||||
| package com.iformall.haikang.netty; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.context.annotation.Configuration; | |||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||||
| @Configuration | |||||
| public class ThreadPoolConfig { | |||||
| @Bean | |||||
| public ThreadPoolTaskExecutor threadPoolTaskExecutor() { | |||||
| ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); | |||||
| executor.setCorePoolSize(10); // 核心线程数 | |||||
| executor.setMaxPoolSize(20); // 最大线程数 | |||||
| executor.setQueueCapacity(100); // 队列容量 | |||||
| executor.setThreadNamePrefix("NIOSocket-ThreadPool-"); // 线程前缀 | |||||
| executor.initialize(); | |||||
| return executor; | |||||
| } | |||||
| } | |||||