2 * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 package org.lttng.ust.agent.context;
20 import java.io.IOException;
21 import java.nio.ByteBuffer;
22 import java.nio.ByteOrder;
23 import java.nio.charset.Charset;
24 import java.util.Collection;
28 * This class is used to serialize the list of "context info" objects to pass
31 * The protocol expects a single byte array parameter. This byte array consists
32 * of a series of fixed-size entries, where each entry contains the following
33 * elements (with their size in bytes in parenthesis):
36 * <li>The full context name, like "$app.myprovider:mycontext" (256)</li>
37 * <li>The context value type (1)</li>
38 * <li>The context value itself(256)</li>
41 * So the total size of each entry is 513 bytes. All unused bytes will be
44 * @author Alexandre Montplaisir
46 public class ContextInfoSerializer {
48 private enum DataType {
59 private final byte value;
61 private DataType(int value) {
62 this.value = (byte) value;
65 public byte getValue() {
70 private static final String UST_APP_CTX_PREFIX = "$app.";
71 private static final int ELEMENT_LENGTH = 256;
72 private static final int ENTRY_LENGTH = 513;
73 private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder();
74 private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
75 private static final byte[] EMPTY_ARRAY = new byte[0];
78 * From the list of requested contexts in the tracing session, look them up
79 * in the {@link ContextInfoManager}, retrieve the available ones, and
80 * serialize them into a byte array.
82 * @param enabledContexts
83 * The contexts that are enabled in the tracing session (indexed
84 * first by retriever name, then by index names). Should come
85 * from the LTTng Agent.
86 * @return The byte array representing the intersection of the requested and
89 public static byte[] queryAndSerializeRequestedContexts(Collection<Map.Entry<String, Map<String, Integer>>> enabledContexts) {
90 if (enabledContexts.isEmpty()) {
91 /* Early return if there is no requested context information */
95 /* Compute the total number of contexts (flatten the map) */
96 int totalArraySize = 0;
97 for (Map.Entry<String, Map<String, Integer>> contexts : enabledContexts) {
98 totalArraySize += contexts.getValue().size() * ENTRY_LENGTH;
101 ContextInfoManager contextManager;
103 contextManager = ContextInfoManager.getInstance();
104 } catch (IOException e) {
106 * The JNI library is not available, do not send any context
107 * information. No retriever could have been defined anyways.
112 ByteBuffer buffer = ByteBuffer.allocate(totalArraySize);
113 buffer.order(NATIVE_ORDER);
116 for (Map.Entry<String, Map<String, Integer>> entry : enabledContexts) {
117 String requestedRetrieverName = entry.getKey();
118 Map<String, Integer> requestedContexts = entry.getValue();
120 IContextInfoRetriever retriever = contextManager.getContextInfoRetriever(requestedRetrieverName);
122 for (String requestedContext : requestedContexts.keySet()) {
124 if (retriever == null) {
127 contextInfo = retriever.retrieveContextInfo(requestedContext);
129 * 'contextInfo' can still be null here, which would
130 * indicate the retriever does not supply this context. We
131 * will still write this information so that the tracer can
136 /* Serialize the result to the buffer */
137 // FIXME Eventually pass the retriever name only once?
138 String fullContextName = (UST_APP_CTX_PREFIX + requestedRetrieverName + ':' + requestedContext);
139 byte[] strArray = fullContextName.getBytes(UTF8_CHARSET);
140 int remainingBytes = ELEMENT_LENGTH - strArray.length;
141 // FIXME Handle case where name is too long...
142 buffer.put(strArray);
143 buffer.position(buffer.position() + remainingBytes);
145 serializeContextInfo(buffer, contextInfo);
148 return buffer.array();
151 private static void serializeContextInfo(ByteBuffer buffer, Object contextInfo) {
153 if (contextInfo == null) {
154 buffer.put(DataType.NULL.getValue());
155 remainingBytes = ELEMENT_LENGTH;
157 } else if (contextInfo instanceof Integer) {
158 buffer.put(DataType.INTEGER.getValue());
159 buffer.putInt(((Integer) contextInfo).intValue());
160 remainingBytes = ELEMENT_LENGTH - 4;
162 } else if (contextInfo instanceof Long) {
163 buffer.put(DataType.LONG.getValue());
164 buffer.putLong(((Long) contextInfo).longValue());
165 remainingBytes = ELEMENT_LENGTH - 8;
167 } else if (contextInfo instanceof Double) {
168 buffer.put(DataType.DOUBLE.getValue());
169 buffer.putDouble(((Double) contextInfo).doubleValue());
170 remainingBytes = ELEMENT_LENGTH - 8;
172 } else if (contextInfo instanceof Float) {
173 buffer.put(DataType.FLOAT.getValue());
174 buffer.putFloat(((Float) contextInfo).floatValue());
175 remainingBytes = ELEMENT_LENGTH - 4;
177 } else if (contextInfo instanceof Byte) {
178 buffer.put(DataType.BYTE.getValue());
179 buffer.put(((Byte) contextInfo).byteValue());
180 remainingBytes = ELEMENT_LENGTH - 1;
182 } else if (contextInfo instanceof Short) {
183 buffer.put(DataType.SHORT.getValue());
184 buffer.putShort(((Short) contextInfo).shortValue());
185 remainingBytes = ELEMENT_LENGTH - 2;
187 } else if (contextInfo instanceof Boolean) {
188 buffer.put(DataType.BOOLEAN.getValue());
189 boolean b = ((Boolean) contextInfo).booleanValue();
190 /* Converted to one byte, write 1 for true, 0 for false */
191 buffer.put((byte) (b ? 1 : 0));
192 remainingBytes = ELEMENT_LENGTH - 1;
195 /* We'll write the object as a string. Also includes the case of Character. */
196 String str = contextInfo.toString();
197 byte[] strArray = str.getBytes(UTF8_CHARSET);
199 buffer.put(DataType.STRING.getValue());
200 if (strArray.length >= ELEMENT_LENGTH) {
201 /* Trim the string to the max allowed length */
202 buffer.put(strArray, 0, ELEMENT_LENGTH);
205 buffer.put(strArray);
206 remainingBytes = ELEMENT_LENGTH - strArray.length;
209 buffer.position(buffer.position() + remainingBytes);