0
# Metadata and Reflection
1
2
Runtime metadata support for Thrift objects, enabling reflection, introspection, and dynamic handling of Thrift structures. The metadata system provides detailed information about Thrift object structure, field types, and requirements.
3
4
## Capabilities
5
6
### Field Metadata
7
8
Core metadata classes for describing Thrift struct fields and their properties.
9
10
```java { .api }
11
/**
12
* Stores metadata information about a Thrift struct field
13
*/
14
public class FieldMetaData {
15
/** Field name */
16
public final String fieldName;
17
18
/** Field requirement type (REQUIRED, OPTIONAL, DEFAULT) */
19
public final byte requirementType;
20
21
/** Value metadata describing the field's type */
22
public final FieldValueMetaData valueMetaData;
23
24
/** Create field metadata */
25
public FieldMetaData(String fieldName, byte requirementType, FieldValueMetaData valueMetaData);
26
27
/** Add struct metadata map for a Thrift class */
28
public static <T extends TBase<T, F>, F extends TFieldIdEnum> void addStructMetaDataMap(
29
Class<T> sClass, Map<F, FieldMetaData> map);
30
31
/** Get struct metadata map for a Thrift class */
32
public static <T extends TBase<T, F>, F extends TFieldIdEnum> Map<F, FieldMetaData> getStructMetaDataMap(
33
Class<T> sClass);
34
35
/** Get field annotations */
36
public Map<String, String> getFieldAnnotations();
37
}
38
39
/**
40
* Base class for field value metadata describing field types
41
*/
42
public class FieldValueMetaData {
43
/** Thrift type constant for this field */
44
public final byte type;
45
46
/** Type name for typedef fields */
47
public final String typedefName;
48
49
/** Create field value metadata */
50
public FieldValueMetaData(byte type);
51
52
/** Create field value metadata with typedef name */
53
public FieldValueMetaData(byte type, String typedefName);
54
55
/** Check if this is a typedef field */
56
public boolean isTypedef();
57
58
/** Get typedef name (if applicable) */
59
public String getTypedefName();
60
61
/** Check if this field represents binary data */
62
public boolean isBinary();
63
}
64
```
65
66
**Usage Examples:**
67
68
```java
69
import org.apache.thrift.meta_data.FieldMetaData;
70
import org.apache.thrift.meta_data.FieldValueMetaData;
71
import org.apache.thrift.TFieldRequirementType;
72
import org.apache.thrift.protocol.TType;
73
74
// Access metadata for a Thrift struct
75
public class MetadataExample {
76
public void inspectStruct() {
77
// Get metadata map for MyStruct class
78
Map<MyStruct._Fields, FieldMetaData> metaDataMap =
79
FieldMetaData.getStructMetaDataMap(MyStruct.class);
80
81
// Iterate through all fields
82
for (Map.Entry<MyStruct._Fields, FieldMetaData> entry : metaDataMap.entrySet()) {
83
MyStruct._Fields field = entry.getKey();
84
FieldMetaData metaData = entry.getValue();
85
86
System.out.println("Field: " + metaData.fieldName);
87
System.out.println(" Type: " + getTypeName(metaData.valueMetaData.type));
88
System.out.println(" Required: " + isRequired(metaData.requirementType));
89
System.out.println(" Typedef: " + metaData.valueMetaData.isTypedef());
90
}
91
}
92
93
private String getTypeName(byte type) {
94
switch (type) {
95
case TType.STRING: return "string";
96
case TType.I32: return "i32";
97
case TType.I64: return "i64";
98
case TType.BOOL: return "bool";
99
case TType.STRUCT: return "struct";
100
case TType.LIST: return "list";
101
case TType.MAP: return "map";
102
case TType.SET: return "set";
103
default: return "unknown";
104
}
105
}
106
107
private boolean isRequired(byte requirementType) {
108
return requirementType == TFieldRequirementType.REQUIRED;
109
}
110
}
111
```
112
113
### Specialized Metadata Types
114
115
Specific metadata classes for different Thrift data types.
116
117
```java { .api }
118
/**
119
* Metadata for struct types
120
*/
121
public class StructMetaData extends FieldValueMetaData {
122
/** The struct class */
123
public final Class<? extends TBase> structClass;
124
125
/** Create struct metadata */
126
public StructMetaData(byte type, Class<? extends TBase> structClass);
127
}
128
129
/**
130
* Metadata for list types
131
*/
132
public class ListMetaData extends FieldValueMetaData {
133
/** Element type metadata */
134
public final FieldValueMetaData elemMetaData;
135
136
/** Create list metadata */
137
public ListMetaData(byte type, FieldValueMetaData elemMetaData);
138
}
139
140
/**
141
* Metadata for map types
142
*/
143
public class MapMetaData extends FieldValueMetaData {
144
/** Key type metadata */
145
public final FieldValueMetaData keyMetaData;
146
147
/** Value type metadata */
148
public final FieldValueMetaData valueMetaData;
149
150
/** Create map metadata */
151
public MapMetaData(byte type, FieldValueMetaData keyMetaData, FieldValueMetaData valueMetaData);
152
}
153
154
/**
155
* Metadata for set types
156
*/
157
public class SetMetaData extends FieldValueMetaData {
158
/** Element type metadata */
159
public final FieldValueMetaData elemMetaData;
160
161
/** Create set metadata */
162
public SetMetaData(byte type, FieldValueMetaData elemMetaData);
163
}
164
165
/**
166
* Metadata for enum types
167
*/
168
public class EnumMetaData extends FieldValueMetaData {
169
/** The enum class */
170
public final Class<? extends TEnum> enumClass;
171
172
/** Create enum metadata */
173
public EnumMetaData(byte type, Class<? extends TEnum> enumClass);
174
}
175
```
176
177
**Usage Examples:**
178
179
```java
180
import org.apache.thrift.meta_data.*;
181
import org.apache.thrift.protocol.TType;
182
183
// Example of working with complex type metadata
184
public class ComplexMetadataExample {
185
public void analyzeComplexField(FieldMetaData fieldMetaData) {
186
FieldValueMetaData valueMetaData = fieldMetaData.valueMetaData;
187
188
switch (valueMetaData.type) {
189
case TType.STRUCT:
190
StructMetaData structMeta = (StructMetaData) valueMetaData;
191
System.out.println("Struct type: " + structMeta.structClass.getSimpleName());
192
break;
193
194
case TType.LIST:
195
ListMetaData listMeta = (ListMetaData) valueMetaData;
196
System.out.println("List of: " + getTypeName(listMeta.elemMetaData.type));
197
break;
198
199
case TType.MAP:
200
MapMetaData mapMeta = (MapMetaData) valueMetaData;
201
System.out.println("Map<" +
202
getTypeName(mapMeta.keyMetaData.type) + ", " +
203
getTypeName(mapMeta.valueMetaData.type) + ">");
204
break;
205
206
case TType.SET:
207
SetMetaData setMeta = (SetMetaData) valueMetaData;
208
System.out.println("Set of: " + getTypeName(setMeta.elemMetaData.type));
209
break;
210
211
case TType.ENUM: // Note: This would be a custom extension
212
EnumMetaData enumMeta = (EnumMetaData) valueMetaData;
213
System.out.println("Enum type: " + enumMeta.enumClass.getSimpleName());
214
break;
215
216
default:
217
System.out.println("Primitive type: " + getTypeName(valueMetaData.type));
218
}
219
}
220
221
private String getTypeName(byte type) {
222
// Implementation from previous example
223
switch (type) {
224
case TType.STRING: return "string";
225
case TType.I32: return "i32";
226
case TType.I64: return "i64";
227
case TType.BOOL: return "bool";
228
case TType.DOUBLE: return "double";
229
case TType.BYTE: return "byte";
230
case TType.I16: return "i16";
231
default: return "type_" + type;
232
}
233
}
234
}
235
```
236
237
### Generated Code Integration
238
239
Generated Thrift classes automatically include metadata initialization.
240
241
```java
242
// Example of how metadata is typically set up in generated code
243
public class MyStruct implements TBase<MyStruct, MyStruct._Fields> {
244
245
// Field enum
246
public enum _Fields implements TFieldIdEnum {
247
NAME((short)1, "name"),
248
VALUE((short)2, "value"),
249
ITEMS((short)3, "items");
250
251
private final short _thriftId;
252
private final String _fieldName;
253
254
_Fields(short thriftId, String fieldName) {
255
_thriftId = thriftId;
256
_fieldName = fieldName;
257
}
258
259
public short getThriftFieldId() {
260
return _thriftId;
261
}
262
263
public String getFieldName() {
264
return _fieldName;
265
}
266
}
267
268
// Metadata map setup
269
public static final Map<_Fields, FieldMetaData> metaDataMap;
270
static {
271
Map<_Fields, FieldMetaData> tmpMap = new EnumMap<_Fields, FieldMetaData>(_Fields.class);
272
273
tmpMap.put(_Fields.NAME, new FieldMetaData("name",
274
TFieldRequirementType.REQUIRED,
275
new FieldValueMetaData(TType.STRING)));
276
277
tmpMap.put(_Fields.VALUE, new FieldMetaData("value",
278
TFieldRequirementType.OPTIONAL,
279
new FieldValueMetaData(TType.I32)));
280
281
tmpMap.put(_Fields.ITEMS, new FieldMetaData("items",
282
TFieldRequirementType.OPTIONAL,
283
new ListMetaData(TType.LIST, new FieldValueMetaData(TType.STRING))));
284
285
metaDataMap = Collections.unmodifiableMap(tmpMap);
286
FieldMetaData.addStructMetaDataMap(MyStruct.class, metaDataMap);
287
}
288
}
289
```
290
291
### Runtime Field Validation
292
293
Using metadata for runtime validation and introspection.
294
295
```java
296
import org.apache.thrift.meta_data.FieldMetaData;
297
import org.apache.thrift.TFieldRequirementType;
298
299
// Example utility for validating Thrift objects using metadata
300
public class ThriftValidator {
301
302
public static <T extends TBase<T, F>, F extends TFieldIdEnum> boolean validate(T struct) {
303
Map<F, FieldMetaData> metaDataMap = FieldMetaData.getStructMetaDataMap((Class<T>) struct.getClass());
304
305
for (Map.Entry<F, FieldMetaData> entry : metaDataMap.entrySet()) {
306
F field = entry.getKey();
307
FieldMetaData metaData = entry.getValue();
308
309
// Check required fields
310
if (metaData.requirementType == TFieldRequirementType.REQUIRED) {
311
if (!struct.isSet(field)) {
312
System.err.println("Required field missing: " + metaData.fieldName);
313
return false;
314
}
315
}
316
317
// Additional type-specific validation could be added here
318
if (struct.isSet(field)) {
319
Object value = struct.getFieldValue(field);
320
if (!validateFieldValue(value, metaData.valueMetaData)) {
321
System.err.println("Invalid value for field: " + metaData.fieldName);
322
return false;
323
}
324
}
325
}
326
327
return true;
328
}
329
330
private static boolean validateFieldValue(Object value, FieldValueMetaData metaData) {
331
if (value == null) return true;
332
333
switch (metaData.type) {
334
case TType.STRING:
335
return value instanceof String;
336
case TType.I32:
337
return value instanceof Integer;
338
case TType.I64:
339
return value instanceof Long;
340
case TType.BOOL:
341
return value instanceof Boolean;
342
case TType.DOUBLE:
343
return value instanceof Double;
344
case TType.LIST:
345
return value instanceof List;
346
case TType.MAP:
347
return value instanceof Map;
348
case TType.SET:
349
return value instanceof Set;
350
case TType.STRUCT:
351
return value instanceof TBase;
352
default:
353
return true; // Unknown type, assume valid
354
}
355
}
356
}
357
```
358
359
### Dynamic Object Creation
360
361
Using metadata to create and populate Thrift objects dynamically.
362
363
```java
364
import org.apache.thrift.meta_data.FieldMetaData;
365
import org.apache.thrift.meta_data.StructMetaData;
366
import java.util.Map;
367
368
// Example utility for creating Thrift objects from data maps
369
public class DynamicThriftBuilder {
370
371
@SuppressWarnings("unchecked")
372
public static <T extends TBase<T, F>, F extends TFieldIdEnum> T build(
373
Class<T> clazz, Map<String, Object> data) throws Exception {
374
375
// Create instance
376
T instance = clazz.newInstance();
377
378
// Get metadata
379
Map<F, FieldMetaData> metaDataMap = FieldMetaData.getStructMetaDataMap(clazz);
380
381
// Populate fields from data map
382
for (Map.Entry<F, FieldMetaData> entry : metaDataMap.entrySet()) {
383
F field = entry.getKey();
384
FieldMetaData metaData = entry.getValue();
385
String fieldName = metaData.fieldName;
386
387
if (data.containsKey(fieldName)) {
388
Object value = data.get(fieldName);
389
Object convertedValue = convertValue(value, metaData.valueMetaData);
390
instance.setFieldValue(field, convertedValue);
391
}
392
}
393
394
return instance;
395
}
396
397
private static Object convertValue(Object value, FieldValueMetaData metaData) {
398
if (value == null) return null;
399
400
// Simple type conversion logic
401
switch (metaData.type) {
402
case TType.STRING:
403
return value.toString();
404
case TType.I32:
405
return value instanceof Number ? ((Number) value).intValue() : Integer.parseInt(value.toString());
406
case TType.I64:
407
return value instanceof Number ? ((Number) value).longValue() : Long.parseLong(value.toString());
408
case TType.BOOL:
409
return value instanceof Boolean ? value : Boolean.parseBoolean(value.toString());
410
case TType.DOUBLE:
411
return value instanceof Number ? ((Number) value).doubleValue() : Double.parseDouble(value.toString());
412
default:
413
return value; // Return as-is for complex types
414
}
415
}
416
}
417
418
// Usage example
419
Map<String, Object> data = new HashMap<>();
420
data.put("name", "example");
421
data.put("value", 42);
422
data.put("items", Arrays.asList("item1", "item2"));
423
424
MyStruct struct = DynamicThriftBuilder.build(MyStruct.class, data);
425
```
426
427
### Debugging and Introspection
428
429
Using metadata for debugging and development tools.
430
431
```java
432
// Utility for pretty-printing Thrift objects using metadata
433
public class ThriftPrinter {
434
435
public static <T extends TBase<T, F>, F extends TFieldIdEnum> String toString(T struct) {
436
StringBuilder sb = new StringBuilder();
437
Map<F, FieldMetaData> metaDataMap = FieldMetaData.getStructMetaDataMap((Class<T>) struct.getClass());
438
439
sb.append(struct.getClass().getSimpleName()).append("{\n");
440
441
for (Map.Entry<F, FieldMetaData> entry : metaDataMap.entrySet()) {
442
F field = entry.getKey();
443
FieldMetaData metaData = entry.getValue();
444
445
sb.append(" ").append(metaData.fieldName).append(": ");
446
447
if (struct.isSet(field)) {
448
Object value = struct.getFieldValue(field);
449
String requirement = getRequirementString(metaData.requirementType);
450
sb.append(value).append(" (").append(requirement).append(")");
451
} else {
452
sb.append("<not set>");
453
}
454
sb.append("\n");
455
}
456
457
sb.append("}");
458
return sb.toString();
459
}
460
461
private static String getRequirementString(byte requirementType) {
462
switch (requirementType) {
463
case TFieldRequirementType.REQUIRED:
464
return "required";
465
case TFieldRequirementType.OPTIONAL:
466
return "optional";
467
default:
468
return "default";
469
}
470
}
471
}
472
```
473
474
The metadata system provides powerful runtime introspection capabilities, enabling dynamic handling, validation, and debugging of Thrift objects without requiring compile-time knowledge of their structure.