WebSocket实现preOnMessage功能

This commit is contained in:
Redkale
2018-01-26 11:10:45 +08:00
parent 66baca51d7
commit 5c1c2b18e4
8 changed files with 152 additions and 53 deletions

View File

@@ -75,6 +75,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
ConvertColumnEntry ref; ConvertColumnEntry ref;
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue; if (Modifier.isStatic(field.getModifiers())) continue;
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(field); ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue; if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(field.getGenericType(), this.type); Type t = TypeToken.createClassType(field.getGenericType(), this.type);

View File

@@ -66,6 +66,7 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
ConvertColumnEntry ref; ConvertColumnEntry ref;
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue; if (Modifier.isStatic(field.getModifiers())) continue;
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(field); ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue; if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(field.getGenericType(), this.type); Type t = TypeToken.createClassType(field.getGenericType(), this.type);

View File

@@ -18,7 +18,7 @@ import org.redkale.asm.*;
import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES;
import static org.redkale.asm.Opcodes.*; import static org.redkale.asm.Opcodes.*;
import org.redkale.asm.Type; import org.redkale.asm.Type;
import org.redkale.convert.Convert; import org.redkale.convert.*;
import org.redkale.convert.json.*; import org.redkale.convert.json.*;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -249,7 +249,8 @@ public final class Rest {
messageMethods.add(method); messageMethods.add(method);
} }
//---------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------
final String convertDisabledDesc = Type.getDescriptor(ConvertDisabled.class);
final String webSocketParamName = Type.getInternalName(WebSocketParam.class);
final String supDynName = WebSocketServlet.class.getName().replace('.', '/'); final String supDynName = WebSocketServlet.class.getName().replace('.', '/');
final String webServletDesc = Type.getDescriptor(WebServlet.class); final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String webSocketInternalName = Type.getInternalName(webSocketType); final String webSocketInternalName = Type.getInternalName(webSocketType);
@@ -381,19 +382,19 @@ public final class Rest {
RestClassLoader newLoader = new RestClassLoader(loader); RestClassLoader newLoader = new RestClassLoader(loader);
for (int i = 0; i < messageMethods.size(); i++) { // _DyncXXXWebSocketMessage List for (int i = 0; i < messageMethods.size(); i++) { // _DyncXXXWebSocketMessage 子消息List
Method method = messageMethods.get(i); Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES);
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName + endfix, null, "java/lang/Object", null); cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName + endfix, null, "java/lang/Object", new String[]{webSocketParamName, "java/lang/Runnable"});
cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC); cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC);
Set<String> paramnames = new HashSet<>(); Set<String> paramnames = new HashSet<>();
String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method); String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method);
List<String> names = asmParamMap.get(methodesc); List<String> names = asmParamMap.get(methodesc);
Parameter[] params = method.getParameters(); Parameter[] params = method.getParameters();
for (int j = 0; j < params.length; j++) { final LinkedHashMap<String, Parameter> paramap = new LinkedHashMap(); //必须使用LinkedHashMap确保顺序
for (int j = 0; j < params.length; j++) { //字段列表
Parameter param = params[j]; Parameter param = params[j];
String paramname = param.getName(); String paramname = param.getName();
RestParam rp = param.getAnnotation(RestParam.class); RestParam rp = param.getAnnotation(RestParam.class);
@@ -404,11 +405,18 @@ public final class Rest {
} }
if (paramnames.contains(paramname)) throw new RuntimeException(method + " has same @RestParam.name"); if (paramnames.contains(paramname)) throw new RuntimeException(method + " has same @RestParam.name");
paramnames.add(paramname); paramnames.add(paramname);
paramap.put(paramname, param);
fv = cw2.visitField(ACC_PUBLIC, paramname, Type.getDescriptor(param.getType()), fv = cw2.visitField(ACC_PUBLIC, paramname, Type.getDescriptor(param.getType()),
param.getType() == param.getParameterizedType() ? null : Utility.getTypeDescriptor(param.getParameterizedType()), null); param.getType() == param.getParameterizedType() ? null : Utility.getTypeDescriptor(param.getParameterizedType()), null);
fv.visitEnd(); fv.visitEnd();
} }
{ //构造函数 { //_redkale_websocket
fv = cw2.visitField(ACC_PUBLIC, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";", null, null);
av0 = fv.visitAnnotation(convertDisabledDesc, true);
av0.visitEnd();
fv.visitEnd();
}
{ //空构造函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)); mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
@@ -416,6 +424,59 @@ public final class Rest {
mv.visitMaxs(1, 1); mv.visitMaxs(1, 1);
mv.visitEnd(); mv.visitEnd();
} }
{ //getValue
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "getValue", "(Ljava/lang/String;)Ljava/lang/Object;", "<T:Ljava/lang/Object;>(Ljava/lang/String;)TT;", null));
for (Map.Entry<String, Parameter> en : paramap.entrySet()) {
Class paramType = en.getValue().getType();
mv.visitLdcInsn(en.getKey());
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
Label l1 = new Label();
mv.visitJumpInsn(IFEQ, l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName + endfix, en.getKey(), Type.getDescriptor(paramType));
if (paramType.isPrimitive()) {
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(paramType, 1), 0).getClass();
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(paramType) + ")" + Type.getDescriptor(bigclaz), false);
}
mv.visitInsn(ARETURN);
mv.visitLabel(l1);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
{ //execute
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "execute", "(L" + newDynWebSokcetFullName + ";)V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, newDynMessageFullName + endfix, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";");
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(method.getAnnotation(RestOnMessage.class).name());
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, "preOnMessage", "(Ljava/lang/String;Lorg/redkale/net/http/WebSocketParam;Ljava/lang/Runnable;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(4, 2);
mv.visitEnd();
}
{ //run
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "run", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName + endfix, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";");
for (Map.Entry<String, Parameter> en : paramap.entrySet()) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, (newDynMessageFullName + endfix), en.getKey(), Type.getDescriptor(en.getValue().getType()));
}
mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, method.getName(), Type.getMethodDescriptor(method), false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 1);
mv.visitEnd();
}
{ //toString { //toString
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv.visitMethodInsn(INVOKESTATIC, "org/redkale/convert/json/JsonConvert", "root", "()Lorg/redkale/convert/json/JsonConvert;", false); mv.visitMethodInsn(INVOKESTATIC, "org/redkale/convert/json/JsonConvert", "root", "()Lorg/redkale/convert/json/JsonConvert;", false);
@@ -523,32 +584,16 @@ public final class Rest {
final Method method = messageMethods.get(i); final Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
final String messagename = method.getAnnotation(RestOnMessage.class).name(); final String messagename = method.getAnnotation(RestOnMessage.class).name();
String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method);
List<String> names = asmParamMap.get(methodesc);
Parameter[] params = method.getParameters();
mv.visitVarInsn(ALOAD, 4); mv.visitVarInsn(ALOAD, 4);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";"); mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
Label ifLabel = new Label(); Label ifLabel = new Label();
mv.visitJumpInsn(IFNULL, ifLabel); mv.visitJumpInsn(IFNULL, ifLabel);
mv.visitVarInsn(ALOAD, 4);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
mv.visitVarInsn(ALOAD, 3); mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, (newDynMessageFullName + endfix), "execute", "(L" + newDynWebSokcetFullName + ";)V", false);
for (int j = 0; j < params.length; j++) {
Parameter param = params[j];
String paramname = param.getName();
RestParam rp = param.getAnnotation(RestParam.class);
if (rp != null && !rp.name().isEmpty()) {
paramname = rp.name();
} else if (names != null && names.size() > j) {
paramname = names.get(j);
}
mv.visitVarInsn(ALOAD, 4);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
mv.visitFieldInsn(GETFIELD, (newDynMessageFullName + endfix), paramname, Type.getDescriptor(param.getType()));
}
mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, method.getName(), Type.getMethodDescriptor(method), false);
mv.visitInsn(RETURN); mv.visitInsn(RETURN);
mv.visitLabel(ifLabel); mv.visitLabel(ifLabel);
} }

View File

@@ -695,15 +695,15 @@ public abstract class WebSocket<G extends Serializable, T> {
} }
/** /**
* 尚未实现~~
* *
* 接收到消息前的拦截方法, ping/pong不在其内 <br> * 接收到消息前的拦截方法, ping/pong不在其内 <br>
* 注意:处理完后需要调用 messageEvent.run() 才能响应onMessage * 注意:处理完后需要调用 messageEvent.run() 才能响应onMessage
* *
* @param restmapping Rest的方法名没有则为空字符串 * @param restmapping Rest的方法名没有则为空字符串
* @param param onMessage方法的参数
* @param messageEvent onMessage事件 * @param messageEvent onMessage事件
*/ */
public void preOnMessage(String restmapping, Runnable messageEvent) { public void preOnMessage(String restmapping, WebSocketParam param, Runnable messageEvent) {
messageEvent.run(); messageEvent.run();
} }

View File

@@ -0,0 +1,19 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
/**
*
* 供WebSocket.preOnMessage 方法获取RestWebSocket里OnMessage方法的参数 <br>
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public interface WebSocketParam {
public <T> T getValue(String name);
}

View File

@@ -307,23 +307,6 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
return null; return null;
} }
//供Rest构建RestWebSocket时使用
protected static interface MessageEventRunner extends Runnable {
public void execute() throws Throwable;
@Override
default void run() {
try {
execute();
} catch (RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
private static MessageDigest getMessageDigest() { private static MessageDigest getMessageDigest() {
try { try {
return MessageDigest.getInstance("SHA-1"); return MessageDigest.getInstance("SHA-1");

View File

@@ -5,7 +5,8 @@
*/ */
package org.redkale.test.ws; package org.redkale.test.ws;
import java.util.Map; import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Resource; import javax.annotation.Resource;
@@ -42,4 +43,13 @@ public class ChatWebSocket extends WebSocket<Integer, Object> {
System.out.println("加入房间: roomid: " + roomid); System.out.println("加入房间: roomid: " + roomid);
} }
public static void main(String[] args) throws Throwable {
Method method = Arrays.asList(Rest.class.getDeclaredMethods())
.stream().filter(m -> "createRestWebSocketServlet".equals(m.getName()))
.findFirst().get();
method.setAccessible(true);
System.out.println(method.invoke(null, Thread.currentThread().getContextClassLoader(), ChatWebSocket.class));
}
} }

View File

@@ -9,6 +9,7 @@ import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.redkale.convert.ConvertDisabled;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.*; import org.redkale.net.http.*;
import org.redkale.test.ws.ChatMessage; import org.redkale.test.ws.ChatMessage;
@@ -50,9 +51,9 @@ public final class _DyncChatWebSocketServlet extends WebSocketServlet {
public static class _DyncChatWebSocketMessage { public static class _DyncChatWebSocketMessage {
public _DyncChatWebSocketMessage_sendmessagee sendmessage; public _DyncChatWebSocketMessage_sendmessagee_00 sendmessage;
public _DyncChatWebSocketMessage_joinroom joinroom; public _DyncChatWebSocketMessage_joinroom_01 joinroom;
@Override @Override
public String toString() { public String toString() {
@@ -60,22 +61,61 @@ public final class _DyncChatWebSocketServlet extends WebSocketServlet {
} }
} }
public static class _DyncChatWebSocketMessage_sendmessagee { public static class _DyncChatWebSocketMessage_sendmessagee_00 implements WebSocketParam, Runnable {
public ChatMessage message; public ChatMessage message;
public Map<String, String> extmap; public Map<String, String> extmap;
@ConvertDisabled
public _DyncChatWebSocket _redkale_websocket;
@Override
public <T> T getValue(String name) {
if ("message".equals(name)) return (T) message;
if ("extmap".equals(name)) return (T) extmap;
return null;
}
public void execute(_DyncChatWebSocket websocket) {
this._redkale_websocket = websocket;
websocket.preOnMessage("sendmessage", this, this);
}
@Override
public void run() {
_redkale_websocket.onChatMessage(this.message, this.extmap);
}
@Override @Override
public String toString() { public String toString() {
return JsonConvert.root().convertTo(this); return JsonConvert.root().convertTo(this);
} }
} }
public static class _DyncChatWebSocketMessage_joinroom { public static class _DyncChatWebSocketMessage_joinroom_01 implements WebSocketParam, Runnable {
public int roomid; public int roomid;
@ConvertDisabled
public _DyncChatWebSocket _redkale_websocket;
@Override
public <T> T getValue(String name) {
if ("roomid".equals(name)) return (T) (Integer) roomid;
return null;
}
public void execute(_DyncChatWebSocket websocket) {
this._redkale_websocket = websocket;
websocket.preOnMessage("joinroom", this, this);
}
@Override
public void run() {
_redkale_websocket.onJoinRoom(this.roomid);
}
@Override @Override
public String toString() { public String toString() {
return JsonConvert.root().convertTo(this); return JsonConvert.root().convertTo(this);
@@ -89,11 +129,11 @@ public final class _DyncChatWebSocketServlet extends WebSocketServlet {
_DyncChatWebSocket websocket = (_DyncChatWebSocket) websocket0; _DyncChatWebSocket websocket = (_DyncChatWebSocket) websocket0;
_DyncChatWebSocketMessage message = (_DyncChatWebSocketMessage) message0; _DyncChatWebSocketMessage message = (_DyncChatWebSocketMessage) message0;
if (message.sendmessage != null) { if (message.sendmessage != null) {
websocket.onChatMessage(message.sendmessage.message, message.sendmessage.extmap); message.sendmessage.execute(websocket);
return; return;
} }
if (message.joinroom != null) { if (message.joinroom != null) {
websocket.onJoinRoom(message.joinroom.roomid); message.joinroom.execute(websocket);
return; return;
} }
} }