I have webservices to receive and send attachments, and I'd like to use JAXB as marshaller, but so far it is not working right, as JAXB inlines any attachment coming in or going out in the message body as base64 strings, consuming a lot of memory and frequently leading to OutOfMemoryError. I'm outlining my setup and fix attempts, and hope someone can help me get it right.
Axiom is my choice over SAAJ as message factory, as I have to handle big attachments. I can successfully use JAXB as a marshaller for parameters and return types of endpoint methods, except when an attachment is involved (inline issue). This is my setup for it:
Webservices config XML:
<beans xmlns=...>
<context:component-scan base-package="com.example.webservice" />
<sws:annotation-driven />
<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.example.webservice.oxm.FileTestResponse</value>
<value>com.example.webservice.oxm.FileTestRequest</value>
</list>
</property>
<property name="mtomEnabled" value="true"/>
</bean>
<bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
<property name="payloadCaching" value="true"/>
<property name="attachmentCaching" value="true"/>
</bean>
<sws:dynamic-wsdl id="fileTest" portTypeName="fileTest" locationUri="/webservice/fileTest/" targetNamespace="http://example.com/webservice/definitions" >
<sws:xsd location="/WEB-INF/fileTest.xsd" />
</sws:dynamic-wsdl>
</beans>
Part of my XSD:
<!-- I generate the marshalling classes with XJB, and using
xmime:expectedContentTypes it correctly creates mtomData field
with DataHandler type instead of byte[] -->
<xs:element name="fileTestRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="mtomData" type="xs:base64Binary"
xmime:expectedContentTypes="application/octet-stream"/>
</xs:sequence>
</xs:complexType>
</xs:element>
My endpoint class:
package com.example.webservice;
import ...
@Endpoint
@Namespace(prefix = "s", uri = FileTestEndpoint.NAMESPACE)
public class FileTestEndpoint {
public static final String NAMESPACE = "http://example.com/webservice/schemas";
@PayloadRoot(localPart = "fileTestRequest", namespace = NAMESPACE)
@ResponsePayload
public FileTestResponse fileTest(@RequestPayload FileTestRequest req) throws IOException {
// req.getMtomData() and do something with it
FileTestResponse resp = new FileTestResponse();
DataHandler dataHandler = new DataHandler(new ByteArrayDataSource("my file download data".getBytes(), "text/plain"));
resp.setData(dataHandler);
return resp;
}
}
So, this code works, but not as it would with attachments. I'm searching for some time for a workign solution:
- Spring Forums: correct MTOM handling? : suggests extending some Axiom classes, but the code changed considerably ever since (2008), and I couldn't get it working;
- Spring Forums: Response-Attachment/Saaj/Jaxb : possible cause by a JVM bug fixed in 1.6u14, which is the one used by my Weblogic version (plus it didn't work for the topic creator);
- Spring Forums: sending large attachments with Spring-WS client : someone solved using Axis2 directly bypassing Spring WS, which is not the point;
- Stackoverflow: Spring-WS webservice with MTOM attachement - Hello world test : same issue as mine, 2 weeks old, no answers;
Apparently the best help came from this other SO question I posted about this inlining problem happening with WSS4J. Blaise Doughan said that I'd need AttachmentMarshaller
and AttachmentUnmarshaller
set on the (un)marshaller to get it handled properly, like he posted in his blog.
So, I'm assuming attachment marshallers are the key to solve this issue.
To set them on the (un)marshaller, I saw no other way but extend Jaxb2Marshaller
and overide initJaxbMarshaller
and initJaxbUnmarshaller
, setting attachment marshallers (I copied Doughan's code for them) on the given (un)marshaller.
But my own Jaxb2Marshaller
is not being used, not even when setting it manually to sws:annotation-driven
:
<sws:annotation-driven marshaller="jaxb2Marshaller" unmarshaller="jaxb2Marshaller"/>
<bean id="jaxb2Marshaller" class="com.example.webservice.MyJaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.example.webservice.oxm.FileTestResponse</value>
<value>com.example.webservice.oxm.FileTestRequest</value>
</list>
</property>
<property name="mtomEnabled" value="true"/>
</bean>
This marshaller class is created but never used, I don't know why, so I still couldn't test if AttachmentMarshaller
s can solve the issue.
This is all I can say for now. There are quite a few approaches to try:
- find out why
MyJaxb2Marshaller
is being ignored, probably the easiest; - fix JAXB attachment inlining some other way if
AttachmentMarshaller
s won't solve it, and I don't know what that would be; - replace JAXB with other marshaller, that works just as well (mainly Axiom support, possibly WSS4J).
I'm on a long time on this issue, and I must be missing the obvious solution. Any help is most welcome.
Thanks!
Library versions:
- Spring 3.1.0 (core, beans, oxm and such)
- Spring WS 2.1.0 (core and Spring XML)
- StAX2 2.1
- WoodSToX 3.2.9 (wstx)
- JAXB 2.2.5-2 (API+impl)
- Apache Axiom 1.2.13 (API+impl+c14n+dom)
- Apache Mime4j 0.7.2 (core)
App server is Oracle 11g R1 Patchset 1 with Java 1.6.0u14.