blob: ca83e0c7ad985775fbfa907120295785e805dd71 [file] [log] [blame]
lryane4bd1c72014-09-08 14:03:35 -07001package com.google.net.stubby;
2
3import static java.nio.charset.StandardCharsets.UTF_8;
4
5import com.google.common.annotations.VisibleForTesting;
6import com.google.common.base.Function;
7import com.google.common.base.Preconditions;
8import com.google.common.collect.Iterables;
9import com.google.common.collect.Iterators;
10import com.google.common.collect.LinkedListMultimap;
11import com.google.common.collect.ListMultimap;
12import com.google.common.collect.Lists;
13
14import java.nio.charset.StandardCharsets;
15import java.util.Iterator;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
19
20import javax.annotation.concurrent.NotThreadSafe;
21
22/**
23 * Provides access to read and write metadata values to be exchanged during a call.
24 * <p>
25 * This class is not thread safe, implementations should ensure that header reads and writes
26 * do not occur in multiple threads concurrently.
27 * </p>
28 */
29@NotThreadSafe
30public abstract class Metadata<S extends Metadata> {
31
32 /**
33 * Interleave keys and values into a single iterator.
34 */
35 private static Iterator<String> fromMapEntries(Iterable<Map.Entry<String, String>> entries) {
36 final Iterator<Map.Entry<String, String>> iterator = entries.iterator();
37 return new Iterator<String>() {
38 Map.Entry<String, String> last;
39 @Override
40 public boolean hasNext() {
41 return last != null || iterator.hasNext();
42 }
43
44 @Override
45 public String next() {
46 if (last == null) {
47 last = iterator.next();
48 return last.getKey();
49 } else {
50 String val = last.getValue();
51 last = null;
52 return val;
53 }
54 }
55
56 @Override
57 public void remove() {
58 throw new UnsupportedOperationException();
59 }
60 };
61 }
62
63 /**
64 * Simple metadata marshaller that encodes strings as either UTF-8 or ASCII bytes.
65 */
66 public static final Marshaller<String> STRING_MARSHALLER =
67 new Marshaller<String>() {
68
69 @Override
70 public byte[] toBytes(String value) {
71 return value.getBytes(UTF_8);
72 }
73
74 @Override
75 public String toAscii(String value) {
76 return value;
77 }
78
79 @Override
80 public String parseBytes(byte[] serialized) {
81 return new String(serialized, UTF_8);
82 }
83
84 @Override
85 public String parseAscii(String ascii) {
86 return ascii;
87 }
88 };
89
90 private final ListMultimap<String, MetadataEntry> store;
91 private final boolean serializable;
92
93 /**
94 * Constructor called by the transport layer when it receives binary metadata.
95 */
96 // TODO(user): Convert to use ByteString so we can cache transformations
97 private Metadata(byte[]... binaryValues) {
98 store = LinkedListMultimap.create();
99 for (int i = 0; i < binaryValues.length; i++) {
100 String name = new String(binaryValues[i], StandardCharsets.US_ASCII);
101 store.put(name, new MetadataEntry(binaryValues[++i]));
102 }
103 this.serializable = false;
104 }
105
106 /**
107 * Constructor called by the transport layer when it receives ASCII metadata.
108 */
109 private Metadata(String... asciiValues) {
110 store = LinkedListMultimap.create();
111 for (int i = 0; i < asciiValues.length; i++) {
112 store.put(asciiValues[i], new MetadataEntry(asciiValues[++i]));
113 }
114 this.serializable = false;
115 }
116
117 /**
118 * Constructor called by the application layer when it wants to send metadata.
119 */
120 private Metadata() {
121 store = LinkedListMultimap.create();
122 this.serializable = true;
123 }
124
125 /**
126 * Returns true if a value is defined for the given key.
127 */
128 public <T> boolean containsKey(Key key) {
129 return store.containsKey(key.name);
130 }
131
132 /**
133 * Returns the last metadata entry added with the name 'name' parsed as T.
134 * @return the parsed metadata entry or null if not defined
135 */
136 public <T> T get(Key<T> key) {
137 MetadataEntry metadataEntry = Iterables.getLast(store.get(key.name()));
138 return metadataEntry == null ? null : metadataEntry.getParsed(key);
139 }
140
141 /**
142 * Returns all the metadata entries named 'name', in the order they were received,
143 * parsed as T.
144 */
145 public <T> Iterable<T> getAll(final Key<T> key) {
146 return Iterables.transform(
147 store.get(key.name()),
148 new Function<MetadataEntry, T>() {
149 @Override
150 public T apply(MetadataEntry entry) {
151 return entry.getParsed(key);
152 }
153 });
154 }
155
156 public <T> void put(Key<T> key, T value) {
157 store.put(key.name(), new MetadataEntry(key, value));
158 }
159
160 /**
161 * Remove a specific value.
162 */
163 public <T> boolean remove(Key<T> key, T value) {
164 return store.remove(key.name(), value);
165 }
166
167 /**
168 * Remove all values for the given key.
169 */
170 public <T> List<T> removeAll(final Key<T> key) {
171 return Lists.transform(store.removeAll(key.name()), new Function<MetadataEntry, T>() {
172 @Override
173 public T apply(MetadataEntry metadataEntry) {
174 return metadataEntry.getParsed(key);
175 }
176 });
177 }
178
179 /**
180 * Can this metadata be serialized. Metadata constructed from raw binary or ascii values
181 * cannot be serialized without merging it into a serializable instance using
182 * {@link #merge(Metadata, java.util.Set)}
183 */
184 public boolean isSerializable() {
185 return serializable;
186 }
187
188 /**
189 * Serialize all the metadata entries
190 */
191 public byte[][] serialize() {
192 Preconditions.checkState(serializable, "Can't serialize raw metadata");
193 byte[][] serialized = new byte[store.size() * 2][];
194 int i = 0;
195 for (Map.Entry<String, MetadataEntry> entry : store.entries()) {
196 serialized[i++] = entry.getValue().key.asciiName();
197 serialized[i++] = entry.getValue().getSerialized();
198 }
199 return serialized;
200 }
201
202 /**
203 * Serialize all the metadata entries
204 */
205 public String[] serializeAscii() {
206 Preconditions.checkState(serializable, "Can't serialize received metadata");
207 String[] serialized = new String[store.size() * 2];
208 int i = 0;
209 for (Map.Entry<String, MetadataEntry> entry : store.entries()) {
210 serialized[i++] = entry.getValue().key.name();
211 serialized[i++] = entry.getValue().getSerializedAscii();
212 }
213 return serialized;
214 }
215
216 /**
217 * Perform a simple merge of two sets of metadata.
218 * <p>
219 * Note that we can't merge non-serializable metadata into serializable.
220 * </p>
221 */
222 public void merge(Metadata other) {
223 Preconditions.checkNotNull(other);
224 if (this.serializable) {
225 if (!other.serializable) {
226 throw new IllegalArgumentException(
227 "Cannot merge non-serializable metadata into serializable metadata without keys");
228 }
229 }
230 store.putAll(other.store);
231 }
232
233 /**
234 * Merge values for the given set of keys into this set of metadata.
235 */
236 public void merge(Metadata other, Set<Key> keys) {
237 Preconditions.checkNotNull(other);
238 for (Key key : keys) {
239 if (other.containsKey(key)) {
240 Iterable values = other.getAll(key);
241 for (Object value : values) {
242 put(key, value);
243 }
244 }
245 }
246 }
247
248 /**
249 * Concrete instance for metadata attached to the start of a call.
250 */
251 public static class Headers extends Metadata<Headers> {
252 private String path;
253 private String authority;
254
255 /**
256 * Called by the transport layer to create headers from their binary serialized values.
257 */
258 public Headers(byte[]... headers) {
259 super(headers);
260 }
261
262 /**
263 * Called by the transport layer to create headers from their ASCII serialized values.
264 */
265 public Headers(String... asciiValues) {
266 super(asciiValues);
267 }
268
269 /**
270 * Called by the transport layer to create headers from their ASCII serialized values.
271 */
272 public Headers(Iterable<Map.Entry<String, String>> mapEntries) {
273 super(Iterators.toArray(fromMapEntries(mapEntries), String.class));
274 }
275
276 /**
277 * Called by the application layer to construct headers prior to passing them to the
278 * transport for serialization.
279 */
280 public Headers() {
281 }
282
283 /**
284 * The path for the operation.
285 */
286 public String getPath() {
287 return path;
288 }
289
290 public void setPath(String path) {
291 this.path = path;
292 }
293
294 /**
295 * The serving authority for the operation.
296 */
297 public String getAuthority() {
298 return authority;
299 }
300
301 public void setAuthority(String authority) {
302 this.authority = authority;
303 }
304 }
305
306 /**
307 * Concrete instance for metadata attached to the end of the call. Only provided by
308 * servers.
309 */
310 public static class Trailers extends Metadata<Headers> {
311 /**
312 * Called by the transport layer to create trailers from their binary serialized values.
313 */
314 public Trailers(byte[]... headers) {
315 super(headers);
316 }
317
318 /**
319 * Called by the transport layer to create trailers from their ASCII serialized values.
320 */
321 public Trailers(String... asciiValues) {
322 super(asciiValues);
323 }
324
325 /**
326 * Called by the transport layer to create headers from their ASCII serialized values.
327 */
328 public Trailers(Iterable<Map.Entry<String, String>> mapEntries) {
329 super(Iterators.toArray(fromMapEntries(mapEntries), String.class));
330 }
331
332 /**
333 * Called by the application layer to construct trailers prior to passing them to the
334 * transport for serialization.
335 */
336 public Trailers() {
337 }
338 }
339
340
341 /**
342 * Marshaller for metadata values.
343 */
344 public static interface Marshaller<T> {
345 /**
346 * Serialize a metadata value to bytes.
347 * @param value to serialize
348 * @return serialized version of value, or null if value cannot be transmitted.
349 */
350 public byte[] toBytes(T value);
351
352 /**
353 * Serialize a metadata value to an ASCII string
354 * @param value to serialize
355 * @return serialized ascii version of value, or null if value cannot be transmitted.
356 */
357 public String toAscii(T value);
358
359 /**
360 * Parse a serialized metadata value from bytes.
361 * @param serialized value of metadata to parse
362 * @return a parsed instance of type T
363 */
364 public T parseBytes(byte[] serialized);
365
366 /**
367 * Parse a serialized metadata value from an ascii string.
368 * @param ascii string value of metadata to parse
369 * @return a parsed instance of type T
370 */
371 public T parseAscii(String ascii);
372 }
373
374 /**
375 * Key for metadata entries. Allows for parsing and serialization of metadata.
376 */
377 public static class Key<T> {
378
379 private final String name;
380 private final byte[] asciiName;
381 private final Marshaller<T> marshaller;
382
383 /**
384 * Keys have a name and a marshaller used for serialization.
385 */
386 public Key(String name, Marshaller<T> marshaller) {
387 this.name = Preconditions.checkNotNull(name, "name").intern();
388 this.asciiName = name.getBytes(StandardCharsets.US_ASCII);
389 this.marshaller = Preconditions.checkNotNull(marshaller);
390 }
391
392 public String name() {
393 return name;
394 }
395
396 @VisibleForTesting
397 byte[] asciiName() {
398 return asciiName;
399 }
400
401 public Marshaller<T> getMarshaller() {
402 return marshaller;
403 }
404 }
405
406 private static class MetadataEntry {
407 Object parsed;
408 Key key;
409 byte[] serializedBinary;
410 String serializedAscii;
411
412 /**
413 * Constructor used when application layer adds a parsed value.
414 */
415 private MetadataEntry(Key key, Object parsed) {
416 this.parsed = Preconditions.checkNotNull(parsed);
417 this.key = Preconditions.checkNotNull(key);
418 }
419
420 /**
421 * Constructor used when reading a value from the transport.
422 */
423 private MetadataEntry(byte[] serialized) {
424 Preconditions.checkNotNull(serialized);
425 this.serializedBinary = serialized;
426 }
427
428 /**
429 * Constructor used when reading a value from the transport.
430 */
431 private MetadataEntry(String serializedAscii) {
432 this.serializedAscii = Preconditions.checkNotNull(serializedAscii);
433 }
434
435 public <T> T getParsed(Key<T> key) {
436 @SuppressWarnings("unchecked")
437 T value = (T) parsed;
438 if (value != null) {
439 if (this.key != key) {
440 // Keys don't match so serialize using the old key
441 serializedBinary = this.key.getMarshaller().toBytes(value);
442 } else {
443 return value;
444 }
445 }
446 this.key = key;
447 if (serializedBinary != null) {
448 value = key.getMarshaller().parseBytes(serializedBinary);
449 } else if (serializedAscii != null) {
450 value = key.getMarshaller().parseAscii(serializedAscii);
451 }
452 parsed = value;
453 return value;
454 }
455
456 @SuppressWarnings("unchecked")
457 public byte[] getSerialized() {
458 return serializedBinary =
459 serializedBinary == null
460 ? key.getMarshaller().toBytes(parsed) :
461 serializedBinary;
462 }
463
464 @SuppressWarnings("unchecked")
465 public String getSerializedAscii() {
466 return serializedAscii =
467 serializedAscii == null
468 ? key.getMarshaller().toAscii(parsed) :
469 serializedAscii;
470 }
471 }
472}