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;
for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue;
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;
for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue;
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.Opcodes.*;
import org.redkale.asm.Type;
import org.redkale.convert.Convert;
import org.redkale.convert.*;
import org.redkale.convert.json.*;
import org.redkale.service.*;
import org.redkale.util.*;
@@ -249,7 +249,8 @@ public final class Rest {
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 webServletDesc = Type.getDescriptor(WebServlet.class);
final String webSocketInternalName = Type.getInternalName(webSocketType);
@@ -381,19 +382,19 @@ public final class Rest {
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);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
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);
Set<String> paramnames = new HashSet<>();
String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method);
List<String> names = asmParamMap.get(methodesc);
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];
String paramname = param.getName();
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");
paramnames.add(paramname);
paramap.put(paramname, param);
fv = cw2.visitField(ACC_PUBLIC, paramname, Type.getDescriptor(param.getType()),
param.getType() == param.getParameterizedType() ? null : Utility.getTypeDescriptor(param.getParameterizedType()), null);
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.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
@@ -416,6 +424,59 @@ public final class Rest {
mv.visitMaxs(1, 1);
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
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);
@@ -523,32 +584,16 @@ public final class Rest {
final Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
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.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
Label ifLabel = new Label();
mv.visitJumpInsn(IFNULL, ifLabel);
mv.visitVarInsn(ALOAD, 4);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
mv.visitVarInsn(ALOAD, 3);
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.visitMethodInsn(INVOKEVIRTUAL, (newDynMessageFullName + endfix), "execute", "(L" + newDynWebSokcetFullName + ";)V", false);
mv.visitInsn(RETURN);
mv.visitLabel(ifLabel);
}

View File

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

View File

@@ -5,7 +5,8 @@
*/
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.atomic.AtomicInteger;
import javax.annotation.Resource;
@@ -42,4 +43,13 @@ public class ChatWebSocket extends WebSocket<Integer, Object> {
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.function.BiConsumer;
import javax.annotation.Resource;
import org.redkale.convert.ConvertDisabled;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.*;
import org.redkale.test.ws.ChatMessage;
@@ -50,9 +51,9 @@ public final class _DyncChatWebSocketServlet extends WebSocketServlet {
public static class _DyncChatWebSocketMessage {
public _DyncChatWebSocketMessage_sendmessagee sendmessage;
public _DyncChatWebSocketMessage_sendmessagee_00 sendmessage;
public _DyncChatWebSocketMessage_joinroom joinroom;
public _DyncChatWebSocketMessage_joinroom_01 joinroom;
@Override
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 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
public String toString() {
return JsonConvert.root().convertTo(this);
}
}
public static class _DyncChatWebSocketMessage_joinroom {
public static class _DyncChatWebSocketMessage_joinroom_01 implements WebSocketParam, Runnable {
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
public String toString() {
return JsonConvert.root().convertTo(this);
@@ -89,11 +129,11 @@ public final class _DyncChatWebSocketServlet extends WebSocketServlet {
_DyncChatWebSocket websocket = (_DyncChatWebSocket) websocket0;
_DyncChatWebSocketMessage message = (_DyncChatWebSocketMessage) message0;
if (message.sendmessage != null) {
websocket.onChatMessage(message.sendmessage.message, message.sendmessage.extmap);
message.sendmessage.execute(websocket);
return;
}
if (message.joinroom != null) {
websocket.onJoinRoom(message.joinroom.roomid);
message.joinroom.execute(websocket);
return;
}
}