Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.tongbanjie.tarzan.rpc.protocol; import com.tongbanjie.tarzan.rpc.exception.RpcCommandException; import com.tongbanjie.tarzan.rpc.protocol.header.CustomHeader; import com.tongbanjie.tarzan.rpc.util.ClassUtils; import com.tongbanjie.tarzan.common.TarzanVersion; import com.tongbanjie.tarzan.rpc.util.RpcSerializeUtils; import org.apache.commons.collections4.MapUtils; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import java.text.ParseException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * Rpc <p> * ?? * * @author zixiao * @date 16/9/27 */ public class RpcCommand implements Serializable { private static final long serialVersionUID = -2662995105054208710L; //Request public static final byte REQUEST_COMMAND = 0x00; //Response public static final byte RESPONSE_COMMAND = 0x01; private static AtomicInteger requestId = new AtomicInteger(0); /**************** header ****************/ /** * ? * * @see RequestCode * @see ResponseCode */ private int cmdCode; /** * ?? * * @see SerializeType */ private SerializeType serializeType = SerializeType.JSON; /** * * * RpcCommand.REQUEST_COMMAND * RpcCommand.RESPONSE_COMMAND */ private byte cmdType = REQUEST_COMMAND; /** * ? */ private int version = TarzanVersion.CURRENT.getValue(); // customHeader private Map<String, String> customFields; /** * cmdId * ?RequestResponse * */ private int opaque = requestId.getAndIncrement(); private boolean oneWayRpc = false; private String remark; /**************** body, customHeader ****************/ private transient CustomHeader customHeader; private transient byte[] body; /*************** ***************/ private static final Map<Class<? extends CustomHeader>, Field[]> clazzFieldsCache = new HashMap<Class<? extends CustomHeader>, Field[]>(); private static final Map<Class, String> canonicalNameCache = new HashMap<Class, String>(); protected RpcCommand() { } public static RpcCommand decode(final byte[] array) { ByteBuffer byteBuffer = ByteBuffer.wrap(array); return decode(byteBuffer); } public static RpcCommand decode(final ByteBuffer byteBuffer) { // int length = byteBuffer.limit(); //?? 1byte byte protocolType = byteBuffer.get(); //Header 4type int headerLength = byteBuffer.getInt(); //Header byte[] headerData = new byte[headerLength]; byteBuffer.get(headerData); RpcCommand cmd = headerDecode(headerData, getProtocolType(protocolType)); //body int bodyLength = length - 5 - headerLength; byte[] bodyData = null; if (bodyLength > 0) { bodyData = new byte[bodyLength]; byteBuffer.get(bodyData); } cmd.body = bodyData; return cmd; } public static RpcCommand headerDecode(byte[] headerData, SerializeType type) { return RpcSerializeUtils.deserialize(headerData, RpcCommand.class, type); } public static int createNewRequestId() { return requestId.incrementAndGet(); } public CustomHeader decodeCustomHeader(Class<? extends CustomHeader> classHeader) throws RpcCommandException { CustomHeader objectHeader; try { objectHeader = classHeader.newInstance(); } catch (InstantiationException e) { return null; } catch (IllegalAccessException e) { return null; } if (MapUtils.isNotEmpty(this.customFields)) { Field[] fields = getClazzFields(classHeader); for (Field field : fields) { String fieldName = field.getName(); Class clazz = field.getType(); if (Modifier.isStatic(field.getModifiers()) || fieldName.startsWith("this")) { continue; } String value = this.customFields.get(fieldName); if (value == null) { continue; } field.setAccessible(true); Object valueParsed; if (clazz.isEnum()) { valueParsed = Enum.valueOf(clazz, value); } else { String type = getCanonicalName(clazz); try { valueParsed = ClassUtils.parseSimpleValue(type, value); } catch (ParseException e) { throw new RpcCommandException("Encode the header failed, the custom field <" + fieldName + "> type <" + getCanonicalName(clazz) + "> parse error:" + e.getMessage()); } } if (valueParsed == null) { throw new RpcCommandException("Encode the header failed, the custom field <" + fieldName + "> type <" + getCanonicalName(clazz) + "> is not supported."); } try { field.set(objectHeader, valueParsed); } catch (IllegalAccessException e) { throw new RpcCommandException( "Encode the header failed, set the value of field < " + fieldName + "> error.", e); } } objectHeader.checkFields(); } return objectHeader; } private Field[] getClazzFields(Class<? extends CustomHeader> classHeader) { Field[] field = clazzFieldsCache.get(classHeader); if (field == null) { field = classHeader.getDeclaredFields(); synchronized (clazzFieldsCache) { clazzFieldsCache.put(classHeader, field); } } return field; } private String getCanonicalName(Class clazz) { String name = canonicalNameCache.get(clazz); if (name == null) { name = clazz.getCanonicalName(); synchronized (canonicalNameCache) { canonicalNameCache.put(clazz, name); } } return name; } public ByteBuffer encode() throws RpcCommandException { /******* ? *******/ // 1> protocol type size int length = Protocol.PROTOCOL_TYPE_SIZE; // 2> header length size length += Protocol.HEADER_LENGTH_SIZE; // 3> header data length byte[] headerData = this.headerEncode(); length += headerData.length; // 4> body data length if (this.body != null) { length += body.length; } /******* ByteBuffer *******/ //? ByteBuffer result = ByteBuffer.allocate(Protocol.TOTAL_LENGTH_SIZE + length); // 0?length result.putInt(length); // 1?protocol type result.put(markProtocolType(serializeType)); // 2?header length result.putInt(headerData.length); // 3?header data result.put(headerData); // 4?body data; if (this.body != null) { result.put(this.body); } result.flip(); return result; } public ByteBuffer encodeHeader() throws RpcCommandException { return encodeHeader(this.body != null ? this.body.length : 0); } public ByteBuffer encodeHeader(final int bodyLength) throws RpcCommandException { /******* ? *******/ // 1> protocol type size int length = Protocol.PROTOCOL_TYPE_SIZE; // 2> header length size length += Protocol.HEADER_LENGTH_SIZE; // 3> header data length byte[] headerData; headerData = this.headerEncode(); length += headerData.length; // 4> body data length length += bodyLength; /******* ByteBuffer *******/ //??body ByteBuffer result = ByteBuffer.allocate(Protocol.TOTAL_LENGTH_SIZE + length - bodyLength); // 0?length result.putInt(length); // 1?protocol type result.put(markProtocolType(serializeType)); // 2?header length result.putInt(headerData.length); // 3?header data result.put(headerData); result.flip(); return result; } private byte[] headerEncode() throws RpcCommandException { this.customHeaderToMap(); return RpcSerializeUtils.serialize(this, serializeType); } /** * ?? * @param type * @return */ public static byte markProtocolType(SerializeType type) { return type.getCode(); } /** * ??? * @param source * @return */ public static SerializeType getProtocolType(byte source) { return SerializeType.valueOf(source); } private void customHeaderToMap() throws RpcCommandException { if (this.customHeader != null) { Field[] fields = getClazzFields(customHeader.getClass()); if (null == this.customFields) { this.customFields = new HashMap<String, String>(8); } for (Field field : fields) { String fieldName = field.getName(); Class clazz = field.getType(); if (Modifier.isStatic(field.getModifiers()) || fieldName.startsWith("this")) { continue; } Object value; try { field.setAccessible(true); value = field.get(this.customHeader); } catch (Exception e) { throw new RpcCommandException( "Encode the header failed, get the value of field <" + fieldName + "> error.", e); } if (value == null) { continue; } if (clazz.isEnum()) { this.customFields.put(fieldName, value.toString()); } else { String type = getCanonicalName(clazz); String strValue = ClassUtils.simpleValueToString(type, value); if (strValue == null) { throw new RpcCommandException("Encode the header failed, the field <" + fieldName + "> type <" + getCanonicalName(clazz) + "> is not supported."); } this.customFields.put(fieldName, strValue); } } } } public SerializeType getSerializeType() { return serializeType; } public void setSerializeType(SerializeType serializeType) { this.serializeType = serializeType; } public int getCmdCode() { return cmdCode; } public void setCmdCode(int cmdCode) { this.cmdCode = cmdCode; } public byte getCmdType() { return cmdType; } public void setCmdType(byte cmdType) { this.cmdType = cmdType; } public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } public Map<String, String> getCustomFields() { return customFields; } public void setCustomFields(Map<String, String> customFields) { this.customFields = customFields; } public int getOpaque() { return opaque; } public void setOpaque(int opaque) { this.opaque = opaque; } public boolean isOneWayRpc() { return oneWayRpc; } public void setOneWayRpc(boolean oneWayRpc) { this.oneWayRpc = oneWayRpc; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public byte[] getBody() { return body; } public <T> T getBody(Class<T> clazz) { if (this.body == null) { return null; } return RpcSerializeUtils.deserialize(this.body, clazz, this.serializeType); } public void setBody(byte[] body) { this.body = body; } public void setBody(Object obj) { this.body = RpcSerializeUtils.serialize(obj, this.serializeType); } public CustomHeader getCustomHeader() { return customHeader; } public void setCustomHeader(CustomHeader customHeader) { this.customHeader = customHeader; } @Override public String toString() { return "RpcCommand{" + "cmdCode=" + cmdCode + ", serializeType=" + serializeType + ", cmdType=" + cmdType + ", version=" + version + ", opaque=" + opaque + ", oneWayRpc=" + oneWayRpc + ", remark='" + remark + '\'' + ", customHeader=" + customHeader + ", body=" + Arrays.toString(body) + '}'; } }