Implement Java agent application context retrieval
[lttng-ust.git] / liblttng-ust-java-agent / java / lttng-ust-agent-common / org / lttng / ust / agent / context / ContextInfoSerializer.java
CommitLineData
8ab5c06b
AM
1/*
2 * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
3 *
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.
7 *
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
11 * for more details.
12 *
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
16 */
17
18package org.lttng.ust.agent.context;
19
20import java.io.IOException;
21import java.nio.ByteBuffer;
22import java.nio.ByteOrder;
23import java.nio.charset.Charset;
24import java.util.Collection;
25import java.util.Map;
26
27/**
28 * This class is used to serialize the list of "context info" objects to pass
29 * through JNI.
30 *
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):
34 *
35 * <ul>
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>
39 * </ul>
40 *
41 * So the total size of each entry is 513 bytes. All unused bytes will be
42 * zero'ed.
43 *
44 * @author Alexandre Montplaisir
45 */
46public class ContextInfoSerializer {
47
48 private enum DataType {
49 NULL(0),
50 INTEGER(1),
51 LONG(2),
52 DOUBLE(3),
53 FLOAT(4),
54 BYTE(5),
55 SHORT(6),
56 BOOLEAN(7),
57 STRING(8);
58
59 private final byte value;
60
61 private DataType(int value) {
62 this.value = (byte) value;
63 }
64
65 public byte getValue() {
66 return value;
67 }
68 }
69
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];
76
77 /**
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.
81 *
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
87 * available contexts.
88 */
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 */
92 return EMPTY_ARRAY;
93 }
94
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;
99 }
100
101 ContextInfoManager contextManager;
102 try {
103 contextManager = ContextInfoManager.getInstance();
104 } catch (IOException e) {
105 /*
106 * The JNI library is not available, do not send any context
107 * information. No retriever could have been defined anyways.
108 */
109 return EMPTY_ARRAY;
110 }
111
112 ByteBuffer buffer = ByteBuffer.allocate(totalArraySize);
113 buffer.order(NATIVE_ORDER);
114 buffer.clear();
115
116 for (Map.Entry<String, Map<String, Integer>> entry : enabledContexts) {
117 String requestedRetrieverName = entry.getKey();
118 Map<String, Integer> requestedContexts = entry.getValue();
119
120 IContextInfoRetriever retriever = contextManager.getContextInfoRetriever(requestedRetrieverName);
121
122 for (String requestedContext : requestedContexts.keySet()) {
123 Object contextInfo;
124 if (retriever == null) {
125 contextInfo = null;
126 } else {
127 contextInfo = retriever.retrieveContextInfo(requestedContext);
128 /*
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
132 * know about it.
133 */
134 }
135
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);
144
145 serializeContextInfo(buffer, contextInfo);
146 }
147 }
148 return buffer.array();
149 }
150
151 private static void serializeContextInfo(ByteBuffer buffer, Object contextInfo) {
152 int remainingBytes;
153 if (contextInfo == null) {
154 buffer.put(DataType.NULL.getValue());
155 remainingBytes = ELEMENT_LENGTH;
156
157 } else if (contextInfo instanceof Integer) {
158 buffer.put(DataType.INTEGER.getValue());
159 buffer.putInt(((Integer) contextInfo).intValue());
160 remainingBytes = ELEMENT_LENGTH - 4;
161
162 } else if (contextInfo instanceof Long) {
163 buffer.put(DataType.LONG.getValue());
164 buffer.putLong(((Long) contextInfo).longValue());
165 remainingBytes = ELEMENT_LENGTH - 8;
166
167 } else if (contextInfo instanceof Double) {
168 buffer.put(DataType.DOUBLE.getValue());
169 buffer.putDouble(((Double) contextInfo).doubleValue());
170 remainingBytes = ELEMENT_LENGTH - 8;
171
172 } else if (contextInfo instanceof Float) {
173 buffer.put(DataType.FLOAT.getValue());
174 buffer.putFloat(((Float) contextInfo).floatValue());
175 remainingBytes = ELEMENT_LENGTH - 4;
176
177 } else if (contextInfo instanceof Byte) {
178 buffer.put(DataType.BYTE.getValue());
179 buffer.put(((Byte) contextInfo).byteValue());
180 remainingBytes = ELEMENT_LENGTH - 1;
181
182 } else if (contextInfo instanceof Short) {
183 buffer.put(DataType.SHORT.getValue());
184 buffer.putShort(((Short) contextInfo).shortValue());
185 remainingBytes = ELEMENT_LENGTH - 2;
186
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;
193
194 } else {
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);
198
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);
203 remainingBytes = 0;
204 } else {
205 buffer.put(strArray);
206 remainingBytes = ELEMENT_LENGTH - strArray.length;
207 }
208 }
209 buffer.position(buffer.position() + remainingBytes);
210 }
211}
This page took 0.031248 seconds and 4 git commands to generate.