Prevent Character Escaping on Spring JAXB - spring

I'm using String class as request object type at my SOAP request.
I need to disable escaping of JAXB and wrote a code like this:
#Bean
public Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
Map<String, Object> map = new HashMap<String, Object>();
map.put("jaxb.formatted.output", true);
jaxb2Marshaller.setPackagesToScan("packageName");
map.put("com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler",
new CharacterEscapeHandler() {
#Override
public void escape(char[] ac, int i, int j, boolean flag,
Writer writer) throws IOException {
writer.write(ac, i, j);
}
});
jaxb2Marshaller.setMarshallerProperties(map);
return jaxb2Marshaller;
}
But, I think Spring doesn't accept com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler property to disable character escaping. It gives error like this:
2022-01-07 19:39:41.124 ERROR 45808 --- [ter.container-1] c.p.d.g.s.SendMailService : org.springframework.oxm.UncategorizedMappingException: Unknown JAXB exception; nested exception is javax.xml.bind.PropertyException: name: com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler value: com.paycore.delivery.gate.soap.SOAPConfig$1#2fbb54f6
at org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:955)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.createMarshaller(Jaxb2Marshaller.java:731)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.marshal(Jaxb2Marshaller.java:701)
at org.springframework.ws.support.MarshallingUtils.marshal(MarshallingUtils.java:81)
at org.springframework.ws.client.core.WebServiceTemplate$2.doWithMessage(WebServiceTemplate.java:399)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:590)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:390)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:378)
How can I disable character escaping on Jaxb2Marshaller object?

Related

Receiving non-unicode REST POST call on SpringBoot 1.5.10 with Jackson2

Spring boot with Jackson2 assumes any JSON request will be Unicode and fails to tolerate non ascii characters if the encoding is not Unicode.
I saw that might be different by using GSON instead of Jackson2 but I want to try to stick to Jackson2.
Jackson2 supports any Java supported encoding and SpringBoot supports handling any of those encodings too but when working together they assume Unicode.
SpringBoot will assume all requests are UTF-8 unless you dissactivate that behaviour:
server.servlet.encoding.force-request=false
But then the method org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(Type, Class<?>, HttpInputMessage) doesn't have access to the request but it can access the headers in HttpInputMessage but it doesn't on SpringBoot 1.5.10.
It passes the InputStream to Jackson2 without encoding specification and it assumes it's Unicode.
The solution would be to create a InputStreamReader with the encoding you can find in the headers.
It seem's that's the actual behaviour in the current version of SpringBoot but I wonder if I can override the old one in SpringBoot 1.5.10 somehow.
I can extend the class MappingJackson2HttpMessageConverter but I don't know how to make SpringBoot to use the new converter instead of the default one for Jackson2.
I could mess with the classpath to override the whole AbstractJackson2HttpMessageConverter with a custom version but I wouldn't like that as it might break things if I make a fat-jar or a war and maybe other ways too.
I found a way to customize MappingJackson2HttpMessageConverter by registering a new bean so I created this configuration class to register an instance of a subclass with the charset behaviour fixed, creating and using a Reader when the charset is not Unicode.
/**
* Configuration class to override the default
* MappingJackson2HttpMessageConverter of SpringBoot 1.5 that fails to use the
* request charset to parse JSON post bodies.
*/
#Configuration
public class Jackson2CharsetSupportConfig {
private final class MappingJackson2HttpMessageConverterExtension extends MappingJackson2HttpMessageConverter {
private MappingJackson2HttpMessageConverterExtension(ObjectMapper objectMapper) {
super(objectMapper);
}
#Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(clazz, null);
return readJavaType(javaType, inputMessage);
}
#Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(type, contextClass);
return readJavaType(javaType, inputMessage);
}
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) {
try {
MediaType contentType = inputMessage.getHeaders().getContentType();
Charset charset = getCharset(contentType);
boolean unicode = charset.name().toUpperCase().startsWith("UTF-");
if (inputMessage instanceof MappingJacksonInputMessage) {
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if (deserializationView != null) {
if (unicode) {
return this.objectMapper.readerWithView(deserializationView).forType(javaType).
readValue(inputMessage.getBody());
} else {
return this.objectMapper.readerWithView(deserializationView).forType(javaType).
readValue(asReader(inputMessage.getBody(), charset));
}
}
}
if (unicode) {
return this.objectMapper.readValue(inputMessage.getBody(), javaType);
} else {
return this.objectMapper.readValue(asReader(inputMessage.getBody(), charset), javaType);
}
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex);
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
}
}
protected Charset getCharset(MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
return contentType.getCharset();
}
else {
return StandardCharsets.UTF_8;
}
}
protected Reader asReader(InputStream is, Charset charset) {
return new InputStreamReader(is, charset);
}
}
#Autowired
private GenericWebApplicationContext webApplicationContext;
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
return new MappingJackson2HttpMessageConverterExtension(Jackson2ObjectMapperBuilder.json().applicationContext(webApplicationContext).build());
}
}

Spring ws - Datahandler with Swaref still null

I used the Spring boot starter web services to develop a SOAP with attachment service.
For an unknown reason attachments aren't unmarshalled.. Jaxb Unmarshaller is used but the property AttachmentUnmarshaller inside is "null" ...so probably the reason why DataHandler unmarshalling isn't done ??
As in a JEE environment the attachmentUnmarshaller is handle by jaxws .. how configure it in a standalone process like spring boot on tomcat ??
Java version : 8_0_191
Spring boot version : 2.1
I faced similar issue, but with marshalling.
Jaxb2Marshaller has its own implementations of AttachmentMarshaller and AttachmentUnarshaller. But for these to work, mtomEnabled property should be set to true. If it's not, defaults will be used, which are not instantiated.
Try setting setMtomEnabled(true) on your Jaxb2Marshaller.
This will probably solve your issue.
For people, who encounter same issue with marshalling - it's a bit more complicated. Jaxb2 AttachmentMarshaller is not correctly implemented as per WS-I Attachment Profile 1.0 - http://www.ws-i.org/Profiles/AttachmentsProfile-1.0.html#Example_Attachment_Description_Using_swaRef
You will have to override marshalling behavior of Jaxb2Marshaller then.
Notice: this solution assumes that MTOM is always disabled.
#Configuration
class SOAPConfiguration {
#Bean
public Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller() {
#Override
public void marshal(Object graph, Result result, #Nullable MimeContainer mimeContainer) throws XmlMappingException {
try {
javax.xml.bind.Marshaller marshaller = createMarshaller();
if (mimeContainer != null) {
marshaller.setAttachmentMarshaller(
new SwaRefAttachmentMarshaller(mimeContainer)
);
marshaller.marshal(graph, result);
} else {
super.marshal(graph, result, null);
}
} catch (JAXBException ex) {
throw convertJaxbException(ex);
}
}
};
marshaller.setPackagesToScan("my.package");
marshaller.setMtomEnabled(false);
return marshaller;
}
private class SwaRefAttachmentMarshaller extends AttachmentMarshaller {
private final MimeContainer mimeContainer;
private SwaRefAttachmentMarshaller(MimeContainer mimeContainer) {
this.mimeContainer = mimeContainer;
}
#Override
public String addMtomAttachment(DataHandler data, String elementNamespace, String elementLocalName) {
return null;
}
#Override
public String addMtomAttachment(byte[] data, int offset, int length, String mimeType, String elementNamespace, String elementLocalName) {
return null;
}
#Override
public String addSwaRefAttachment(DataHandler data) {
String attachmentId = UUID.randomUUID().toString();
mimeContainer.addAttachment("<" + attachmentId + ">", data);
return "cid:" + attachmentId;
}
}
}

Error accessing Spring session data stored in Redis

In my REST controllers Spring project, I want to store Session information in Redis.
In my application.properties I have defined the following:
spring.session.store-type=redis
spring.session.redis.namespace=rdrestcore
com.xyz.redis.host=192.168.201.46
com.xyz.redis.db=0
com.xyz.redis.port=6379
com.xyz.redis.pool.min-idle=5
I also have enabled Http Redis Session with:
#Configuration
#EnableRedisHttpSession
public class SessionConfig extends AbstractHttpSessionApplicationInitializer
{}
I finally have a redis connection factory like this:
#Configuration
#EnableRedisRepositories
public class RdRedisConnectionFactory {
#Autowired
private Environment env;
#Value("${com.xyz.redis.host}")
private String redisHost;
#Value("${com.xyz.redis.db}")
private Integer redisDb;
#Value("${com.xyz.redis.port}")
private Integer redisPort;
#Value("${com.xyz.redis.pool.min-idle}")
private Integer redisPoolMinIdle;
#Bean
JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
if(redisPoolMinIdle!=null) poolConfig.setMinIdle(redisPoolMinIdle);
return poolConfig;
}
#Bean
JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory jedisConFactory = new JedisConnectionFactory();
if(redisHost!=null) jedisConFactory.setHostName(redisHost);
if(redisPort!=null) jedisConFactory.setPort(redisPort);
if(redisDb!=null) jedisConFactory.setDatabase(redisDb);
jedisConFactory.setPoolConfig(jedisPoolConfig());
return jedisConFactory;
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
final RedisTemplate< String, Object > template = new RedisTemplate();
template.setConnectionFactory( jedisConnectionFactorySpring());
template.setKeySerializer( new StringRedisSerializer() );
template.setValueSerializer( new GenericJackson2JsonRedisSerializer() );
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer( new GenericJackson2JsonRedisSerializer() );
return template;
}
}
With this configuration, the session information gets stored in Redis, but, it is serialized very strangely. I mean, the keys are readable, but the values stored are not (I query the information from a program called "Redis Desktop Manager")... for example... for a new session, I get a hash with key:
*spring:session:sessions:c1110241-0aed-4d40-9861-43553b3526cb*
and the keys this hash contains are: maxInactiveInterval, lastAccessedTime, creationTime, sessionAttr:SPRING_SECURITY_CONTEXT
but their values are all they coded like something similar to this:
\xAC\xED\x00\x05sr\x00\x0Ejava.lang.Long;\x8B\xE4\x90\xCC\x8F#\xDF\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xAC\x95\x1D\x0B\x94\xE0\x8B\x02\x00\x00xp\x00\x00\x01b$G\x88*
(for the creationTime key)
and if I try to access this information from code, with the redisTemplate, it rises an exception like this one:
Exception occurred in target VM: Cannot deserialize; nested exception is
org.springframework.core.serializer.support.SerializationFailedException:
Failed to deserialize payload. Is the byte array a result of
corresponding serialization for DefaultDeserializer?; nested exception
is java.io.StreamCorruptedException: invalid stream header: 73657373
org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is
org.springframework.core.serializer.support.SerializationFailedException:
Failed to deserialize payload. Is the byte array a result of
corresponding serialization for DefaultDeserializer?; nested exception
is java.io.StreamCorruptedException: invalid stream header: 73657373
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:82)
I think it is some kind of problem with the serialization/deserialization of the Spring session information, but I don't know what else to do to be able to control that.
Does anyone know what Im doing wrong?
Thank you
You're on the right track, your problem is serialization indeed. Try this configuration (configure your template with these serializers only):
template.setHashValueSerializer(new JdkSerializationRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setKeySerializer(new StringRedisSerializer());
template.setDefaultSerializer(new JdkSerializationRedisSerializer());

Define prefix root node for Jackson Serialization/Deserialization YAML document to POJO with Prefix

I found https://github.com/FasterXML/jackson-dataformat-yaml to deserialize/serialize YAML files. However, I'm having a hard time to deserialize/serialize the following:
I want to define a prefix to the actual document to be parsed as a POJO. Similar to a subtree of the document.
I want to define the POJO that represents the simple object representation instead of creating multiple objects.
The Error "Unrecognized field "spring" (class ConfigServerProperties), not marked as ignorable (one known property: "repos"])" is shown. But I don't know how to represent the prefix "spring.cloud.config.server.git" to be the root element of the POJO.
Document
spring:
cloud:
config:
server:
git:
repos:
publisher:
uri: 'https://github.company.com/toos/spring-cloud-config-publisher-config'
cloneOnStart: true
username: myuser
password: password
pullOnRequest: false
differentProperty: My Value
config_test_server_config:
uri: 'https://github.company.com/mdesales/config-test-server-config'
cloneOnStart: true
username: 226b4bb85aa131cd6393acee9c484ec426111d16
password: ""
completelyDifferentProp: this is a different one
For this document, the requirements are as follows:
* I want to define the prefix as "spring.cloud.config.server.git".
* I want to create a POJO that represents the object.
POJO
I created the following POJOs to represent this.
ConfigServerProperties: represents the top pojo containing the list of repos.
ConfigServerOnboard: represents each of the elements of the document.
Each properties are stored in a map, so that we can add as many different properties as possible.
Each class is as follows:
public class ConfigServerProperties {
private Map<String, ConfigServerOnboard> repos;
public void setRepos(Map<String, ConfigServerOnboard> repos) {
this.repos = repos;
}
public Map<String, ConfigServerOnboard> getRepos() {
return this.repos;
}
}
The second class is as follows:
public class ConfigServerOnboard {
private Map<String, String> properties;
public Map<String, String> getProperties() {
return properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
}
Deserialize
The deserialization strategy I tried is as follows:
public static ConfigServerProperties parseProperties(File filePath)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
JsonNodeFactory jsonNodeFactory = new JsonNodeFactory(false);
jsonNodeFactory.textNode("spring.cloud.config");
// tried to use this attempting to get the prefix
mapper.setNodeFactory(jsonNodeFactory);
ConfigServerProperties user = mapper.readValue(filePath, ConfigServerProperties.class);
return user;
}
Error Returned
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "spring" (class com.company.platform.config.onboarding.files.config.model.ConfigServerProperties), not marked as ignorable (one known property: "repos"])
at [Source: /tmp/config-server-onboards.yml; line: 3, column: 3] (through reference chain: com.company.platform.config.onboarding.files.config.model.ConfigServerProperties["spring"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:62)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:834)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1094)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1470)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1448)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:282)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2740)
at com.company.platform.config.onboarding.files.config.model.ConfigServerProperties.parseProperties(ConfigServerProperties.java:37)
at com.company.platform.config.onboarding.files.config.model.ConfigServerProperties.main(ConfigServerProperties.java:42)
Edit 1: Looking for a possible SpringBoot Solution
I'm open to solutions using SpringBoot's ConfigurationProperties("spring.cloud.config.server.git"). That way, we could have the following:
#ConfigurationProperties("spring.cloud.config.server.git")
public class Configuration {
private Map<String, Map<String, String>> repos = new LinkedHashMap<String, new HashMap<String, String>>();
// getter/setter
}
Questions
How to set the root element of the document?
Deserialization must read the document and produce instances of the POJOs.
Serialization must produce the same document with updated values.
I had to come up with the following:
Create 6 classes, each of them with the property required for the prefix "spring.cloud.config.server.git"
SpringCloudConfigSpring.java
SpringCloudConfigCloud.java
SpringCloudConfigConfig.java
SpringCloudConfigServer.java
SpringCloudConfigGit.java
The holder of all of them is SpringCloudConfigFile.java.
The holder and all the classes have a reference to the next property, which has a reference to the next, etc, with their own setter/getter methods as usual.
public class SpringCloudConfigSpring {
private SpringCloudConfigCloud cloud;
public SpringCloudConfigCloud getCloud() {
return cloud;
}
public void setCloud(SpringCloudConfigCloud cloud) {
this.cloud = cloud;
}
}
Implemented the representation of the map easily.
The last one I used the reference of a TreeMap to keep the keys sorted, another map to represent any property that may be added, without changing the representation.
public class SpringCloudConfigGit {
TreeMap<String, Map<String, Object>> repos;
public TreeMap<String, Map<String, Object>> getRepos() {
return repos;
}
public void setRepos(TreeMap<String, Map<String, Object>> repos) {
this.repos = repos;
}
}
Results
Creating the verification as follows:
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
File config = new File("/tmp/config-server-onboards.yml");
SpringCloudConfigFile props = ConfigServerProperties.parseProperties(config);
props.getSpring().getCloud().getConfig().getServer().getGit().getRepos().forEach((appName, properties) -> {
System.out.println("################## " + appName + " #######################3");
System.out.println(properties);
if (appName.equals("github_pages_reference")) {
properties.put("name", "Marcello");
properties.put("cloneOnStart", true);
}
System.out.println("");
});
saveProperties(new File(config.getAbsoluteFile().getParentFile(), "updated-config-onboards.yml"), props);
}
The output is as follows:
################## config_onboarding #######################3
{uri=https://github.company.com/servicesplatform-tools/spring-cloud-config-onboarding-config, cloneOnStart=true, username=226b4bb85aa131cd6393acee9c484ec426111d16, password=, pullOnRequest=false}
################## config_test_server_config #######################3
{uri=https://github.company.com/rlynch2/config-test-server-config, cloneOnStart=true, username=226b4bb85aa131cd6393acee9c484ec426111d16, password=, pullOnRequest=false}
################## github_pages_reference #######################3
{uri=https://github.company.com/servicesplatform-tools/spring-cloud-config-reference-service-config, cloneOnStart=true, username=226b4bb85aa131cd6393acee9c484ec426111d16, password=, pullOnRequest=false}
There are obvious improvements required:
I'd like to have a solution with a single class;
I'd like to have a an ObjetMapper method that specifies the "subtree" of the YAML object tree that I'd like to parse.
Maybe a more sophisticated SpringBoot-like #ConfigurationProperties("spring.cloud.config.server.git") would help.
Helper methods for loading and saving the state of these instances.
Load Method
public static SpringCloudConfigFile parseProperties(File filePath)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
SpringCloudConfigFile file = mapper.readValue(filePath, SpringCloudConfigFile.class);
return file;
}
Save Properties
public static void saveProperties(File filePath, SpringCloudConfigFile file) throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
mapper.writeValue(filePath, file);
}
File Saved
It maintained the sorted keys as implemented.

spring mvc processing xml with relative path to dtd

My webservice receives an xml from a third-party source, which contains a !DOCTYPE declaration. Therefore I must use the second method in my controller to parse the xml document, the first one gives me this exception:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not unmarshal to [class com.example.MeterBusXml]: null; nested exception is javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 48; DOCTYPE is disallowed when the feature "http://apache.org/xml/features/disallow-doctype-decl" set to true.]
I have no control over the application which posts the xml, so I must adapt my webservice to parse it with the dtd.
My question is, what is the spring framework's way of injecting the EntityResolver into every XMLReader instance?
#RestController
public class MeterBusDataController {
#RequestMapping (
consumes = APPLICATION_XML_VALUE,
method = POST,
path = "/meterbus1"
)
public void method1(#RequestBody MeterBusXml xml) {
System.out.println(xml);
}
#RequestMapping(
method = POST,
path = "/meterbus2"
)
public void method2(HttpServletRequest rq) throws IOException, ParserConfigurationException, SAXException, JAXBException {
JAXBContext jc = newInstance(MeterBusXml.class);
Unmarshaller um = jc.createUnmarshaller();
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(true);
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
xr.setEntityResolver(new EntityResolver() {
#Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return new InputSource(new StringReader(""));
}
});
BufferedReader reader = rq.getReader();
InputSource inputSource = new InputSource(reader);
SAXSource saxSource = new SAXSource(xr, inputSource);
MeterBusXml xml = (MeterBusXml)um.unmarshal(saxSource);
System.out.println(xml);
}
}
See the following document for an example of the mbus.xml I'm trying to unmarshal.
http://prevodniky.sk/products/product_EthMBus_common/download/Ethernet_converters_exports_v1_02_EN.pdf
I've found the root of the problem. First I tried to create and configure a Jaxb2Marshaller bean, but that did not work out. Then I realized, I need a HttpMessageConverter, so I had to override the extendMessageConverters method in the WebMvcConfigurerAdapter class, and set the required properties on Jaxb2RootElementHttpMessageConverter. This message converter does not use a Jaxb2Marshaller, but it's internal workings are very similar.
setSupportDtd(true) is required, to force the parser to accept the !DOCTYPE declaration.
setProcessExternalEntities(false) is required, because if this property is false, then the converter uses a blank EntityResolver, just as I did in method2.
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void extendMessageConverters(List<HttpMessageConverter<Jaxb2RootElementHttpMessageConverter?>> converters) {
for (final Iterator<HttpMessageConverter<?>> iterator = converters.iterator(); iterator.hasNext();) {
HttpMessageConverter<?> next = iterator.next();
if (next instanceof Jaxb2RootElementHttpMessageConverter) {
Jaxb2RootElementHttpMessageConverter jaxbConverter = (Jaxb2RootElementHttpMessageConverter) next;
jaxbConverter.setProcessExternalEntities(false);
jaxbConverter.setSupportDtd(true);
}
}
}
}

Resources