Skip to content

Commit 611019b

Browse files
committed
Fix memory leak for Jaxb2XmlEncoder
This commit fixes a memory leak in Jaxb2XmlEncoder that occurs when the input stream contains an error. Test added as well. Issue: SPR-17419
1 parent 11a017d commit 611019b

File tree

2 files changed

+38
-17
lines changed

2 files changed

+38
-17
lines changed

spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlEncoder.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.core.codec.Hints;
3535
import org.springframework.core.io.buffer.DataBuffer;
3636
import org.springframework.core.io.buffer.DataBufferFactory;
37+
import org.springframework.core.io.buffer.DataBufferUtils;
3738
import org.springframework.core.log.LogFormatUtils;
3839
import org.springframework.lang.Nullable;
3940
import org.springframework.util.ClassUtils;
@@ -78,27 +79,38 @@ public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType
7879
@Override
7980
protected Flux<DataBuffer> encode(Object value, DataBufferFactory dataBufferFactory,
8081
ResolvableType type, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
82+
83+
if (!Hints.isLoggingSuppressed(hints)) {
84+
LogFormatUtils.traceDebug(logger, traceOn -> {
85+
String formatted = LogFormatUtils.formatValue(value, !traceOn);
86+
return Hints.getLogPrefix(hints) + "Encoding [" + formatted + "]";
87+
});
88+
}
89+
90+
boolean release = true;
91+
DataBuffer buffer = dataBufferFactory.allocateBuffer(1024);
92+
OutputStream outputStream = buffer.asOutputStream();
93+
Class<?> clazz = ClassUtils.getUserClass(value);
94+
8195
try {
82-
if (!Hints.isLoggingSuppressed(hints)) {
83-
LogFormatUtils.traceDebug(logger, traceOn -> {
84-
String formatted = LogFormatUtils.formatValue(value, !traceOn);
85-
return Hints.getLogPrefix(hints) + "Encoding [" + formatted + "]";
86-
});
87-
}
88-
DataBuffer buffer = dataBufferFactory.allocateBuffer(1024);
89-
OutputStream outputStream = buffer.asOutputStream();
90-
Class<?> clazz = ClassUtils.getUserClass(value);
9196
Marshaller marshaller = this.jaxbContexts.createMarshaller(clazz);
9297
marshaller.setProperty(Marshaller.JAXB_ENCODING, StandardCharsets.UTF_8.name());
9398
marshaller.marshal(value, outputStream);
99+
release = false;
94100
return Flux.just(buffer);
95101
}
96102
catch (MarshalException ex) {
97-
return Flux.error(new EncodingException("Could not marshal " + value.getClass() + " to XML", ex));
103+
return Flux.error(new EncodingException(
104+
"Could not marshal " + value.getClass() + " to XML", ex));
98105
}
99106
catch (JAXBException ex) {
100107
return Flux.error(new CodecException("Invalid JAXB configuration", ex));
101108
}
109+
finally {
110+
if (release) {
111+
DataBufferUtils.release(buffer);
112+
}
113+
}
102114
}
103115

104116
}

spring-web/src/test/java/org/springframework/http/codec/xml/Jaxb2XmlEncoderTests.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
import java.util.Arrays;
2121
import java.util.Collections;
2222
import java.util.List;
23+
import javax.xml.bind.annotation.XmlElement;
24+
import javax.xml.bind.annotation.XmlElements;
25+
import javax.xml.bind.annotation.XmlRootElement;
2326

2427
import org.junit.Test;
2528
import reactor.core.publisher.Flux;
@@ -34,15 +37,9 @@
3437
import org.springframework.http.MediaType;
3538
import org.springframework.http.codec.Pojo;
3639

37-
import static org.junit.Assert.assertFalse;
38-
import static org.junit.Assert.assertThat;
39-
import static org.junit.Assert.assertTrue;
40+
import static org.junit.Assert.*;
4041
import static org.xmlunit.matchers.CompareMatcher.isSimilarTo;
4142

42-
import javax.xml.bind.annotation.XmlElement;
43-
import javax.xml.bind.annotation.XmlElements;
44-
import javax.xml.bind.annotation.XmlRootElement;
45-
4643
/**
4744
* @author Sebastien Deleuze
4845
* @author Arjen Poutsma
@@ -94,6 +91,18 @@ public void encode() {
9491
.verifyComplete();
9592
}
9693

94+
@Test
95+
public void encodeError() {
96+
Flux<Pojo> source = Flux.error(RuntimeException::new);
97+
Flux<DataBuffer> output = this.encoder.encode(source, this.bufferFactory,
98+
ResolvableType.forClass(Pojo.class),
99+
MediaType.APPLICATION_XML, Collections.emptyMap());
100+
101+
StepVerifier.create(output)
102+
.expectError(RuntimeException.class)
103+
.verify();
104+
}
105+
97106
@Test
98107
public void encodeElementsWithCommonType() {
99108
Mono<Container> source = Mono.just(new Container());

0 commit comments

Comments
 (0)