1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.http.codec.xml;
18
19 import java.io.OutputStream;
20 import java.util.Map;
21 import javax.xml.bind.JAXBException;
22 import javax.xml.bind.MarshalException;
23 import javax.xml.bind.Marshaller;
24 import org.bremersee.xml.JaxbContextBuilder;
25 import org.springframework.core.ResolvableType;
26 import org.springframework.core.codec.AbstractSingleValueEncoder;
27 import org.springframework.core.codec.CodecException;
28 import org.springframework.core.codec.EncodingException;
29 import org.springframework.core.codec.Hints;
30 import org.springframework.core.io.buffer.DataBuffer;
31 import org.springframework.core.io.buffer.DataBufferFactory;
32 import org.springframework.core.io.buffer.DataBufferUtils;
33 import org.springframework.core.log.LogFormatUtils;
34 import org.springframework.lang.NonNull;
35 import org.springframework.lang.Nullable;
36 import org.springframework.util.MimeType;
37 import org.springframework.util.MimeTypeUtils;
38 import org.springframework.validation.annotation.Validated;
39 import reactor.core.publisher.Flux;
40 import reactor.core.publisher.Mono;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 @Validated
56 public class ReactiveJaxbEncoder extends AbstractSingleValueEncoder<Object> {
57
58 private final JaxbContextBuilder jaxbContextBuilder;
59
60
61
62
63
64
65 public ReactiveJaxbEncoder(final JaxbContextBuilder jaxbContextBuilder) {
66 super(MimeTypeUtils.APPLICATION_XML, MimeTypeUtils.TEXT_XML);
67 this.jaxbContextBuilder = jaxbContextBuilder != null
68 ? jaxbContextBuilder
69 : JaxbContextBuilder.builder()
70 .withCanUnmarshal(JaxbContextBuilder.CAN_UNMARSHAL_ALL);
71 }
72
73 @Override
74 public boolean canEncode(@NonNull ResolvableType elementType, @Nullable final MimeType mimeType) {
75 if (super.canEncode(elementType, mimeType)) {
76 final Class<?> outputClass = elementType.toClass();
77 return jaxbContextBuilder.canMarshal(outputClass);
78 } else {
79 return false;
80 }
81 }
82
83 @NonNull
84 @Override
85 protected Flux<DataBuffer> encode(@NonNull Object value, @NonNull DataBufferFactory bufferFactory,
86 @NonNull ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
87
88
89 return Mono.fromCallable(() -> encodeValue(value, bufferFactory, valueType, mimeType, hints))
90 .flux();
91 }
92
93 @NonNull
94 @Override
95 public DataBuffer encodeValue(@NonNull Object value, @NonNull DataBufferFactory bufferFactory,
96 @NonNull ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
97
98 if (!Hints.isLoggingSuppressed(hints)) {
99 LogFormatUtils.traceDebug(logger, traceOn -> {
100 String formatted = LogFormatUtils.formatValue(value, !traceOn);
101 return Hints.getLogPrefix(hints) + "Encoding [" + formatted + "]";
102 });
103 }
104
105 boolean release = true;
106 DataBuffer buffer = bufferFactory.allocateBuffer(1024);
107 try {
108 OutputStream outputStream = buffer.asOutputStream();
109 Marshaller marshaller = jaxbContextBuilder.buildMarshaller(value);
110 marshaller.marshal(value, outputStream);
111 release = false;
112 return buffer;
113 } catch (MarshalException ex) {
114 throw new EncodingException("Could not marshal " + value.getClass() + " to XML", ex);
115 } catch (JAXBException ex) {
116 throw new CodecException("Invalid JAXB configuration", ex);
117 } finally {
118 if (release) {
119 DataBufferUtils.release(buffer);
120 }
121 }
122 }
123
124 }