Skip to content

Commit 7e52f35

Browse files
committed
GH-3238: Fix Unmarshaller to close File resource
Fixes #3238 * Extract an `InputStream` from a `File` payload in the `UnmarshallingTransformer` before parsing an XML. Close this `InputStream` in the `finally` block to release the file resource **Cherry-pick to 5.2.x, 5.1.x & 4.3.x**
1 parent 9f892e3 commit 7e52f35

File tree

2 files changed

+65
-29
lines changed

2 files changed

+65
-29
lines changed

spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/UnmarshallingTransformer.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,7 +18,9 @@
1818

1919
import java.io.ByteArrayInputStream;
2020
import java.io.File;
21+
import java.io.FileInputStream;
2122
import java.io.IOException;
23+
import java.io.InputStream;
2224
import java.io.UncheckedIOException;
2325

2426
import javax.xml.transform.Source;
@@ -37,11 +39,10 @@
3739

3840
/**
3941
* An implementation of {@link org.springframework.integration.transformer.Transformer}
40-
* that delegates to an OXM
41-
* {@link Unmarshaller}. Expects the payload to be of type {@link Document},
42-
* {@link String}, {@link File}, {@link Source} or to have an instance of
43-
* {@link SourceFactory} that can convert to a {@link Source}. If
44-
* {@link #alwaysUseSourceFactory} is set to true, then the {@link SourceFactory}
42+
* that delegates to an OXM {@link Unmarshaller}.
43+
* Expects the payload to be of type {@link Document}, {@link String}, {@link File}, {@link Source}
44+
* or to have an instance of {@link SourceFactory} that can convert to a {@link Source}.
45+
* If {@link #alwaysUseSourceFactory} is set to true, then the {@link SourceFactory}
4546
* will be used to create the {@link Source} regardless of payload type.
4647
* <p>
4748
* The {@link #alwaysUseSourceFactory} is ignored if payload is
@@ -97,6 +98,7 @@ public String getComponentType() {
9798
@Override
9899
public Object transformPayload(Object payload) {
99100
Source source;
101+
InputStream inputStream = null;
100102
try {
101103
if (this.mimeMessageUnmarshallerHelper != null) {
102104
Object result = this.mimeMessageUnmarshallerHelper.maybeUnmarshalMimeMessage(payload);
@@ -115,7 +117,9 @@ else if (payload instanceof byte[]) {
115117
source = new StreamSource(new ByteArrayInputStream((byte[]) payload));
116118
}
117119
else if (payload instanceof File) {
118-
source = new StreamSource((File) payload);
120+
File file = (File) payload;
121+
inputStream = new FileInputStream(file);
122+
source = new StreamSource(inputStream, file.toURI().toASCIIString());
119123
}
120124
else if (payload instanceof Document) {
121125
source = new DOMSource((Document) payload);
@@ -135,6 +139,16 @@ else if (payload instanceof Source) {
135139
catch (IOException e) {
136140
throw new UncheckedIOException("failed to unmarshal payload", e);
137141
}
142+
finally {
143+
if (inputStream != null) {
144+
try {
145+
inputStream.close();
146+
}
147+
catch (IOException e) {
148+
// Ignore
149+
}
150+
}
151+
}
138152
}
139153

140154
private static class MimeMessageUnmarshallerHelper {
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,68 +17,90 @@
1717
package org.springframework.integration.xml.transformer.jaxbmarshaling;
1818

1919
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
21+
22+
import java.io.IOException;
23+
import java.nio.file.Files;
24+
import java.nio.file.Path;
2025

21-
import javax.xml.transform.Result;
2226
import javax.xml.transform.Source;
2327
import javax.xml.transform.dom.DOMResult;
2428

25-
import org.junit.Test;
29+
import org.junit.jupiter.api.Test;
30+
import org.junit.jupiter.api.io.TempDir;
2631
import org.w3c.dom.Document;
2732

2833
import org.springframework.beans.factory.annotation.Autowired;
2934
import org.springframework.beans.factory.annotation.Qualifier;
35+
import org.springframework.integration.transformer.MessageTransformationException;
36+
import org.springframework.messaging.Message;
3037
import org.springframework.messaging.MessageChannel;
3138
import org.springframework.messaging.PollableChannel;
3239
import org.springframework.messaging.support.GenericMessage;
33-
import org.springframework.test.context.ContextConfiguration;
34-
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
40+
import org.springframework.oxm.UnmarshallingFailureException;
41+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
3542
import org.springframework.xml.transform.StringSource;
3643

3744
/**
3845
* @author Jonas Partner
46+
* @author Artem Bilan
3947
*/
40-
@ContextConfiguration
41-
public class JaxbMarshallingIntegrationTests extends AbstractJUnit4SpringContextTests {
48+
@SpringJUnitConfig
49+
public class JaxbMarshallingIntegrationTests {
4250

43-
@Autowired @Qualifier("marshallIn")
51+
@Autowired
52+
@Qualifier("marshallIn")
4453
MessageChannel marshallIn;
4554

46-
@Autowired @Qualifier("marshallOut")
55+
@Autowired
56+
@Qualifier("marshallOut")
4757
PollableChannel marshalledOut;
4858

49-
@Autowired @Qualifier("unmarshallIn")
59+
@Autowired
60+
@Qualifier("unmarshallIn")
5061
MessageChannel unmarshallIn;
5162

52-
@Autowired @Qualifier("unmarshallOut")
63+
@Autowired
64+
@Qualifier("unmarshallOut")
5365
PollableChannel unmarshallOut;
5466

67+
@TempDir
68+
Path tempDirectory;
5569

56-
@SuppressWarnings("unchecked")
5770
@Test
58-
public void testMarshalling() throws Exception {
71+
public void testMarshalling() {
5972
JaxbAnnotatedPerson person = new JaxbAnnotatedPerson();
6073
person.setFirstName("john");
61-
marshallIn.send(new GenericMessage<Object>(person));
62-
GenericMessage<Result> res = (GenericMessage<Result>) marshalledOut.receive(2000);
63-
assertThat(res).as("No response recevied").isNotNull();
74+
this.marshallIn.send(new GenericMessage<>(person));
75+
Message<?> res = this.marshalledOut.receive(2000);
76+
assertThat(res).as("No response received").isNotNull();
6477
assertThat(res.getPayload() instanceof DOMResult).as("payload was not a DOMResult").isTrue();
6578
Document doc = (Document) ((DOMResult) res.getPayload()).getNode();
6679
assertThat(doc.getDocumentElement().getLocalName()).as("Wrong name for root element ").isEqualTo("person");
6780
}
6881

6982

70-
@SuppressWarnings("unchecked")
7183
@Test
72-
public void testUnmarshalling() throws Exception {
84+
public void testUnmarshalling() {
7385
StringSource source = new StringSource("<person><firstname>bob</firstname></person>");
74-
unmarshallIn.send(new GenericMessage<Source>(source));
75-
GenericMessage<Object> res = (GenericMessage<Object>) unmarshallOut.receive(2000);
86+
this.unmarshallIn.send(new GenericMessage<Source>(source));
87+
Message<?> res = this.unmarshallOut.receive(2000);
7688
assertThat(res).as("No response").isNotNull();
7789
assertThat(res.getPayload() instanceof JaxbAnnotatedPerson).as("Not a Person ").isTrue();
7890
JaxbAnnotatedPerson person = (JaxbAnnotatedPerson) res.getPayload();
79-
assertThat(person.getFirstName()).as("Worng firstname").isEqualTo("bob");
80-
91+
assertThat(person.getFirstName()).as("Wrong firstname").isEqualTo("bob");
8192
}
8293

94+
@Test
95+
public void testFileUnlockedAfterUnmarshallingFailure() throws IOException {
96+
Path tempFile = Files.createTempFile(this.tempDirectory, null, null);
97+
Files.write(tempFile, "junk".getBytes());
98+
assertThatExceptionOfType(MessageTransformationException.class)
99+
.isThrownBy(() -> this.unmarshallIn.send(new GenericMessage<>(tempFile.toFile())))
100+
.withCauseInstanceOf(UnmarshallingFailureException.class)
101+
.withStackTraceContaining("Content is not allowed in prolog.");
102+
103+
Files.delete(tempFile);
104+
}
83105

84106
}

0 commit comments

Comments
 (0)