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
);