/* * 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 com.navercorp.pinpoint.common.Charsets; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * @author koo.taejin */ public class ControlMessageDecoder { private Charset charset; public ControlMessageDecoder() { this.charset = Charsets.UTF_8; } public Object decode(byte[] in) throws ProtocolException { return decode(ByteBuffer.wrap(in)); } public Object decode(ByteBuffer in) throws ProtocolException { byte type = in.get(); switch (type) { case ControlMessageProtocolConstant.TYPE_CHARACTER_NULL: return null; case ControlMessageProtocolConstant.TYPE_CHARACTER_BOOL_TRUE: return Boolean.TRUE; case ControlMessageProtocolConstant.TYPE_CHARACTER_BOOL_FALSE: return Boolean.FALSE; case ControlMessageProtocolConstant.TYPE_CHARACTER_INT: return in.getInt(); case ControlMessageProtocolConstant.TYPE_CHARACTER_LONG: return in.getLong(); case ControlMessageProtocolConstant.TYPE_CHARACTER_DOUBLE: return Double.longBitsToDouble(in.getLong()); case ControlMessageProtocolConstant.TYPE_CHARACTER_STRING: return decodeString(in); case ControlMessageProtocolConstant.CONTROL_CHARACTER_LIST_START: List<Object> answerList = new ArrayList<Object>(); while (!isListFinished(in)) { answerList.add(decode(in)); } in.get(); // Skip the terminator return answerList; case ControlMessageProtocolConstant.CONTROL_CHARACTER_MAP_START: Map<Object, Object> answerMap = new LinkedHashMap<Object, Object>(); while (!isMapFinished(in)) { Object key = decode(in); Object value = decode(in); answerMap.put(key, value); } in.get(); // Skip the terminator return answerMap; default: throw new ProtocolException("invalid type character: " + (char) type + " (" + "0x" + Integer.toHexString(type) + ")"); } } private Object decodeString(ByteBuffer in) { int length = readStringLength(in); byte[] bytesToEncode = new byte[length]; in.get(bytesToEncode); return new String(bytesToEncode, charset); } private boolean isMapFinished(ByteBuffer in) { return in.get(in.position()) == ControlMessageProtocolConstant.CONTROL_CHARACTER_MAP_END; } private boolean isListFinished(ByteBuffer in) { return in.get(in.position()) == ControlMessageProtocolConstant.CONTROL_CHARACTER_LIST_END; } private int readStringLength(ByteBuffer in) { int result = 0; int shift = 0; while (true) { byte b = in.get(); result |= (b & 0x7F) << shift; if ((b & 0x80) != 128) break; shift += 7; } return result; } }