Remove dependency on web classes from new Encoder/Decoder implementation

The web dependency still need to be there since it appears that spring-rsocket requires it
This commit is contained in:
Oleg Zhurakousky
2021-03-05 15:08:25 +01:00
parent 498a52ee84
commit 9f8c29a4e7
2 changed files with 80 additions and 13 deletions

View File

@@ -16,27 +16,35 @@
package org.springframework.cloud.function.rsocket; package org.springframework.cloud.function.rsocket;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
import org.reactivestreams.Publisher;
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
import org.springframework.cloud.function.json.JsonMapper; import org.springframework.cloud.function.json.JsonMapper;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.codec.AbstractDecoder;
import org.springframework.core.codec.DecodingException; import org.springframework.core.codec.DecodingException;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.codec.json.Jackson2JsonDecoder; import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.messaging.support.MessageBuilder; import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.MimeType; import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils; import org.springframework.util.MimeTypeUtils;
import reactor.core.publisher.Flux;
/** /**
* *
* @author Oleg Zhurakousky * @author Oleg Zhurakousky
* @since 3.1 * @since 3.1
* *
*/ */
class MessageAwareJsonDecoder extends Jackson2JsonDecoder { class MessageAwareJsonDecoder extends AbstractDecoder<Object> {
private final JsonMapper jsonMapper; private final JsonMapper jsonMapper;
@@ -49,22 +57,25 @@ class MessageAwareJsonDecoder extends Jackson2JsonDecoder {
return mimeType.isCompatibleWith(MimeTypeUtils.APPLICATION_JSON); return mimeType.isCompatibleWith(MimeTypeUtils.APPLICATION_JSON);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Object decode(DataBuffer dataBuffer, ResolvableType targetType, public Object decode(DataBuffer dataBuffer, ResolvableType targetType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) throws DecodingException { @Nullable MimeType mimeType, @Nullable Map<String, Object> hints)
throws DecodingException {
ResolvableType type = ResolvableType.forClassWithGenerics(Map.class, String.class, Object.class); ResolvableType type = ResolvableType.forClassWithGenerics(Map.class, String.class,
Map<String, Object> messageMap = (Map<String, Object>) super.decode(dataBuffer, type, mimeType, hints); Object.class);
Map<String, Object> messageMap = (Map<String, Object>) doDecode(dataBuffer, type,
mimeType, hints);
if (messageMap.containsKey(FunctionRSocketUtils.PAYLOAD)) { if (messageMap.containsKey(FunctionRSocketUtils.PAYLOAD)) {
Type requestedType = FunctionTypeUtils.getGenericType(targetType.getType()); Type requestedType = FunctionTypeUtils.getGenericType(targetType.getType());
Object payload = this.jsonMapper.fromJson(messageMap.get(FunctionRSocketUtils.PAYLOAD), requestedType); Object payload = this.jsonMapper.fromJson(
messageMap.get(FunctionRSocketUtils.PAYLOAD), requestedType);
if (FunctionTypeUtils.isMessage(targetType.getType())) { if (FunctionTypeUtils.isMessage(targetType.getType())) {
return MessageBuilder.withPayload(payload) return MessageBuilder.withPayload(payload).copyHeaders(
.copyHeaders((Map<String, ?>) messageMap.get(FunctionRSocketUtils.HEADERS)) (Map<String, ?>) messageMap.get(FunctionRSocketUtils.HEADERS))
.build(); .build();
} }
else { else {
return payload; return payload;
@@ -73,6 +84,45 @@ class MessageAwareJsonDecoder extends Jackson2JsonDecoder {
else { else {
return messageMap; return messageMap;
} }
}
private Object doDecode(DataBuffer dataBuffer, ResolvableType targetType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints)
throws DecodingException {
try {
byte[] data = toByteArray(dataBuffer.asInputStream());
return this.jsonMapper.fromJson(data, targetType.getType());
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
finally {
DataBufferUtils.release(dataBuffer);
}
}
private byte[] toByteArray(final InputStream input) throws IOException {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
copyLarge(input, output, new byte[2048]);
return output.toByteArray();
}
}
private long copyLarge(final InputStream input, final OutputStream output,
final byte[] buffer) throws IOException {
long count = 0;
int n;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
@Override
public Flux<Object> decode(Publisher<DataBuffer> inputStream,
ResolvableType elementType, MimeType mimeType, Map<String, Object> hints) {
return Flux.from(inputStream).map(buffer -> decode(buffer, elementType, mimeType, hints));
} }
} }

View File

@@ -20,29 +20,36 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.reactivestreams.Publisher;
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
import org.springframework.cloud.function.json.JsonMapper; import org.springframework.cloud.function.json.JsonMapper;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.codec.AbstractEncoder;
import org.springframework.core.codec.ByteArrayEncoder;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.util.MimeType; import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils; import org.springframework.util.MimeTypeUtils;
import reactor.core.publisher.Flux;
/** /**
* *
* An extended implementation
* @author Oleg Zhurakousky * @author Oleg Zhurakousky
* @since 3.1 * @since 3.1
* *
*/ */
class MessageAwareJsonEncoder extends Jackson2JsonEncoder { class MessageAwareJsonEncoder extends AbstractEncoder<Object> {
private final JsonMapper mapper; private final JsonMapper mapper;
private final boolean isClient; private final boolean isClient;
private final ByteArrayEncoder byteArrayEncoder;
MessageAwareJsonEncoder(JsonMapper mapper) { MessageAwareJsonEncoder(JsonMapper mapper) {
this(mapper, false); this(mapper, false);
} }
@@ -50,6 +57,7 @@ class MessageAwareJsonEncoder extends Jackson2JsonEncoder {
MessageAwareJsonEncoder(JsonMapper mapper, boolean isClient) { MessageAwareJsonEncoder(JsonMapper mapper, boolean isClient) {
this.mapper = mapper; this.mapper = mapper;
this.isClient = isClient; this.isClient = isClient;
this.byteArrayEncoder = new ByteArrayEncoder();
} }
@Override @Override
@@ -81,6 +89,15 @@ class MessageAwareJsonEncoder extends Jackson2JsonEncoder {
} }
value = Collections.singletonMap(FunctionRSocketUtils.PAYLOAD, value); value = Collections.singletonMap(FunctionRSocketUtils.PAYLOAD, value);
} }
return super.encodeValue(value, bufferFactory, valueType, mimeType, hints); byte[] data = this.mapper.toJson(value);
return this.byteArrayEncoder.encodeValue(data, bufferFactory, valueType, mimeType, hints);
}
@Override
public Flux<DataBuffer> encode(Publisher<? extends Object> inputStream,
DataBufferFactory bufferFactory, ResolvableType elementType,
MimeType mimeType, Map<String, Object> hints) {
return Flux.from(inputStream).map(value ->
encodeValue(value, bufferFactory, elementType, mimeType, hints));
} }
} }