or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-message-api.mddescriptors-reflection.mdextensions.mdindex.mdjson-format.mdserialization-io.mdwell-known-types.md
tile.json

extensions.mddocs/

Extension System

Support for protocol buffer extensions allowing fields to be added to messages without modifying the original .proto files. This enables third-party extensibility and backwards-compatible schema evolution in the shaded akka.protobufv3.internal package.

Capabilities

Extension Class

Represents a protocol buffer extension with type-safe access to extended fields.

/**
 * Represents a protocol buffer extension.
 * Provides type-safe access to extension fields in messages.
 */
class Extension<ContainingType extends ExtendableMessage<ContainingType>, Type> {
    /** Get the field descriptor for this extension */
    Descriptors.FieldDescriptor getDescriptor();
    
    /** Get the message type that contains this extension */
    Class<ContainingType> getContainingType();
    
    /** Get the type of this extension's value */
    Class<Type> getType();
    
    /** Check if this extension has a default value */
    boolean hasDefaultValue();
    
    /** Get the default value for this extension */
    Type getDefaultValue();
    
    /** Check if this is a repeated extension */
    boolean isRepeated();
    
    /** Check if this extension is packed (for repeated numeric types) */
    boolean isPacked();
    
    /** Get the message type for message extensions */
    Class<? extends Message> getMessageDefaultInstance();
}

GeneratedExtension Class

Implementation of Extension for generated extension fields.

/**
 * Generated implementation of Extension for protocol buffer extensions
 * created by the protocol compiler.
 */
static class GeneratedMessage.GeneratedExtension<ContainingType extends Message, Type>
    extends Extension<ContainingType, Type> {
    
    /** Get the field number for this extension */
    int getNumber();
    
    /** Get the wire type for this extension */
    WireFormat.FieldType getLiteType();
    
    /** Check if this extension is lite */
    boolean isLite();
    
    /** Create a new instance of the extension */
    GeneratedExtension<ContainingType, Type> clone();
}

ExtensionRegistry Class

Registry for protocol buffer extensions that enables parsing messages with extension fields.

/**
 * Registry for protocol buffer extensions.
 * Required when parsing messages that contain extension fields.
 */
class ExtensionRegistry extends ExtensionRegistryLite {
    /** Create a new mutable extension registry */
    static ExtensionRegistry newInstance();
    
    /** Get the empty extension registry */
    static ExtensionRegistry getEmptyRegistry();
    
    /** Add an extension to this registry */
    void add(Extension<?, ?> extension);
    
    /** Add an extension with explicit descriptor */
    void add(Descriptors.FieldDescriptor type);
    
    /** Add all extensions from another registry */
    void addAll(ExtensionRegistry other);
    
    /** Find extension by containing type and field number */
    Extension<?, ?> findExtensionByNumber(Descriptors.Descriptor containingType, int fieldNumber);
    
    /** Find immutable extension by field descriptor */
    Extension<?, ?> findImmutableExtensionByName(String fullName);
    
    /** Find mutable extension by field descriptor */
    Extension<?, ?> findMutableExtensionByName(String fullName);
    
    /** Get an unmodifiable view of this registry */
    ExtensionRegistry getUnmodifiable();
    
    /** Create a copy of this registry */
    ExtensionRegistry clone();
}

ExtensionRegistryLite Class

Lite version of extension registry with minimal functionality for resource-constrained environments.

/**
 * Lite version of extension registry with minimal functionality.
 * Used when full extension support is not needed.
 */
class ExtensionRegistryLite {
    /** Create a new mutable lite extension registry */
    static ExtensionRegistryLite newInstance();
    
    /** Get the empty lite extension registry */
    static ExtensionRegistryLite getEmptyRegistry();
    
    /** Add an extension to this registry */
    void add(GeneratedMessageLite.GeneratedExtension<?, ?> extension);
    
    /** Add a message extension to this registry */
    <ContainingType extends MessageLite> void add(
        GeneratedMessageLite.GeneratedExtension<ContainingType, ?> extension);
    
    /** Find extension by containing type and field number */
    GeneratedMessageLite.GeneratedExtension<?, ?> findLiteExtensionByNumber(
        ExtendableMessageLite<?> containingTypeDefaultInstance, int fieldNumber);
    
    /** Get an unmodifiable view of this registry */
    ExtensionRegistryLite getUnmodifiable();
    
    /** Create a copy of this registry */
    ExtensionRegistryLite clone();
}

ExtendableMessage Interface

Interface for messages that can be extended with additional fields.

/**
 * Interface for protocol buffer messages that support extensions.
 * Provides methods to access and check extension fields.
 */
interface ExtendableMessage<MessageType extends ExtendableMessage<MessageType>> 
    extends Message {
    
    /** Check if an extension field is set */
    <Type> boolean hasExtension(Extension<MessageType, Type> extension);
    
    /** Get the value of an extension field */
    <Type> Type getExtension(Extension<MessageType, Type> extension);
    
    /** Get the count of elements in a repeated extension field */
    <Type> int getExtensionCount(Extension<MessageType, List<Type>> extension);
    
    /** Get an element from a repeated extension field */
    <Type> Type getExtension(Extension<MessageType, List<Type>> extension, int index);
    
    /** Get all extension fields as a map */
    Map<Descriptors.FieldDescriptor, Object> getAllExtensions();
    
    /** ExtendableMessage builder interface */
    interface Builder<MessageType extends ExtendableMessage<MessageType>,
                     BuilderType extends Builder<MessageType, BuilderType>>
        extends Message.Builder {
        
        /** Set the value of an extension field */
        <Type> BuilderType setExtension(Extension<MessageType, Type> extension, Type value);
        
        /** Add a value to a repeated extension field */
        <Type> BuilderType addExtension(Extension<MessageType, List<Type>> extension, Type value);
        
        /** Set an element in a repeated extension field */
        <Type> BuilderType setExtension(Extension<MessageType, List<Type>> extension, 
                                       int index, Type value);
        
        /** Clear an extension field */
        <Type> BuilderType clearExtension(Extension<MessageType, Type> extension);
        
        /** Check if an extension field is set */
        <Type> boolean hasExtension(Extension<MessageType, Type> extension);
        
        /** Get the value of an extension field */
        <Type> Type getExtension(Extension<MessageType, Type> extension);
        
        /** Get the count of elements in a repeated extension field */
        <Type> int getExtensionCount(Extension<MessageType, List<Type>> extension);
        
        /** Get an element from a repeated extension field */
        <Type> Type getExtension(Extension<MessageType, List<Type>> extension, int index);
    }
}

ExtendableMessageLite Interface

Lite version of extendable message interface with minimal functionality.

/**
 * Lite version of extendable message interface.
 * Provides basic extension support without full reflection capabilities.
 */
interface ExtendableMessageLite<MessageType extends ExtendableMessageLite<MessageType>>
    extends MessageLite {
    
    /** Check if an extension field is set */
    <Type> boolean hasExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
    
    /** Get the value of an extension field */
    <Type> Type getExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
    
    /** Get the count of elements in a repeated extension field */
    <Type> int getExtensionCount(GeneratedMessageLite.GeneratedExtension<MessageType, List<Type>> extension);
    
    /** Get an element from a repeated extension field */
    <Type> Type getExtension(GeneratedMessageLite.GeneratedExtension<MessageType, List<Type>> extension, 
                           int index);
    
    /** ExtendableMessageLite builder interface */
    interface Builder<MessageType extends ExtendableMessageLite<MessageType>,
                     BuilderType extends Builder<MessageType, BuilderType>>
        extends MessageLite.Builder {
        
        /** Set the value of an extension field */
        <Type> BuilderType setExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension, 
                                       Type value);
        
        /** Add a value to a repeated extension field */
        <Type> BuilderType addExtension(GeneratedMessageLite.GeneratedExtension<MessageType, List<Type>> extension, 
                                       Type value);
        
        /** Clear an extension field */
        <Type> BuilderType clearExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
        
        /** Check if an extension field is set */
        <Type> boolean hasExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
        
        /** Get the value of an extension field */
        <Type> Type getExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
    }
}

Usage Examples

Defining Extensions

Extensions are typically defined in .proto files and generated as static fields:

// In your .proto file
extend MyMessage {
  optional string custom_field = 1001;
  repeated int32 custom_numbers = 1002;
}

The generated Java code would look like:

// Generated extension fields (example)
// public static final Extension<MyMessage, String> customField = 
//     GeneratedMessage.newFileScopedGeneratedExtension(String.class, null);
// 
// public static final Extension<MyMessage, List<Integer>> customNumbers = 
//     GeneratedMessage.newFileScopedGeneratedExtension(Integer.class, null);

Using Extensions in Messages

import akka.protobufv3.internal.*;

// Set extension fields in a message (assumes MyMessage and extensions exist)
// MyMessage message = MyMessage.newBuilder()
//     .setExtension(MyExtensions.customField, "Hello Extension!")
//     .addExtension(MyExtensions.customNumbers, 42)
//     .addExtension(MyExtensions.customNumbers, 84)
//     .build();

// Read extension fields
// boolean hasCustomField = message.hasExtension(MyExtensions.customField);
// if (hasCustomField) {
//     String customValue = message.getExtension(MyExtensions.customField);
//     System.out.println("Custom field: " + customValue);
// }

// Read repeated extension
// int count = message.getExtensionCount(MyExtensions.customNumbers);
// for (int i = 0; i < count; i++) {
//     Integer number = message.getExtension(MyExtensions.customNumbers, i);
//     System.out.println("Custom number " + i + ": " + number);
// }

Working with Extension Registry

import akka.protobufv3.internal.*;

// Create extension registry
ExtensionRegistry registry = ExtensionRegistry.newInstance();

// Add extensions to registry
// registry.add(MyExtensions.customField);
// registry.add(MyExtensions.customNumbers);

// Parse message with extensions
byte[] data = getMessageBytes();
try {
    // MyMessage message = MyMessage.parseFrom(data, registry);
    
    // Extensions will be properly parsed and accessible
    // String customValue = message.getExtension(MyExtensions.customField);
} catch (InvalidProtocolBufferException e) {
    System.err.println("Failed to parse message with extensions: " + e.getMessage());
}

Dynamic Extension Access

import akka.protobufv3.internal.*;

// Access extensions dynamically using descriptors
// MyMessage message = getMessageWithExtensions();

// Get all extension fields
Map<Descriptors.FieldDescriptor, Object> allExtensions = message.getAllExtensions();
for (Map.Entry<Descriptors.FieldDescriptor, Object> entry : allExtensions.entrySet()) {
    Descriptors.FieldDescriptor field = entry.getKey();
    Object value = entry.getValue();
    
    System.out.println("Extension field " + field.getName() + 
                      " (number " + field.getNumber() + "): " + value);
}

// Find extension by field number
ExtensionRegistry registry = getExtensionRegistry();
Descriptors.Descriptor messageType = message.getDescriptorForType();
Extension<?, ?> extension = registry.findExtensionByNumber(messageType, 1001);

if (extension != null) {
    System.out.println("Found extension: " + extension.getDescriptor().getName());
}

Building Messages with Extensions

import akka.protobufv3.internal.*;

// Create builder and set both regular and extension fields
// MyMessage.Builder builder = MyMessage.newBuilder();

// Set regular fields
// builder.setName("Regular Field");
// builder.setId(123);

// Set extension fields
// builder.setExtension(MyExtensions.customField, "Extension Value");
// builder.addExtension(MyExtensions.customNumbers, 1);
// builder.addExtension(MyExtensions.customNumbers, 2);
// builder.addExtension(MyExtensions.customNumbers, 3);

// Build the message
// MyMessage message = builder.build();

// Verify extensions are set
// System.out.println("Has custom field: " + message.hasExtension(MyExtensions.customField));
// System.out.println("Custom numbers count: " + message.getExtensionCount(MyExtensions.customNumbers));

Clearing and Modifying Extensions

import akka.protobufv3.internal.*;

// Start with a message that has extensions
// MyMessage original = getMessageWithExtensions();

// Create builder from existing message
// MyMessage.Builder builder = original.toBuilder();

// Clear specific extension
// builder.clearExtension(MyExtensions.customField);

// Modify repeated extension
// builder.setExtension(MyExtensions.customNumbers, 0, 999); // Set first element
// builder.addExtension(MyExtensions.customNumbers, 888);    // Add new element

// Build modified message
// MyMessage modified = builder.build();

Lite Extension Registry

import akka.protobufv3.internal.*;

// For lite messages, use ExtensionRegistryLite
ExtensionRegistryLite liteRegistry = ExtensionRegistryLite.newInstance();

// Add lite extensions
// liteRegistry.add(MyLiteExtensions.customLiteField);

// Parse lite message with extensions
byte[] liteData = getLiteMessageBytes();
try {
    // MyLiteMessage message = MyLiteMessage.parseFrom(liteData, liteRegistry);
    
    // Access lite extensions
    // String value = message.getExtension(MyLiteExtensions.customLiteField);
} catch (InvalidProtocolBufferException e) {
    System.err.println("Failed to parse lite message: " + e.getMessage());
}

Extension Registry Management

import akka.protobufv3.internal.*;

// Create base registry
ExtensionRegistry baseRegistry = ExtensionRegistry.newInstance();
// baseRegistry.add(CommonExtensions.timestampField);
// baseRegistry.add(CommonExtensions.versionField);

// Create specialized registry
ExtensionRegistry specializedRegistry = ExtensionRegistry.newInstance();
// specializedRegistry.addAll(baseRegistry); // Include base extensions
// specializedRegistry.add(SpecialExtensions.debugField);
// specializedRegistry.add(SpecialExtensions.metadataField);

// Get unmodifiable view for safety
ExtensionRegistry readOnlyRegistry = specializedRegistry.getUnmodifiable();

// Use appropriate registry based on context
// if (isDebugMode()) {
//     message = MyMessage.parseFrom(data, specializedRegistry);
// } else {
//     message = MyMessage.parseFrom(data, baseRegistry);
// }

Error Handling with Extensions

import akka.protobufv3.internal.*;

try {
    // Parse message without proper extension registry
    byte[] data = getMessageWithExtensions();
    // MyMessage message = MyMessage.parseFrom(data); // Extensions will be in unknown fields
    
    // Access extension (will return default value)
    // String customValue = message.getExtension(MyExtensions.customField);
    // System.out.println("Custom value: " + customValue); // Will be default/empty
    
    // Check unknown fields for unparsed extensions
    UnknownFieldSet unknownFields = message.getUnknownFields();
    if (!unknownFields.asMap().isEmpty()) {
        System.out.println("Message has unknown fields (possibly extensions)");
    }
    
} catch (InvalidProtocolBufferException e) {
    System.err.println("Failed to parse message: " + e.getMessage());
}