/**
* Copyright (C) 2010-2013 Alibaba Group Holding Limited
*
* 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.moshi.receptionist.remoting.protocol;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import com.alibaba.fastjson.annotation.JSONField;
import com.moshi.receptionist.remoting.CommandCustomHeader;
import com.moshi.receptionist.remoting.annotation.CFNotNull;
import com.moshi.receptionist.remoting.exception.RemotingCommandException;
/**
* Remoting模块中,服务器与客户端通过传递RemotingCommand来交互
*
* @author shijia.wxr<vintage.wang@gmail.com>
* @since 2013-7-13
*/
public class RemotingCommand {
public static String RemotingVersionKey = "rocketmq.remoting.version";
private static volatile int ConfigVersion = -1;
private static AtomicInteger RequestId = new AtomicInteger(0);
private static final int RPC_TYPE = 0; // 0, REQUEST_COMMAND
// 1, RESPONSE_COMMAND
private static final int RPC_ONEWAY = 1; // 0, RPC
// 1, Oneway
/**
* Header 部分
*/
private int code;
private LanguageCode language = LanguageCode.JAVA;
private int version = 0;
private int opaque = RequestId.getAndIncrement();
private int flag = 0;
private String remark;
private HashMap<String, String> extFields;
private transient CommandCustomHeader customHeader;
/**
* Body 部分
*/
private transient byte[] body;
protected RemotingCommand() {
}
public static RemotingCommand createRequestCommand(int code, CommandCustomHeader customHeader) {
RemotingCommand cmd = new RemotingCommand();
cmd.setCode(code);
cmd.customHeader = customHeader;
setCmdVersion(cmd);
return cmd;
}
public static RemotingCommand createResponseCommand(Class<? extends CommandCustomHeader> classHeader) {
RemotingCommand cmd =
createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR, "not set any response code",
classHeader);
return cmd;
}
public static RemotingCommand createResponseCommand(int code, String remark) {
return createResponseCommand(code, remark, null);
}
/**
* 只有通信层内部会调用,业务不会调用
*/
public static RemotingCommand createResponseCommand(int code, String remark,
Class<? extends CommandCustomHeader> classHeader) {
RemotingCommand cmd = new RemotingCommand();
cmd.markResponseType();
cmd.setCode(code);
cmd.setRemark(remark);
setCmdVersion(cmd);
if (classHeader != null) {
try {
CommandCustomHeader objectHeader = classHeader.newInstance();
cmd.customHeader = objectHeader;
}
catch (InstantiationException e) {
return null;
}
catch (IllegalAccessException e) {
return null;
}
}
return cmd;
}
private static void setCmdVersion(RemotingCommand cmd) {
if (ConfigVersion >= 0) {
cmd.setVersion(ConfigVersion);
}
else {
String v = System.getProperty(RemotingVersionKey);
if (v != null) {
int value = Integer.parseInt(v);
cmd.setVersion(value);
ConfigVersion = value;
}
}
}
private void makeCustomHeaderToNet() {
if (this.customHeader != null) {
Field[] fields = this.customHeader.getClass().getDeclaredFields();
if (null == this.extFields) {
this.extFields = new HashMap<String, String>();
}
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers())) {
String name = field.getName();
if (!name.startsWith("this")) {
Object value = null;
try {
field.setAccessible(true);
value = field.get(this.customHeader);
}
catch (IllegalArgumentException e) {
}
catch (IllegalAccessException e) {
}
if (value != null) {
this.extFields.put(name, value.toString());
}
}
}
}
}
}
public CommandCustomHeader getCustomHeader() {
return customHeader;
}
public CommandCustomHeader decodeCommandCustomHeader(Class<? extends CommandCustomHeader> classHeader)
throws RemotingCommandException {
if (this.extFields != null) {
CommandCustomHeader objectHeader;
try {
objectHeader = classHeader.newInstance();
}
catch (InstantiationException e) {
return null;
}
catch (IllegalAccessException e) {
return null;
}
Iterator<Entry<String, String>> it = this.extFields.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String> entry = it.next();
String name = entry.getKey();
String value = entry.getValue();
try {
Field field = objectHeader.getClass().getDeclaredField(name);
field.setAccessible(true);
String type = field.getType().getSimpleName();
Object valueParsed = null;
if (type.equals("String")) {
valueParsed = value;
}
else if (type.equals("Integer")) {
valueParsed = Integer.parseInt(value);
}
else if (type.equals("Long")) {
valueParsed = Long.parseLong(value);
}
else if (type.equals("Boolean")) {
valueParsed = Boolean.parseBoolean(value);
}
else if (type.equals("Double")) {
valueParsed = Double.parseDouble(value);
}
else if (type.equals("int")) {
valueParsed = Integer.parseInt(value);
}
else if (type.equals("long")) {
valueParsed = Long.parseLong(value);
}
else if (type.equals("boolean")) {
valueParsed = Boolean.parseBoolean(value);
}
else if (type.equals("double")) {
valueParsed = Double.parseDouble(value);
}
field.set(objectHeader, valueParsed);
}
catch (Throwable e) {
}
}
// 检查返回对象是否有效
Field[] fields = objectHeader.getClass().getDeclaredFields();
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers())) {
String name = field.getName();
if (!name.startsWith("this")) {
Object value = null;
try {
field.setAccessible(true);
value = field.get(objectHeader);
}
catch (IllegalArgumentException e) {
}
catch (IllegalAccessException e) {
}
// 空值检查
if (null == value) {
Annotation annotation = field.getAnnotation(CFNotNull.class);
if (annotation != null) {
throw new RemotingCommandException("the custom field <" + name + "> is null");
}
}
}
}
}
objectHeader.checkFields();
return objectHeader;
}
return null;
}
private byte[] buildHeader() {
this.makeCustomHeaderToNet();
return RemotingSerializable.encode(this);
}
public ByteBuffer encode() {
// 1> header length size
int length = 4;
// 2> header data length
byte[] headerData = this.buildHeader();
length += headerData.length;
// 3> body data length
if (this.body != null) {
length += body.length;
}
ByteBuffer result = ByteBuffer.allocate(4 + length);
// length
result.putInt(length);
// header length
result.putInt(headerData.length);
// header data
result.put(headerData);
// body data;
if (this.body != null) {
result.put(this.body);
}
result.flip();
return result;
}
public ByteBuffer encodeHeader() {
return encodeHeader(this.body != null ? this.body.length : 0);
}
/**
* 只打包Header,body部分独立传输
*/
public ByteBuffer encodeHeader(final int bodyLength) {
// 1> header length size
int length = 4;
// 2> header data length
byte[] headerData = this.buildHeader();
length += headerData.length;
// 3> body data length
length += bodyLength;
ByteBuffer result = ByteBuffer.allocate(4 + length - bodyLength);
// length
result.putInt(length);
// header length
result.putInt(headerData.length);
// header data
result.put(headerData);
result.flip();
return result;
}
public static RemotingCommand decode(final byte[] array) {
ByteBuffer byteBuffer = ByteBuffer.wrap(array);
return decode(byteBuffer);
}
public static RemotingCommand decode(final ByteBuffer byteBuffer) {
int length = byteBuffer.limit();
int headerLength = byteBuffer.getInt();
byte[] headerData = new byte[headerLength];
byteBuffer.get(headerData);
int bodyLength = length - 4 - headerLength;
byte[] bodyData = null;
if (bodyLength > 0) {
bodyData = new byte[bodyLength];
byteBuffer.get(bodyData);
}
RemotingCommand cmd = RemotingSerializable.decode(headerData, RemotingCommand.class);
cmd.body = bodyData;
return cmd;
}
public void markResponseType() {
int bits = 1 << RPC_TYPE;
this.flag |= bits;
}
@JSONField(serialize = false)
public boolean isResponseType() {
int bits = 1 << RPC_TYPE;
return (this.flag & bits) == bits;
}
public void markOnewayRPC() {
int bits = 1 << RPC_ONEWAY;
this.flag |= bits;
}
@JSONField(serialize = false)
public boolean isOnewayRPC() {
int bits = 1 << RPC_ONEWAY;
return (this.flag & bits) == bits;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
@JSONField(serialize = false)
public RemotingCommandType getType() {
if (this.isResponseType()) {
return RemotingCommandType.RESPONSE_COMMAND;
}
return RemotingCommandType.REQUEST_COMMAND;
}
public LanguageCode getLanguage() {
return language;
}
public void setLanguage(LanguageCode language) {
this.language = language;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public int getOpaque() {
return opaque;
}
public void setOpaque(int opaque) {
this.opaque = opaque;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
public HashMap<String, String> getExtFields() {
return extFields;
}
public void setExtFields(HashMap<String, String> extFields) {
this.extFields = extFields;
}
@Override
public String toString() {
return "RemotingCommand [code=" + code + ", language=" + language + ", version=" + version
+ ", opaque=" + opaque + ", flag(B)=" + Integer.toBinaryString(flag) + ", remark=" + remark
+ ", extFields=" + extFields + "]";
}
}