Configurable Marshaller/Unmarshaller in JAXB2 codecs

Issue: SPR-17388
This commit is contained in:
Rossen Stoyanchev
2018-11-13 16:56:56 -05:00
parent 093254b2b3
commit 46a5fb7a91
5 changed files with 132 additions and 64 deletions

View File

@@ -127,6 +127,22 @@ public interface CodecConfigurer {
*/
void protobufEncoder(Encoder<?> encoder);
/**
* Override the default JAXB2 {@code Decoder}.
* @param decoder the decoder instance to use
* @since 5.1.3
* @see org.springframework.http.codec.xml.Jaxb2XmlDecoder
*/
void jaxb2Decoder(Decoder<?> decoder);
/**
* Override the default JABX2 {@code Encoder}.
* @param encoder the encoder instance to use
* @since 5.1.3
* @see org.springframework.http.codec.xml.Jaxb2XmlEncoder
*/
void jaxb2Encoder(Encoder<?> encoder);
/**
* Whether to log form data at DEBUG level, and headers at TRACE level.
* Both may contain sensitive information.

View File

@@ -89,6 +89,12 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
@Nullable
private Encoder<?> protobufEncoder;
@Nullable
private Decoder<?> jaxb2Decoder;
@Nullable
private Encoder<?> jaxb2Encoder;
private boolean enableLoggingRequestDetails = false;
private boolean registerDefaults = true;
@@ -114,6 +120,16 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
this.protobufEncoder = encoder;
}
@Override
public void jaxb2Decoder(Decoder<?> decoder) {
this.jaxb2Decoder = decoder;
}
@Override
public void jaxb2Encoder(Encoder<?> encoder) {
this.jaxb2Encoder = encoder;
}
@Override
public void enableLoggingRequestDetails(boolean enable) {
this.enableLoggingRequestDetails = enable;
@@ -145,7 +161,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
readers.add(new DecoderHttpMessageReader<>(new ResourceDecoder()));
readers.add(new DecoderHttpMessageReader<>(StringDecoder.textPlainOnly()));
if (protobufPresent) {
readers.add(new DecoderHttpMessageReader<>(getProtobufDecoder()));
Decoder<?> decoder = this.protobufDecoder != null ? this.protobufDecoder : new ProtobufDecoder();
readers.add(new DecoderHttpMessageReader<>(decoder));
}
FormHttpMessageReader formReader = new FormHttpMessageReader();
@@ -178,7 +195,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
readers.add(new DecoderHttpMessageReader<>(new Jackson2SmileDecoder()));
}
if (jaxb2Present) {
readers.add(new DecoderHttpMessageReader<>(new Jaxb2XmlDecoder()));
Decoder<?> decoder = this.jaxb2Decoder != null ? this.jaxb2Decoder : new Jaxb2XmlDecoder();
readers.add(new DecoderHttpMessageReader<>(decoder));
}
extendObjectReaders(readers);
return readers;
@@ -224,7 +242,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
extendTypedWriters(writers);
}
if (protobufPresent) {
writers.add(new ProtobufHttpMessageWriter((Encoder) getProtobufEncoder()));
Encoder<?> encoder = this.protobufEncoder != null ? this.protobufEncoder : new ProtobufEncoder();
writers.add(new ProtobufHttpMessageWriter((Encoder) encoder));
}
return writers;
}
@@ -253,7 +272,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
writers.add(new EncoderHttpMessageWriter<>(new Jackson2SmileEncoder()));
}
if (jaxb2Present) {
writers.add(new EncoderHttpMessageWriter<>(new Jaxb2XmlEncoder()));
Encoder<?> encoder = this.jaxb2Encoder != null ? this.jaxb2Encoder : new Jaxb2XmlEncoder();
writers.add(new EncoderHttpMessageWriter<>(encoder));
}
// No client or server specific multipart writers currently..
if (!forMultipart) {
@@ -291,12 +311,4 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
return (this.jackson2JsonEncoder != null ? this.jackson2JsonEncoder : new Jackson2JsonEncoder());
}
protected Decoder<?> getProtobufDecoder() {
return (this.protobufDecoder != null ? this.protobufDecoder : new ProtobufDecoder());
}
protected Encoder<?> getProtobufEncoder() {
return (this.protobufEncoder != null ? this.protobufEncoder : new ProtobufEncoder());
}
}

View File

@@ -75,12 +75,32 @@ public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
private final JaxbContextContainer jaxbContexts = new JaxbContextContainer();
private Function<Unmarshaller, Unmarshaller> unmarshallerProcessor = Function.identity();
public Jaxb2XmlDecoder() {
super(MimeTypeUtils.APPLICATION_XML, MimeTypeUtils.TEXT_XML);
}
/**
* Configure a processor function to customize Unmarshaller instances.
* @param processor the function to use
* @since 5.1.3
*/
public void setUnmarshallerProcessor(Function<Unmarshaller, Unmarshaller> processor) {
this.unmarshallerProcessor = this.unmarshallerProcessor.andThen(processor);
}
/**
* Return the configured processor for customizing Unmarshaller instances.
* @since 5.1.3
*/
public Function<Unmarshaller, Unmarshaller> getUnmarshallerProcessor() {
return this.unmarshallerProcessor;
}
@Override
public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) {
if (super.canDecode(elementType, mimeType)) {
@@ -123,7 +143,7 @@ public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
private Object unmarshal(List<XMLEvent> events, Class<?> outputClass) {
try {
Unmarshaller unmarshaller = this.jaxbContexts.createUnmarshaller(outputClass);
Unmarshaller unmarshaller = initUnmarshaller(outputClass);
XMLEventReader eventReader = StaxUtils.createXMLEventReader(events);
if (outputClass.isAnnotationPresent(XmlRootElement.class)) {
return unmarshaller.unmarshal(eventReader);
@@ -141,6 +161,11 @@ public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
}
}
private Unmarshaller initUnmarshaller(Class<?> outputClass) throws JAXBException {
Unmarshaller unmarshaller = this.jaxbContexts.createUnmarshaller(outputClass);
return this.unmarshallerProcessor.apply(unmarshaller);
}
/**
* Returns the qualified name for the given class, according to the mapping rules
* in the JAXB specification.

View File

@@ -19,6 +19,7 @@ package org.springframework.http.codec.xml;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.function.Function;
import javax.xml.bind.JAXBException;
import javax.xml.bind.MarshalException;
import javax.xml.bind.Marshaller;
@@ -57,12 +58,32 @@ public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder<Object> {
private final JaxbContextContainer jaxbContexts = new JaxbContextContainer();
private Function<Marshaller, Marshaller> marshallerProcessor = Function.identity();
public Jaxb2XmlEncoder() {
super(MimeTypeUtils.APPLICATION_XML, MimeTypeUtils.TEXT_XML);
}
/**
* Configure a processor function to customize Marshaller instances.
* @param processor the function to use
* @since 5.1.3
*/
public void setMarshallerProcessor(Function<Marshaller, Marshaller> processor) {
this.marshallerProcessor = this.marshallerProcessor.andThen(processor);
}
/**
* Return the configured processor for customizing Marshaller instances.
* @since 5.1.3
*/
public Function<Marshaller, Marshaller> getMarshallerProcessor() {
return this.marshallerProcessor;
}
@Override
public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType) {
if (super.canEncode(elementType, mimeType)) {
@@ -92,8 +113,7 @@ public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder<Object> {
Class<?> clazz = ClassUtils.getUserClass(value);
try {
Marshaller marshaller = this.jaxbContexts.createMarshaller(clazz);
marshaller.setProperty(Marshaller.JAXB_ENCODING, StandardCharsets.UTF_8.name());
Marshaller marshaller = initMarshaller(clazz);
marshaller.marshal(value, outputStream);
release = false;
return Flux.just(buffer);
@@ -112,4 +132,11 @@ public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder<Object> {
}
}
private Marshaller initMarshaller(Class<?> clazz) throws JAXBException {
Marshaller marshaller = this.jaxbContexts.createMarshaller(clazz);
marshaller.setProperty(Marshaller.JAXB_ENCODING, StandardCharsets.UTF_8.name());
marshaller = this.marshallerProcessor.apply(marshaller);
return marshaller;
}
}