Apache Thrift Java Library - A lightweight, language-independent software stack for point-to-point RPC implementation providing clean abstractions and implementations for data transport, data serialization, and application level processing
—
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.
Core metadata classes for describing Thrift struct fields and their properties.
/**
* Stores metadata information about a Thrift struct field
*/
public class FieldMetaData {
/** Field name */
public final String fieldName;
/** Field requirement type (REQUIRED, OPTIONAL, DEFAULT) */
public final byte requirementType;
/** Value metadata describing the field's type */
public final FieldValueMetaData valueMetaData;
/** Create field metadata */
public FieldMetaData(String fieldName, byte requirementType, FieldValueMetaData valueMetaData);
/** Add struct metadata map for a Thrift class */
public static <T extends TBase<T, F>, F extends TFieldIdEnum> void addStructMetaDataMap(
Class<T> sClass, Map<F, FieldMetaData> map);
/** Get struct metadata map for a Thrift class */
public static <T extends TBase<T, F>, F extends TFieldIdEnum> Map<F, FieldMetaData> getStructMetaDataMap(
Class<T> sClass);
/** Get field annotations */
public Map<String, String> getFieldAnnotations();
}
/**
* Base class for field value metadata describing field types
*/
public class FieldValueMetaData {
/** Thrift type constant for this field */
public final byte type;
/** Type name for typedef fields */
public final String typedefName;
/** Create field value metadata */
public FieldValueMetaData(byte type);
/** Create field value metadata with typedef name */
public FieldValueMetaData(byte type, String typedefName);
/** Check if this is a typedef field */
public boolean isTypedef();
/** Get typedef name (if applicable) */
public String getTypedefName();
/** Check if this field represents binary data */
public boolean isBinary();
}Usage Examples:
import org.apache.thrift.meta_data.FieldMetaData;
import org.apache.thrift.meta_data.FieldValueMetaData;
import org.apache.thrift.TFieldRequirementType;
import org.apache.thrift.protocol.TType;
// Access metadata for a Thrift struct
public class MetadataExample {
public void inspectStruct() {
// Get metadata map for MyStruct class
Map<MyStruct._Fields, FieldMetaData> metaDataMap =
FieldMetaData.getStructMetaDataMap(MyStruct.class);
// Iterate through all fields
for (Map.Entry<MyStruct._Fields, FieldMetaData> entry : metaDataMap.entrySet()) {
MyStruct._Fields field = entry.getKey();
FieldMetaData metaData = entry.getValue();
System.out.println("Field: " + metaData.fieldName);
System.out.println(" Type: " + getTypeName(metaData.valueMetaData.type));
System.out.println(" Required: " + isRequired(metaData.requirementType));
System.out.println(" Typedef: " + metaData.valueMetaData.isTypedef());
}
}
private String getTypeName(byte type) {
switch (type) {
case TType.STRING: return "string";
case TType.I32: return "i32";
case TType.I64: return "i64";
case TType.BOOL: return "bool";
case TType.STRUCT: return "struct";
case TType.LIST: return "list";
case TType.MAP: return "map";
case TType.SET: return "set";
default: return "unknown";
}
}
private boolean isRequired(byte requirementType) {
return requirementType == TFieldRequirementType.REQUIRED;
}
}Specific metadata classes for different Thrift data types.
/**
* Metadata for struct types
*/
public class StructMetaData extends FieldValueMetaData {
/** The struct class */
public final Class<? extends TBase> structClass;
/** Create struct metadata */
public StructMetaData(byte type, Class<? extends TBase> structClass);
}
/**
* Metadata for list types
*/
public class ListMetaData extends FieldValueMetaData {
/** Element type metadata */
public final FieldValueMetaData elemMetaData;
/** Create list metadata */
public ListMetaData(byte type, FieldValueMetaData elemMetaData);
}
/**
* Metadata for map types
*/
public class MapMetaData extends FieldValueMetaData {
/** Key type metadata */
public final FieldValueMetaData keyMetaData;
/** Value type metadata */
public final FieldValueMetaData valueMetaData;
/** Create map metadata */
public MapMetaData(byte type, FieldValueMetaData keyMetaData, FieldValueMetaData valueMetaData);
}
/**
* Metadata for set types
*/
public class SetMetaData extends FieldValueMetaData {
/** Element type metadata */
public final FieldValueMetaData elemMetaData;
/** Create set metadata */
public SetMetaData(byte type, FieldValueMetaData elemMetaData);
}
/**
* Metadata for enum types
*/
public class EnumMetaData extends FieldValueMetaData {
/** The enum class */
public final Class<? extends TEnum> enumClass;
/** Create enum metadata */
public EnumMetaData(byte type, Class<? extends TEnum> enumClass);
}Usage Examples:
import org.apache.thrift.meta_data.*;
import org.apache.thrift.protocol.TType;
// Example of working with complex type metadata
public class ComplexMetadataExample {
public void analyzeComplexField(FieldMetaData fieldMetaData) {
FieldValueMetaData valueMetaData = fieldMetaData.valueMetaData;
switch (valueMetaData.type) {
case TType.STRUCT:
StructMetaData structMeta = (StructMetaData) valueMetaData;
System.out.println("Struct type: " + structMeta.structClass.getSimpleName());
break;
case TType.LIST:
ListMetaData listMeta = (ListMetaData) valueMetaData;
System.out.println("List of: " + getTypeName(listMeta.elemMetaData.type));
break;
case TType.MAP:
MapMetaData mapMeta = (MapMetaData) valueMetaData;
System.out.println("Map<" +
getTypeName(mapMeta.keyMetaData.type) + ", " +
getTypeName(mapMeta.valueMetaData.type) + ">");
break;
case TType.SET:
SetMetaData setMeta = (SetMetaData) valueMetaData;
System.out.println("Set of: " + getTypeName(setMeta.elemMetaData.type));
break;
case TType.ENUM: // Note: This would be a custom extension
EnumMetaData enumMeta = (EnumMetaData) valueMetaData;
System.out.println("Enum type: " + enumMeta.enumClass.getSimpleName());
break;
default:
System.out.println("Primitive type: " + getTypeName(valueMetaData.type));
}
}
private String getTypeName(byte type) {
// Implementation from previous example
switch (type) {
case TType.STRING: return "string";
case TType.I32: return "i32";
case TType.I64: return "i64";
case TType.BOOL: return "bool";
case TType.DOUBLE: return "double";
case TType.BYTE: return "byte";
case TType.I16: return "i16";
default: return "type_" + type;
}
}
}Generated Thrift classes automatically include metadata initialization.
// Example of how metadata is typically set up in generated code
public class MyStruct implements TBase<MyStruct, MyStruct._Fields> {
// Field enum
public enum _Fields implements TFieldIdEnum {
NAME((short)1, "name"),
VALUE((short)2, "value"),
ITEMS((short)3, "items");
private final short _thriftId;
private final String _fieldName;
_Fields(short thriftId, String fieldName) {
_thriftId = thriftId;
_fieldName = fieldName;
}
public short getThriftFieldId() {
return _thriftId;
}
public String getFieldName() {
return _fieldName;
}
}
// Metadata map setup
public static final Map<_Fields, FieldMetaData> metaDataMap;
static {
Map<_Fields, FieldMetaData> tmpMap = new EnumMap<_Fields, FieldMetaData>(_Fields.class);
tmpMap.put(_Fields.NAME, new FieldMetaData("name",
TFieldRequirementType.REQUIRED,
new FieldValueMetaData(TType.STRING)));
tmpMap.put(_Fields.VALUE, new FieldMetaData("value",
TFieldRequirementType.OPTIONAL,
new FieldValueMetaData(TType.I32)));
tmpMap.put(_Fields.ITEMS, new FieldMetaData("items",
TFieldRequirementType.OPTIONAL,
new ListMetaData(TType.LIST, new FieldValueMetaData(TType.STRING))));
metaDataMap = Collections.unmodifiableMap(tmpMap);
FieldMetaData.addStructMetaDataMap(MyStruct.class, metaDataMap);
}
}Using metadata for runtime validation and introspection.
import org.apache.thrift.meta_data.FieldMetaData;
import org.apache.thrift.TFieldRequirementType;
// Example utility for validating Thrift objects using metadata
public class ThriftValidator {
public static <T extends TBase<T, F>, F extends TFieldIdEnum> boolean validate(T struct) {
Map<F, FieldMetaData> metaDataMap = FieldMetaData.getStructMetaDataMap((Class<T>) struct.getClass());
for (Map.Entry<F, FieldMetaData> entry : metaDataMap.entrySet()) {
F field = entry.getKey();
FieldMetaData metaData = entry.getValue();
// Check required fields
if (metaData.requirementType == TFieldRequirementType.REQUIRED) {
if (!struct.isSet(field)) {
System.err.println("Required field missing: " + metaData.fieldName);
return false;
}
}
// Additional type-specific validation could be added here
if (struct.isSet(field)) {
Object value = struct.getFieldValue(field);
if (!validateFieldValue(value, metaData.valueMetaData)) {
System.err.println("Invalid value for field: " + metaData.fieldName);
return false;
}
}
}
return true;
}
private static boolean validateFieldValue(Object value, FieldValueMetaData metaData) {
if (value == null) return true;
switch (metaData.type) {
case TType.STRING:
return value instanceof String;
case TType.I32:
return value instanceof Integer;
case TType.I64:
return value instanceof Long;
case TType.BOOL:
return value instanceof Boolean;
case TType.DOUBLE:
return value instanceof Double;
case TType.LIST:
return value instanceof List;
case TType.MAP:
return value instanceof Map;
case TType.SET:
return value instanceof Set;
case TType.STRUCT:
return value instanceof TBase;
default:
return true; // Unknown type, assume valid
}
}
}Using metadata to create and populate Thrift objects dynamically.
import org.apache.thrift.meta_data.FieldMetaData;
import org.apache.thrift.meta_data.StructMetaData;
import java.util.Map;
// Example utility for creating Thrift objects from data maps
public class DynamicThriftBuilder {
@SuppressWarnings("unchecked")
public static <T extends TBase<T, F>, F extends TFieldIdEnum> T build(
Class<T> clazz, Map<String, Object> data) throws Exception {
// Create instance
T instance = clazz.newInstance();
// Get metadata
Map<F, FieldMetaData> metaDataMap = FieldMetaData.getStructMetaDataMap(clazz);
// Populate fields from data map
for (Map.Entry<F, FieldMetaData> entry : metaDataMap.entrySet()) {
F field = entry.getKey();
FieldMetaData metaData = entry.getValue();
String fieldName = metaData.fieldName;
if (data.containsKey(fieldName)) {
Object value = data.get(fieldName);
Object convertedValue = convertValue(value, metaData.valueMetaData);
instance.setFieldValue(field, convertedValue);
}
}
return instance;
}
private static Object convertValue(Object value, FieldValueMetaData metaData) {
if (value == null) return null;
// Simple type conversion logic
switch (metaData.type) {
case TType.STRING:
return value.toString();
case TType.I32:
return value instanceof Number ? ((Number) value).intValue() : Integer.parseInt(value.toString());
case TType.I64:
return value instanceof Number ? ((Number) value).longValue() : Long.parseLong(value.toString());
case TType.BOOL:
return value instanceof Boolean ? value : Boolean.parseBoolean(value.toString());
case TType.DOUBLE:
return value instanceof Number ? ((Number) value).doubleValue() : Double.parseDouble(value.toString());
default:
return value; // Return as-is for complex types
}
}
}
// Usage example
Map<String, Object> data = new HashMap<>();
data.put("name", "example");
data.put("value", 42);
data.put("items", Arrays.asList("item1", "item2"));
MyStruct struct = DynamicThriftBuilder.build(MyStruct.class, data);Using metadata for debugging and development tools.
// Utility for pretty-printing Thrift objects using metadata
public class ThriftPrinter {
public static <T extends TBase<T, F>, F extends TFieldIdEnum> String toString(T struct) {
StringBuilder sb = new StringBuilder();
Map<F, FieldMetaData> metaDataMap = FieldMetaData.getStructMetaDataMap((Class<T>) struct.getClass());
sb.append(struct.getClass().getSimpleName()).append("{\n");
for (Map.Entry<F, FieldMetaData> entry : metaDataMap.entrySet()) {
F field = entry.getKey();
FieldMetaData metaData = entry.getValue();
sb.append(" ").append(metaData.fieldName).append(": ");
if (struct.isSet(field)) {
Object value = struct.getFieldValue(field);
String requirement = getRequirementString(metaData.requirementType);
sb.append(value).append(" (").append(requirement).append(")");
} else {
sb.append("<not set>");
}
sb.append("\n");
}
sb.append("}");
return sb.toString();
}
private static String getRequirementString(byte requirementType) {
switch (requirementType) {
case TFieldRequirementType.REQUIRED:
return "required";
case TFieldRequirementType.OPTIONAL:
return "optional";
default:
return "default";
}
}
}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.
Install with Tessl CLI
npx tessl i tessl/maven-org-apache-thrift--libthrift