com.weibo.api.motan.protocol.v2motan.MotanV2Codec.java Source code

Java tutorial

Introduction

Here is the source code for com.weibo.api.motan.protocol.v2motan.MotanV2Codec.java

Source

/*
 *  Copyright 2009-2016 Weibo, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.weibo.api.motan.protocol.v2motan;

import com.weibo.api.motan.codec.AbstractCodec;
import com.weibo.api.motan.codec.Serialization;
import com.weibo.api.motan.common.URLParamType;
import com.weibo.api.motan.core.extension.ExtensionLoader;
import com.weibo.api.motan.core.extension.SpiMeta;
import com.weibo.api.motan.exception.MotanErrorMsgConstant;
import com.weibo.api.motan.exception.MotanFrameworkException;
import com.weibo.api.motan.exception.MotanServiceException;
import com.weibo.api.motan.rpc.DefaultRequest;
import com.weibo.api.motan.rpc.DefaultResponse;
import com.weibo.api.motan.rpc.Request;
import com.weibo.api.motan.rpc.Response;
import com.weibo.api.motan.serialize.DeserializableObject;
import com.weibo.api.motan.transport.Channel;
import com.weibo.api.motan.transport.support.DefaultRpcHeartbeatFactory;
import com.weibo.api.motan.util.ByteUtil;
import com.weibo.api.motan.util.ExceptionUtil;
import com.weibo.api.motan.util.LoggerUtil;
import com.weibo.api.motan.util.MathUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;

import static com.weibo.api.motan.common.MotanConstants.*;

@SpiMeta(name = "motan2")
public class MotanV2Codec extends AbstractCodec {

    private static final byte MASK = 0x07;
    private static final int HEADER_SIZE = 13;

    static {
        initAllSerialziation();
    }

    @Override
    public byte[] encode(Channel channel, Object message) throws IOException {
        try {
            if (DefaultRpcHeartbeatFactory.isHeartbeatRequest(message)) {
                return encodeHeartbeat(((Request) message).getRequestId(), true);
            }
            if (DefaultRpcHeartbeatFactory.isHeartbeatResponse(message)) {
                return encodeHeartbeat(((Response) message).getRequestId(), false);
            }
            MotanV2Header header = new MotanV2Header();
            byte[] body = null;
            String serialName = channel.getUrl().getParameter(URLParamType.serialize.getName(),
                    URLParamType.serialize.getValue());
            Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class)
                    .getExtension(serialName);
            if (serialization == null) {
                throw new MotanServiceException("can not found serialization " + serialName);
            }
            header.setSerialize(serialization.getSerializationNumber());

            GrowableByteBuffer buf = new GrowableByteBuffer(4096);
            //meta
            int index = HEADER_SIZE;
            buf.position(index);
            buf.putInt(0);//metasize

            if (message instanceof Request) {
                Request request = (Request) message;
                putString(buf, M2_PATH);
                putString(buf, request.getInterfaceName());
                putString(buf, M2_METHOD);
                putString(buf, request.getMethodName());
                if (request.getParamtersDesc() != null) {
                    putString(buf, M2_METHOD_DESC);
                    putString(buf, request.getParamtersDesc());
                }
                if (request.getAttachments() != null
                        && request.getAttachments().get(URLParamType.group.getName()) != null) {
                    request.setAttachment(M2_GROUP, request.getAttachments().get(URLParamType.group.getName()));
                }

                putMap(buf, request.getAttachments());

                header.setRequestId(request.getRequestId());
                if (request.getArguments() != null) {
                    body = serialization.serializeMulti(request.getArguments());
                }

            } else if (message instanceof Response) {
                Response response = (Response) message;
                putString(buf, M2_PROCESS_TIME);
                putString(buf, String.valueOf(response.getProcessTime()));
                if (response.getException() != null) {
                    putString(buf, M2_ERROR);
                    putString(buf, ExceptionUtil.toMessage(response.getException()));
                    header.setStatus(MotanV2Header.MessageStatus.EXCEPTION.getStatus());
                }
                putMap(buf, response.getAttachments());

                header.setRequestId(response.getRequestId());
                header.setRequest(false);
                if (response.getException() == null) {
                    body = serialization.serialize(response.getValue());
                }
            }

            buf.position(buf.position() - 1);
            int metalength = buf.position() - index - 4;
            buf.putInt(index, metalength);

            //body
            if (body != null && body.length > 0) {
                if (channel.getUrl().getBooleanParameter(URLParamType.usegz.getName(),
                        URLParamType.usegz.getBooleanValue())
                        && body.length > channel.getUrl().getIntParameter(URLParamType.mingzSize.getName(),
                                URLParamType.mingzSize.getIntValue())) {
                    try {
                        body = ByteUtil.gzip(body);
                        header.setGzip(true);
                    } catch (IOException e) {
                        LoggerUtil.warn("MotanV2Codec encode gzip fail. so not gzip body.", e);
                    }
                }
                buf.putInt(body.length);
                buf.put(body);
            } else {
                buf.putInt(0);
            }

            //header
            int position = buf.position();
            buf.position(0);
            buf.put(header.toBytes());
            buf.position(position);
            buf.flip();
            byte[] result = new byte[buf.remaining()];
            buf.get(result);
            return result;
        } catch (Exception e) {
            String errmsg = "";
            if (message != null) {
                if (message instanceof Request) {
                    errmsg = "type:request, " + message.toString();
                } else {
                    errmsg = "type:response, " + message.toString();
                }
            }
            LoggerUtil.warn("motan2 encode error." + errmsg, e);
            if (ExceptionUtil.isMotanException(e)) {
                throw (RuntimeException) e;
            } else {
                throw new MotanFrameworkException("encode error!" + errmsg + ", origin errmsg:" + e.getMessage(), e,
                        MotanErrorMsgConstant.FRAMEWORK_ENCODE_ERROR);
            }
        }
    }

    private void putString(GrowableByteBuffer buf, String content) throws UnsupportedEncodingException {
        buf.put(content.getBytes("UTF-8"));
        buf.put("\n".getBytes("UTF-8"));
    }

    private void putMap(GrowableByteBuffer buf, Map<String, String> map) throws UnsupportedEncodingException {
        if (!map.isEmpty()) {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                putString(buf, entry.getKey());
                putString(buf, entry.getValue());
            }
        }
    }

    private byte[] encodeHeartbeat(long requestId, boolean isRequest) {
        MotanV2Header header = new MotanV2Header();
        header.setHeartbeat(true);
        header.setRequestId(requestId);
        if (!isRequest) {
            header.setRequest(false);
        }
        GrowableByteBuffer buf = new GrowableByteBuffer(32);
        buf.put(header.toBytes());
        buf.putInt(0);//metasize
        buf.putInt(0);//bodysize
        buf.flip();
        byte[] result = new byte[buf.remaining()];
        buf.get(result);
        return result;
    }

    /**
     * decode data
     *
     * @return
     * @throws IOException
     */
    @Override
    public Object decode(Channel channel, String remoteIp, byte[] data) throws IOException {
        MotanV2Header header = MotanV2Header.buildHeader(data);
        Map<String, String> metaMap = new HashMap<String, String>();
        ByteBuffer buf = ByteBuffer.wrap(data);
        int metaSize = buf.getInt(HEADER_SIZE);
        int index = HEADER_SIZE + 4;
        if (metaSize > 0) {
            byte[] meta = new byte[metaSize];
            buf.position(index);
            buf.get(meta);
            metaMap = decodeMeta(meta);
            index += metaSize;
        }
        int bodySize = buf.getInt(index);
        index += 4;
        Object obj = null;
        if (bodySize > 0) {
            byte[] body = new byte[bodySize];
            buf.position(index);
            buf.get(body);
            if (header.isGzip()) {
                body = ByteUtil.unGzip(body);
            }
            //?
            Serialization serialization = getSerializaiontByNum(header.getSerialize());
            obj = new DeserializableObject(serialization, body);
        }
        if (header.isRequest()) {
            if (header.isHeartbeat()) {
                return DefaultRpcHeartbeatFactory.getDefaultHeartbeatRequest(header.getRequestId());
            } else {
                DefaultRequest request = new DefaultRequest();
                request.setRequestId(header.getRequestId());
                request.setInterfaceName(metaMap.remove(M2_PATH));
                request.setMethodName(metaMap.remove(M2_METHOD));
                request.setParamtersDesc(metaMap.remove(M2_METHOD_DESC));
                request.setAttachments(metaMap);
                if (obj != null) {
                    request.setArguments(new Object[] { obj });
                }
                if (metaMap.get(M2_GROUP) != null) {
                    request.setAttachment(URLParamType.group.getName(), metaMap.get(M2_GROUP));
                }

                if (StringUtils.isNotBlank(metaMap.get(M2_VERSION))) {
                    request.setAttachment(URLParamType.version.getName(), metaMap.get(M2_VERSION));
                }

                return request;
            }

        } else {
            if (header.isHeartbeat()) {
                return DefaultRpcHeartbeatFactory.getDefaultHeartbeatResponse(header.getRequestId());
            }
            DefaultResponse response = new DefaultResponse();
            response.setRequestId(header.getRequestId());
            response.setProcessTime(MathUtil.parseLong(metaMap.remove(M2_PROCESS_TIME), 0));
            response.setAttachments(metaMap);
            if (header.getStatus() == MotanV2Header.MessageStatus.NORMAL.getStatus()) {//???
                response.setValue(obj);
            } else {
                String errmsg = metaMap.remove(M2_ERROR);
                Exception e = ExceptionUtil.fromMessage(errmsg);
                if (e == null) {
                    e = (Exception) new MotanServiceException("default remote exception. remote errmsg:" + errmsg);
                }
                response.setException(e);
            }
            return response;
        }

    }

    private Map<String, String> decodeMeta(byte[] meta) {
        Map<String, String> map = new HashMap<String, String>();
        if (meta != null && meta.length > 0) {
            String[] s = new String(meta).split("\n");
            for (int i = 0; i < s.length - 1; i++) {
                map.put(s[i++], s[i]);
            }
        }
        return map;
    }
}