Merge branch '7.0.x'

This commit is contained in:
rstoyanchev
2026-08-19 14:19:30 +03:00
6 changed files with 58 additions and 42 deletions
@@ -173,6 +173,19 @@ public class ProtobufDecoder extends ProtobufCodecSupport implements Decoder<Mes
}
}
/**
* Create a new {@code Message.Builder} instance for the given class.
* <p>This method uses a ConcurrentHashMap for caching method lookups.
*/
protected static Message.Builder getMessageBuilder(Class<?> clazz) throws Exception {
Method method = methodCache.get(clazz);
if (method == null) {
method = clazz.getMethod("newBuilder");
methodCache.put(clazz, method);
}
return (Message.Builder) method.invoke(clazz);
}
/**
* Use merge methods on {@link Message.Builder} to read a single message
* from the given {@code DataBuffer}.
@@ -184,20 +197,6 @@ public class ProtobufDecoder extends ProtobufCodecSupport implements Decoder<Mes
builder.mergeFrom(CodedInputStream.newInstance(byteBuffer), this.extensionRegistry);
}
/**
* Create a new {@code Message.Builder} instance for the given class.
* <p>This method uses a ConcurrentHashMap for caching method lookups.
*/
private static Message.Builder getMessageBuilder(Class<?> clazz) throws Exception {
Method method = methodCache.get(clazz);
if (method == null) {
method = clazz.getMethod("newBuilder");
methodCache.put(clazz, method);
}
return (Message.Builder) method.invoke(clazz);
}
@Override
public List<MimeType> getDecodableMimeTypes() {
return getMimeTypes();
@@ -54,6 +54,7 @@ import org.springframework.util.MimeType;
*
* @author Sebastien Deleuze
* @since 5.1
* @see ProtobufHttpMessageWriter
* @see ProtobufDecoder
*/
public class ProtobufEncoder extends ProtobufCodecSupport implements HttpMessageEncoder<Message> {
@@ -95,6 +95,19 @@ public class ProtobufHttpMessageWriter extends EncoderHttpMessageWriter<Message>
}
}
/**
* Create a new {@code Message.Builder} instance for the given class.
* <p>This method uses a ConcurrentHashMap for caching method lookups.
*/
protected static Message.Builder getMessageBuilder(Class<?> clazz) throws Exception {
Method method = methodCache.get(clazz);
if (method == null) {
method = clazz.getMethod("newBuilder");
methodCache.put(clazz, method);
}
return (Message.Builder) method.invoke(clazz);
}
/**
* Return the {@code MediaType} to use when the input Publisher is multivalued.
* @since 7.0
@@ -118,17 +131,4 @@ public class ProtobufHttpMessageWriter extends EncoderHttpMessageWriter<Message>
protected void extendHeaders(ReactiveHttpOutputMessage message, Map<String, Object> hints) {
}
/**
* Create a new {@code Message.Builder} instance for the given class.
* <p>This method uses a ConcurrentHashMap for caching method lookups.
*/
private static Message.Builder getMessageBuilder(Class<?> clazz) throws Exception {
Method method = methodCache.get(clazz);
if (method == null) {
method = clazz.getMethod("newBuilder");
methodCache.put(clazz, method);
}
return (Message.Builder) method.invoke(clazz);
}
}
@@ -161,7 +161,7 @@ public class ProtobufJsonDecoder implements Decoder<Message> {
* Create a new {@code Message.Builder} instance for the given class.
* <p>This method uses a ConcurrentHashMap for caching method lookups.
*/
private static Message.Builder getMessageBuilder(Class<?> clazz) throws Exception {
protected static Message.Builder getMessageBuilder(Class<?> clazz) throws Exception {
Method method = methodCache.get(clazz);
if (method == null) {
method = clazz.getMethod("newBuilder");
@@ -49,6 +49,7 @@ import org.springframework.util.MimeType;
*
* @author Brian Clozel
* @since 6.2
* @see ProtobufHttpMessageWriter
* @see ProtobufJsonDecoder
*/
public class ProtobufJsonEncoder implements HttpMessageEncoder<Message> {
@@ -59,7 +60,8 @@ public class ProtobufJsonEncoder implements HttpMessageEncoder<Message> {
private static final List<MimeType> defaultMimeTypes = List.of(
MediaType.APPLICATION_JSON,
new MediaType("application", "*+json"));
new MediaType("application", "*+json"),
MediaType.APPLICATION_NDJSON);
private final JsonFormat.Printer printer;
@@ -106,7 +108,10 @@ public class ProtobufJsonEncoder implements HttpMessageEncoder<Message> {
}
@Override
public Flux<DataBuffer> encode(Publisher<? extends Message> inputStream, DataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
public Flux<DataBuffer> encode(
Publisher<? extends Message> inputStream, DataBufferFactory bufferFactory,
ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
if (inputStream instanceof Mono) {
return Mono.from(inputStream)
.map(value -> encodeValue(value, bufferFactory, elementType, mimeType, hints))
@@ -120,17 +125,21 @@ public class ProtobufJsonEncoder implements HttpMessageEncoder<Message> {
.map(value -> {
byte[] prefix = helper.getPrefix();
byte[] delimiter = helper.getDelimiter();
DataBuffer delimiterBuffer = bufferFactory.wrap(delimiter);
DataBuffer dataBuffer = encodeValue(value, bufferFactory, MESSAGE_TYPE, mimeType, hints);
return (prefix.length > 0 ?
bufferFactory.join(List.of(bufferFactory.wrap(prefix), bufferFactory.wrap(delimiter), dataBuffer)) :
bufferFactory.join(List.of(bufferFactory.wrap(delimiter), dataBuffer)));
bufferFactory.join(List.of(bufferFactory.wrap(prefix), delimiterBuffer, dataBuffer)) :
bufferFactory.join(List.of(delimiterBuffer, dataBuffer)));
})
.switchIfEmpty(Mono.fromCallable(() -> bufferFactory.wrap(helper.getPrefix())))
.concatWith(Mono.fromCallable(() -> bufferFactory.wrap(helper.getSuffix())));
}
@Override
public DataBuffer encodeValue(Message message, DataBufferFactory bufferFactory, ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
public DataBuffer encodeValue(
Message message, DataBufferFactory bufferFactory, ResolvableType valueType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
FastByteArrayOutputStream bos = new FastByteArrayOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(bos, StandardCharsets.UTF_8);
try {
@@ -144,6 +153,7 @@ public class ProtobufJsonEncoder implements HttpMessageEncoder<Message> {
}
}
private static class JsonArrayJoinHelper {
private static final byte[] COMMA_SEPARATOR = {','};
@@ -43,22 +43,28 @@ import static org.springframework.core.ResolvableType.forClass;
*/
class ProtobufJsonEncoderTests extends AbstractEncoderTests<ProtobufJsonEncoder> {
private Msg msg1 =
Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(123).build()).build();
private Msg msg1 = Msg.newBuilder()
.setFoo("Foo")
.setBlah(SecondMsg.newBuilder().setBlah(123).build())
.build();
private Msg msg2 = Msg.newBuilder()
.setFoo("Bar")
.setBlah(SecondMsg.newBuilder().setBlah(456).build())
.build();
private Msg msg2 =
Msg.newBuilder().setFoo("Bar").setBlah(SecondMsg.newBuilder().setBlah(456).build()).build();
public ProtobufJsonEncoderTests() {
super(new ProtobufJsonEncoder(JsonFormat.printer().omittingInsignificantWhitespace()));
}
@Override
@Test
protected void canEncode() throws Exception {
assertThat(this.encoder.canEncode(forClass(Msg.class), null)).isFalse();
assertThat(this.encoder.canEncode(forClass(Msg.class), MediaType.APPLICATION_JSON)).isTrue();
assertThat(this.encoder.canEncode(forClass(Msg.class), MediaType.APPLICATION_NDJSON)).isFalse();
assertThat(this.encoder.canEncode(forClass(Msg.class), MediaType.APPLICATION_NDJSON)).isTrue();
assertThat(this.encoder.canEncode(forClass(Object.class), MediaType.APPLICATION_JSON)).isFalse();
}
@@ -69,7 +75,7 @@ class ProtobufJsonEncoderTests extends AbstractEncoderTests<ProtobufJsonEncoder>
ResolvableType inputType = forClass(Msg.class);
testEncode(input, inputType, MediaType.APPLICATION_JSON, null, step -> step
.assertNext(dataBuffer -> assertBufferEqualsJson(dataBuffer, "{\"foo\":\"Foo\",\"blah\":{\"blah\":123}}"))
.assertNext(buffer -> assertBufferEqualsJson(buffer, "{\"foo\":\"Foo\",\"blah\":{\"blah\":123}}"))
.verifyComplete());
testEncodeError(input, inputType, MediaType.APPLICATION_JSON, null);
testEncodeCancel(input, inputType, MediaType.APPLICATION_JSON, null);
@@ -91,9 +97,9 @@ class ProtobufJsonEncoderTests extends AbstractEncoderTests<ProtobufJsonEncoder>
ResolvableType inputType = forClass(Msg.class);
testEncode(input, inputType, MediaType.APPLICATION_JSON, null, step -> step
.assertNext(dataBuffer -> assertBufferEqualsJson(dataBuffer, "[{\"foo\":\"Foo\",\"blah\":{\"blah\":123}}"))
.assertNext(dataBuffer -> assertBufferEqualsJson(dataBuffer, ",{\"foo\":\"Bar\",\"blah\":{\"blah\":456}}"))
.assertNext(dataBuffer -> assertBufferEqualsJson(dataBuffer, "]"))
.assertNext(buffer -> assertBufferEqualsJson(buffer, "[{\"foo\":\"Foo\",\"blah\":{\"blah\":123}}"))
.assertNext(buffer -> assertBufferEqualsJson(buffer, ",{\"foo\":\"Bar\",\"blah\":{\"blah\":456}}"))
.assertNext(buffer -> assertBufferEqualsJson(buffer, "]"))
.verifyComplete());
}