Skip to content

Commit

Permalink
CodecConfigurer implementation refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
rstoyanchev committed Dec 12, 2019
1 parent fa8f083 commit 9d65830
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 128 deletions.
Expand Up @@ -95,6 +95,8 @@ interface ClientDefaultCodecs extends DefaultCodecs {
* <p>By default if this is not set, and Jackson is available, the
* {@link #jackson2JsonDecoder} override is used instead. Use this property
* if you want to further customize the SSE decoder.
* <p>Note that {@link #maxInMemorySize(int)}, if configured, will be
* applied to the given decoder.
* @param decoder the decoder to use
*/
void serverSentEventDecoder(Decoder<?> decoder);
Expand Down
Expand Up @@ -109,6 +109,8 @@ interface DefaultCodecs {

/**
* Override the default Jackson JSON {@code Decoder}.
* <p>Note that {@link #maxInMemorySize(int)}, if configured, will be
* applied to the given decoder.
* @param decoder the decoder instance to use
* @see org.springframework.http.codec.json.Jackson2JsonDecoder
*/
Expand All @@ -123,6 +125,8 @@ interface DefaultCodecs {

/**
* Override the default Protobuf {@code Decoder}.
* <p>Note that {@link #maxInMemorySize(int)}, if configured, will be
* applied to the given decoder.
* @param decoder the decoder instance to use
* @since 5.1
* @see org.springframework.http.codec.protobuf.ProtobufDecoder
Expand All @@ -140,6 +144,8 @@ interface DefaultCodecs {

/**
* Override the default JAXB2 {@code Decoder}.
* <p>Note that {@link #maxInMemorySize(int)}, if configured, will be
* applied to the given decoder.
* @param decoder the decoder instance to use
* @since 5.1.3
* @see org.springframework.http.codec.xml.Jaxb2XmlDecoder
Expand Down Expand Up @@ -245,7 +251,8 @@ interface DefaultCodecConfig {
* Whether to log form data at DEBUG level, and headers at TRACE level.
* Both may contain sensitive information.
*/
boolean isEnableLoggingRequestDetails();
@Nullable
Boolean isEnableLoggingRequestDetails();
}

}
Expand Up @@ -91,6 +91,9 @@ interface ServerDefaultCodecs extends DefaultCodecs {
* MultipartHttpMessageReader} created with an instance of
* {@link org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader
* SynchronossPartHttpMessageReader}.
* <p>Note that {@link #maxInMemorySize(int)} and/or
* {@link #enableLoggingRequestDetails(boolean)}, if configured, will be
* applied to the given reader, if applicable.
* @param reader the message reader to use for multipart requests.
* @since 5.1.11
*/
Expand Down
Expand Up @@ -149,6 +149,16 @@ public List<HttpMessageWriter<?>> getPartWriters() {
return Collections.unmodifiableList(this.partWriters);
}


/**
* Return the configured form writer.
* @since 5.1.13
*/
@Nullable
public HttpMessageWriter<MultiValueMap<String, String>> getFormWriter() {
return this.formWriter;
}

/**
* Set the character set to use for part headers such as
* "Content-Disposition" (and its filename parameter).
Expand Down
Expand Up @@ -36,15 +36,20 @@
import org.springframework.http.codec.DecoderHttpMessageReader;
import org.springframework.http.codec.EncoderHttpMessageWriter;
import org.springframework.http.codec.FormHttpMessageReader;
import org.springframework.http.codec.FormHttpMessageWriter;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.ResourceHttpMessageReader;
import org.springframework.http.codec.ResourceHttpMessageWriter;
import org.springframework.http.codec.ServerSentEventHttpMessageReader;
import org.springframework.http.codec.json.AbstractJackson2Decoder;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.http.codec.json.Jackson2SmileDecoder;
import org.springframework.http.codec.json.Jackson2SmileEncoder;
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
import org.springframework.http.codec.multipart.MultipartHttpMessageWriter;
import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader;
import org.springframework.http.codec.protobuf.ProtobufDecoder;
import org.springframework.http.codec.protobuf.ProtobufEncoder;
import org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter;
Expand All @@ -70,13 +75,16 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure

private static final boolean protobufPresent;

static final boolean synchronossMultipartPresent;

static {
ClassLoader classLoader = BaseCodecConfigurer.class.getClassLoader();
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
protobufPresent = ClassUtils.isPresent("com.google.protobuf.Message", classLoader);
synchronossMultipartPresent = ClassUtils.isPresent("org.synchronoss.cloud.nio.multipart.NioMultipartParser", classLoader);
}


Expand All @@ -101,7 +109,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
@Nullable
private Integer maxInMemorySize;

private boolean enableLoggingRequestDetails = false;
@Nullable
private Boolean enableLoggingRequestDetails;

private boolean registerDefaults = true;

Expand Down Expand Up @@ -171,7 +180,8 @@ public void enableLoggingRequestDetails(boolean enable) {
}

@Override
public boolean isEnableLoggingRequestDetails() {
@Nullable
public Boolean isEnableLoggingRequestDetails() {
return this.enableLoggingRequestDetails;
}

Expand All @@ -191,48 +201,107 @@ final List<HttpMessageReader<?>> getTypedReaders() {
return Collections.emptyList();
}
List<HttpMessageReader<?>> readers = new ArrayList<>();
readers.add(new DecoderHttpMessageReader<>(init(new ByteArrayDecoder())));
readers.add(new DecoderHttpMessageReader<>(init(new ByteBufferDecoder())));
readers.add(new DecoderHttpMessageReader<>(init(new DataBufferDecoder())));
readers.add(new ResourceHttpMessageReader(init(new ResourceDecoder())));
readers.add(new DecoderHttpMessageReader<>(init(StringDecoder.textPlainOnly())));
addCodec(readers, new DecoderHttpMessageReader<>(new ByteArrayDecoder()));
addCodec(readers, new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
addCodec(readers, new DecoderHttpMessageReader<>(new DataBufferDecoder()));
addCodec(readers, new ResourceHttpMessageReader(new ResourceDecoder()));
addCodec(readers, new DecoderHttpMessageReader<>(StringDecoder.textPlainOnly()));
if (protobufPresent) {
Decoder<?> decoder = this.protobufDecoder != null ? this.protobufDecoder : init(new ProtobufDecoder());
readers.add(new DecoderHttpMessageReader<>(decoder));
}

FormHttpMessageReader formReader = new FormHttpMessageReader();
if (this.maxInMemorySize != null) {
formReader.setMaxInMemorySize(this.maxInMemorySize);
Decoder<?> decoder = this.protobufDecoder != null ? this.protobufDecoder : new ProtobufDecoder();
addCodec(readers, new DecoderHttpMessageReader<>(decoder));
}
formReader.setEnableLoggingRequestDetails(this.enableLoggingRequestDetails);
readers.add(formReader);
addCodec(readers, new FormHttpMessageReader());

// client vs server..
extendTypedReaders(readers);

return readers;
}

private <T extends Decoder<?>> T init(T decoder) {
if (this.maxInMemorySize != null) {
if (decoder instanceof AbstractDataBufferDecoder) {
((AbstractDataBufferDecoder<?>) decoder).setMaxInMemorySize(this.maxInMemorySize);
/**
* Initialize a codec and add it to the List.
* @since 5.1.13
*/
protected <T> void addCodec(List<T> codecs, T codec) {
initCodec(codec);
codecs.add(codec);
}

/**
* Apply {@link #maxInMemorySize()} and {@link #enableLoggingRequestDetails},
* if configured by the application, to the given codec , including any
* codec it contains.
*/
private void initCodec(@Nullable Object codec) {

if (codec instanceof DecoderHttpMessageReader) {
codec = ((DecoderHttpMessageReader) codec).getDecoder();
}
else if (codec instanceof ServerSentEventHttpMessageReader) {
codec = ((ServerSentEventHttpMessageReader) codec).getDecoder();
}

if (codec == null) {
return;
}

Integer size = this.maxInMemorySize;
if (size != null) {
if (codec instanceof AbstractDataBufferDecoder) {
((AbstractDataBufferDecoder<?>) codec).setMaxInMemorySize(size);
}
if (decoder instanceof ProtobufDecoder) {
((ProtobufDecoder) decoder).setMaxMessageSize(this.maxInMemorySize);
if (protobufPresent) {
if (codec instanceof ProtobufDecoder) {
((ProtobufDecoder) codec).setMaxMessageSize(size);
}
}
if (jackson2Present) {
if (decoder instanceof AbstractJackson2Decoder) {
((AbstractJackson2Decoder) decoder).setMaxInMemorySize(this.maxInMemorySize);
if (codec instanceof AbstractJackson2Decoder) {
((AbstractJackson2Decoder) codec).setMaxInMemorySize(size);
}
}
if (jaxb2Present) {
if (decoder instanceof Jaxb2XmlDecoder) {
((Jaxb2XmlDecoder) decoder).setMaxInMemorySize(this.maxInMemorySize);
if (codec instanceof Jaxb2XmlDecoder) {
((Jaxb2XmlDecoder) codec).setMaxInMemorySize(size);
}
}
if (codec instanceof FormHttpMessageReader) {
((FormHttpMessageReader) codec).setMaxInMemorySize(size);
}
if (synchronossMultipartPresent) {
if (codec instanceof SynchronossPartHttpMessageReader) {
((SynchronossPartHttpMessageReader) codec).setMaxInMemorySize(size);
}
}
}

Boolean enable = this.enableLoggingRequestDetails;
if (enable != null) {
if (codec instanceof FormHttpMessageReader) {
((FormHttpMessageReader) codec).setEnableLoggingRequestDetails(enable);
}
if (codec instanceof MultipartHttpMessageReader) {
((MultipartHttpMessageReader) codec).setEnableLoggingRequestDetails(enable);
}
if (synchronossMultipartPresent) {
if (codec instanceof SynchronossPartHttpMessageReader) {
((SynchronossPartHttpMessageReader) codec).setEnableLoggingRequestDetails(enable);
}
}
if (codec instanceof FormHttpMessageWriter) {
((FormHttpMessageWriter) codec).setEnableLoggingRequestDetails(enable);
}
if (codec instanceof MultipartHttpMessageWriter) {
((MultipartHttpMessageWriter) codec).setEnableLoggingRequestDetails(enable);
}
}

if (codec instanceof MultipartHttpMessageReader) {
initCodec(((MultipartHttpMessageReader) codec).getPartReader());
}
else if (codec instanceof MultipartHttpMessageWriter) {
initCodec(((MultipartHttpMessageWriter) codec).getFormWriter());
}
return decoder;
}

/**
Expand All @@ -250,16 +319,19 @@ final List<HttpMessageReader<?>> getObjectReaders() {
}
List<HttpMessageReader<?>> readers = new ArrayList<>();
if (jackson2Present) {
readers.add(new DecoderHttpMessageReader<>(init(getJackson2JsonDecoder())));
addCodec(readers, new DecoderHttpMessageReader<>(getJackson2JsonDecoder()));
}
if (jackson2SmilePresent) {
readers.add(new DecoderHttpMessageReader<>(init(new Jackson2SmileDecoder())));
addCodec(readers, new DecoderHttpMessageReader<>(new Jackson2SmileDecoder()));
}
if (jaxb2Present) {
Decoder<?> decoder = this.jaxb2Decoder != null ? this.jaxb2Decoder : init(new Jaxb2XmlDecoder());
readers.add(new DecoderHttpMessageReader<>(decoder));
Decoder<?> decoder = this.jaxb2Decoder != null ? this.jaxb2Decoder : new Jaxb2XmlDecoder();
addCodec(readers, new DecoderHttpMessageReader<>(decoder));
}

// client vs server..
extendObjectReaders(readers);

return readers;
}

Expand All @@ -276,9 +348,9 @@ final List<HttpMessageReader<?>> getCatchAllReaders() {
if (!this.registerDefaults) {
return Collections.emptyList();
}
List<HttpMessageReader<?>> result = new ArrayList<>();
result.add(new DecoderHttpMessageReader<>(init(StringDecoder.allMimeTypes())));
return result;
List<HttpMessageReader<?>> readers = new ArrayList<>();
addCodec(readers, new DecoderHttpMessageReader<>(StringDecoder.allMimeTypes()));
return readers;
}

/**
Expand Down Expand Up @@ -365,11 +437,17 @@ List<HttpMessageWriter<?>> getCatchAllWriters() {
// Accessors for use in subclasses...

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

protected Encoder<?> getJackson2JsonEncoder() {
return (this.jackson2JsonEncoder != null ? this.jackson2JsonEncoder : new Jackson2JsonEncoder());
if (this.jackson2JsonEncoder == null) {
this.jackson2JsonEncoder = new Jackson2JsonEncoder();
}
return this.jackson2JsonEncoder;
}

}
Expand Up @@ -95,24 +95,17 @@ public ClientDefaultCodecsImpl clone() {

@Override
protected void extendObjectReaders(List<HttpMessageReader<?>> objectReaders) {
objectReaders.add(new ServerSentEventHttpMessageReader(getSseDecoder()));
}

@Nullable
private Decoder<?> getSseDecoder() {
return (this.sseDecoder != null ? this.sseDecoder : jackson2Present ? getJackson2JsonDecoder() : null);
Decoder<?> decoder = (this.sseDecoder != null ?
this.sseDecoder :
jackson2Present ? getJackson2JsonDecoder() : null);

addCodec(objectReaders, new ServerSentEventHttpMessageReader(decoder));
}

@Override
protected void extendTypedWriters(List<HttpMessageWriter<?>> typedWriters) {

FormHttpMessageWriter formWriter = new FormHttpMessageWriter();
formWriter.setEnableLoggingRequestDetails(isEnableLoggingRequestDetails());

MultipartHttpMessageWriter multipartWriter = new MultipartHttpMessageWriter(getPartWriters(), formWriter);
multipartWriter.setEnableLoggingRequestDetails(isEnableLoggingRequestDetails());

typedWriters.add(multipartWriter);
addCodec(typedWriters, new MultipartHttpMessageWriter(getPartWriters(), new FormHttpMessageWriter()));
}

private List<HttpMessageWriter<?>> getPartWriters() {
Expand Down
Expand Up @@ -25,7 +25,6 @@
import org.springframework.http.codec.multipart.MultipartHttpMessageReader;
import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;

/**
* Default implementation of {@link ServerCodecConfigurer.ServerDefaultCodecs}.
Expand All @@ -34,11 +33,6 @@
*/
class ServerDefaultCodecsImpl extends BaseDefaultCodecs implements ServerCodecConfigurer.ServerDefaultCodecs {

private static final boolean synchronossMultipartPresent =
ClassUtils.isPresent("org.synchronoss.cloud.nio.multipart.NioMultipartParser",
DefaultServerCodecConfigurer.class.getClassLoader());


@Nullable
private HttpMessageReader<?> multipartReader;

Expand Down Expand Up @@ -70,23 +64,13 @@ public void serverSentEventEncoder(Encoder<?> encoder) {
@Override
protected void extendTypedReaders(List<HttpMessageReader<?>> typedReaders) {
if (this.multipartReader != null) {
typedReaders.add(this.multipartReader);
addCodec(typedReaders, this.multipartReader);
return;
}
if (synchronossMultipartPresent) {
boolean enable = isEnableLoggingRequestDetails();

SynchronossPartHttpMessageReader partReader = new SynchronossPartHttpMessageReader();
Integer size = maxInMemorySize();
if (size != null) {
partReader.setMaxInMemorySize(size);
}
partReader.setEnableLoggingRequestDetails(enable);
typedReaders.add(partReader);

MultipartHttpMessageReader reader = new MultipartHttpMessageReader(partReader);
reader.setEnableLoggingRequestDetails(enable);
typedReaders.add(reader);
addCodec(typedReaders, partReader);
addCodec(typedReaders, new MultipartHttpMessageReader(partReader));
}
}

Expand Down

0 comments on commit 9d65830

Please sign in to comment.