Skip to content

Commit 754372a

Browse files
committed
Allow ProtobufHttpMessageConverter extensions
Make ProtobufFormatDelegate protected and visible to subclasses. Expose constructor that allows passing the delegate in. See spring-projectsgh-35403
1 parent 87607cc commit 754372a

File tree

3 files changed

+54
-33
lines changed

3 files changed

+54
-33
lines changed

spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
package org.springframework.http.converter.protobuf;
1818

1919
import java.io.IOException;
20-
import java.io.InputStream;
2120
import java.io.InputStreamReader;
22-
import java.io.OutputStream;
2321
import java.io.OutputStreamWriter;
2422
import java.lang.reflect.Method;
2523
import java.nio.charset.Charset;
@@ -105,7 +103,7 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<M
105103

106104
final ExtensionRegistry extensionRegistry;
107105

108-
private final @Nullable ProtobufFormatSupport protobufFormatSupport;
106+
private final ProtobufHttpMessageConverter.@Nullable ProtobufFormatDelegate protobufFormatDelegate;
109107

110108

111109
/**
@@ -124,21 +122,27 @@ public ProtobufHttpMessageConverter(ExtensionRegistry extensionRegistry) {
124122
this(null, extensionRegistry);
125123
}
126124

127-
ProtobufHttpMessageConverter(@Nullable ProtobufFormatSupport formatSupport,
125+
/**
126+
* Constructor for a subclass that supports additional formats.
127+
* @param formatDelegate delegate to read and write additional formats
128+
* @param extensionRegistry the registry to populate
129+
*/
130+
protected ProtobufHttpMessageConverter(
131+
ProtobufHttpMessageConverter.@Nullable ProtobufFormatDelegate formatDelegate,
128132
@Nullable ExtensionRegistry extensionRegistry) {
129133

130-
if (formatSupport != null) {
131-
this.protobufFormatSupport = formatSupport;
134+
if (formatDelegate != null) {
135+
this.protobufFormatDelegate = formatDelegate;
132136
}
133137
else if (PROTOBUF_JSON_FORMAT_PRESENT) {
134-
this.protobufFormatSupport = new ProtobufJavaUtilSupport(null, null);
138+
this.protobufFormatDelegate = new ProtobufJavaUtilDelegate(null, null);
135139
}
136140
else {
137-
this.protobufFormatSupport = null;
141+
this.protobufFormatDelegate = null;
138142
}
139143

140-
setSupportedMediaTypes(Arrays.asList(this.protobufFormatSupport != null ?
141-
this.protobufFormatSupport.supportedMediaTypes() : new MediaType[] {PROTOBUF, PLUS_PROTOBUF, TEXT_PLAIN}));
144+
setSupportedMediaTypes(Arrays.asList(this.protobufFormatDelegate != null ?
145+
this.protobufFormatDelegate.supportedMediaTypes() : new MediaType[] {PROTOBUF, PLUS_PROTOBUF, TEXT_PLAIN}));
142146

143147
this.extensionRegistry = (extensionRegistry == null ? ExtensionRegistry.newInstance() : extensionRegistry);
144148
}
@@ -172,13 +176,13 @@ protected Message readInternal(Class<? extends Message> clazz, HttpInputMessage
172176
PLUS_PROTOBUF.isCompatibleWith(contentType)) {
173177
builder.mergeFrom(inputMessage.getBody(), this.extensionRegistry);
174178
}
175-
else if (TEXT_PLAIN.isCompatibleWith(contentType)) {
179+
else if (MediaType.TEXT_PLAIN.isCompatibleWith(contentType)) {
176180
InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), charset);
177181
TextFormat.merge(reader, this.extensionRegistry, builder);
178182
}
179-
else if (this.protobufFormatSupport != null) {
180-
this.protobufFormatSupport.merge(
181-
inputMessage.getBody(), charset, contentType, this.extensionRegistry, builder);
183+
else if (this.protobufFormatDelegate != null) {
184+
this.protobufFormatDelegate.merge(
185+
inputMessage, contentType, charset, builder, this.extensionRegistry);
182186
}
183187
return builder.build();
184188
}
@@ -206,7 +210,7 @@ private Message.Builder getMessageBuilder(Class<? extends Message> clazz) {
206210
@Override
207211
protected boolean canWrite(@Nullable MediaType mediaType) {
208212
return (super.canWrite(mediaType) ||
209-
(this.protobufFormatSupport != null && this.protobufFormatSupport.supportsWriteOnly(mediaType)));
213+
(this.protobufFormatDelegate != null && this.protobufFormatDelegate.supportsWriteOnly(mediaType)));
210214
}
211215

212216
@Override
@@ -235,8 +239,8 @@ else if (TEXT_PLAIN.isCompatibleWith(contentType)) {
235239
outputStreamWriter.flush();
236240
outputMessage.getBody().flush();
237241
}
238-
else if (this.protobufFormatSupport != null) {
239-
this.protobufFormatSupport.print(message, outputMessage.getBody(), contentType, charset);
242+
else if (this.protobufFormatDelegate != null) {
243+
this.protobufFormatDelegate.print(message, outputMessage, contentType, charset);
240244
outputMessage.getBody().flush();
241245
}
242246
}
@@ -259,34 +263,51 @@ protected boolean supportsRepeatableWrites(Message message) {
259263

260264

261265
/**
262-
* Protobuf format support.
266+
* Contract to enable subclasses to plug in support for additional formats.
263267
*/
264-
interface ProtobufFormatSupport {
265-
268+
protected interface ProtobufFormatDelegate {
269+
270+
/**
271+
* Return the supported media types for the converter.
272+
* <p>Note that {@link #PROTOBUF}, {@link #PLUS_PROTOBUF}, and {@link MediaType#TEXT_PLAIN}
273+
* have built-in support and can be listed in addition to formats
274+
* specific to this delegate.
275+
*/
266276
MediaType[] supportedMediaTypes();
267277

278+
/**
279+
* Whether the media type is supported for writing.
280+
*/
268281
boolean supportsWriteOnly(@Nullable MediaType mediaType);
269282

270-
void merge(InputStream input, Charset charset, MediaType contentType,
271-
ExtensionRegistry extensionRegistry, Message.Builder builder)
283+
/**
284+
* Use merge methods on {@link Message.Builder} to read a message from
285+
* the given {@code HttpInputMessage}.
286+
*/
287+
void merge(HttpInputMessage inputMessage, MediaType contentType, Charset charset,
288+
Message.Builder builder, ExtensionRegistry extensionRegistry)
272289
throws IOException, HttpMessageConversionException;
273290

274-
void print(Message message, OutputStream output, MediaType contentType, Charset charset)
291+
/**
292+
* Use print methods on {@link Message.Builder} to write the message to
293+
* the given {@code HttpOutputMessage}.
294+
*/
295+
void print(Message message, HttpOutputMessage outputMessage, MediaType contentType, Charset charset)
275296
throws IOException, HttpMessageConversionException;
276297
}
277298

278299

279300
/**
280-
* {@link ProtobufFormatSupport} implementation used when
301+
* {@link ProtobufFormatDelegate} implementation used when
281302
* {@code com.google.protobuf.util.JsonFormat} is available.
282303
*/
283-
static class ProtobufJavaUtilSupport implements ProtobufFormatSupport {
304+
static class ProtobufJavaUtilDelegate implements ProtobufFormatDelegate {
284305

285306
private final JsonFormat.Parser parser;
286307

287308
private final JsonFormat.Printer printer;
288309

289-
public ProtobufJavaUtilSupport(JsonFormat.@Nullable Parser parser, JsonFormat.@Nullable Printer printer) {
310+
public ProtobufJavaUtilDelegate(JsonFormat.@Nullable Parser parser, JsonFormat.@Nullable Printer printer) {
290311
this.parser = (parser != null ? parser : JsonFormat.parser());
291312
this.printer = (printer != null ? printer : JsonFormat.printer());
292313
}
@@ -302,12 +323,12 @@ public boolean supportsWriteOnly(@Nullable MediaType mediaType) {
302323
}
303324

304325
@Override
305-
public void merge(InputStream input, Charset charset, MediaType contentType,
306-
ExtensionRegistry extensionRegistry, Message.Builder builder)
326+
public void merge(HttpInputMessage inputMessage, MediaType contentType, Charset charset,
327+
Message.Builder builder, ExtensionRegistry extensionRegistry)
307328
throws IOException, HttpMessageConversionException {
308329

309330
if (contentType.isCompatibleWith(APPLICATION_JSON)) {
310-
InputStreamReader reader = new InputStreamReader(input, charset);
331+
InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), charset);
311332
this.parser.merge(reader, builder);
312333
}
313334
else {
@@ -317,11 +338,11 @@ public void merge(InputStream input, Charset charset, MediaType contentType,
317338
}
318339

319340
@Override
320-
public void print(Message message, OutputStream output, MediaType contentType, Charset charset)
341+
public void print(Message message, HttpOutputMessage outputMessage, MediaType contentType, Charset charset)
321342
throws IOException, HttpMessageConversionException {
322343

323344
if (contentType.isCompatibleWith(APPLICATION_JSON)) {
324-
OutputStreamWriter writer = new OutputStreamWriter(output, charset);
345+
OutputStreamWriter writer = new OutputStreamWriter(outputMessage.getBody(), charset);
325346
this.printer.appendTo(message, writer);
326347
writer.flush();
327348
}

spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufJsonFormatHttpMessageConverter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public ProtobufJsonFormatHttpMessageConverter(
7171
public ProtobufJsonFormatHttpMessageConverter(JsonFormat.@Nullable Parser parser,
7272
JsonFormat.@Nullable Printer printer, @Nullable ExtensionRegistry extensionRegistry) {
7373

74-
super(new ProtobufJavaUtilSupport(parser, printer), extensionRegistry);
74+
super(new ProtobufJavaUtilDelegate(parser, printer), extensionRegistry);
7575
}
7676

7777
}

spring-web/src/test/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverterTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ void writeProtobuf() throws IOException {
107107
@Test
108108
void writeJsonWithGoogleProtobuf() throws IOException {
109109
this.converter = new ProtobufHttpMessageConverter(
110-
new ProtobufHttpMessageConverter.ProtobufJavaUtilSupport(null, null),
110+
new ProtobufHttpMessageConverter.ProtobufJavaUtilDelegate(null, null),
111111
this.extensionRegistry);
112112
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
113113
MediaType contentType = MediaType.APPLICATION_JSON;

0 commit comments

Comments
 (0)