From 8b552fa63235240d83aa005222c15e915f7a201f Mon Sep 17 00:00:00 2001 From: XSWL1018 <824576966@qq.com> Date: Mon, 14 Oct 2024 15:36:01 +0800 Subject: [PATCH 1/3] init --- ruoyi-plugins/pom.xml | 6 + ruoyi-plugins/ruoyi-websocket/pom.xml | 5 + .../ruoyi/websocket/NettyServerRunner.java | 22 +++ .../annotations/NettyWebSocketEndpoint.java | 12 ++ .../NettyWebSocketEndpointHandler.java | 74 ++++++++ .../nettyServer/NettyWebSocketServer.java | 64 +++++++ .../nettyServer/handler/WebSocketHandler.java | 168 ++++++++++++++++++ .../com/ruoyi/websocket/utils/CommonUtil.java | 78 ++++++++ 8 files changed, 429 insertions(+) create mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/NettyServerRunner.java create mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/annotations/NettyWebSocketEndpoint.java create mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketEndpointHandler.java create mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketServer.java create mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/handler/WebSocketHandler.java create mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java diff --git a/ruoyi-plugins/pom.xml b/ruoyi-plugins/pom.xml index 358faf8..ba12644 100644 --- a/ruoyi-plugins/pom.xml +++ b/ruoyi-plugins/pom.xml @@ -13,6 +13,7 @@ 3.10.8 3.5.8 + 4.1.112.Final @@ -72,6 +73,11 @@ ruoyi-mybatis-interceptor ${ruoyi.version} + + io.netty + netty-all + ${netty.version} + diff --git a/ruoyi-plugins/ruoyi-websocket/pom.xml b/ruoyi-plugins/ruoyi-websocket/pom.xml index 66fccdb..ee7ac53 100644 --- a/ruoyi-plugins/ruoyi-websocket/pom.xml +++ b/ruoyi-plugins/ruoyi-websocket/pom.xml @@ -26,6 +26,11 @@ spring-boot-starter-websocket + + io.netty + netty-all + + diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/NettyServerRunner.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/NettyServerRunner.java new file mode 100644 index 0000000..9e5632c --- /dev/null +++ b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/NettyServerRunner.java @@ -0,0 +1,22 @@ +package com.ruoyi.websocket; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.web.embedded.netty.NettyWebServer; +import org.springframework.stereotype.Component; + +import com.ruoyi.websocket.nettyServer.NettyWebSocketServer; + +@Component +public class NettyServerRunner implements ApplicationRunner { + + @Autowired + private NettyWebSocketServer server; + + @Override + public void run(ApplicationArguments args) throws Exception { + server.start(); + } + +} diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/annotations/NettyWebSocketEndpoint.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/annotations/NettyWebSocketEndpoint.java new file mode 100644 index 0000000..dbe0541 --- /dev/null +++ b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/annotations/NettyWebSocketEndpoint.java @@ -0,0 +1,12 @@ +package com.ruoyi.websocket.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface NettyWebSocketEndpoint { + String path(); +} diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketEndpointHandler.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketEndpointHandler.java new file mode 100644 index 0000000..b2e7eaf --- /dev/null +++ b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketEndpointHandler.java @@ -0,0 +1,74 @@ +package com.ruoyi.websocket.nettyServer; + +import java.nio.channels.Channel; +import java.util.Map; + +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.handler.codec.http.FullHttpMessage; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.util.concurrent.GlobalEventExecutor; + +public abstract class NettyWebSocketEndpointHandler { + + private final ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + + private Map pathParam; + + private Map urlParam; + + public void sendAll(String msg) { + group.writeAndFlush(msg); + } + + public static void sendMsg(ChannelHandlerContext context, String msg) { + TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame(msg); + context.channel().writeAndFlush(textWebSocketFrame); + } + + public abstract void onMessage(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame); + + public abstract void onOpen(ChannelHandlerContext channelHandlerContext, FullHttpMessage fullHttpMessage); + + public abstract void onClose(ChannelHandlerContext channelHandlerContext); + + public abstract void onError(ChannelHandlerContext channelHandlerContext, Throwable throwable); + + public ChannelGroup getGroup() { + return group; + } + + public Map getPathParam() { + return pathParam; + } + + public void setPathParam(Map pathParam) { + this.pathParam = pathParam; + } + + public Map getUrlParam() { + return urlParam; + } + + public void setUrlParam(Map urlParam) { + this.urlParam = urlParam; + } + + public Long getLongPathParam(String key) { + return Long.valueOf(pathParam.get(key)); + } + + public String getPathParam(String key) { + return pathParam.get(key); + } + + public Double getDoublePathParam(String key) { + return Double.parseDouble(pathParam.get(key)); + } + + public void closeChannel(ChannelHandlerContext channelHandlerContext) { + channelHandlerContext.close().addListener(ChannelFutureListener.CLOSE); + } +} \ No newline at end of file diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketServer.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketServer.java new file mode 100644 index 0000000..94dd558 --- /dev/null +++ b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketServer.java @@ -0,0 +1,64 @@ +package com.ruoyi.websocket.nettyServer; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.ruoyi.websocket.nettyServer.handler.WebSocketHandler; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; + +@Component +public class NettyWebSocketServer { + + private static ServerBootstrap serverBootstrap; + + @Value("${netty.websocket.maxMessageSize}") + private Long messageSize; + + @Value("${netty.websocket.bossThreads}") + private Long bossThreads; + + @Value("${netty.websocket.workerThreads}") + private Long workerThreads; + + @Value("${netty.websocket.port}") + private Long port; + + @Value("${netty.websocket.enable}") + private Boolean enable; + + public ServerBootstrap start() throws InterruptedException { + if (!enable) { + return null; + } + ServerBootstrap serverBootstrap = new ServerBootstrap(); + NioEventLoopGroup boss = new NioEventLoopGroup(4); + NioEventLoopGroup worker = new NioEventLoopGroup(workerThreads.intValue()); + serverBootstrap.group(boss, worker); + serverBootstrap.channel(NioServerSocketChannel.class); + serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); + serverBootstrap.childHandler(new ChannelInitializer() { + @Override + protected void initChannel(NioSocketChannel channel) throws Exception { + ChannelPipeline pipeline = channel.pipeline(); + pipeline.addLast(new HttpServerCodec()); + pipeline.addLast(new HttpObjectAggregator(messageSize.intValue())); + pipeline.addLast(new WebSocketHandler()); + pipeline.addLast(new WebSocketServerProtocolHandler("/", true)); + } + }); + serverBootstrap.bind(port.intValue()).sync(); + System.out.println( + "----------------------------------------------------------------------------------- \n Arknights!"); + NettyWebSocketServer.serverBootstrap = serverBootstrap; + return serverBootstrap; + } +} diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/handler/WebSocketHandler.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/handler/WebSocketHandler.java new file mode 100644 index 0000000..79407c0 --- /dev/null +++ b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/handler/WebSocketHandler.java @@ -0,0 +1,168 @@ +package com.ruoyi.websocket.nettyServer.handler; + +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.websocket.annotations.NettyWebSocketEndpoint; +import com.ruoyi.websocket.nettyServer.NettyWebSocketEndpointHandler; +import com.ruoyi.websocket.utils.CommonUtil; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelId; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.util.concurrent.GlobalEventExecutor; +import jakarta.annotation.PostConstruct; + +import java.lang.reflect.Constructor; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; + +@Component +public class WebSocketHandler extends SimpleChannelInboundHandler { + + @Autowired + private List handlers; + + private static final Map uriHandlerMapper = new ConcurrentHashMap<>(); + + public static final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + + public static final Map channelHandlerMap = new ConcurrentHashMap<>(); + + @PostConstruct + private void init() throws URISyntaxException, NoSuchMethodException, SecurityException { + for (NettyWebSocketEndpointHandler handler : handlers) { + Class handlerClass = handler.getClass(); + NettyWebSocketEndpoint annotation = handlerClass.getAnnotation(NettyWebSocketEndpoint.class); + if (annotation == null || StringUtils.isEmpty(annotation.path())) { + throw new RuntimeException("未配置路径的 netty websocket endpoint "); + } + // uriHandlerMap.put(uri.getPath(), handler); + PathMatchModel pathMachModel = parseHandler(annotation.path(), handlerClass); + uriHandlerMapper.put(pathMachModel.path, pathMachModel); + } + } + + @Override + protected void channelRead0(ChannelHandlerContext context, TextWebSocketFrame webSocketFrame) throws Exception { + NettyWebSocketEndpointHandler handler = channelHandlerMap.get(context.channel().id()); + handler.onMessage(context, webSocketFrame); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + channelGroup.add(ctx.channel()); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + NettyWebSocketEndpointHandler handler = channelHandlerMap.get(ctx.channel().id()); + if (handler != null) { + handler.onClose(ctx); + handler.getGroup().remove(ctx.channel()); + } + + channelHandlerMap.remove(ctx.channel().id()); + channelGroup.remove(ctx.channel()); + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + channelHandlerMap.get(ctx.channel().id()).onError(ctx, cause); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof FullHttpRequest) { + FullHttpRequest fullHttpRequest = (FullHttpRequest) msg; + if (channelHandlerMap.get(ctx.channel().id()) != null) { + super.channelRead(ctx, fullHttpRequest); + return; + } + URI uri = new URI(fullHttpRequest.uri()); + PathMatchModel mathPathMachModel = mathPathMachModel(uri.getPath()); + if (mathPathMachModel == null) { + ctx.channel() + .writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND)); + ctx.close().addListener(ChannelFutureListener.CLOSE); + return; + } + NettyWebSocketEndpointHandler newInstance = (NettyWebSocketEndpointHandler) mathPathMachModel.handlerConstructor + .newInstance(); + if (!(mathPathMachModel.pathParams == null || mathPathMachModel.pathParams.isEmpty())) { + newInstance.setPathParam( + CommonUtil.parsePathParam(uri.getPath(), mathPathMachModel.pathParams, mathPathMachModel.path)); + super.channelRead(ctx, msg); + } + newInstance.setUrlParam(CommonUtil.parseQueryParameters(uri.getQuery())); + + channelHandlerMap.put(ctx.channel().id(), newInstance); + newInstance.onOpen(ctx, fullHttpRequest); + } else if (msg instanceof TextWebSocketFrame) { + super.channelRead(ctx, msg); + } + + } + + private static PathMatchModel parseHandler(String path, Class handlerClass) + throws NoSuchMethodException, SecurityException { + List paramName = new ArrayList<>(); + String[] split = path.split("/"); + for (int index = 1; index < split.length; index++) { + String item = split[index]; + if (item.startsWith("{") && item.endsWith("}")) { + paramName.add(item.substring(1, item.length() - 1).trim()); + split[index] = "?"; + } + } + StringBuilder finalPath = new StringBuilder(""); + for (int index = 1; index < split.length; index++) { + finalPath.append("/").append(split[index]); + } + return new PathMatchModel(paramName, finalPath.toString(), handlerClass.getDeclaredConstructor()); + } + + private static PathMatchModel mathPathMachModel(String uri) { + Map map = new HashMap<>(); + for (String key : uriHandlerMapper.keySet()) { + int mathUri = CommonUtil.mathUri(uri, key); + if (mathUri > 0) { + map.put(mathUri, uriHandlerMapper.get(key)); + } + } + if (map.keySet() == null || map.keySet().isEmpty()) { + return null; + } + Integer max = CommonUtil.getMax(map.keySet()); + return map.get(max); + } + + private static final class PathMatchModel { + private final List pathParams; + + private final String path; + + private final Constructor handlerConstructor; + + public PathMatchModel(List pathParams, String path, Constructor handlerConstructor) { + this.pathParams = pathParams; + this.path = path; + this.handlerConstructor = handlerConstructor; + } + + } +} diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java new file mode 100644 index 0000000..d2b0228 --- /dev/null +++ b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java @@ -0,0 +1,78 @@ +package com.ruoyi.websocket.utils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +public class CommonUtil { + + /** + * @param uri + * @param uriTemplates + * @return + */ + public static int mathUri(String uri, String uriTemplate) { + String[] uriSplit = uri.split("/"); + String[] tempalteSplit = uriTemplate.split("/"); + if (uriSplit.length != tempalteSplit.length) { + return -1; + } + int mathLevel = 0; + for (int index = 1; index < tempalteSplit.length; index++) { + if (tempalteSplit[index].equals("?")) { + mathLevel = mathLevel + index; + continue; + } + if (!tempalteSplit[index].equals(uriSplit[index])) { + return -1; + } else { + mathLevel = mathLevel + tempalteSplit.length + 1; + } + } + return mathLevel; + } + + public static Map parseQueryParameters(String query) { + if (query == null || query.isEmpty()) { + return Map.of(); + } + + Map params = new HashMap<>(); + String[] pairs = query.split("&"); + for (String pair : pairs) { + String[] keyValue = pair.split("="); + if (keyValue.length > 1) { + params.put(keyValue[0], keyValue[1]); + } else { + params.put(keyValue[0], ""); + } + } + return params; + } + + public static Map parsePathParam(String uri, List pathParams, String uriTemplate) { + int index = 0; + String[] split = uriTemplate.split("/"); + String[] split2 = uri.split("/"); + Map map = new HashMap<>(); + for (int i = 1; i < split.length; i++) { + if (split[i].equals("?")) { + map.put(pathParams.get(index), split2[i]); + index++; + } + } + return map; + } + + public static Integer getMax(Set set) { + Optional maxNumber = set.stream().max(Integer::compare); + if (maxNumber.isPresent()) { + System.out.println("Max number: " + maxNumber.get()); + } else { + System.out.println("The list is empty"); + } + return maxNumber.get(); + } +} -- Gitee From 59ccedce9bb243a04956020032261a06f238393b Mon Sep 17 00:00:00 2001 From: XSWL1018 <824576966@qq.com> Date: Tue, 15 Oct 2024 23:21:32 +0800 Subject: [PATCH 2/3] u --- ruoyi-plugins/pom.xml | 9 + .../aspectj/DataSecurityAspect.java | 9 +- .../SqlContextHolder.java | 24 ++- .../{sql => handler}/MybatisAfterHandler.java | 2 +- .../{sql => handler}/MybatisPreHandler.java | 3 +- .../dataSecurity/DataSecurityPreHandler.java | 63 +++++-- .../page/PageAfterHandler.java | 4 +- .../{sql => handler}/page/PagePreHandler.java | 4 +- .../mybatis/MybatisInterceptor.java | 4 +- .../util/DataSecurityUtil.java | 3 +- ruoyi-plugins/ruoyi-netty/pom.xml | 30 ++++ .../netty/websocket/NettyServerRunner.java | 21 +++ .../annotations/NettyWebSocketEndpoint.java | 12 ++ .../endpoints/TestNettyWebSocket.java | 44 +++++ .../NettyWebSocketEndpointHandler.java | 66 +++++++ .../nettyServer/NettyWebSocketServer.java | 64 +++++++ .../nettyServer/handler/WebSocketHandler.java | 167 ++++++++++++++++++ .../netty/websocket/utils/CommonUtil.java | 78 ++++++++ ruoyi-plugins/ruoyi-plugins-starter/pom.xml | 5 + .../com/ruoyi/websocket/utils/CommonUtil.java | 1 + 20 files changed, 574 insertions(+), 39 deletions(-) rename ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/{dataSecurity => sqlContext}/SqlContextHolder.java (67%) rename ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/{sql => handler}/MybatisAfterHandler.java (68%) rename ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/{sql => handler}/MybatisPreHandler.java (92%) rename ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/{sql => handler}/dataSecurity/DataSecurityPreHandler.java (58%) rename ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/{sql => handler}/page/PageAfterHandler.java (87%) rename ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/{sql => handler}/page/PagePreHandler.java (98%) create mode 100644 ruoyi-plugins/ruoyi-netty/pom.xml create mode 100644 ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/NettyServerRunner.java create mode 100644 ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/annotations/NettyWebSocketEndpoint.java create mode 100644 ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/endpoints/TestNettyWebSocket.java create mode 100644 ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/NettyWebSocketEndpointHandler.java create mode 100644 ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/NettyWebSocketServer.java create mode 100644 ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/handler/WebSocketHandler.java create mode 100644 ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/utils/CommonUtil.java diff --git a/ruoyi-plugins/pom.xml b/ruoyi-plugins/pom.xml index ba12644..9807d98 100644 --- a/ruoyi-plugins/pom.xml +++ b/ruoyi-plugins/pom.xml @@ -68,11 +68,19 @@ ruoyi-plugins-starter ${ruoyi.version} + com.ruoyi ruoyi-mybatis-interceptor ${ruoyi.version} + + + com.ruoyi + ruoyi-netty + ${ruoyi.version} + + io.netty netty-all @@ -90,6 +98,7 @@ ruoyi-websocket ruoyi-plugins-starter ruoyi-mybatis-interceptor + ruoyi-netty pom diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/aspectj/DataSecurityAspect.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/aspectj/DataSecurityAspect.java index 166b87f..d6ee916 100644 --- a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/aspectj/DataSecurityAspect.java +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/aspectj/DataSecurityAspect.java @@ -1,21 +1,19 @@ package com.ruoyi.mybatisinterceptor.aspectj; import com.ruoyi.mybatisinterceptor.annotation.DataSecurity; +import com.ruoyi.mybatisinterceptor.context.sqlContext.SqlContextHolder; import com.ruoyi.mybatisinterceptor.model.JoinTableModel; import com.ruoyi.mybatisinterceptor.model.WhereModel; -import com.ruoyi.mybatisinterceptor.context.dataSecurity.SqlContextHolder; + import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; - - import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; - @Aspect @Component public class DataSecurityAspect { @@ -50,7 +48,6 @@ public class DataSecurityAspect { if (!StringUtils.isEmpty(dataSecurity.joinTableAlise())) { createByTableModel.setJoinTableAlise(dataSecurity.joinTableAlise()); } - createByTableModel.setFromTableColumn("create_by"); createByTableModel.setJoinTableColumn("user_name"); SqlContextHolder.addJoinTable(createByTableModel); @@ -63,12 +60,10 @@ public class DataSecurityAspect { if (!StringUtils.isEmpty(dataSecurity.joinTableAlise())) { userIdTableModel.setJoinTableAlise(dataSecurity.joinTableAlise()); } - userIdTableModel.setFromTableColumn("user_id"); userIdTableModel.setJoinTableColumn("user_id"); SqlContextHolder.addJoinTable(userIdTableModel); break; - default: break; } diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/dataSecurity/SqlContextHolder.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/sqlContext/SqlContextHolder.java similarity index 67% rename from ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/dataSecurity/SqlContextHolder.java rename to ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/sqlContext/SqlContextHolder.java index 54407a6..150856b 100644 --- a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/dataSecurity/SqlContextHolder.java +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/sqlContext/SqlContextHolder.java @@ -1,4 +1,4 @@ -package com.ruoyi.mybatisinterceptor.context.dataSecurity; +package com.ruoyi.mybatisinterceptor.context.sqlContext; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; @@ -10,11 +10,11 @@ public class SqlContextHolder { private static final ThreadLocal SQL_CONTEXT_HOLDER = new ThreadLocal<>(); public static void startDataSecurity() { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("isSecurity", Boolean.TRUE); - jsonObject.put(SqlType.WHERE.getSqlType(), new JSONArray()); - jsonObject.put(SqlType.JOIN.getSqlType(), new JSONArray()); - SQL_CONTEXT_HOLDER.set(jsonObject); + SQL_CONTEXT_HOLDER.get().put("isSecurity", Boolean.TRUE); + } + + public static void startLogicSelect() { + SQL_CONTEXT_HOLDER.get().put("isLogic", Boolean.TRUE); } public static void addWhereParam(WhereModel whereModel) { @@ -26,7 +26,6 @@ public class SqlContextHolder { } public static boolean isSecurity() { - return SQL_CONTEXT_HOLDER.get() != null && SQL_CONTEXT_HOLDER.get().getBooleanValue("isSecurity"); } @@ -42,4 +41,15 @@ public class SqlContextHolder { public static JSONArray getJoinTables() { return SQL_CONTEXT_HOLDER.get().getJSONArray(SqlType.JOIN.getSqlType()); } + + public static void startInterceptor() { + JSONObject jsonObject = SQL_CONTEXT_HOLDER.get(); + if (jsonObject != null) { + return; + } + JSONObject object = new JSONObject(); + object.put(SqlType.JOIN.getSqlType(), new JSONArray()); + object.put(SqlType.WHERE.getSqlType(), new JSONArray()); + SQL_CONTEXT_HOLDER.set(object); + } } diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisAfterHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/MybatisAfterHandler.java similarity index 68% rename from ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisAfterHandler.java rename to ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/MybatisAfterHandler.java index 133f111..dafed1e 100644 --- a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisAfterHandler.java +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/MybatisAfterHandler.java @@ -1,4 +1,4 @@ -package com.ruoyi.mybatisinterceptor.sql; +package com.ruoyi.mybatisinterceptor.handler; public interface MybatisAfterHandler { diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisPreHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/MybatisPreHandler.java similarity index 92% rename from ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisPreHandler.java rename to ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/MybatisPreHandler.java index b0a61fe..db17e53 100644 --- a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisPreHandler.java +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/MybatisPreHandler.java @@ -1,4 +1,4 @@ -package com.ruoyi.mybatisinterceptor.sql; +package com.ruoyi.mybatisinterceptor.handler; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.executor.Executor; @@ -6,6 +6,7 @@ import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; + public interface MybatisPreHandler { void preHandle(Executor executor, MappedStatement mappedStatement, Object params, diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/dataSecurity/DataSecurityPreHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/dataSecurity/DataSecurityPreHandler.java similarity index 58% rename from ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/dataSecurity/DataSecurityPreHandler.java rename to ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/dataSecurity/DataSecurityPreHandler.java index c1e8c86..0bc4a5e 100644 --- a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/dataSecurity/DataSecurityPreHandler.java +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/dataSecurity/DataSecurityPreHandler.java @@ -1,6 +1,7 @@ -package com.ruoyi.mybatisinterceptor.sql.dataSecurity; +package com.ruoyi.mybatisinterceptor.handler.dataSecurity; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.List; import org.apache.ibatis.cache.CacheKey; @@ -12,13 +13,14 @@ import org.apache.ibatis.session.RowBounds; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; +import com.alibaba.fastjson2.JSONArray; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.sql.SqlUtil; import com.ruoyi.mybatisinterceptor.annotation.MybatisHandlerOrder; -import com.ruoyi.mybatisinterceptor.context.dataSecurity.SqlContextHolder; +import com.ruoyi.mybatisinterceptor.context.sqlContext.SqlContextHolder; +import com.ruoyi.mybatisinterceptor.handler.MybatisPreHandler; import com.ruoyi.mybatisinterceptor.model.JoinTableModel; import com.ruoyi.mybatisinterceptor.model.WhereModel; -import com.ruoyi.mybatisinterceptor.sql.MybatisPreHandler; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Alias; @@ -28,9 +30,11 @@ import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.select.Join; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.update.Update; @MybatisHandlerOrder(1) @Component @@ -43,7 +47,7 @@ public class DataSecurityPreHandler implements MybatisPreHandler { @Override public void preHandle(Executor executor, MappedStatement mappedStatement, Object params, RowBounds rowBounds, - ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws Throwable { + ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws Throwable { if (SqlContextHolder.isSecurity()) { Statement sql = parseSql(SqlUtil.parseSql(boundSql.getSql())); sqlFiled.set(boundSql, sql.toString()); @@ -62,27 +66,56 @@ public class DataSecurityPreHandler implements MybatisPreHandler { } } - private static void handleWhere(Select select) throws JSQLParserException { - PlainSelect plain = select.getPlainSelect(); - Expression expWhere = plain.getWhere(); + private static void handleWhere(Statement statement) throws JSQLParserException { + if (statement instanceof Select) { + Select select = (Select) statement; + PlainSelect plainSelect = select.getPlainSelect(); + plainSelect.setWhere(getConfigedWhereExpression(plainSelect.getWhere())); + } else if (statement instanceof Update) { + Update update = (Update) statement; + update.setWhere(getConfigedWhereExpression(update.getWhere())); + } else if (statement instanceof Delete) { + Delete delete = (Delete) statement; + delete.setWhere(getConfigedWhereExpression(delete.getWhere())); + } + + } + + private static Expression getConfigedWhereExpression(Expression expWhere) throws JSQLParserException { StringBuilder whereParam = new StringBuilder(" "); String where = expWhere != null ? expWhere.toString() : null; if (SqlContextHolder.getWhere() == null || SqlContextHolder.getWhere().size() <= 0) { - return; + return expWhere; } - SqlContextHolder.getWhere().forEach(item -> { + JSONArray wehreArray = SqlContextHolder.getWhere(); + wehreArray.forEach(item -> { whereParam.append(((WhereModel) item).getSqlString()); }); - where = StringUtils.isEmpty(where) ? whereParam.toString().substring(5, whereParam.length()) + WhereModel whereModel = (WhereModel) wehreArray.get(0); + where = StringUtils.isEmpty(where) + ? whereParam.toString().substring(whereModel.getConnectType().length() + 2, whereParam.length()) : where + " " + whereParam.toString(); - plain.setWhere(CCJSqlParserUtil.parseCondExpression(where)); + return CCJSqlParserUtil.parseCondExpression(where); } - private static void handleJoin(Select select) { - PlainSelect selectBody = select.getPlainSelect(); + private static void handleJoin(Statement statement) { if (SqlContextHolder.getJoinTables() == null || SqlContextHolder.getJoinTables().size() <= 0) { return; } + if (statement instanceof Select) { + Select select = (Select) statement; + select.getPlainSelect().addJoins(getConfigedJoinExpression()); + } else if (statement instanceof Update) { + Update update = (Update) statement; + update.addJoins(getConfigedJoinExpression()); + } else if (statement instanceof Delete) { + Delete delete = (Delete) statement; + delete.addJoins(getConfigedJoinExpression()); + } + } + + private static List getConfigedJoinExpression() { + List joins = new ArrayList<>(); SqlContextHolder.getJoinTables().forEach(item -> { JoinTableModel tableModel = (JoinTableModel) item; Table table = new Table(tableModel.getJoinTable()); @@ -93,8 +126,8 @@ public class DataSecurityPreHandler implements MybatisPreHandler { Expression onExpression = new EqualsTo(new Column(tableModel.getFromTableColumnString()), new Column(tableModel.getJoinTableColumnString())); join.setOnExpressions(List.of(onExpression)); - selectBody.addJoins(join); + joins.add(join); }); + return joins; } - } diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PageAfterHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/page/PageAfterHandler.java similarity index 87% rename from ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PageAfterHandler.java rename to ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/page/PageAfterHandler.java index 4826924..bdd91b1 100644 --- a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PageAfterHandler.java +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/page/PageAfterHandler.java @@ -1,4 +1,4 @@ -package com.ruoyi.mybatisinterceptor.sql.page; +package com.ruoyi.mybatisinterceptor.handler.page; import java.util.List; @@ -7,7 +7,7 @@ import org.springframework.stereotype.Component; import com.ruoyi.mybatisinterceptor.annotation.MybatisHandlerOrder; import com.ruoyi.mybatisinterceptor.context.page.PageContextHolder; import com.ruoyi.mybatisinterceptor.context.page.model.TableInfo; -import com.ruoyi.mybatisinterceptor.sql.MybatisAfterHandler; +import com.ruoyi.mybatisinterceptor.handler.MybatisAfterHandler; @MybatisHandlerOrder(1) @Component diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PagePreHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/page/PagePreHandler.java similarity index 98% rename from ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PagePreHandler.java rename to ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/page/PagePreHandler.java index 0e21921..5fde0b1 100644 --- a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PagePreHandler.java +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/handler/page/PagePreHandler.java @@ -1,4 +1,4 @@ -package com.ruoyi.mybatisinterceptor.sql.page; +package com.ruoyi.mybatisinterceptor.handler.page; import java.lang.reflect.Field; import java.sql.SQLException; @@ -21,7 +21,7 @@ import com.ruoyi.common.utils.sql.SqlUtil; import com.ruoyi.mybatisinterceptor.annotation.MybatisHandlerOrder; import com.ruoyi.mybatisinterceptor.context.page.PageContextHolder; import com.ruoyi.mybatisinterceptor.context.page.model.PageInfo; -import com.ruoyi.mybatisinterceptor.sql.MybatisPreHandler; +import com.ruoyi.mybatisinterceptor.handler.MybatisPreHandler; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.Statement; diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/interceptor/mybatis/MybatisInterceptor.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/interceptor/mybatis/MybatisInterceptor.java index 98af73a..2afd1d2 100644 --- a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/interceptor/mybatis/MybatisInterceptor.java +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/interceptor/mybatis/MybatisInterceptor.java @@ -17,8 +17,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.ruoyi.mybatisinterceptor.annotation.MybatisHandlerOrder; -import com.ruoyi.mybatisinterceptor.sql.MybatisAfterHandler; -import com.ruoyi.mybatisinterceptor.sql.MybatisPreHandler; +import com.ruoyi.mybatisinterceptor.handler.MybatisAfterHandler; +import com.ruoyi.mybatisinterceptor.handler.MybatisPreHandler; import jakarta.annotation.PostConstruct; diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/DataSecurityUtil.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/DataSecurityUtil.java index cb360d5..a7590fa 100644 --- a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/DataSecurityUtil.java +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/DataSecurityUtil.java @@ -1,7 +1,6 @@ package com.ruoyi.mybatisinterceptor.util; - -import com.ruoyi.mybatisinterceptor.context.dataSecurity.SqlContextHolder; +import com.ruoyi.mybatisinterceptor.context.sqlContext.SqlContextHolder; public class DataSecurityUtil { diff --git a/ruoyi-plugins/ruoyi-netty/pom.xml b/ruoyi-plugins/ruoyi-netty/pom.xml new file mode 100644 index 0000000..71b0b47 --- /dev/null +++ b/ruoyi-plugins/ruoyi-netty/pom.xml @@ -0,0 +1,30 @@ + + + + ruoyi-plugins + com.ruoyi + 3.8.8.3.1 + + 4.0.0 + + ruoyi-netty + + + 19 + 19 + UTF-8 + + + + + io.netty + netty-all + + + com.ruoyi + ruoyi-common + + + diff --git a/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/NettyServerRunner.java b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/NettyServerRunner.java new file mode 100644 index 0000000..fa69d6d --- /dev/null +++ b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/NettyServerRunner.java @@ -0,0 +1,21 @@ +package com.ruoyi.netty.websocket; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import com.ruoyi.netty.websocket.nettyServer.NettyWebSocketServer; + +@Component +public class NettyServerRunner implements ApplicationRunner { + + @Autowired + private NettyWebSocketServer server; + + @Override + public void run(ApplicationArguments args) throws Exception { + server.start(); + } + +} diff --git a/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/annotations/NettyWebSocketEndpoint.java b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/annotations/NettyWebSocketEndpoint.java new file mode 100644 index 0000000..15f64ae --- /dev/null +++ b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/annotations/NettyWebSocketEndpoint.java @@ -0,0 +1,12 @@ +package com.ruoyi.netty.websocket.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface NettyWebSocketEndpoint { + String path(); +} diff --git a/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/endpoints/TestNettyWebSocket.java b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/endpoints/TestNettyWebSocket.java new file mode 100644 index 0000000..3e827f6 --- /dev/null +++ b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/endpoints/TestNettyWebSocket.java @@ -0,0 +1,44 @@ +package com.ruoyi.netty.websocket.endpoints; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.stereotype.Component; + +import com.ruoyi.netty.websocket.annotations.NettyWebSocketEndpoint; +import com.ruoyi.netty.websocket.nettyServer.NettyWebSocketEndpointHandler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.FullHttpMessage; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; + +@Component +@NettyWebSocketEndpoint(path = "/test/{seqNumber}") +public class TestNettyWebSocket extends NettyWebSocketEndpointHandler { + + private static final Map map = new ConcurrentHashMap<>(); + + @Override + public void onMessage(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) { + System.out.println(textWebSocketFrame.text()); + } + + @Override + public void onOpen(ChannelHandlerContext channelHandlerContext, FullHttpMessage fullHttpMessage) { + map.put(getPathParam("seqNumber"), channelHandlerContext); + } + + @Override + public void onClose(ChannelHandlerContext channelHandlerContext) { + map.remove(getPathParam("seqNumber")); + } + + @Override + public void onError(ChannelHandlerContext channelHandlerContext, Throwable throwable) { + + } + + public static void send(String seqNumber, String msg) { + sendMsg(map.get(seqNumber), msg); + } +} diff --git a/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/NettyWebSocketEndpointHandler.java b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/NettyWebSocketEndpointHandler.java new file mode 100644 index 0000000..357c1c4 --- /dev/null +++ b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/NettyWebSocketEndpointHandler.java @@ -0,0 +1,66 @@ +package com.ruoyi.netty.websocket.nettyServer; + +import java.util.Map; + +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.handler.codec.http.FullHttpMessage; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.util.concurrent.GlobalEventExecutor; + +public abstract class NettyWebSocketEndpointHandler { + + + private Map pathParam; + + private Map urlParam; + + + public static void sendMsg(ChannelHandlerContext context, String msg) { + TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame(msg); + context.channel().writeAndFlush(textWebSocketFrame); + } + + public abstract void onMessage(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame); + + public abstract void onOpen(ChannelHandlerContext channelHandlerContext, FullHttpMessage fullHttpMessage); + + public abstract void onClose(ChannelHandlerContext channelHandlerContext); + + public abstract void onError(ChannelHandlerContext channelHandlerContext, Throwable throwable); + + + public Map getPathParam() { + return pathParam; + } + + public void setPathParam(Map pathParam) { + this.pathParam = pathParam; + } + + public Map getUrlParam() { + return urlParam; + } + + public void setUrlParam(Map urlParam) { + this.urlParam = urlParam; + } + + public Long getLongPathParam(String key) { + return Long.valueOf(pathParam.get(key)); + } + + public String getPathParam(String key) { + return pathParam.get(key); + } + + public Double getDoublePathParam(String key) { + return Double.parseDouble(pathParam.get(key)); + } + + public void closeChannel(ChannelHandlerContext channelHandlerContext) { + channelHandlerContext.close().addListener(ChannelFutureListener.CLOSE); + } +} diff --git a/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/NettyWebSocketServer.java b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/NettyWebSocketServer.java new file mode 100644 index 0000000..6350541 --- /dev/null +++ b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/NettyWebSocketServer.java @@ -0,0 +1,64 @@ +package com.ruoyi.netty.websocket.nettyServer; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.ruoyi.netty.websocket.nettyServer.handler.WebSocketHandler; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; + +@Component +public class NettyWebSocketServer { + + private static ServerBootstrap serverBootstrap; + + @Value("${netty.websocket.maxMessageSize}") + private Long messageSize; + + @Value("${netty.websocket.bossThreads}") + private Long bossThreads; + + @Value("${netty.websocket.workerThreads}") + private Long workerThreads; + + @Value("${netty.websocket.port}") + private Long port; + + @Value("${netty.websocket.enable}") + private Boolean enable; + + public ServerBootstrap start() throws InterruptedException { + if (!enable) { + return null; + } + ServerBootstrap serverBootstrap = new ServerBootstrap(); + NioEventLoopGroup boss = new NioEventLoopGroup(4); + NioEventLoopGroup worker = new NioEventLoopGroup(workerThreads.intValue()); + serverBootstrap.group(boss, worker); + serverBootstrap.channel(NioServerSocketChannel.class); + serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); + serverBootstrap.childHandler(new ChannelInitializer() { + @Override + protected void initChannel(NioSocketChannel channel) throws Exception { + ChannelPipeline pipeline = channel.pipeline(); + pipeline.addLast(new HttpServerCodec()); + pipeline.addLast(new HttpObjectAggregator(messageSize.intValue())); + pipeline.addLast(new WebSocketHandler()); + pipeline.addLast(new WebSocketServerProtocolHandler("/", true)); + } + }); + serverBootstrap.bind(port.intValue()).sync(); + System.out.println( + "----------------------------------------------------------------------------------- \n Arknights!"); + NettyWebSocketServer.serverBootstrap = serverBootstrap; + return serverBootstrap; + } +} diff --git a/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/handler/WebSocketHandler.java b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/handler/WebSocketHandler.java new file mode 100644 index 0000000..da50f71 --- /dev/null +++ b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/nettyServer/handler/WebSocketHandler.java @@ -0,0 +1,167 @@ +package com.ruoyi.netty.websocket.nettyServer.handler; + +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.netty.websocket.annotations.NettyWebSocketEndpoint; +import com.ruoyi.netty.websocket.nettyServer.NettyWebSocketEndpointHandler; +import com.ruoyi.netty.websocket.utils.CommonUtil; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelId; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.util.concurrent.GlobalEventExecutor; +import jakarta.annotation.PostConstruct; + +import java.lang.reflect.Constructor; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; + +@Component +public class WebSocketHandler extends SimpleChannelInboundHandler { + + @Autowired + private List handlers; + + private static final Map uriHandlerMapper = new ConcurrentHashMap<>(); + + public static final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + + public static final Map channelHandlerMap = new ConcurrentHashMap<>(); + + @PostConstruct + private void init() throws URISyntaxException, NoSuchMethodException, SecurityException { + for (NettyWebSocketEndpointHandler handler : handlers) { + Class handlerClass = handler.getClass(); + NettyWebSocketEndpoint annotation = handlerClass.getAnnotation(NettyWebSocketEndpoint.class); + if (annotation == null || StringUtils.isEmpty(annotation.path())) { + throw new RuntimeException("未配置路径的 netty websocket endpoint "); + } + // uriHandlerMap.put(uri.getPath(), handler); + PathMatchModel pathMachModel = parseHandler(annotation.path(), handlerClass); + uriHandlerMapper.put(pathMachModel.path, pathMachModel); + } + } + + @Override + protected void channelRead0(ChannelHandlerContext context, TextWebSocketFrame webSocketFrame) throws Exception { + NettyWebSocketEndpointHandler handler = channelHandlerMap.get(context.channel().id()); + handler.onMessage(context, webSocketFrame); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + channelGroup.add(ctx.channel()); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + NettyWebSocketEndpointHandler handler = channelHandlerMap.get(ctx.channel().id()); + if (handler != null) { + handler.onClose(ctx); + } + + channelHandlerMap.remove(ctx.channel().id()); + channelGroup.remove(ctx.channel()); + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + channelHandlerMap.get(ctx.channel().id()).onError(ctx, cause); + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof FullHttpRequest) { + FullHttpRequest fullHttpRequest = (FullHttpRequest) msg; + if (channelHandlerMap.get(ctx.channel().id()) != null) { + super.channelRead(ctx, fullHttpRequest); + return; + } + URI uri = new URI(fullHttpRequest.uri()); + PathMatchModel mathPathMachModel = mathPathMachModel(uri.getPath()); + if (mathPathMachModel == null) { + ctx.channel() + .writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND)); + ctx.close().addListener(ChannelFutureListener.CLOSE); + return; + } + NettyWebSocketEndpointHandler newInstance = (NettyWebSocketEndpointHandler) mathPathMachModel.handlerConstructor + .newInstance(); + if (!(mathPathMachModel.pathParams == null || mathPathMachModel.pathParams.isEmpty())) { + newInstance.setPathParam( + CommonUtil.parsePathParam(uri.getPath(), mathPathMachModel.pathParams, mathPathMachModel.path)); + super.channelRead(ctx, msg); + } + newInstance.setUrlParam(CommonUtil.parseQueryParameters(uri.getQuery())); + + channelHandlerMap.put(ctx.channel().id(), newInstance); + newInstance.onOpen(ctx, fullHttpRequest); + } else if (msg instanceof TextWebSocketFrame) { + super.channelRead(ctx, msg); + } + + } + + private static PathMatchModel parseHandler(String path, Class handlerClass) + throws NoSuchMethodException, SecurityException { + List paramName = new ArrayList<>(); + String[] split = path.split("/"); + for (int index = 1; index < split.length; index++) { + String item = split[index]; + if (item.startsWith("{") && item.endsWith("}")) { + paramName.add(item.substring(1, item.length() - 1).trim()); + split[index] = "?"; + } + } + StringBuilder finalPath = new StringBuilder(""); + for (int index = 1; index < split.length; index++) { + finalPath.append("/").append(split[index]); + } + return new PathMatchModel(paramName, finalPath.toString(), handlerClass.getDeclaredConstructor()); + } + + private static PathMatchModel mathPathMachModel(String uri) { + Map map = new HashMap<>(); + for (String key : uriHandlerMapper.keySet()) { + int mathUri = CommonUtil.mathUri(uri, key); + if (mathUri > 0) { + map.put(mathUri, uriHandlerMapper.get(key)); + } + } + if (map.keySet() == null || map.keySet().isEmpty()) { + return null; + } + Integer max = CommonUtil.getMax(map.keySet()); + return map.get(max); + } + + private static final class PathMatchModel { + private final List pathParams; + + private final String path; + + private final Constructor handlerConstructor; + + public PathMatchModel(List pathParams, String path, Constructor handlerConstructor) { + this.pathParams = pathParams; + this.path = path; + this.handlerConstructor = handlerConstructor; + } + + } +} diff --git a/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/utils/CommonUtil.java b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/utils/CommonUtil.java new file mode 100644 index 0000000..9c029db --- /dev/null +++ b/ruoyi-plugins/ruoyi-netty/src/main/java/com/ruoyi/netty/websocket/utils/CommonUtil.java @@ -0,0 +1,78 @@ +package com.ruoyi.netty.websocket.utils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +public class CommonUtil { + + /** + * @param uri + * @param uriTemplates + * @return + */ + public static int mathUri(String uri, String uriTemplate) { + String[] uriSplit = uri.split("/"); + String[] tempalteSplit = uriTemplate.split("/"); + if (uriSplit.length != tempalteSplit.length) { + return -1; + } + int mathLevel = 0; + for (int index = 1; index < tempalteSplit.length; index++) { + if (tempalteSplit[index].equals("?")) { + mathLevel = mathLevel + index; + continue; + } + if (!tempalteSplit[index].equals(uriSplit[index])) { + return -1; + } else { + mathLevel = mathLevel + tempalteSplit.length + 1; + } + } + return mathLevel; + } + + public static Map parseQueryParameters(String query) { + if (query == null || query.isEmpty()) { + return Map.of(); + } + + Map params = new HashMap<>(); + String[] pairs = query.split("&"); + for (String pair : pairs) { + String[] keyValue = pair.split("="); + if (keyValue.length > 1) { + params.put(keyValue[0], keyValue[1]); + } else { + params.put(keyValue[0], ""); + } + } + return params; + } + + public static Map parsePathParam(String uri, List pathParams, String uriTemplate) { + int index = 0; + String[] split = uriTemplate.split("/"); + String[] split2 = uri.split("/"); + Map map = new HashMap<>(); + for (int i = 1; i < split.length; i++) { + if (split[i].equals("?")) { + map.put(pathParams.get(index), split2[i]); + index++; + } + } + return map; + } + + public static Integer getMax(Set set) { + Optional maxNumber = set.stream().max(Integer::compare); + if (maxNumber.isPresent()) { + System.out.println("Max number: " + maxNumber.get()); + } else { + System.out.println("The list is empty"); + } + return maxNumber.get(); + } +} diff --git a/ruoyi-plugins/ruoyi-plugins-starter/pom.xml b/ruoyi-plugins/ruoyi-plugins-starter/pom.xml index 183d4cd..fc7eb6d 100644 --- a/ruoyi-plugins/ruoyi-plugins-starter/pom.xml +++ b/ruoyi-plugins/ruoyi-plugins-starter/pom.xml @@ -50,6 +50,11 @@ com.ruoyi ruoyi-mybatis-interceptor + + + com.ruoyi + ruoyi-netty + diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java index d2b0228..cd3bf70 100644 --- a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java +++ b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java @@ -75,4 +75,5 @@ public class CommonUtil { } return maxNumber.get(); } + } -- Gitee From e6736e99b197e4ec1d9763d2c5a2ebbbed54c223 Mon Sep 17 00:00:00 2001 From: XSWL1018 <824576966@qq.com> Date: Sat, 19 Oct 2024 15:20:49 +0800 Subject: [PATCH 3/3] u --- .../ruoyi/websocket/NettyServerRunner.java | 22 --- .../annotations/NettyWebSocketEndpoint.java | 12 -- .../NettyWebSocketEndpointHandler.java | 74 -------- .../nettyServer/NettyWebSocketServer.java | 64 ------- .../nettyServer/handler/WebSocketHandler.java | 168 ------------------ .../com/ruoyi/websocket/utils/CommonUtil.java | 79 -------- 6 files changed, 419 deletions(-) delete mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/NettyServerRunner.java delete mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/annotations/NettyWebSocketEndpoint.java delete mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketEndpointHandler.java delete mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketServer.java delete mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/handler/WebSocketHandler.java delete mode 100644 ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/NettyServerRunner.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/NettyServerRunner.java deleted file mode 100644 index 9e5632c..0000000 --- a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/NettyServerRunner.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.ruoyi.websocket; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.boot.web.embedded.netty.NettyWebServer; -import org.springframework.stereotype.Component; - -import com.ruoyi.websocket.nettyServer.NettyWebSocketServer; - -@Component -public class NettyServerRunner implements ApplicationRunner { - - @Autowired - private NettyWebSocketServer server; - - @Override - public void run(ApplicationArguments args) throws Exception { - server.start(); - } - -} diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/annotations/NettyWebSocketEndpoint.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/annotations/NettyWebSocketEndpoint.java deleted file mode 100644 index dbe0541..0000000 --- a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/annotations/NettyWebSocketEndpoint.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.ruoyi.websocket.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface NettyWebSocketEndpoint { - String path(); -} diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketEndpointHandler.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketEndpointHandler.java deleted file mode 100644 index b2e7eaf..0000000 --- a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketEndpointHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.ruoyi.websocket.nettyServer; - -import java.nio.channels.Channel; -import java.util.Map; - -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.util.concurrent.GlobalEventExecutor; - -public abstract class NettyWebSocketEndpointHandler { - - private final ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - private Map pathParam; - - private Map urlParam; - - public void sendAll(String msg) { - group.writeAndFlush(msg); - } - - public static void sendMsg(ChannelHandlerContext context, String msg) { - TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame(msg); - context.channel().writeAndFlush(textWebSocketFrame); - } - - public abstract void onMessage(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame); - - public abstract void onOpen(ChannelHandlerContext channelHandlerContext, FullHttpMessage fullHttpMessage); - - public abstract void onClose(ChannelHandlerContext channelHandlerContext); - - public abstract void onError(ChannelHandlerContext channelHandlerContext, Throwable throwable); - - public ChannelGroup getGroup() { - return group; - } - - public Map getPathParam() { - return pathParam; - } - - public void setPathParam(Map pathParam) { - this.pathParam = pathParam; - } - - public Map getUrlParam() { - return urlParam; - } - - public void setUrlParam(Map urlParam) { - this.urlParam = urlParam; - } - - public Long getLongPathParam(String key) { - return Long.valueOf(pathParam.get(key)); - } - - public String getPathParam(String key) { - return pathParam.get(key); - } - - public Double getDoublePathParam(String key) { - return Double.parseDouble(pathParam.get(key)); - } - - public void closeChannel(ChannelHandlerContext channelHandlerContext) { - channelHandlerContext.close().addListener(ChannelFutureListener.CLOSE); - } -} \ No newline at end of file diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketServer.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketServer.java deleted file mode 100644 index 94dd558..0000000 --- a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/NettyWebSocketServer.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.ruoyi.websocket.nettyServer; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import com.ruoyi.websocket.nettyServer.handler.WebSocketHandler; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; - -@Component -public class NettyWebSocketServer { - - private static ServerBootstrap serverBootstrap; - - @Value("${netty.websocket.maxMessageSize}") - private Long messageSize; - - @Value("${netty.websocket.bossThreads}") - private Long bossThreads; - - @Value("${netty.websocket.workerThreads}") - private Long workerThreads; - - @Value("${netty.websocket.port}") - private Long port; - - @Value("${netty.websocket.enable}") - private Boolean enable; - - public ServerBootstrap start() throws InterruptedException { - if (!enable) { - return null; - } - ServerBootstrap serverBootstrap = new ServerBootstrap(); - NioEventLoopGroup boss = new NioEventLoopGroup(4); - NioEventLoopGroup worker = new NioEventLoopGroup(workerThreads.intValue()); - serverBootstrap.group(boss, worker); - serverBootstrap.channel(NioServerSocketChannel.class); - serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); - serverBootstrap.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(NioSocketChannel channel) throws Exception { - ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast(new HttpServerCodec()); - pipeline.addLast(new HttpObjectAggregator(messageSize.intValue())); - pipeline.addLast(new WebSocketHandler()); - pipeline.addLast(new WebSocketServerProtocolHandler("/", true)); - } - }); - serverBootstrap.bind(port.intValue()).sync(); - System.out.println( - "----------------------------------------------------------------------------------- \n Arknights!"); - NettyWebSocketServer.serverBootstrap = serverBootstrap; - return serverBootstrap; - } -} diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/handler/WebSocketHandler.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/handler/WebSocketHandler.java deleted file mode 100644 index 79407c0..0000000 --- a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/nettyServer/handler/WebSocketHandler.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.ruoyi.websocket.nettyServer.handler; - -import java.util.concurrent.ConcurrentHashMap; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.websocket.annotations.NettyWebSocketEndpoint; -import com.ruoyi.websocket.nettyServer.NettyWebSocketEndpointHandler; -import com.ruoyi.websocket.utils.CommonUtil; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelId; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.util.concurrent.GlobalEventExecutor; -import jakarta.annotation.PostConstruct; - -import java.lang.reflect.Constructor; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.*; - -@Component -public class WebSocketHandler extends SimpleChannelInboundHandler { - - @Autowired - private List handlers; - - private static final Map uriHandlerMapper = new ConcurrentHashMap<>(); - - public static final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - public static final Map channelHandlerMap = new ConcurrentHashMap<>(); - - @PostConstruct - private void init() throws URISyntaxException, NoSuchMethodException, SecurityException { - for (NettyWebSocketEndpointHandler handler : handlers) { - Class handlerClass = handler.getClass(); - NettyWebSocketEndpoint annotation = handlerClass.getAnnotation(NettyWebSocketEndpoint.class); - if (annotation == null || StringUtils.isEmpty(annotation.path())) { - throw new RuntimeException("未配置路径的 netty websocket endpoint "); - } - // uriHandlerMap.put(uri.getPath(), handler); - PathMatchModel pathMachModel = parseHandler(annotation.path(), handlerClass); - uriHandlerMapper.put(pathMachModel.path, pathMachModel); - } - } - - @Override - protected void channelRead0(ChannelHandlerContext context, TextWebSocketFrame webSocketFrame) throws Exception { - NettyWebSocketEndpointHandler handler = channelHandlerMap.get(context.channel().id()); - handler.onMessage(context, webSocketFrame); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - channelGroup.add(ctx.channel()); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - NettyWebSocketEndpointHandler handler = channelHandlerMap.get(ctx.channel().id()); - if (handler != null) { - handler.onClose(ctx); - handler.getGroup().remove(ctx.channel()); - } - - channelHandlerMap.remove(ctx.channel().id()); - channelGroup.remove(ctx.channel()); - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - channelHandlerMap.get(ctx.channel().id()).onError(ctx, cause); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof FullHttpRequest) { - FullHttpRequest fullHttpRequest = (FullHttpRequest) msg; - if (channelHandlerMap.get(ctx.channel().id()) != null) { - super.channelRead(ctx, fullHttpRequest); - return; - } - URI uri = new URI(fullHttpRequest.uri()); - PathMatchModel mathPathMachModel = mathPathMachModel(uri.getPath()); - if (mathPathMachModel == null) { - ctx.channel() - .writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND)); - ctx.close().addListener(ChannelFutureListener.CLOSE); - return; - } - NettyWebSocketEndpointHandler newInstance = (NettyWebSocketEndpointHandler) mathPathMachModel.handlerConstructor - .newInstance(); - if (!(mathPathMachModel.pathParams == null || mathPathMachModel.pathParams.isEmpty())) { - newInstance.setPathParam( - CommonUtil.parsePathParam(uri.getPath(), mathPathMachModel.pathParams, mathPathMachModel.path)); - super.channelRead(ctx, msg); - } - newInstance.setUrlParam(CommonUtil.parseQueryParameters(uri.getQuery())); - - channelHandlerMap.put(ctx.channel().id(), newInstance); - newInstance.onOpen(ctx, fullHttpRequest); - } else if (msg instanceof TextWebSocketFrame) { - super.channelRead(ctx, msg); - } - - } - - private static PathMatchModel parseHandler(String path, Class handlerClass) - throws NoSuchMethodException, SecurityException { - List paramName = new ArrayList<>(); - String[] split = path.split("/"); - for (int index = 1; index < split.length; index++) { - String item = split[index]; - if (item.startsWith("{") && item.endsWith("}")) { - paramName.add(item.substring(1, item.length() - 1).trim()); - split[index] = "?"; - } - } - StringBuilder finalPath = new StringBuilder(""); - for (int index = 1; index < split.length; index++) { - finalPath.append("/").append(split[index]); - } - return new PathMatchModel(paramName, finalPath.toString(), handlerClass.getDeclaredConstructor()); - } - - private static PathMatchModel mathPathMachModel(String uri) { - Map map = new HashMap<>(); - for (String key : uriHandlerMapper.keySet()) { - int mathUri = CommonUtil.mathUri(uri, key); - if (mathUri > 0) { - map.put(mathUri, uriHandlerMapper.get(key)); - } - } - if (map.keySet() == null || map.keySet().isEmpty()) { - return null; - } - Integer max = CommonUtil.getMax(map.keySet()); - return map.get(max); - } - - private static final class PathMatchModel { - private final List pathParams; - - private final String path; - - private final Constructor handlerConstructor; - - public PathMatchModel(List pathParams, String path, Constructor handlerConstructor) { - this.pathParams = pathParams; - this.path = path; - this.handlerConstructor = handlerConstructor; - } - - } -} diff --git a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java b/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java deleted file mode 100644 index cd3bf70..0000000 --- a/ruoyi-plugins/ruoyi-websocket/src/main/java/com/ruoyi/websocket/utils/CommonUtil.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.ruoyi.websocket.utils; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -public class CommonUtil { - - /** - * @param uri - * @param uriTemplates - * @return - */ - public static int mathUri(String uri, String uriTemplate) { - String[] uriSplit = uri.split("/"); - String[] tempalteSplit = uriTemplate.split("/"); - if (uriSplit.length != tempalteSplit.length) { - return -1; - } - int mathLevel = 0; - for (int index = 1; index < tempalteSplit.length; index++) { - if (tempalteSplit[index].equals("?")) { - mathLevel = mathLevel + index; - continue; - } - if (!tempalteSplit[index].equals(uriSplit[index])) { - return -1; - } else { - mathLevel = mathLevel + tempalteSplit.length + 1; - } - } - return mathLevel; - } - - public static Map parseQueryParameters(String query) { - if (query == null || query.isEmpty()) { - return Map.of(); - } - - Map params = new HashMap<>(); - String[] pairs = query.split("&"); - for (String pair : pairs) { - String[] keyValue = pair.split("="); - if (keyValue.length > 1) { - params.put(keyValue[0], keyValue[1]); - } else { - params.put(keyValue[0], ""); - } - } - return params; - } - - public static Map parsePathParam(String uri, List pathParams, String uriTemplate) { - int index = 0; - String[] split = uriTemplate.split("/"); - String[] split2 = uri.split("/"); - Map map = new HashMap<>(); - for (int i = 1; i < split.length; i++) { - if (split[i].equals("?")) { - map.put(pathParams.get(index), split2[i]); - index++; - } - } - return map; - } - - public static Integer getMax(Set set) { - Optional maxNumber = set.stream().max(Integer::compare); - if (maxNumber.isPresent()) { - System.out.println("Max number: " + maxNumber.get()); - } else { - System.out.println("The list is empty"); - } - return maxNumber.get(); - } - -} -- Gitee