Skip to content

Commit

Permalink
add @OnOpen/@OnMessage/@onclose supported
Browse files Browse the repository at this point in the history
  • Loading branch information
ydq committed Dec 18, 2018
1 parent 3a1be7f commit ab801a5
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 43 deletions.
14 changes: 14 additions & 0 deletions src/main/java/com/blade/mvc/annotation/OnClose.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.blade.mvc.annotation;

import java.lang.annotation.*;

/**
* @author darren
* @description invoke websocketHandler onDisConnect method
* @date 2018/12/17 18:41
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OnClose {
}
14 changes: 14 additions & 0 deletions src/main/java/com/blade/mvc/annotation/OnMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.blade.mvc.annotation;

import java.lang.annotation.*;

/**
* @author darren
* @description invoke websocketHandler onText method
* @date 2018/12/17 18:41
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OnMessage {
}
14 changes: 14 additions & 0 deletions src/main/java/com/blade/mvc/annotation/OnOpen.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.blade.mvc.annotation;

import java.lang.annotation.*;

/**
* @author darren
* @description invoke websocketHandler onConnect method
* @date 2018/12/17 18:41
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OnOpen {
}
12 changes: 6 additions & 6 deletions src/main/java/com/blade/mvc/annotation/WebSocket.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
@Documented
@Bean
public @interface WebSocket {

/**
* alias for path
* @return
* @return websocket path
*/
String[] value() default {};
String value() default "/websocket";


/**
* websocket path
* @return
* @return websocket description
*/
String[] path() default {};
String description() default "";
}
125 changes: 125 additions & 0 deletions src/main/java/com/blade/mvc/handler/WebSocketHandlerWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.blade.mvc.handler;

import com.blade.Blade;
import com.blade.kit.ReflectKit;
import com.blade.mvc.annotation.OnClose;
import com.blade.mvc.annotation.OnMessage;
import com.blade.mvc.annotation.OnOpen;
import com.blade.mvc.websocket.WebSocketContext;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* @author darren
* @description
* @date 2018/12/17 18:41
*/
@Slf4j
public final class WebSocketHandlerWrapper implements WebSocketHandler {

private final Map<String,Class<?>> handlers = new HashMap<>(4);
private final Map<String, Map<Class<? extends Annotation>, Method>> methodCache = new HashMap<>(4);
private final ThreadLocal<String> path = ThreadLocal.withInitial(() -> null);
private final Blade blade;

public static WebSocketHandlerWrapper init(Blade blade) {
return new WebSocketHandlerWrapper(blade);
}

private WebSocketHandlerWrapper(Blade blade) {
this.blade = blade;
}

public void setPath(String path) {
this.path.set(path);
}

/**
* add @WebSocket handler mapper
*
* @param path
* @param handler
*/
public void wrapHandler(String path, Class<?> handler) {
Method[] methods = handler.getMethods();
Map<Class<? extends Annotation>, Method> cache = new HashMap<>(3);
cacheMethod(cache, methods, OnOpen.class);
cacheMethod(cache, methods, OnMessage.class);
cacheMethod(cache, methods, OnClose.class);
if (cache.size() > 0) {
methodCache.put(path, cache);
handlers.put(path,handler);
} else {
throw new RuntimeException("Do not found any annotation of [@OnOpen / @OnMessage / @OnClose] in class: " + handler.getName());
}
}

private static void cacheMethod(Map<Class<? extends Annotation>, Method> cache, Method[] methods, Class<? extends Annotation> filter) {
List<Method> methodList = Stream.of(methods)
.filter(method -> method.isAnnotationPresent(filter))
.collect(Collectors.toList());
if (methodList.size() == 1) {
cache.put(filter, methodList.get(0));
} else if (methodList.size() > 1) {
throw new RuntimeException("Duplicate annotation @" + filter.getSimpleName() + " in class: " + methodList.get(0).getDeclaringClass().getName());
}
}

@Override
public void onConnect(WebSocketContext ctx) {
invoke(ctx, OnOpen.class);
}

@Override
public void onText(WebSocketContext ctx) {
invoke(ctx, OnMessage.class);
}

@Override
public void onDisConnect(WebSocketContext ctx) {
invoke(ctx, OnClose.class);
}

/**
* invoke target handler methods
*
* @param ctx WebSocket context
* @param event WebSocket event type
*/
private void invoke(WebSocketContext ctx, Class<? extends Annotation> event) {
Map<Class<? extends Annotation>, Method> methodCache = this.methodCache.get(path.get());
if (methodCache != null) {
Method method = methodCache.get(event);
if (method != null) {
Class<?>[] paramTypes = method.getParameterTypes();
Object[] param = new Object[paramTypes.length];
try {
for (int i = 0; i < paramTypes.length; i++) {
Class<?> paramType = paramTypes[i];
if (paramType == WebSocketContext.class) {
param[i] = ctx;
} else {
Object bean = this.blade.getBean(paramType);
if (bean != null) {
param[i] = bean;
} else {
param[i] = ReflectKit.newInstance(paramType);
}
}
}
method.invoke(blade.getBean(handlers.get(path.get())), param);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
}
35 changes: 20 additions & 15 deletions src/main/java/com/blade/server/netty/NettyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.blade.mvc.handler.DefaultExceptionHandler;
import com.blade.mvc.handler.ExceptionHandler;
import com.blade.mvc.handler.WebSocketHandler;
import com.blade.mvc.handler.WebSocketHandlerWrapper;
import com.blade.mvc.hook.WebHook;
import com.blade.mvc.http.session.SessionCleaner;
import com.blade.mvc.route.RouteBuilder;
Expand Down Expand Up @@ -69,10 +70,7 @@
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
Expand Down Expand Up @@ -296,10 +294,10 @@ private void parseAndCreate(Class<?> clazz) {
blade.register(clazz);
}
if (null != clazz.getAnnotation(Path.class)) {
if (null == blade.ioc().getBean(clazz)) {
if (null == blade.getBean(clazz)) {
blade.register(clazz);
}
Object controller = blade.ioc().getBean(clazz);
Object controller = blade.getBean(clazz);
routeBuilder.addRouter(clazz, controller);
}

Expand All @@ -314,25 +312,32 @@ private void parseAndCreate(Class<?> clazz) {
}

if (ReflectKit.hasInterface(clazz, BladeLoader.class) && null != clazz.getAnnotation(Bean.class)) {
this.loaders.add((BladeLoader) blade.ioc().getBean(clazz));
this.loaders.add((BladeLoader) blade.getBean(clazz));
}
if (ReflectKit.hasInterface(clazz, BeanProcessor.class) && null != clazz.getAnnotation(Bean.class)) {
this.processors.add((BeanProcessor) blade.ioc().getBean(clazz));
this.processors.add((BeanProcessor) blade.getBean(clazz));
}
if (isExceptionHandler(clazz)) {
ExceptionHandler exceptionHandler = (ExceptionHandler) blade.ioc().getBean(clazz);
ExceptionHandler exceptionHandler = (ExceptionHandler) blade.getBean(clazz);
blade.exceptionHandler(exceptionHandler);
}
WebSocket webSocket;
if(ReflectKit.hasInterface(clazz, WebSocketHandler.class) && null != (webSocket = clazz.getAnnotation(WebSocket.class))) {
if (null == blade.ioc().getBean(clazz)) {
if(null != (webSocket = clazz.getAnnotation(WebSocket.class))) {
if (null == blade.getBean(clazz)) {
blade.register(clazz);
}
Set<String> wsPath = new HashSet<>();
wsPath.addAll(Arrays.asList(webSocket.path()));
wsPath.addAll(Arrays.asList(webSocket.value()));
WebSocketHandler websocketHandler = (WebSocketHandler) blade.ioc().getBean(clazz);
wsPath.forEach(path->blade.webSocket(path,websocketHandler));
if(ReflectKit.hasInterface(clazz, WebSocketHandler.class)){
blade.webSocket(webSocket.value(),(WebSocketHandler)blade.getBean(clazz));
} else {
WebSocketHandlerWrapper wrapper = blade.getBean(WebSocketHandlerWrapper.class);
if(wrapper == null){
wrapper = WebSocketHandlerWrapper.init(blade);
blade.register(wrapper);
}
blade.webSocket(webSocket.value(),wrapper);
wrapper.wrapHandler(webSocket.value(),clazz);
}

}
}

Expand Down
13 changes: 12 additions & 1 deletion src/main/java/com/blade/server/netty/WebSocketHandler.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.blade.server.netty;

import com.blade.Blade;
import com.blade.mvc.handler.WebSocketHandlerWrapper;
import com.blade.mvc.websocket.WebSocketContext;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
Expand All @@ -20,6 +21,7 @@ public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {
private WebSocketServerHandshaker handshaker;
private WebSocketContext context;
private com.blade.mvc.handler.WebSocketHandler handler;
private String uri;
private Blade blade;


Expand All @@ -32,6 +34,7 @@ protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof HttpRequest) {
handleHttpRequest(ctx, (HttpRequest) msg);
} else if (msg instanceof WebSocketFrame) {
initHandlerWrapper();
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
} else {
ctx.fireChannelRead(msg);
Expand All @@ -53,7 +56,9 @@ private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) {
} else {
this.handshaker.handshake(ctx.channel(), req);
this.context = new WebSocketContext(ctx);
this.handler.onConnect(context);
this.uri = req.uri();
initHandlerWrapper();
this.handler.onConnect(this.context);
}
} else {
ctx.fireChannelRead(req);
Expand Down Expand Up @@ -92,4 +97,10 @@ private boolean isWebSocketRequest(HttpRequest req){
&& "websocket".equals(req.headers().get("Upgrade"));
}

private void initHandlerWrapper(){
if(this.handler != null && this.handler instanceof WebSocketHandlerWrapper){
((WebSocketHandlerWrapper) this.handler).setPath(this.uri);
}
}

}
24 changes: 24 additions & 0 deletions src/test/java/netty_hello/BaseWebSocketHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package netty_hello;

import com.blade.mvc.annotation.OnClose;
import com.blade.mvc.annotation.OnOpen;
import com.blade.mvc.websocket.WebSocketContext;

/**
* @author darren
* @description
* @date 2018/12/18 13:29
*/
public abstract class BaseWebSocketHandler {


@OnOpen
public void OnOpen(WebSocketContext ctx) {
System.out.println("ws from annotation @OnOpen:" + ctx.getSession().getUuid());
}

@OnClose
public void OnClose(WebSocketContext ctx) {
System.out.println("ws from annotation @OnClose:" + ctx.getSession().getUuid() + " disconnect");
}
}
14 changes: 10 additions & 4 deletions src/test/java/netty_hello/CustomWebSocketHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package netty_hello;

import com.blade.ioc.annotation.Inject;
import com.blade.mvc.annotation.WebSocket;
import com.blade.mvc.handler.WebSocketHandler;
import com.blade.mvc.websocket.WebSocketContext;
Expand All @@ -8,20 +9,25 @@
* @author darren
* @date 2018-12-10 21:27
*/
@WebSocket(value="/websocket_anno_value",path = {"/websocket_anno_path","/websocket_anno_value"})
@WebSocket("/ws_impl")
public class CustomWebSocketHandler implements WebSocketHandler {

@Inject
CService cService;

@Override
public void onConnect(WebSocketContext ctx) {
System.out.println("connect success:session="+ctx.getSession().getUuid());
cService.sayHello();
System.out.println("ws from implements interface:onConnect:"+ctx.getSession().getUuid());
}

@Override
public void onText(WebSocketContext ctx) {
System.out.println(ctx.getSession().getUuid() + " said:" + ctx.getReqText());
System.out.println("ws from implements interface:onText:"+ctx.getSession().getUuid() + " said:" + ctx.getReqText());
}

@Override
public void onDisConnect(WebSocketContext ctx) {
System.out.println(ctx.getSession().getUuid() + " disconnect");
System.out.println("ws from implements interface:onDisConnect:"+ctx.getSession().getUuid() + " disconnect");
}
}
19 changes: 19 additions & 0 deletions src/test/java/netty_hello/CustomWebSocketHandlerAnno.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package netty_hello;

import com.blade.mvc.annotation.OnMessage;
import com.blade.mvc.annotation.WebSocket;
import com.blade.mvc.websocket.WebSocketContext;

/**
* @author darren
* @description
* @date 2018/12/18 11:01
*/
@WebSocket("/ws_anno")
public class CustomWebSocketHandlerAnno extends BaseWebSocketHandler {

@OnMessage
public void OnMessage(WebSocketContext ctx) {
System.out.println("ws from annotation @OnMessage:" + ctx.getSession().getUuid() + " said:" + ctx.getReqText());
}
}
Loading

0 comments on commit ab801a5

Please sign in to comment.