Skip to content

Commit 3899b7a

Browse files
committed
Refactor DefaultCodecs.protobufWriter into protobufEncoder
Includes nullability declarations for the protobuf package. Issue: SPR-15776
1 parent fd8e4ab commit 3899b7a

File tree

7 files changed

+74
-38
lines changed

7 files changed

+74
-38
lines changed

spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,13 @@ interface DefaultCodecs {
119119
void protobufDecoder(Decoder<?> decoder);
120120

121121
/**
122-
* Override the default Protobuf {@code HttpMessageReader}.
123-
* @param decoder the decoder instance to use
122+
* Override the default Protobuf {@code Encoder}.
123+
* @param encoder the encoder instance to use
124124
* @since 5.1
125+
* @see org.springframework.http.codec.protobuf.ProtobufEncoder
125126
* @see org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter
126127
*/
127-
void protobufWriter(HttpMessageWriter<?> decoder);
128+
void protobufEncoder(Encoder<?> encoder);
128129

129130
/**
130131
* Whether to log form data at DEBUG level, and headers at TRACE level.

spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufCodecSupport.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@ protected boolean supportsMimeType(@Nullable MimeType mimeType) {
4848
protected List<MimeType> getMimeTypes() {
4949
return MIME_TYPES;
5050
}
51+
5152
}

spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufDecoder.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import java.lang.reflect.Method;
2121
import java.util.List;
2222
import java.util.Map;
23-
import java.util.concurrent.ConcurrentHashMap;
23+
import java.util.concurrent.ConcurrentMap;
2424
import java.util.function.Function;
2525

2626
import com.google.protobuf.CodedInputStream;
@@ -35,7 +35,9 @@
3535
import org.springframework.core.codec.DecodingException;
3636
import org.springframework.core.io.buffer.DataBuffer;
3737
import org.springframework.core.io.buffer.DataBufferUtils;
38+
import org.springframework.lang.Nullable;
3839
import org.springframework.util.Assert;
40+
import org.springframework.util.ConcurrentReferenceHashMap;
3941
import org.springframework.util.MimeType;
4042

4143
/**
@@ -66,7 +68,7 @@ public class ProtobufDecoder extends ProtobufCodecSupport implements Decoder<Mes
6668
*/
6769
protected static final int DEFAULT_MESSAGE_MAX_SIZE = 64 * 1024;
6870

69-
private static final ConcurrentHashMap<Class<?>, Method> methodCache = new ConcurrentHashMap<>();
71+
private static final ConcurrentMap<Class<?>, Method> methodCache = new ConcurrentReferenceHashMap<>();
7072

7173
private final ExtensionRegistry extensionRegistry;
7274

@@ -90,29 +92,32 @@ public ProtobufDecoder(ExtensionRegistry extensionRegistry) {
9092
this.extensionRegistry = extensionRegistry;
9193
}
9294

95+
9396
public void setMaxMessageSize(int maxMessageSize) {
9497
this.maxMessageSize = maxMessageSize;
9598
}
9699

100+
97101
@Override
98-
public boolean canDecode(ResolvableType elementType, MimeType mimeType) {
99-
return Message.class.isAssignableFrom(elementType.getRawClass()) && supportsMimeType(mimeType);
102+
public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) {
103+
return Message.class.isAssignableFrom(elementType.toClass()) && supportsMimeType(mimeType);
100104
}
101105

102106
@Override
103107
public Flux<Message> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
104-
MimeType mimeType, Map<String, Object> hints) {
108+
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
105109

106110
return Flux.from(inputStream)
107111
.concatMap(new MessageDecoderFunction(elementType, this.maxMessageSize));
108112
}
109113

110114
@Override
111115
public Mono<Message> decodeToMono(Publisher<DataBuffer> inputStream, ResolvableType elementType,
112-
MimeType mimeType, Map<String, Object> hints) {
116+
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
117+
113118
return DataBufferUtils.join(inputStream).map(dataBuffer -> {
114119
try {
115-
Message.Builder builder = getMessageBuilder(elementType.getRawClass());
120+
Message.Builder builder = getMessageBuilder(elementType.toClass());
116121
builder.mergeFrom(CodedInputStream.newInstance(dataBuffer.asByteBuffer()), this.extensionRegistry);
117122
Message message = builder.build();
118123
DataBufferUtils.release(dataBuffer);
@@ -153,6 +158,7 @@ private class MessageDecoderFunction implements Function<DataBuffer, Publisher<?
153158

154159
private final int maxMessageSize;
155160

161+
@Nullable
156162
private DataBuffer output;
157163

158164
private int messageBytesToRead;
@@ -162,10 +168,10 @@ public MessageDecoderFunction(ResolvableType elementType, int maxMessageSize) {
162168
this.maxMessageSize = maxMessageSize;
163169
}
164170

165-
// TODO Instead of the recursive call, loop over the current DataBuffer, produce a list of as many messages as are contained, and save any remaining bytes with flatMapIterable
171+
// TODO Instead of the recursive call, loop over the current DataBuffer,
172+
// produce a list of as many messages as are contained, and save any remaining bytes with flatMapIterable
166173
@Override
167174
public Publisher<? extends Message> apply(DataBuffer input) {
168-
169175
try {
170176
if (this.output == null) {
171177
int firstByte = input.read();
@@ -176,7 +182,7 @@ public Publisher<? extends Message> apply(DataBuffer input) {
176182
if (this.messageBytesToRead > this.maxMessageSize) {
177183
return Flux.error(new DecodingException(
178184
"The number of bytes to read parsed in the incoming stream (" +
179-
this.messageBytesToRead + ") exceeds the configured limit (" + this.maxMessageSize + ")"));
185+
this.messageBytesToRead + ") exceeds the configured limit (" + this.maxMessageSize + ")"));
180186
}
181187
this.output = input.factory().allocateBuffer(this.messageBytesToRead);
182188
}
@@ -187,7 +193,7 @@ public Publisher<? extends Message> apply(DataBuffer input) {
187193
this.messageBytesToRead -= chunkBytesToRead;
188194
Message message = null;
189195
if (this.messageBytesToRead == 0) {
190-
Message.Builder builder = getMessageBuilder(this.elementType.getRawClass());
196+
Message.Builder builder = getMessageBuilder(this.elementType.toClass());
191197
builder.mergeFrom(CodedInputStream.newInstance(this.output.asByteBuffer()), extensionRegistry);
192198
message = builder.build();
193199
DataBufferUtils.release(this.output);
@@ -209,4 +215,5 @@ public Publisher<? extends Message> apply(DataBuffer input) {
209215
}
210216
}
211217
}
218+
212219
}

spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufEncoder.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.core.io.buffer.DataBufferFactory;
3434
import org.springframework.http.MediaType;
3535
import org.springframework.http.codec.HttpMessageEncoder;
36+
import org.springframework.lang.Nullable;
3637
import org.springframework.util.MimeType;
3738

3839
/**
@@ -58,17 +59,20 @@ public class ProtobufEncoder extends ProtobufCodecSupport implements HttpMessage
5859

5960
private static final List<MediaType> streamingMediaTypes = MIME_TYPES
6061
.stream()
61-
.map(mimeType -> new MediaType(mimeType.getType(), mimeType.getSubtype(), Collections.singletonMap(DELIMITED_KEY, DELIMITED_VALUE)))
62+
.map(mimeType -> new MediaType(mimeType.getType(), mimeType.getSubtype(),
63+
Collections.singletonMap(DELIMITED_KEY, DELIMITED_VALUE)))
6264
.collect(Collectors.toList());
6365

66+
6467
@Override
65-
public boolean canEncode(ResolvableType elementType, MimeType mimeType) {
66-
return Message.class.isAssignableFrom(elementType.getRawClass()) && supportsMimeType(mimeType);
68+
public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType) {
69+
return Message.class.isAssignableFrom(elementType.toClass()) && supportsMimeType(mimeType);
6770
}
6871

6972
@Override
70-
public Flux<DataBuffer> encode(Publisher<? extends Message> inputStream,
71-
DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, Map<String, Object> hints) {
73+
public Flux<DataBuffer> encode(Publisher<? extends Message> inputStream, DataBufferFactory bufferFactory,
74+
ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
75+
7276
return Flux
7377
.from(inputStream)
7478
.map(message -> encodeMessage(message, bufferFactory, !(inputStream instanceof Mono)));
@@ -100,4 +104,5 @@ public List<MediaType> getStreamingMediaTypes() {
100104
public List<MimeType> getEncodableMimeTypes() {
101105
return getMimeTypes();
102106
}
107+
103108
}

spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufHttpMessageWriter.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import java.lang.reflect.Method;
2020
import java.util.HashMap;
2121
import java.util.Map;
22-
import java.util.concurrent.ConcurrentHashMap;
22+
import java.util.concurrent.ConcurrentMap;
2323

2424
import com.google.protobuf.Descriptors;
2525
import com.google.protobuf.Message;
@@ -29,11 +29,13 @@
2929

3030
import org.springframework.core.ResolvableType;
3131
import org.springframework.core.codec.DecodingException;
32+
import org.springframework.core.codec.Encoder;
3233
import org.springframework.http.MediaType;
3334
import org.springframework.http.ReactiveHttpOutputMessage;
3435
import org.springframework.http.codec.EncoderHttpMessageWriter;
3536
import org.springframework.http.codec.HttpMessageEncoder;
3637
import org.springframework.lang.Nullable;
38+
import org.springframework.util.ConcurrentReferenceHashMap;
3739

3840
/**
3941
* {@code HttpMessageWriter} that can write a protobuf {@link Message} and adds
@@ -49,18 +51,25 @@
4951
*/
5052
public class ProtobufHttpMessageWriter extends EncoderHttpMessageWriter<Message> {
5153

52-
private static final ConcurrentHashMap<Class<?>, Method> methodCache = new ConcurrentHashMap<>();
53-
5454
private static final String X_PROTOBUF_SCHEMA_HEADER = "X-Protobuf-Schema";
5555

5656
private static final String X_PROTOBUF_MESSAGE_HEADER = "X-Protobuf-Message";
5757

58+
private static final ConcurrentMap<Class<?>, Method> methodCache = new ConcurrentReferenceHashMap<>();
59+
5860

61+
/**
62+
* Create a new {@code ProtobufHttpMessageWriter} with a default {@link ProtobufEncoder}.
63+
*/
5964
public ProtobufHttpMessageWriter() {
6065
super(new ProtobufEncoder());
6166
}
6267

63-
public ProtobufHttpMessageWriter(ProtobufEncoder encoder) {
68+
/**
69+
* Create a new {@code ProtobufHttpMessageWriter} with the given encoder.
70+
* @param encoder the Protobuf message encoder to use
71+
*/
72+
public ProtobufHttpMessageWriter(Encoder<Message> encoder) {
6473
super(encoder);
6574
}
6675

@@ -71,7 +80,7 @@ public Mono<Void> write(Publisher<? extends Message> inputStream, ResolvableType
7180
@Nullable MediaType mediaType, ReactiveHttpOutputMessage message, Map<String, Object> hints) {
7281

7382
try {
74-
Message.Builder builder = getMessageBuilder(elementType.getRawClass());
83+
Message.Builder builder = getMessageBuilder(elementType.toClass());
7584
Descriptors.Descriptor descriptor = builder.getDescriptorForType();
7685
message.getHeaders().add(X_PROTOBUF_SCHEMA_HEADER, descriptor.getFile().getName());
7786
message.getHeaders().add(X_PROTOBUF_MESSAGE_HEADER, descriptor.getFullName());
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Provides an encoder and a decoder for
3+
* <a href="https://developers.google.com/protocol-buffers/">Google Protocol Buffers</a>.
4+
*/
5+
@NonNullApi
6+
@NonNullFields
7+
package org.springframework.http.codec.protobuf;
8+
9+
import org.springframework.lang.NonNullApi;
10+
import org.springframework.lang.NonNullFields;

spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
package org.springframework.http.codec.support;
1718

1819
import java.util.ArrayList;
@@ -42,6 +43,7 @@
4243
import org.springframework.http.codec.json.Jackson2SmileDecoder;
4344
import org.springframework.http.codec.json.Jackson2SmileEncoder;
4445
import org.springframework.http.codec.protobuf.ProtobufDecoder;
46+
import org.springframework.http.codec.protobuf.ProtobufEncoder;
4547
import org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter;
4648
import org.springframework.http.codec.xml.Jaxb2XmlDecoder;
4749
import org.springframework.http.codec.xml.Jaxb2XmlEncoder;
@@ -53,6 +55,7 @@
5355
* as a base for client and server specific variants.
5456
*
5557
* @author Rossen Stoyanchev
58+
* @author Sebastien Deleuze
5659
*/
5760
class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
5861

@@ -78,13 +81,13 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
7881
private Decoder<?> jackson2JsonDecoder;
7982

8083
@Nullable
81-
private Decoder<?> protobufDecoder;
84+
private Encoder<?> jackson2JsonEncoder;
8285

8386
@Nullable
84-
private Encoder<?> jackson2JsonEncoder;
87+
private Decoder<?> protobufDecoder;
8588

8689
@Nullable
87-
private HttpMessageWriter<?> protobufWriter;
90+
private Encoder<?> protobufEncoder;
8891

8992
private boolean enableLoggingRequestDetails = false;
9093

@@ -107,8 +110,8 @@ public void protobufDecoder(Decoder<?> decoder) {
107110
}
108111

109112
@Override
110-
public void protobufWriter(HttpMessageWriter<?> writer) {
111-
this.protobufWriter = writer;
113+
public void protobufEncoder(Encoder<?> encoder) {
114+
this.protobufEncoder = encoder;
112115
}
113116

114117
@Override
@@ -205,6 +208,7 @@ final List<HttpMessageReader<?>> getCatchAllReaders() {
205208
* or for multipart requests only ("true"). Generally the two sets are the
206209
* same except for the multipart writer itself.
207210
*/
211+
@SuppressWarnings("unchecked")
208212
final List<HttpMessageWriter<?>> getTypedWriters(boolean forMultipart) {
209213
if (!this.registerDefaults) {
210214
return Collections.emptyList();
@@ -220,7 +224,7 @@ final List<HttpMessageWriter<?>> getTypedWriters(boolean forMultipart) {
220224
extendTypedWriters(writers);
221225
}
222226
if (protobufPresent) {
223-
writers.add(getProtobufWriter());
227+
writers.add(new ProtobufHttpMessageWriter((Encoder) getProtobufEncoder()));
224228
}
225229
return writers;
226230
}
@@ -277,23 +281,22 @@ List<HttpMessageWriter<?>> getCatchAllWriters() {
277281
}
278282

279283

280-
// Accessors for use in sub-classes...
284+
// Accessors for use in subclasses...
281285

282286
protected Decoder<?> getJackson2JsonDecoder() {
283287
return (this.jackson2JsonDecoder != null ? this.jackson2JsonDecoder : new Jackson2JsonDecoder());
284288
}
285289

286-
protected Decoder<?> getProtobufDecoder() {
287-
return (this.protobufDecoder != null ? this.protobufDecoder : new ProtobufDecoder());
288-
}
289-
290290
protected Encoder<?> getJackson2JsonEncoder() {
291291
return (this.jackson2JsonEncoder != null ? this.jackson2JsonEncoder : new Jackson2JsonEncoder());
292292
}
293293

294-
protected HttpMessageWriter<?> getProtobufWriter() {
295-
return (this.protobufWriter != null ? this.protobufWriter : new ProtobufHttpMessageWriter());
294+
protected Decoder<?> getProtobufDecoder() {
295+
return (this.protobufDecoder != null ? this.protobufDecoder : new ProtobufDecoder());
296296
}
297297

298-
}
298+
protected Encoder<?> getProtobufEncoder() {
299+
return (this.protobufEncoder != null ? this.protobufEncoder : new ProtobufEncoder());
300+
}
299301

302+
}

0 commit comments

Comments
 (0)