package org.codesharp.traffic.netty; import java.util.Map; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.util.CharsetUtil; import org.codesharp.traffic.Asserter; import org.codesharp.traffic.Commands; import org.codesharp.traffic.Status; import org.codesharp.traffic.drpc.DRPCMessageHandle; import com.google.gson.Gson; /*** * * https://github.com/wsky/top-traffic/issues/2#issuecomment-66725466 * * DRPC message protocol * * { type:'REQ', id:0, to:'dst' } * { type:'REP', id:0 } */ public class DRPCMessageHandleImpl extends MessageHandleImpl implements DRPCMessageHandle { public final static String TYPE = "type"; public final static String REQ = "REQ"; public final static String REP = "REP"; public final static String ID = "id"; public final static String TO = "to"; private Gson gson = new Gson(); public DRPCMessageHandleImpl(ByteBufAllocator allocator) { super(allocator); } public Object resolve(Object msg) { if (msg instanceof String) return this.resolve((String) msg); if (msg instanceof TextWebSocketFrame) return this.resolve((TextWebSocketFrame) msg); throw Asserter.unsupported(msg); } public Map<?, ?> resolve(TextWebSocketFrame msg) { return this.resolve(msg.text()); } public Map<?, ?> resolve(String msg) { return (Map<?, ?>) this.gson.fromJson((String) msg, Object.class); } public boolean isRequest(Object msg) { return ((Map<?, ?>) msg).get(TYPE).equals(REQ); } public boolean isReply(Object msg) { return ((Map<?, ?>) msg).get(TYPE).equals(REP); } public long getId(Map<?, ?> msg) { return this.getLong(msg, ID); } @SuppressWarnings({ "unchecked", "rawtypes" }) public void setId(Map msg, long id) { msg.put(ID, id); } public Object getReplyId(Object reply) { return this.getId((Map<?, ?>) reply); } @SuppressWarnings({ "rawtypes" }) public Object newMessage(Object request) { Map msg = (Map) request; long dst = this.getLong(msg, TO); long inId = this.getId(msg); long outId = inId; // FIXME generaete global outId this.setId(msg, outId); return this.newMessage( Commands.MSG, Status.NORMAL, dst, 4, this.parseBody(msg), inId, outId); } @SuppressWarnings({ "rawtypes" }) public Object newAck(Object reply, Object msg) { Map replyMsg = (Map) reply; ByteBuf buf = (ByteBuf) msg; this.setId(replyMsg, this.getIncomeId(buf)); this.setCommand(buf, Commands.ACK); this.setBody(buf, this.parseBody(replyMsg)); return buf; } public long getIncomeId(Object msg) { return (Long) this.getHeader(msg, 0); } public Long getOutcomeId(Object msg) { // HACK retain buffer reused for reply ((ByteBuf) msg).retain(); return (Long) this.getHeader(msg, 1); } public Object getBody(Object msg) { return this.getMessageBody(msg); } @Override public Object unknownDestination(Object msg) { super.unknownDestination(msg); // FIXME support errorcode at fontend this.setBody((ByteBuf) msg, "{error:'unknownDestination'}".getBytes(CharsetUtil.UTF_8)); return msg; } @SuppressWarnings({ "rawtypes" }) private long getLong(Map msg, String key) { return msg.containsKey(key) ? ((Number) msg.get(key)).longValue() : -1L; } private byte[] parseBody(Object msg) { return this.gson.toJson(msg).getBytes(CharsetUtil.UTF_8); } }