/* * Copyright 2014 NAVER Corp. * * 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.navercorp.pinpoint.rpc.control; import java.lang.reflect.Array; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import com.navercorp.pinpoint.common.Charsets; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; /** be simple. this is similar to NPC but use "bit" operation instead of Chunk in String. permit only utf-8 encoding. * @author koo.taejin */ public class ControlMessageEncoder { private Charset charset; public ControlMessageEncoder() { this.charset = Charsets.UTF_8; } public byte[] encode(Map<String, Object> value) throws ProtocolException { ChannelBuffer cb = ChannelBuffers.dynamicBuffer(100); encode(value, cb); int writeIndex = cb.writerIndex(); byte[] result = new byte[writeIndex]; cb.readBytes(result); return result; } private void encode(Map<String, Object> value, ChannelBuffer cb) throws ProtocolException { encodeMap(value, cb); } private void encode(Object value, ChannelBuffer cb) throws ProtocolException { // try { if (value == null) { encodeNull(cb); } else if (value instanceof String) { encodeString((String) value, cb); } else if (value instanceof Boolean) { encodeBoolean((Boolean) value, cb); } else if (value instanceof Short) { encodeInt((Short) value, cb); } else if (value instanceof Integer) { encodeInt((Integer) value, cb); } else if (value instanceof Long) { encodeLong((Long) value, cb); } else if (value instanceof Float) { encodeDouble(((Float) value).doubleValue(), cb); } else if (value instanceof Double) { encodeDouble((Double) value, cb); } else if (value instanceof Number) { // Other numbers (i.e. // BigInteger and BigDecimal) encodeString(value.toString(), cb); } else if (value instanceof Collection) { encodeCollection((Collection<?>) value, cb); } else if (value instanceof Map) { encodeMap((Map<?, ?>) value, cb); } else if (value.getClass().isArray()) { int arraySize = Array.getLength(value); List<Object> arrayToList = new ArrayList<Object>(arraySize); for (int i = 0; i < arraySize; i++) { arrayToList.add(Array.get(value, i)); } encodeCollection(arrayToList, cb); } else { throw new ProtocolException("Unsupported type : " + value.getClass().getName()); } // } catch (Exception e) { // throw new ProtocolException(e); // } } private void encodeNull(ChannelBuffer out) { out.writeByte((byte) ControlMessageProtocolConstant.TYPE_CHARACTER_NULL); } private void encodeString(String value, ChannelBuffer out) { out.writeByte((byte) ControlMessageProtocolConstant.TYPE_CHARACTER_STRING); putPrefixedBytes(value.getBytes(charset), out); } private void encodeBoolean(boolean value, ChannelBuffer out) { if (value) { out.writeByte((byte) ControlMessageProtocolConstant.TYPE_CHARACTER_BOOL_TRUE); } else { out.writeByte((byte) ControlMessageProtocolConstant.TYPE_CHARACTER_BOOL_FALSE); } } private void encodeInt(int value, ChannelBuffer out) { out.writeByte((byte) ControlMessageProtocolConstant.TYPE_CHARACTER_INT); out.writeByte((byte) (value >> 24)); out.writeByte((byte) (value >> 16)); out.writeByte((byte) (value >> 8)); out.writeByte((byte) (value)); } private void encodeLong(long value, ChannelBuffer out) { out.writeByte((byte) ControlMessageProtocolConstant.TYPE_CHARACTER_LONG); out.writeByte((byte) (value >> 56)); out.writeByte((byte) (value >> 48)); out.writeByte((byte) (value >> 40)); out.writeByte((byte) (value >> 32)); out.writeByte((byte) (value >> 24)); out.writeByte((byte) (value >> 16)); out.writeByte((byte) (value >> 8)); out.writeByte((byte) (value)); } private void encodeDouble(double value, ChannelBuffer out) { out.writeByte((byte) ControlMessageProtocolConstant.TYPE_CHARACTER_DOUBLE); long longValue = Double.doubleToLongBits(value); out.writeByte((byte) (longValue >> 56)); out.writeByte((byte) (longValue >> 48)); out.writeByte((byte) (longValue >> 40)); out.writeByte((byte) (longValue >> 32)); out.writeByte((byte) (longValue >> 24)); out.writeByte((byte) (longValue >> 16)); out.writeByte((byte) (longValue >> 8)); out.writeByte((byte) (longValue)); } private void encodeCollection(Collection<?> collection, ChannelBuffer out) throws ProtocolException { out.writeByte((byte) ControlMessageProtocolConstant.CONTROL_CHARACTER_LIST_START); for (Object element : collection) { encode(element, out); } out.writeByte((byte) ControlMessageProtocolConstant.CONTROL_CHARACTER_LIST_END); } private void encodeMap(Map<?, ?> map, ChannelBuffer out) throws ProtocolException { out.writeByte((byte) ControlMessageProtocolConstant.CONTROL_CHARACTER_MAP_START); for (Object element : map.entrySet()) { Map.Entry<?, ?> entry = (Map.Entry<?, ?>) element; encode(entry.getKey(), out); encode(entry.getValue(), out); } out.writeByte((byte) ControlMessageProtocolConstant.CONTROL_CHARACTER_MAP_END); } private void putPrefixedBytes(byte[] value, ChannelBuffer out) { int length = value.length; byte[] lengthBuf = new byte[5]; int idx = 0; while (true) { if ((length & 0xFFFFFF80) == 0) { lengthBuf[(idx++)] = (byte) length; break; } lengthBuf[(idx++)] = (byte) (length & 0x7F | 0x80); length >>>= 7; } for (int i = 0; i < idx; i++) { out.writeByte(lengthBuf[i]); } out.writeBytes(value); } }