0
# Extension System
1
2
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.
3
4
## Capabilities
5
6
### Extension Class
7
8
Represents a protocol buffer extension with type-safe access to extended fields.
9
10
```java { .api }
11
/**
12
* Represents a protocol buffer extension.
13
* Provides type-safe access to extension fields in messages.
14
*/
15
class Extension<ContainingType extends ExtendableMessage<ContainingType>, Type> {
16
/** Get the field descriptor for this extension */
17
Descriptors.FieldDescriptor getDescriptor();
18
19
/** Get the message type that contains this extension */
20
Class<ContainingType> getContainingType();
21
22
/** Get the type of this extension's value */
23
Class<Type> getType();
24
25
/** Check if this extension has a default value */
26
boolean hasDefaultValue();
27
28
/** Get the default value for this extension */
29
Type getDefaultValue();
30
31
/** Check if this is a repeated extension */
32
boolean isRepeated();
33
34
/** Check if this extension is packed (for repeated numeric types) */
35
boolean isPacked();
36
37
/** Get the message type for message extensions */
38
Class<? extends Message> getMessageDefaultInstance();
39
}
40
```
41
42
### GeneratedExtension Class
43
44
Implementation of Extension for generated extension fields.
45
46
```java { .api }
47
/**
48
* Generated implementation of Extension for protocol buffer extensions
49
* created by the protocol compiler.
50
*/
51
static class GeneratedMessage.GeneratedExtension<ContainingType extends Message, Type>
52
extends Extension<ContainingType, Type> {
53
54
/** Get the field number for this extension */
55
int getNumber();
56
57
/** Get the wire type for this extension */
58
WireFormat.FieldType getLiteType();
59
60
/** Check if this extension is lite */
61
boolean isLite();
62
63
/** Create a new instance of the extension */
64
GeneratedExtension<ContainingType, Type> clone();
65
}
66
```
67
68
### ExtensionRegistry Class
69
70
Registry for protocol buffer extensions that enables parsing messages with extension fields.
71
72
```java { .api }
73
/**
74
* Registry for protocol buffer extensions.
75
* Required when parsing messages that contain extension fields.
76
*/
77
class ExtensionRegistry extends ExtensionRegistryLite {
78
/** Create a new mutable extension registry */
79
static ExtensionRegistry newInstance();
80
81
/** Get the empty extension registry */
82
static ExtensionRegistry getEmptyRegistry();
83
84
/** Add an extension to this registry */
85
void add(Extension<?, ?> extension);
86
87
/** Add an extension with explicit descriptor */
88
void add(Descriptors.FieldDescriptor type);
89
90
/** Add all extensions from another registry */
91
void addAll(ExtensionRegistry other);
92
93
/** Find extension by containing type and field number */
94
Extension<?, ?> findExtensionByNumber(Descriptors.Descriptor containingType, int fieldNumber);
95
96
/** Find immutable extension by field descriptor */
97
Extension<?, ?> findImmutableExtensionByName(String fullName);
98
99
/** Find mutable extension by field descriptor */
100
Extension<?, ?> findMutableExtensionByName(String fullName);
101
102
/** Get an unmodifiable view of this registry */
103
ExtensionRegistry getUnmodifiable();
104
105
/** Create a copy of this registry */
106
ExtensionRegistry clone();
107
}
108
```
109
110
### ExtensionRegistryLite Class
111
112
Lite version of extension registry with minimal functionality for resource-constrained environments.
113
114
```java { .api }
115
/**
116
* Lite version of extension registry with minimal functionality.
117
* Used when full extension support is not needed.
118
*/
119
class ExtensionRegistryLite {
120
/** Create a new mutable lite extension registry */
121
static ExtensionRegistryLite newInstance();
122
123
/** Get the empty lite extension registry */
124
static ExtensionRegistryLite getEmptyRegistry();
125
126
/** Add an extension to this registry */
127
void add(GeneratedMessageLite.GeneratedExtension<?, ?> extension);
128
129
/** Add a message extension to this registry */
130
<ContainingType extends MessageLite> void add(
131
GeneratedMessageLite.GeneratedExtension<ContainingType, ?> extension);
132
133
/** Find extension by containing type and field number */
134
GeneratedMessageLite.GeneratedExtension<?, ?> findLiteExtensionByNumber(
135
ExtendableMessageLite<?> containingTypeDefaultInstance, int fieldNumber);
136
137
/** Get an unmodifiable view of this registry */
138
ExtensionRegistryLite getUnmodifiable();
139
140
/** Create a copy of this registry */
141
ExtensionRegistryLite clone();
142
}
143
```
144
145
### ExtendableMessage Interface
146
147
Interface for messages that can be extended with additional fields.
148
149
```java { .api }
150
/**
151
* Interface for protocol buffer messages that support extensions.
152
* Provides methods to access and check extension fields.
153
*/
154
interface ExtendableMessage<MessageType extends ExtendableMessage<MessageType>>
155
extends Message {
156
157
/** Check if an extension field is set */
158
<Type> boolean hasExtension(Extension<MessageType, Type> extension);
159
160
/** Get the value of an extension field */
161
<Type> Type getExtension(Extension<MessageType, Type> extension);
162
163
/** Get the count of elements in a repeated extension field */
164
<Type> int getExtensionCount(Extension<MessageType, List<Type>> extension);
165
166
/** Get an element from a repeated extension field */
167
<Type> Type getExtension(Extension<MessageType, List<Type>> extension, int index);
168
169
/** Get all extension fields as a map */
170
Map<Descriptors.FieldDescriptor, Object> getAllExtensions();
171
172
/** ExtendableMessage builder interface */
173
interface Builder<MessageType extends ExtendableMessage<MessageType>,
174
BuilderType extends Builder<MessageType, BuilderType>>
175
extends Message.Builder {
176
177
/** Set the value of an extension field */
178
<Type> BuilderType setExtension(Extension<MessageType, Type> extension, Type value);
179
180
/** Add a value to a repeated extension field */
181
<Type> BuilderType addExtension(Extension<MessageType, List<Type>> extension, Type value);
182
183
/** Set an element in a repeated extension field */
184
<Type> BuilderType setExtension(Extension<MessageType, List<Type>> extension,
185
int index, Type value);
186
187
/** Clear an extension field */
188
<Type> BuilderType clearExtension(Extension<MessageType, Type> extension);
189
190
/** Check if an extension field is set */
191
<Type> boolean hasExtension(Extension<MessageType, Type> extension);
192
193
/** Get the value of an extension field */
194
<Type> Type getExtension(Extension<MessageType, Type> extension);
195
196
/** Get the count of elements in a repeated extension field */
197
<Type> int getExtensionCount(Extension<MessageType, List<Type>> extension);
198
199
/** Get an element from a repeated extension field */
200
<Type> Type getExtension(Extension<MessageType, List<Type>> extension, int index);
201
}
202
}
203
```
204
205
### ExtendableMessageLite Interface
206
207
Lite version of extendable message interface with minimal functionality.
208
209
```java { .api }
210
/**
211
* Lite version of extendable message interface.
212
* Provides basic extension support without full reflection capabilities.
213
*/
214
interface ExtendableMessageLite<MessageType extends ExtendableMessageLite<MessageType>>
215
extends MessageLite {
216
217
/** Check if an extension field is set */
218
<Type> boolean hasExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
219
220
/** Get the value of an extension field */
221
<Type> Type getExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
222
223
/** Get the count of elements in a repeated extension field */
224
<Type> int getExtensionCount(GeneratedMessageLite.GeneratedExtension<MessageType, List<Type>> extension);
225
226
/** Get an element from a repeated extension field */
227
<Type> Type getExtension(GeneratedMessageLite.GeneratedExtension<MessageType, List<Type>> extension,
228
int index);
229
230
/** ExtendableMessageLite builder interface */
231
interface Builder<MessageType extends ExtendableMessageLite<MessageType>,
232
BuilderType extends Builder<MessageType, BuilderType>>
233
extends MessageLite.Builder {
234
235
/** Set the value of an extension field */
236
<Type> BuilderType setExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension,
237
Type value);
238
239
/** Add a value to a repeated extension field */
240
<Type> BuilderType addExtension(GeneratedMessageLite.GeneratedExtension<MessageType, List<Type>> extension,
241
Type value);
242
243
/** Clear an extension field */
244
<Type> BuilderType clearExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
245
246
/** Check if an extension field is set */
247
<Type> boolean hasExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
248
249
/** Get the value of an extension field */
250
<Type> Type getExtension(GeneratedMessageLite.GeneratedExtension<MessageType, Type> extension);
251
}
252
}
253
```
254
255
## Usage Examples
256
257
### Defining Extensions
258
259
Extensions are typically defined in .proto files and generated as static fields:
260
261
```proto
262
// In your .proto file
263
extend MyMessage {
264
optional string custom_field = 1001;
265
repeated int32 custom_numbers = 1002;
266
}
267
```
268
269
The generated Java code would look like:
270
271
```java
272
// Generated extension fields (example)
273
// public static final Extension<MyMessage, String> customField =
274
// GeneratedMessage.newFileScopedGeneratedExtension(String.class, null);
275
//
276
// public static final Extension<MyMessage, List<Integer>> customNumbers =
277
// GeneratedMessage.newFileScopedGeneratedExtension(Integer.class, null);
278
```
279
280
### Using Extensions in Messages
281
282
```java
283
import akka.protobufv3.internal.*;
284
285
// Set extension fields in a message (assumes MyMessage and extensions exist)
286
// MyMessage message = MyMessage.newBuilder()
287
// .setExtension(MyExtensions.customField, "Hello Extension!")
288
// .addExtension(MyExtensions.customNumbers, 42)
289
// .addExtension(MyExtensions.customNumbers, 84)
290
// .build();
291
292
// Read extension fields
293
// boolean hasCustomField = message.hasExtension(MyExtensions.customField);
294
// if (hasCustomField) {
295
// String customValue = message.getExtension(MyExtensions.customField);
296
// System.out.println("Custom field: " + customValue);
297
// }
298
299
// Read repeated extension
300
// int count = message.getExtensionCount(MyExtensions.customNumbers);
301
// for (int i = 0; i < count; i++) {
302
// Integer number = message.getExtension(MyExtensions.customNumbers, i);
303
// System.out.println("Custom number " + i + ": " + number);
304
// }
305
```
306
307
### Working with Extension Registry
308
309
```java
310
import akka.protobufv3.internal.*;
311
312
// Create extension registry
313
ExtensionRegistry registry = ExtensionRegistry.newInstance();
314
315
// Add extensions to registry
316
// registry.add(MyExtensions.customField);
317
// registry.add(MyExtensions.customNumbers);
318
319
// Parse message with extensions
320
byte[] data = getMessageBytes();
321
try {
322
// MyMessage message = MyMessage.parseFrom(data, registry);
323
324
// Extensions will be properly parsed and accessible
325
// String customValue = message.getExtension(MyExtensions.customField);
326
} catch (InvalidProtocolBufferException e) {
327
System.err.println("Failed to parse message with extensions: " + e.getMessage());
328
}
329
```
330
331
### Dynamic Extension Access
332
333
```java
334
import akka.protobufv3.internal.*;
335
336
// Access extensions dynamically using descriptors
337
// MyMessage message = getMessageWithExtensions();
338
339
// Get all extension fields
340
Map<Descriptors.FieldDescriptor, Object> allExtensions = message.getAllExtensions();
341
for (Map.Entry<Descriptors.FieldDescriptor, Object> entry : allExtensions.entrySet()) {
342
Descriptors.FieldDescriptor field = entry.getKey();
343
Object value = entry.getValue();
344
345
System.out.println("Extension field " + field.getName() +
346
" (number " + field.getNumber() + "): " + value);
347
}
348
349
// Find extension by field number
350
ExtensionRegistry registry = getExtensionRegistry();
351
Descriptors.Descriptor messageType = message.getDescriptorForType();
352
Extension<?, ?> extension = registry.findExtensionByNumber(messageType, 1001);
353
354
if (extension != null) {
355
System.out.println("Found extension: " + extension.getDescriptor().getName());
356
}
357
```
358
359
### Building Messages with Extensions
360
361
```java
362
import akka.protobufv3.internal.*;
363
364
// Create builder and set both regular and extension fields
365
// MyMessage.Builder builder = MyMessage.newBuilder();
366
367
// Set regular fields
368
// builder.setName("Regular Field");
369
// builder.setId(123);
370
371
// Set extension fields
372
// builder.setExtension(MyExtensions.customField, "Extension Value");
373
// builder.addExtension(MyExtensions.customNumbers, 1);
374
// builder.addExtension(MyExtensions.customNumbers, 2);
375
// builder.addExtension(MyExtensions.customNumbers, 3);
376
377
// Build the message
378
// MyMessage message = builder.build();
379
380
// Verify extensions are set
381
// System.out.println("Has custom field: " + message.hasExtension(MyExtensions.customField));
382
// System.out.println("Custom numbers count: " + message.getExtensionCount(MyExtensions.customNumbers));
383
```
384
385
### Clearing and Modifying Extensions
386
387
```java
388
import akka.protobufv3.internal.*;
389
390
// Start with a message that has extensions
391
// MyMessage original = getMessageWithExtensions();
392
393
// Create builder from existing message
394
// MyMessage.Builder builder = original.toBuilder();
395
396
// Clear specific extension
397
// builder.clearExtension(MyExtensions.customField);
398
399
// Modify repeated extension
400
// builder.setExtension(MyExtensions.customNumbers, 0, 999); // Set first element
401
// builder.addExtension(MyExtensions.customNumbers, 888); // Add new element
402
403
// Build modified message
404
// MyMessage modified = builder.build();
405
```
406
407
### Lite Extension Registry
408
409
```java
410
import akka.protobufv3.internal.*;
411
412
// For lite messages, use ExtensionRegistryLite
413
ExtensionRegistryLite liteRegistry = ExtensionRegistryLite.newInstance();
414
415
// Add lite extensions
416
// liteRegistry.add(MyLiteExtensions.customLiteField);
417
418
// Parse lite message with extensions
419
byte[] liteData = getLiteMessageBytes();
420
try {
421
// MyLiteMessage message = MyLiteMessage.parseFrom(liteData, liteRegistry);
422
423
// Access lite extensions
424
// String value = message.getExtension(MyLiteExtensions.customLiteField);
425
} catch (InvalidProtocolBufferException e) {
426
System.err.println("Failed to parse lite message: " + e.getMessage());
427
}
428
```
429
430
### Extension Registry Management
431
432
```java
433
import akka.protobufv3.internal.*;
434
435
// Create base registry
436
ExtensionRegistry baseRegistry = ExtensionRegistry.newInstance();
437
// baseRegistry.add(CommonExtensions.timestampField);
438
// baseRegistry.add(CommonExtensions.versionField);
439
440
// Create specialized registry
441
ExtensionRegistry specializedRegistry = ExtensionRegistry.newInstance();
442
// specializedRegistry.addAll(baseRegistry); // Include base extensions
443
// specializedRegistry.add(SpecialExtensions.debugField);
444
// specializedRegistry.add(SpecialExtensions.metadataField);
445
446
// Get unmodifiable view for safety
447
ExtensionRegistry readOnlyRegistry = specializedRegistry.getUnmodifiable();
448
449
// Use appropriate registry based on context
450
// if (isDebugMode()) {
451
// message = MyMessage.parseFrom(data, specializedRegistry);
452
// } else {
453
// message = MyMessage.parseFrom(data, baseRegistry);
454
// }
455
```
456
457
### Error Handling with Extensions
458
459
```java
460
import akka.protobufv3.internal.*;
461
462
try {
463
// Parse message without proper extension registry
464
byte[] data = getMessageWithExtensions();
465
// MyMessage message = MyMessage.parseFrom(data); // Extensions will be in unknown fields
466
467
// Access extension (will return default value)
468
// String customValue = message.getExtension(MyExtensions.customField);
469
// System.out.println("Custom value: " + customValue); // Will be default/empty
470
471
// Check unknown fields for unparsed extensions
472
UnknownFieldSet unknownFields = message.getUnknownFields();
473
if (!unknownFields.asMap().isEmpty()) {
474
System.out.println("Message has unknown fields (possibly extensions)");
475
}
476
477
} catch (InvalidProtocolBufferException e) {
478
System.err.println("Failed to parse message: " + e.getMessage());
479
}
480
```