Fix concurrency issues in XStreamMarshaller
Prior to this commit, if an instance of XStreamMarshaller was used concurrently from multiple threads without having first invoked the afterPropertiesSet() method, the private `xstream` field could be initialized multiple times resulting in a ConcurrentModificationException in XStream's internal DefaultConverterLookup. This commit fixes these concurrency issues by making the `xstream` field volatile and by implementing a double-checked locking algorithm in getXStream() to avoid concurrent instance creation. Closes gh-25017
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
@@ -188,13 +189,23 @@ public class XStreamMarshallerTests {
|
||||
@Test
|
||||
public void converters() throws Exception {
|
||||
marshaller.setConverters(new EncodedByteArrayConverter());
|
||||
byte[] buf = new byte[]{0x1, 0x2};
|
||||
Writer writer = new StringWriter();
|
||||
marshaller.marshal(buf, new StreamResult(writer));
|
||||
assertThat(writer.toString(), isSimilarTo("<byte-array>AQI=</byte-array>"));
|
||||
Reader reader = new StringReader(writer.toString());
|
||||
byte[] bufResult = (byte[]) marshaller.unmarshal(new StreamSource(reader));
|
||||
assertTrue("Invalid result", Arrays.equals(buf, bufResult));
|
||||
byte[] buf = {0x1, 0x2};
|
||||
|
||||
// Execute multiple times concurrently to ensure there are no concurrency issues.
|
||||
// See https://github.com/spring-projects/spring-framework/issues/25017
|
||||
IntStream.rangeClosed(1, 100).parallel().forEach(n -> {
|
||||
try {
|
||||
Writer writer = new StringWriter();
|
||||
marshaller.marshal(buf, new StreamResult(writer));
|
||||
assertThat(writer.toString(), isSimilarTo("<byte-array>AQI=</byte-array>"));
|
||||
Reader reader = new StringReader(writer.toString());
|
||||
byte[] bufResult = (byte[]) marshaller.unmarshal(new StreamSource(reader));
|
||||
assertTrue("Invalid result", Arrays.equals(buf, bufResult));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user