package com.wangyin.ak47.pipes.dubbo;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;
import com.wangyin.ak47.common.ByteUtil;
import com.wangyin.ak47.common.Logger;
import com.wangyin.ak47.core.Pipe;
import com.wangyin.ak47.core.Request;
import com.wangyin.ak47.core.Response;
import com.wangyin.ak47.core.Buffer;
import com.wangyin.ak47.core.event.SingleThreadEventExecutor;
import com.wangyin.ak47.core.exception.Ak47RuntimeException;
/**
* 所有dubbo协议头的接口请继承此Pipe。
*
* dubbo协议头
* 参见:\dubbo\dubbo-remoting\dubbo-remoting-api\src\main\java\com\alibaba\dubbo\remoting\exchange\codec\ExchangeCodec.java
*
* 一共16个字节:
* [0..1] : magic number (short) 0xdabb
* [2] : flag (byte) bit map: FLAG_REQUEST/FLAG_EVENT/FLAG_TWOWAY/HEARTBEAT_EVENT/SERIALIZATION_MASK/...
* [3] : status (byte) 20 means OK
* [4..11] : request id (long)
* [12..15] : body length (int)
*
*
* @author wyhanyu
*
*/
public abstract class AbstractDubboPipe<Q, R> extends Pipe<Q, R> {
private static final Logger log = new Logger(AbstractDubboPipe.class);
// dubbo header
protected static final byte STATUS_OK = 20;
protected static final short MAGIC_NUMBER = (short) 0xdabb;
protected static final int HEADER_LENGTH = 16;
// message flag.
protected static final byte FLAG_REQUEST = (byte) 0x80;
protected static final byte FLAG_TWOWAY = (byte) 0x40;
protected static final byte FLAG_EVENT = (byte) 0x20;
protected static final int SERIALIZATION_MASK = 0x1f;
// attr key
protected static final String KEY_CURRENT_REQUEST_ID = "currentRequestId";
protected static AtomicLong globalGeneratedRequestId = new AtomicLong(0);
@Override
public void decodeRequest(Buffer buf, Request<Q> request) throws Exception {
DubboData dd = new DubboData();
if( !decodeDubboData(buf, dd) ){
return;
}
decodeDubboRequest(dd, request);
// setAttr requestId
request.requestAttr().set(KEY_CURRENT_REQUEST_ID, dd.getDubboHeader().getRequestId());
}
/**
* DubboData →→→ POJO
*
* @param dd DubboData
* @param request Request with POJO
* @throws Exception in any case
*/
public abstract void decodeDubboRequest(DubboData dd, Request<Q> request)
throws Exception;
@Override
public void encodeRequest(Request<Q> request, Buffer buf) throws Exception {
DubboData dd = new DubboData();
encodeDubboRequest(request, dd);
DubboHeader dh = dd.getDubboHeader();
// set RequestId
if( dh.getRequestId() == 0 ){
dh.setRequestId(globalGeneratedRequestId.getAndIncrement());
}
// set bodylength
dh.setBodyLength(dd.getBody().length);
encodeDubboData(dd, buf);
}
/**
* POJO →→→ DubboData
*
* @param request Request with POJO
* @param dd DubboData
* @throws Exception in any case
*/
public abstract void encodeDubboRequest(Request<Q> request, DubboData dd)
throws Exception;
@Override
public void decodeResponse(Buffer buf, Response<R> response) throws Exception {
DubboData dd = new DubboData();
if( !decodeDubboData(buf, dd) ){
return;
}
decodeDubboResponse(dd, response);
}
/**
* DubboData →→→ POJO
*
* @param dd DubboData
* @param response Response with POJO
* @throws Exception in any case
*/
public abstract void decodeDubboResponse(DubboData dd, Response<R> response)
throws Exception;
@Override
public void encodeResponse(Response<R> response, Buffer buf) throws Exception {
DubboData dd = new DubboData();
encodeDubboResponse(response, dd);
DubboHeader dh = dd.getDubboHeader();
// set status
if( dh.getStatus() == 0 ){
dh.setStatus(STATUS_OK);
}
Long requestId = (Long) response.requestAttr().get(KEY_CURRENT_REQUEST_ID);
if( requestId != null ){
dh.setRequestId(requestId);
}
// set bodylength
dh.setBodyLength(dd.getBody().length);
encodeDubboData(dd, buf);
}
/**
* POJO →→→ DubboData
*
* @param response Response with POJO
* @param dd DubboData
* @throws Exception in any case
*/
public abstract void encodeDubboResponse(Response<R> response, DubboData dd)
throws Exception;
/**
* Buffer →→→ DubboData
*
* @param buf Buffer
* @param dd DubboData
* @return if decode success
*/
private boolean decodeDubboData(Buffer buf, DubboData dd){
int readableBytes = buf.readableBytes();
// header length
if( readableBytes < HEADER_LENGTH ){
log.debug("Need more read for header.");
return false;
}
byte[] bytes = new byte[readableBytes];
int readerIndex = buf.readerIndex();
buf.readBytes(bytes);
// magic number
short magicNumber = ByteUtil.bytes2short(bytes);
if( magicNumber != MAGIC_NUMBER ){
log.error("Magic number fail, close connection.");
// NOT rollback
throw new Ak47RuntimeException("Wrong magic number["+magicNumber+"] expected ["+MAGIC_NUMBER+"]");
}
// body length
int bodyLength = ByteUtil.bytes2int(bytes, 12);
if( bytes.length < HEADER_LENGTH + bodyLength ){
log.debug("Need more read for body.");
buf.readerIndex(readerIndex);
return false;
}
// set header
DubboHeader dh = dd.getDubboHeader();
dh.setMagicNumber(magicNumber);
dh.setFlag(bytes[2]);
dh.setStatus(bytes[3]);
dh.setRequestId(ByteUtil.bytes2long(bytes,4));
dh.setBodyLength(bodyLength);
// set body
dd.setBody(ByteUtil.copyOf(bytes, HEADER_LENGTH, bodyLength));
// pipeline
if( bytes.length > HEADER_LENGTH + bodyLength ){
log.debug("Detect pipeline.");
buf.readerIndex(readerIndex + HEADER_LENGTH + bodyLength);
}
// log.error("decodeDubboData: \n{}", YmlUtil.obj2PrettyYml(dd));
return true;
}
/**
* DubboData →→→ Buffer
*
* @param dd DubboData
* @param buf Buffer
* @return if decode success
*/
private boolean encodeDubboData(DubboData dd, Buffer buf){
DubboHeader dh = dd.getDubboHeader();
byte[] headerBytes = new byte[HEADER_LENGTH];
byte[] magicNumber = ByteUtil.short2bytes( dh.getMagicNumber() );
ByteUtil.copy(magicNumber, 0, headerBytes, 0, 2);
byte flag = dh.getFlag();
ByteUtil.copy(flag,headerBytes,2);
byte status = dh.getStatus();
ByteUtil.copy(status, headerBytes, 3);
byte[] requestId = ByteUtil.long2bytes( dh.getRequestId() );
ByteUtil.copy(requestId, 0, headerBytes, 4, 8);
byte[] bodyLength = ByteUtil.int2bytes( dh.getBodyLength() );
ByteUtil.copy(bodyLength, 0, headerBytes, 12, 4);
buf.writeBytes(headerBytes);
buf.writeBytes(dd.getBody());
// log.error("encodeDubboData: \n{}", YmlUtil.obj2PrettyYml(dd));
return true;
}
/**
* Read buf of one request
*
* @param buf Buffer of all
* @return Buffer of one request
*/
private Buffer readOne(Buffer buf){
int readerIndex = buf.readerIndex();
int readableBytes = buf.readableBytes();
// header length
if( readableBytes < HEADER_LENGTH ){
// log.debug("Need more read for header.");
return null;
}
// read header
byte[] headerBytes = new byte[HEADER_LENGTH];
buf.getBytes(readerIndex, headerBytes);
int bodyLength = ByteUtil.bytes2int(headerBytes, 12);
int reqLength = HEADER_LENGTH + bodyLength;
if( readableBytes < reqLength ){
// log.debug("Need more read for body.");
return null;
}else{
Buffer copybuf = buf.copy(readerIndex, reqLength);
buf.readerIndex(readerIndex + reqLength);
return copybuf;
}
}
@Override
public List<Buffer> split(Buffer buf){
List<Buffer> bufs = new LinkedList<Buffer>();
Buffer one = readOne(buf);
while( null != one ){
bufs.add(one);
one = readOne(buf);
}
return bufs;
}
@Override
public Executor newExecutor(){
return new SingleThreadEventExecutor();
}
}