com.weibo.api.motan.protocol.rpc.CompressRpcCodec.java Source code

Java tutorial

Introduction

Here is the source code for com.weibo.api.motan.protocol.rpc.CompressRpcCodec.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.rpc;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.lang3.StringUtils;

import com.weibo.api.motan.codec.AbstractCodec;
import com.weibo.api.motan.codec.Serialization;
import com.weibo.api.motan.common.MotanConstants;
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.rpc.DefaultRequest;
import com.weibo.api.motan.rpc.DefaultResponse;
import com.weibo.api.motan.rpc.Provider;
import com.weibo.api.motan.rpc.Request;
import com.weibo.api.motan.rpc.Response;
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.ConcurrentHashSet;
import com.weibo.api.motan.util.ExceptionUtil;
import com.weibo.api.motan.util.LoggerUtil;
import com.weibo.api.motan.util.MotanDigestUtil;
import com.weibo.api.motan.util.MotanFrameworkUtil;
import com.weibo.api.motan.util.MotanSwitcherUtil;
import com.weibo.api.motan.util.ReflectUtil;

/**
 * ??codec??gzip
 * 
 * @author zhanglei
 *
 */
@SpiMeta(name = "compressMotan")
public class CompressRpcCodec extends AbstractCodec {
    private static final short MAGIC = (short) 0xF0F0;

    private static final byte MASK = 0x07;

    // ????decode requestserver
    private static ConcurrentHashMap<String, MethodInfo> SIGN_METHOD_MAP = new ConcurrentHashMap<String, MethodInfo>();
    // ????
    private static ConcurrentHashMap<String, String> METHOD_SIGN_MAP = new ConcurrentHashMap<String, String>();

    // ???attachmentapplication?decode requestserver
    private static ConcurrentHashMap<String, AttachmentInfo> SIGN_ATTACHMENT_MAP = new ConcurrentHashMap<String, AttachmentInfo>();

    private static ConcurrentHashSet<String> ACCEPT_ATTACHMENT_SIGN = new ConcurrentHashSet<String>();// ?serverattachment???
                                                                                                      // clientserver?????
    private static final String SIGN_FLAG = "1";// ????

    // attachmentkey_
    private static final String ATTACHMENT_SIGN = "_A";// attachment????key?server???key
    private static final String UN_ATTACHMENT_SIGN = "_UA";// server???key
    private static final String CLIENT_REQUESTID = "_RID";// client requestidkey

    public static final String CODEC_VERSION_SWITCHER = "feature.motanrpc.codecversion.degrade";// codec?falsetruev1?
    public static final String GROUP_CODEC_VERSION_SWITCHER = "feature.motanrpc.codecversion.groupdegrade.";// group?codec?falsetruev1?
    private DefaultRpcCodec v1Codec = new DefaultRpcCodec();

    static {
        LoggerUtil.info("init compress codec");
        MotanSwitcherUtil.initSwitcher(CODEC_VERSION_SWITCHER, false);
    }

    @Override
    public byte[] encode(Channel channel, Object message) throws IOException {
        if (needEncodeV1(message)) {
            return v1Codec.encode(channel, message);
        } else {
            // v2
            return encodeV2(channel, message);
        }

    }

    // v1???clientv1?v1?
    private boolean needEncodeV1(Object message) {

        if (MotanSwitcherUtil.isOpen(CODEC_VERSION_SWITCHER)) {
            return true;
        }
        if (message instanceof Request) {
            // ?
            if (DefaultRpcHeartbeatFactory.isHeartbeatRequest(message)) {
                return true;
            }
            // ???
            String group = MotanFrameworkUtil.getGroupFromRequest((Request) message);
            if (MotanSwitcherUtil.switcherIsOpenWithDefault(GROUP_CODEC_VERSION_SWITCHER + group, false)) {
                return true;
            }
        }
        return message instanceof Response
                && ((Response) message).getRpcProtocolVersion() == RpcProtocolVersion.VERSION_1.getVersion();

    }

    /**
     * decode data
     * 
     * <pre>
    *       client??serverresponse or exception
    *       server: ??clientrequest
    * </pre>
     * 
     * @param data
     * @return
     * @throws IOException
     */
    @Override
    public Object decode(Channel channel, String remoteIp, byte[] data) throws IOException {
        if (MotanSwitcherUtil.isOpen(CODEC_VERSION_SWITCHER)) {
            // ?v1codec
            return v1Codec.decode(channel, remoteIp, data);
        } else {
            if (data.length <= 3) {
                throw new MotanFrameworkException("decode error: format problem",
                        MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
            }
            // ??v1v2
            if (data[2] == RpcProtocolVersion.VERSION_1.getVersion()) {
                return v1Codec.decode(channel, remoteIp, data);
            } else if (data[2] == RpcProtocolVersion.VERSION_2.getVersion()) {
                // v2
                return decodeV2(channel, remoteIp, data);
            } else {
                throw new MotanFrameworkException("decode error: version error. version=" + data[2],
                        MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
            }

        }
    }

    public byte[] encodeV2(Channel channel, Object message) throws IOException {
        try {
            if (message instanceof Request) {
                return encodeRequest(channel, (Request) message);

            } else if (message instanceof Response) {
                return encodeResponse(channel, (Response) message);

            }
        } catch (Exception e) {
            if (ExceptionUtil.isMotanException(e)) {
                throw (RuntimeException) e;
            } else {
                throw new MotanFrameworkException("encode error: isResponse=" + (message instanceof Response), e,
                        MotanErrorMsgConstant.FRAMEWORK_ENCODE_ERROR);
            }
        }

        throw new MotanFrameworkException("encode error: message type not support, " + message.getClass(),
                MotanErrorMsgConstant.FRAMEWORK_ENCODE_ERROR);
    }

    /**
     * decode data
     * 
     * <pre>
     *      client??serverresponse or exception
     *      server: ??clientrequest
     * </pre>
     * 
     * @param data
     * @return
     * @throws IOException
     */

    public Object decodeV2(Channel channel, String remoteIp, byte[] data) throws IOException {
        if (data.length <= RpcProtocolVersion.VERSION_2.getHeaderLength()) {
            throw new MotanFrameworkException("decode error: format problem",
                    MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
        }

        short type = ByteUtil.bytes2short(data, 0);

        if (type != MAGIC) {
            throw new MotanFrameworkException("decode error: magic error",
                    MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
        }

        int bodyLength = ByteUtil.bytes2int(data, 12);

        if (RpcProtocolVersion.VERSION_2.getHeaderLength() + bodyLength != data.length) {
            throw new MotanFrameworkException("decode error: content length error",
                    MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
        }

        byte flag = data[3];
        byte dataType = (byte) (flag & MASK);
        boolean isResponse = (dataType != MotanConstants.FLAG_REQUEST);

        byte[] body = new byte[bodyLength];

        System.arraycopy(data, RpcProtocolVersion.VERSION_1.getHeaderLength(), body, 0, bodyLength);

        long requestId = ByteUtil.bytes2long(data, 4);
        Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(
                channel.getUrl().getParameter(URLParamType.serialize.getName(), URLParamType.serialize.getValue()));

        try {
            if (isResponse) {
                return decodeResponse(body, dataType, requestId, data[2], serialization);
            } else {
                return decodeRequest(body, requestId, remoteIp, serialization);
            }
        } catch (ClassNotFoundException e) {
            throw new MotanFrameworkException(
                    "decode " + (isResponse ? "response" : "request") + " error: class not found", e,
                    MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
        } catch (Exception e) {
            if (ExceptionUtil.isMotanException(e)) {
                throw (RuntimeException) e;
            } else {
                throw new MotanFrameworkException("decode error: isResponse=" + isResponse, e,
                        MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
            }
        }
    }

    /**
     * request body ?
     * 
     * <pre>
    * 
    *     body:
    * 
    *     byte[] data :  
    * 
    *          serialize(interface_name, method_name, method_param_desc, method_param_value, attachments_size, attachments_value) 
    * 
    *   method_param_desc:  for_each (string.append(method_param_interface_name))
    * 
    *   method_param_value: for_each (method_param_name, method_param_value)
    * 
    *     attachments_value:  for_each (attachment_name, attachment_value)
    * 
    * </pre>
     * 
     * @param request
     * @return
     * @throws IOException
     */
    private byte[] encodeRequest(Channel channel, Request request) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ObjectOutput output = createOutput(outputStream);
        addMethodInfo(output, request);

        Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(
                channel.getUrl().getParameter(URLParamType.serialize.getName(), URLParamType.serialize.getValue()));

        if (request.getArguments() != null && request.getArguments().length > 0) {
            for (Object obj : request.getArguments()) {
                serialize(output, obj, serialization);
            }
        }

        if (request.getAttachments() == null || request.getAttachments().isEmpty()) {
            // empty attachments
            output.writeShort(0);
        } else {
            // ?copyattachment????request???
            Map<String, String> attachments = copyMap(request.getAttachments());
            replaceAttachmentParamsBySign(channel, attachments);

            addAttachment(output, attachments);
        }

        output.flush();
        byte[] body = outputStream.toByteArray();

        byte flag = MotanConstants.FLAG_REQUEST;

        output.close();
        Boolean usegz = channel.getUrl().getBooleanParameter(URLParamType.usegz.getName(),
                URLParamType.usegz.getBooleanValue());
        int minGzSize = channel.getUrl().getIntParameter(URLParamType.mingzSize.getName(),
                URLParamType.mingzSize.getIntValue());
        return encode(compress(body, usegz, minGzSize), flag, request.getRequestId());
    }

    private Map<String, String> copyMap(Map<String, String> attachments) {
        Map<String, String> resultMap = new HashMap<String, String>();
        for (Map.Entry<String, String> entry : attachments.entrySet()) {
            resultMap.put(entry.getKey(), entry.getValue());
        }
        return resultMap;
    }

    /**
     * ???
     * 
     * @param output
     * @param request
     * @throws IOException
     */
    private void addMethodInfo(ObjectOutput output, Request request) throws IOException {
        String methodInfoStr = MotanFrameworkUtil.getServiceKey(request) + request.getMethodName()
                + request.getParamtersDesc();
        String methodSign = METHOD_SIGN_MAP.get(methodInfoStr);
        if (methodSign == null) {
            MethodInfo temp = new MethodInfo(MotanFrameworkUtil.getGroupFromRequest(request),
                    request.getInterfaceName(), request.getMethodName(), request.getParamtersDesc(),
                    MotanFrameworkUtil.getVersionFromRequest(request));
            try {
                methodSign = temp.getSign();
                METHOD_SIGN_MAP.putIfAbsent(methodInfoStr, methodSign);
                LoggerUtil.info("add method sign:" + methodSign + ", methodinfo:" + temp.toString());
            } catch (Exception e) {
                LoggerUtil.warn("gen method sign fail!" + e.getMessage());
            }

        }
        if (methodSign != null) {
            output.writeUTF(SIGN_FLAG);// ??
            output.writeUTF(methodSign);
        } else {// ?????
            output.writeUTF(request.getInterfaceName());
            output.writeUTF(request.getMethodName());
            output.writeUTF(request.getParamtersDesc());
        }

    }

    /**
     * ???Attachment??
     * 
     * @param attachments
     */
    private void replaceAttachmentParamsBySign(Channel channel, Map<String, String> attachments) {
        // attachment????server?????
        AttachmentInfo info = getAttachmentInfoMap(attachments);
        if (info != null) {
            String sign = info.getAttachmetnSign();
            if (sign != null) {
                attachments.put(ATTACHMENT_SIGN, sign);

                if (ACCEPT_ATTACHMENT_SIGN.contains(sign)) {// server?????application?
                    removeAttachmentInfoMap(attachments);
                }
            }
        }
        // clientrequestid???key
        // ?????codec?
        String clientRequestid = attachments.get(URLParamType.requestIdFromClient.getName());
        if (clientRequestid != null && !URLParamType.requestIdFromClient.getValue().equals(clientRequestid)) {
            attachments.put(CLIENT_REQUESTID, clientRequestid);
        }
        attachments.remove(URLParamType.requestIdFromClient.getName());
    }

    private void addAttachment(ObjectOutput output, Map<String, String> attachments) throws IOException {
        output.writeShort(attachments.size());
        for (Map.Entry<String, String> entry : attachments.entrySet()) {
            output.writeUTF(entry.getKey());
            output.writeUTF(entry.getValue());
        }
    }

    /**
     * response body ?
     * 
     * <pre>
    * 
    * body:
    * 
    *     byte[] :  serialize (result) or serialize (exception)
    * 
    * </pre>
     *
     * @param channel
     * @param value
     * @return
     * @throws IOException
     */
    private byte[] encodeResponse(Channel channel, Response value) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ObjectOutput output = createOutput(outputStream);
        Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(
                channel.getUrl().getParameter(URLParamType.serialize.getName(), URLParamType.serialize.getValue()));

        byte flag = 0;

        output.writeLong(value.getProcessTime());

        if (value.getException() != null) {
            output.writeUTF(value.getException().getClass().getName());
            serialize(output, value.getException(), serialization);
            flag = MotanConstants.FLAG_RESPONSE_EXCEPTION;
        } else if (value.getValue() == null) {
            flag = MotanConstants.FLAG_RESPONSE_VOID;
        } else {
            output.writeUTF(value.getValue().getClass().getName());
            serialize(output, value.getValue(), serialization);
            // v2?responseattachment
            Map<String, String> attachments = value.getAttachments();
            if (attachments != null) {
                String signed = attachments.get(ATTACHMENT_SIGN);
                String unSigned = attachments.get(UN_ATTACHMENT_SIGN);
                attachments.clear(); // attachment????

                if (StringUtils.isNotBlank(signed)) {
                    attachments.put(ATTACHMENT_SIGN, signed);
                }
                if (StringUtils.isNotBlank(unSigned)) {
                    attachments.put(UN_ATTACHMENT_SIGN, unSigned);
                }
            }
            if (attachments != null && !attachments.isEmpty()) {// ??
                addAttachment(output, attachments);
            } else {
                // empty attachments
                output.writeShort(0);
            }
            flag = MotanConstants.FLAG_RESPONSE_ATTACHMENT; // v2flag
        }

        output.flush();

        byte[] body = outputStream.toByteArray();

        output.close();
        Boolean usegz = channel.getUrl().getBooleanParameter(URLParamType.usegz.getName(),
                URLParamType.usegz.getBooleanValue());
        int minGzSize = channel.getUrl().getIntParameter(URLParamType.mingzSize.getName(),
                URLParamType.mingzSize.getIntValue());
        return encode(compress(body, usegz, minGzSize), flag, value.getRequestId());
    }

    /**
     * ???
     * 
     * <pre>
    * 
    * header:  16 
    * 
    * 0-15 bit    :  magic
    * 16-23 bit   :  version
    * 24-31 bit   :  extend flag ,  29-30 bit: event ??4?eventnormal, exception,  31 bit : 0 is request , 1 is response 
    * 32-95 bit    :  request id
    * 96-127 bit    :  body content length
    * 
    * </pre>
     *
     * @param body
     * @param flag
     * @param requestId
     * @return
     * @throws IOException
     */
    private byte[] encode(byte[] body, byte flag, long requestId) throws IOException {
        byte[] header = new byte[RpcProtocolVersion.VERSION_2.getHeaderLength()];
        int offset = 0;

        // 0 - 15 bit : magic
        ByteUtil.short2bytes(MAGIC, header, offset);
        offset += 2;

        // 16 - 23 bit : version
        header[offset++] = RpcProtocolVersion.VERSION_2.getVersion();

        // 24 - 31 bit : extend flag
        header[offset++] = flag;

        // 32 - 95 bit : requestId
        ByteUtil.long2bytes(requestId, header, offset);
        offset += 8;

        // 96 - 127 bit : body content length
        ByteUtil.int2bytes(body.length, header, offset);

        byte[] data = new byte[header.length + body.length];

        System.arraycopy(header, 0, data, 0, header.length);
        System.arraycopy(body, 0, data, header.length, body.length);

        return data;
    }

    private Object decodeRequest(byte[] body, long requestId, String remoteIp, Serialization serialization)
            throws IOException, ClassNotFoundException {

        ObjectInput input = createInput(getInputStream(body));
        String interfaceName = null;
        String methodName = null;
        String paramtersDesc = null;
        String group = null;
        String version = null;

        String flag = input.readUTF();

        if (SIGN_FLAG.equals(flag)) {// ???
            String sign = input.readUTF();
            MethodInfo mInfo = SIGN_METHOD_MAP.get(sign);
            if (mInfo == null) {
                throw new MotanFrameworkException("decode error: invalid method sign: " + sign,
                        MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
            }
            interfaceName = mInfo.getInterfaceName();
            methodName = mInfo.getMethodName();
            paramtersDesc = mInfo.getParamtersDesc();
            group = mInfo.getGroup();
            version = mInfo.getVersion();
        } else {
            interfaceName = flag;
            methodName = input.readUTF();
            paramtersDesc = input.readUTF();
        }

        DefaultRequest rpcRequest = new DefaultRequest();
        rpcRequest.setRequestId(requestId);
        rpcRequest.setInterfaceName(interfaceName);
        rpcRequest.setMethodName(methodName);
        rpcRequest.setParamtersDesc(paramtersDesc);
        rpcRequest.setArguments(decodeRequestParameter(input, paramtersDesc, serialization));
        rpcRequest.setAttachments(decodeRequestAttachments(input));
        rpcRequest.setRpcProtocolVersion(RpcProtocolVersion.VERSION_2.getVersion());

        input.close();
        Map<String, String> attachments = rpcRequest.getAttachments();
        putSignedAttachment(attachments, remoteIp);// ???client?
        if (attachments.get(URLParamType.group.name()) == null) {
            // attachment sign?methodsigngroup?
            attachments.put(URLParamType.group.name(), group);
            attachments.put(URLParamType.version.name(), version);
        }

        return rpcRequest;
    }

    private void putSignedAttachment(Map<String, String> attachments, String remoteIp) {
        if (attachments != null && !attachments.isEmpty()) {
            AttachmentInfo info = getAttachmentInfoMap(attachments);
            if (info != null) {// clientapplication?
                String sign = attachments.get(ATTACHMENT_SIGN);
                if (StringUtils.isNotBlank(sign)) {
                    SIGN_ATTACHMENT_MAP.put(remoteIp + sign, info);
                    LoggerUtil
                            .info("update attachment sign:" + remoteIp + sign + ", info-group:" + info.getGroup());
                }

            } else {// ??
                String sign = attachments.get(ATTACHMENT_SIGN);
                if (StringUtils.isNotBlank(sign)) {
                    info = SIGN_ATTACHMENT_MAP.get(remoteIp + sign);
                    if (info != null) {
                        // ?requestattachment
                        putAttachmentInfoMap(info, attachments);
                    } else {// serversigninfo?client???info
                        attachments.put(UN_ATTACHMENT_SIGN, sign);
                        LoggerUtil.info("miss attachment sign:" + remoteIp + sign);
                    }
                    // repsponseATTACHMENT_SIGNserver?
                    // clientattachmentinfo?
                    attachments.remove(ATTACHMENT_SIGN);
                } else {
                    LoggerUtil.warn("attachment sign is blankapplication info miss!");
                }
            }

            // client requestid
            String clientRequestid = URLParamType.requestIdFromClient.getValue();// 
            if (attachments.containsKey(CLIENT_REQUESTID)) {
                clientRequestid = attachments.get(CLIENT_REQUESTID);
            }
            attachments.put(URLParamType.requestIdFromClient.getName(), clientRequestid);
        }
    }

    private Object[] decodeRequestParameter(ObjectInput input, String parameterDesc, Serialization serialization)
            throws IOException, ClassNotFoundException {
        if (parameterDesc == null || parameterDesc.equals("")) {
            return null;
        }

        Class<?>[] classTypes = ReflectUtil.forNames(parameterDesc);

        Object[] paramObjs = new Object[classTypes.length];

        for (int i = 0; i < classTypes.length; i++) {
            paramObjs[i] = deserialize((byte[]) input.readObject(), classTypes[i], serialization);
        }

        return paramObjs;
    }

    private Map<String, String> decodeRequestAttachments(ObjectInput input)
            throws IOException, ClassNotFoundException {
        int size = input.readShort();

        if (size <= 0) {
            return null;
        }

        Map<String, String> attachments = new HashMap<String, String>();

        for (int i = 0; i < size; i++) {
            attachments.put(input.readUTF(), input.readUTF());
        }

        return attachments;
    }

    /**
     * 
     * @param body
     * @param dataType
     * @param requestId
     * @param rpcProtocolVersion rpc??????????
     * @param serialization
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private Object decodeResponse(byte[] body, byte dataType, long requestId, byte rpcProtocolVersion,
            Serialization serialization) throws IOException, ClassNotFoundException {

        ObjectInput input = createInput(getInputStream(body));

        long processTime = input.readLong();

        DefaultResponse response = new DefaultResponse();
        response.setRequestId(requestId);
        response.setProcessTime(processTime);

        if (dataType == MotanConstants.FLAG_RESPONSE_VOID) {
            return response;
        }

        String className = input.readUTF();
        Class<?> clz = ReflectUtil.forName(className);

        Object result = deserialize((byte[]) input.readObject(), clz, serialization);

        if (dataType == MotanConstants.FLAG_RESPONSE) {
            response.setValue(result);
        } else if (dataType == MotanConstants.FLAG_RESPONSE_ATTACHMENT) {
            response.setValue(result);
            Map<String, String> attachment = decodeRequestAttachments(input);
            checkAttachment(attachment);
        } else if (dataType == MotanConstants.FLAG_RESPONSE_EXCEPTION) {
            response.setException((Exception) result);
        } else {
            throw new MotanFrameworkException("decode error: response dataType not support " + dataType,
                    MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
        }

        response.setRequestId(requestId);

        input.close();

        return response;
    }

    // repsonse?serversignserversign
    private void checkAttachment(Map<String, String> attachment) {
        if (attachment != null && !attachment.isEmpty()) {
            String acceptSign = attachment.get(ATTACHMENT_SIGN);
            if (StringUtils.isNotBlank(acceptSign)) {// attachment
                                                     // signserver??attachment
                ACCEPT_ATTACHMENT_SIGN.add(acceptSign);
            }
            String notAcceptSign = attachment.get(UN_ATTACHMENT_SIGN);
            if (StringUtils.isNotBlank(notAcceptSign)) {// serversignattachment??
                ACCEPT_ATTACHMENT_SIGN.remove(notAcceptSign);
            }
        }
    }

    // requestattachments?AttachmentInfonull
    private AttachmentInfo getAttachmentInfoMap(Map<String, String> attachments) {
        AttachmentInfo result = null;
        if (attachments != null && attachments.containsKey(URLParamType.application.name())) {
            String group = attachments.get(URLParamType.group.name());
            String application = attachments.get(URLParamType.application.name());
            String module = attachments.get(URLParamType.module.name());
            String version = attachments.get(URLParamType.version.name());
            result = new AttachmentInfo(group, application, module, version);
        }
        return result;
    }

    private void putAttachmentInfoMap(AttachmentInfo attachmentInfo, Map<String, String> attachments) {
        if (attachments != null) {
            attachments.put(URLParamType.group.name(), attachmentInfo.getGroup());
            attachments.put(URLParamType.application.name(), attachmentInfo.getApplication());
            attachments.put(URLParamType.module.name(), attachmentInfo.getModule());
            attachments.put(URLParamType.version.name(), attachmentInfo.getVersion());
        }
    }

    private void removeAttachmentInfoMap(Map<String, String> attachments) {
        if (attachments != null) {
            attachments.remove(URLParamType.group.name());
            attachments.remove(URLParamType.application.name());
            attachments.remove(URLParamType.module.name());
            attachments.remove(URLParamType.version.name());
        }
    }

    /**
     * ??gzip
     *
     * @param data
     * @return
     */
    public static InputStream getInputStream(byte[] data) {
        InputStream ret = new ByteArrayInputStream(data);
        try {
            GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(data));
            return gis;
        } catch (Exception ignore) {
        }
        return ret;
    }

    // rpc body
    public byte[] compress(byte[] org, boolean useGzip, int minGzSize) throws IOException {
        if (useGzip && org.length > minGzSize) {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            GZIPOutputStream gos = new GZIPOutputStream(outputStream);
            gos.write(org);
            gos.finish();
            gos.flush();
            gos.close();
            byte[] ret = outputStream.toByteArray();
            return ret;
        } else {
            return org;
        }

    }

    public static void putMethodSign(Provider<?> provider, List<Method> methods) {
        String group = provider.getUrl().getGroup();
        String interfaceName = provider.getInterface().getName();
        String version = provider.getUrl().getVersion();
        for (Method method : methods) {
            MethodInfo temp = new MethodInfo(group, interfaceName, method.getName(),
                    ReflectUtil.getMethodParamDesc(method), version);
            String sign = temp.getSign();
            MethodInfo priInfo = SIGN_METHOD_MAP.putIfAbsent(sign, temp);
            if (priInfo != null && !temp.equals(priInfo)) {// ???
                throw new MotanFrameworkException(
                        "add method sign conflict! " + temp.toString() + " with " + priInfo.toString(),
                        MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
            } else {
                LoggerUtil.info("add method sign:" + sign + ", methodinfo:" + temp.toString());
            }

        }
    }

    public static void putMethodSign(String methodSign, MethodInfo methodInfo) {
        SIGN_METHOD_MAP.putIfAbsent(methodSign, methodInfo);
    }

    static class MethodInfo {
        String group;
        String interfaceName;
        String methodName;
        String paramtersDesc;
        String version;

        public MethodInfo(String group, String interfaceName, String methodName, String paramtersDesc,
                String version) {
            super();
            this.group = group;
            this.interfaceName = interfaceName;
            this.methodName = methodName;
            this.paramtersDesc = paramtersDesc;
            this.version = version;
        }

        /**
         * ????? ???
         * 
         * @return
         * @throws Exception
         */
        public String getSign() {
            try {
                StringBuilder sb = new StringBuilder();
                sb.append(group).append(interfaceName).append(methodName).append(paramtersDesc).append(version);
                String surfix = MotanDigestUtil.md5LowerCase(sb.toString()).substring(8, 20); // ?32?md58-20?
                int endIndex = methodName.length() > 4 ? 4 : methodName.length();
                String prefix = methodName.substring(0, endIndex);
                return prefix + surfix;
            } catch (Exception e) {
                throw new MotanFrameworkException("gen method sign error! " + this.toString(),
                        MotanErrorMsgConstant.FRAMEWORK_DECODE_ERROR);
            }

        }

        public String getGroup() {
            return group;
        }

        public void setGroup(String group) {
            this.group = group;
        }

        public String getInterfaceName() {
            return interfaceName;
        }

        public void setInterfaceName(String interfaceName) {
            this.interfaceName = interfaceName;
        }

        public String getMethodName() {
            return methodName;
        }

        public void setMethodName(String methodName) {
            this.methodName = methodName;
        }

        public String getParamtersDesc() {
            return paramtersDesc;
        }

        public void setParamtersDesc(String paramtersDesc) {
            this.paramtersDesc = paramtersDesc;
        }

        public String getVersion() {
            return version;
        }

        public void setVersion(String version) {
            this.version = version;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((group == null) ? 0 : group.hashCode());
            result = prime * result + ((interfaceName == null) ? 0 : interfaceName.hashCode());
            result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
            result = prime * result + ((paramtersDesc == null) ? 0 : paramtersDesc.hashCode());
            result = prime * result + ((version == null) ? 0 : version.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            MethodInfo other = (MethodInfo) obj;
            if (group == null) {
                if (other.group != null)
                    return false;
            } else if (!group.equals(other.group))
                return false;
            if (interfaceName == null) {
                if (other.interfaceName != null)
                    return false;
            } else if (!interfaceName.equals(other.interfaceName))
                return false;
            if (methodName == null) {
                if (other.methodName != null)
                    return false;
            } else if (!methodName.equals(other.methodName))
                return false;
            if (paramtersDesc == null) {
                if (other.paramtersDesc != null)
                    return false;
            } else if (!paramtersDesc.equals(other.paramtersDesc))
                return false;
            if (version == null) {
                if (other.version != null)
                    return false;
            } else if (!version.equals(other.version))
                return false;
            return true;
        }

        @Override
        public String toString() {
            return "MethodInfo [group=" + group + ", interfaceName=" + interfaceName + ", methodName=" + methodName
                    + ", paramtersDesc=" + paramtersDesc + ", version=" + version + "]";
        }

    }

    static class AttachmentInfo {
        String group;
        String application;
        String module;
        String version;

        public AttachmentInfo(String group, String application, String module, String version) {
            super();
            this.group = group;
            this.application = application;
            this.module = module;
            this.version = version;
        }

        public String getAttachmetnSign() {
            String signstr = group + application + module + version;
            String hashcodeStr = null;
            try {
                hashcodeStr = MotanDigestUtil.md5LowerCase(signstr).substring(8, 12); // ?md5
            } catch (Exception e) {
                LoggerUtil.warn("getAttachmetnSign fail!" + e.getMessage());
            }
            return hashcodeStr;
        }

        public String getGroup() {
            return group;
        }

        public String getApplication() {
            return application;
        }

        public String getModule() {
            return module;
        }

        public String getVersion() {
            return version;
        }

    }

}