I have the following issue: I need to talk to an old SOAP service, and that one requires me to send a request object where a large amount of data is directly in the SOAP message body, like so:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<MyRequest xmlns="http://my.company.com/xsd/portals/v4_0">
<documentList xmlns="">
<binaryData>
<blob>
VeryLongDataBlobInHere
</blob>
<extension>pdf</extension>
</binaryData>
</documentList>
</MyRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The problem is, Spring automatically turns that into an attachment like this if MTOM is enabled:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<MyRequest xmlns="http://my.company.com/xsd/portals/v4_0">
<documentList xmlns="">
<binaryData>
<blob>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:3be5f4d8-50ed-4f88-8e50-778f6cc70c74%40null"/>
</blob>
<extension>pdf</extension>
</binaryData>
</documentList>
</MyRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
By contrast, if MTOM is disabled, the blob is empty like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<MyRequest xmlns="http://my.company.com/xsd/portals/v4_0">
<documentList xmlns="">
<binaryData>
<blob/>
<extension>pdf</extension>
</binaryData>
</documentList>
</MyRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I have tried various approaches to solve this, including messing with the data types, and trying to adjust the properties of the marshaller in order to increase the MTOM threshold, but nothing I tried worked. Here's my marshaller configuration:
@Configuration
public class Jaxb2MarshallerConfig {
@Bean
public Jaxb2Marshaller myMarshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.company.project.xsd.some_portal.v4_0");
marshaller.setMtomEnabled(true);
return marshaller;
}
}
And here's where the binary data is built and assigned:
private BinaryData buildBinaryData(byte[] documentData) {
BinaryData binaryData = new BinaryData();
byte[] encodedData = Base64.getEncoder().encode(documentData);
DataHandler dataHandler = new DataHandler(encodedData, "application/pdf");
binaryData.setBlob(dataHandler);
binaryData.setExtension("pdf");
return binaryData;
}
BinaryData meanwhile is a generated class built from an WSDL, so I can't change anything in there. But here's how it looks:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BinaryData", propOrder = {
"blob",
"extension"
})
public class BinaryData {
@XmlElement(required = true)
@XmlMimeType("application/octet-stream")
protected DataHandler blob;
@XmlElement(required = true)
protected String extension;
[...]
}
Finally, here's how I sent this whole mess:
@Component
@Log4j2
public class MySoapClient extends WebServiceGatewaySupport {
private final WebServiceTemplate template;
public MySoapClient (
MyServiceProperties properties,
Jaxb2Marshaller marshaller
) {
setMarshaller(marshaller);
setUnmarshaller(marshaller);
setDefaultUri(properties.getTargetUrl());
template = getWebServiceTemplate();
}
@Override
public void sendDocuments(MyRequest request) {
try {
template.marshalSendAndReceive(request);
} catch (Exception e) {
log.error(e, e.getCause());
throw new RuntimeException(e);
}
}
}
My best guess is that I somehow need to increase the MTOM threshold, but I have no idea how. I tried messing around with marshaller.setMarshallerProperties(), but nothing there worked.
Does anyone have any idea of how I can get the marshaller to write the blob inline? Or is the problem somewhere else?
Update
I now created a github repository with the minimum required code, as well as a test to reproduce the issue and check for the desired behavior:
https://github.com/KiraResari/jaxb2-marshalling
If you like, you can check it out and try to get the test to pass somehow.
com.sun.xml.ws.encoding.mtom.thresholdpropterties in Jaxb2MarshallerConfig availablemarshaller.setMarshallerProperties(new Properties());marshaller.getMarshallerProperties().put("com.sun.xml.ws.encoding.mtom.threshold", "1000000");setMarshallerPropertiestakes a map, so here's what I tried:marshaller.setMarshallerProperties(Map.of("com.sun.xml.ws.encoding.mtom.threshold", 1000000));(also, tried writing the value as a String, like"1000000". Unfortunately, both of these cause ajakarta.xml.bind.PropertyException: name: com.sun.xml.ws.encoding.mtom.threshold value: 1000000. I looked into the marshaller properties, and from what I could find that one takes only a very limited set of properties special to JAXB and not the underlying marshaller (at least as far as I can tell).