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;
27 import org.lttng.ust.agent.utils.LttngUstAgentLogger;
30 * This class is used to serialize the list of "context info" objects to pass
33 * The protocol expects a single byte array parameter. This byte array consists
34 * of a series of fixed-size entries, where each entry contains the following
35 * elements (with their size in bytes in parenthesis):
38 * <li>The full context name, like "$app.myprovider:mycontext" (256)</li>
39 * <li>The context value type (1)</li>
40 * <li>The context value itself(256)</li>
43 * So the total size of each entry is 513 bytes. All unused bytes will be
46 * @author Alexandre Montplaisir
48 public class ContextInfoSerializer {
50 private enum DataType {
61 private final byte value;
63 private DataType(int value) {
64 this.value = (byte) value;
67 public byte getValue() {
72 private static final String UST_APP_CTX_PREFIX = "$app.";
73 private static final int ELEMENT_LENGTH = 256;
74 private static final int ENTRY_LENGTH = 513;
75 private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder();
76 private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
77 private static final byte[] EMPTY_ARRAY = new byte[0];
80 * From the list of requested contexts in the tracing session, look them up
81 * in the {@link ContextInfoManager}, retrieve the available ones, and
82 * serialize them into a byte array.
84 * @param enabledContexts
85 * The contexts that are enabled in the tracing session (indexed
86 * first by retriever name, then by index names). Should come
87 * from the LTTng Agent.
88 * @return The byte array representing the intersection of the requested and
91 public static byte[] queryAndSerializeRequestedContexts(Collection<Map.Entry<String, Map<String, Integer>>> enabledContexts) {
92 if (enabledContexts.isEmpty()) {
93 /* Early return if there is no requested context information */
97 /* Compute the total number of contexts (flatten the map) */
98 int totalArraySize = 0;
99 for (Map.Entry<String, Map<String, Integer>> contexts : enabledContexts) {
100 totalArraySize += contexts.getValue().size() * ENTRY_LENGTH;
103 ContextInfoManager contextManager;
105 contextManager = ContextInfoManager.getInstance();
106 } catch (IOException e) {
108 * The JNI library is not available, do not send any context
109 * information. No retriever could have been defined anyways.
114 ByteBuffer buffer = ByteBuffer.allocate(totalArraySize);
115 buffer.order(NATIVE_ORDER);
118 for (Map.Entry<String, Map<String, Integer>> entry : enabledContexts) {
119 String requestedRetrieverName = entry.getKey();
120 Map<String, Integer> requestedContexts = entry.getValue();
122 IContextInfoRetriever retriever = contextManager.getContextInfoRetriever(requestedRetrieverName);
124 for (String requestedContext : requestedContexts.keySet()) {
126 if (retriever == null) {
129 contextInfo = retriever.retrieveContextInfo(requestedContext);
131 * 'contextInfo' can still be null here, which would
132 * indicate the retriever does not supply this context. We
133 * will still write this information so that the tracer can
138 /* Serialize the result to the buffer */
139 // FIXME Eventually pass the retriever name only once?
140 String fullContextName = (UST_APP_CTX_PREFIX + requestedRetrieverName + ':' + requestedContext);
141 byte[] strArray = fullContextName.getBytes(UTF8_CHARSET);
142 int remainingBytes = ELEMENT_LENGTH - strArray.length;
143 // FIXME Handle case where name is too long...
144 buffer.put(strArray);
145 buffer.position(buffer.position() + remainingBytes);
147 LttngUstAgentLogger.log(ContextInfoSerializer.class,
148 "ContextInfoSerializer: Context to be sent through JNI: " + fullContextName + '=' +
149 (contextInfo == null ? "null" : contextInfo.toString()));
151 serializeContextInfo(buffer, contextInfo);
154 return buffer.array();
157 private static void serializeContextInfo(ByteBuffer buffer, Object contextInfo) {
159 if (contextInfo == null) {
160 buffer.put(DataType.NULL.getValue());
161 remainingBytes = ELEMENT_LENGTH;
163 } else if (contextInfo instanceof Integer) {
164 buffer.put(DataType.INTEGER.getValue());
165 buffer.putInt(((Integer) contextInfo).intValue());
166 remainingBytes = ELEMENT_LENGTH - 4;
168 } else if (contextInfo instanceof Long) {
169 buffer.put(DataType.LONG.getValue());
170 buffer.putLong(((Long) contextInfo).longValue());
171 remainingBytes = ELEMENT_LENGTH - 8;
173 } else if (contextInfo instanceof Double) {
174 buffer.put(DataType.DOUBLE.getValue());
175 buffer.putDouble(((Double) contextInfo).doubleValue());
176 remainingBytes = ELEMENT_LENGTH - 8;
178 } else if (contextInfo instanceof Float) {
179 buffer.put(DataType.FLOAT.getValue());
180 buffer.putFloat(((Float) contextInfo).floatValue());
181 remainingBytes = ELEMENT_LENGTH - 4;
183 } else if (contextInfo instanceof Byte) {
184 buffer.put(DataType.BYTE.getValue());
185 buffer.put(((Byte) contextInfo).byteValue());
186 remainingBytes = ELEMENT_LENGTH - 1;
188 } else if (contextInfo instanceof Short) {
189 buffer.put(DataType.SHORT.getValue());
190 buffer.putShort(((Short) contextInfo).shortValue());
191 remainingBytes = ELEMENT_LENGTH - 2;
193 } else if (contextInfo instanceof Boolean) {
194 buffer.put(DataType.BOOLEAN.getValue());
195 boolean b = ((Boolean) contextInfo).booleanValue();
196 /* Converted to one byte, write 1 for true, 0 for false */
197 buffer.put((byte) (b ? 1 : 0));
198 remainingBytes = ELEMENT_LENGTH - 1;
201 /* We'll write the object as a string. Also includes the case of Character. */
202 String str = contextInfo.toString();
203 byte[] strArray = str.getBytes(UTF8_CHARSET);
205 buffer.put(DataType.STRING.getValue());
206 if (strArray.length >= ELEMENT_LENGTH) {
207 /* Trim the string to the max allowed length */
208 buffer.put(strArray, 0, ELEMENT_LENGTH);
211 buffer.put(strArray);
212 remainingBytes = ELEMENT_LENGTH - strArray.length;
215 buffer.position(buffer.position() + remainingBytes);