Search code examples
javaspring-bootweb-servicesjaxbwsdl

JAXB cannot get the attached file from soap (WSDL) service


I need to call a SOAP service that returns an attachment.

The XSD that defines the "attached" data is

<xs:complexType name="transferableFileData">
    <xs:complexContent>
      <xs:extension base="tns:transferableFile">
        <xs:sequence>
          <xs:element name="fileData" type="xs:base64Binary" minOccurs="0"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

I am using this plugin in the pom.xml file to generate classes from the WSDL and XSD files

<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
    <version>0.15.1</version>
</plugin>

The autogenerated class is this

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "transferableFileData", propOrder = {
    "fileData"
})
public class TransferableFileData
    extends TransferableFile
{

    protected byte[] fileData;

    
    public byte[] getFileData() {
        return fileData;
    }

   
    public void setFileData(byte[] value) {
        this.fileData = value;
    }

}

The response is this one from the server:

Cache-Control: max-age=0
Cache-Control: no-cache
Cache-Control: no-store
Server-Timing: ak_p; desc="1705942361025_1611673733_335963847_52691_3577_46_71_-";dur=1
Server-Timing: origin; dur=520
Server-Timing: edge; dur=7
Server-Timing: cdn-cache; desc=MISS
Connection: keep-alive
Set-Cookie: LtpaToken2=Jss03JN+gXMYTd; Path=/; HttpOnly
Expires: Mon
Expires: 22 Jan 2024 16:52:41 GMT
Pragma: no-cache
Content-Length: 2912
Content-Language: en-US
Date: Mon
Date: 22 Jan 2024 16:52:41 GMT
Content-Type: Multipart/Related; boundary="----=_Part_11_2001319686.1705942360849"; type="application/xop+xml"; start-info="text/xml"
SOAPAction: ""
Accept: text/xml
------=_Part_11_2001319686.1705942360849
Content-Type: application/xop+xml; charset=utf-8; type="text/xml"
Content-Transfer-Encoding: binary
Content-ID: <0.f4ee8e08bed2d62c7571a5433a8dea327c0877600e72a88b@apache.org>

<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><ns2:downloadPendingFileResponse xmlns:ns2="http://iris.somewhere.cp,/web_services/external/downloadFile" xmlns:ns3="http://iris.somewhere.com/web_services/external/uploadFile"><downloadPendingFileResult><fileExchangeNo>1174649</fileExchangeNo><fileName>TEST00001_2024-01-22-18.46.08.00000_APA.zip</fileName><fileData><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:05ee8e08bed2d62c7571a5433a8dea327c0877600e72a88b@apache.org"/></fileData></downloadPendingFileResult></ns2:downloadPendingFileResponse></soapenv:Body></soapenv:Envelope>
------=_Part_11_2001319686.1705942360849
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <05ee8e08bed2d62c7571a5433a8dea327c0877600e72a88b@apache.org>

�������f��G��v�+p�,����
��K�ɁZt �K�>b�La���^m��_э���1$�t�dqV�A;�ف� F�K��   ��ކO�X![

My Java code goes like this:

if (response instanceof JAXBElement) {

                DownloadPendingFileResponse downloadPendingFileResponse = ((JAXBElement<DownloadPendingFileResponse>) response).getValue();
                if(downloadPendingFileResponse == null) {

and later

final TransferableFileData transferableData = response.getDownloadPendingFileResult();
...
byte[] bytes = transferableData.getFileData();

log.info("length {}", bytes.length);

However the length is always zero. It seems that I cannot get the file properly.

What I noticed is that the WSDL defines the fileData element as base64Binary and the POJO has them as byte[]. It seems that the data stream isn't unmarhsaled. Any idea how to resolve this issue?


Solution

  • I got the exact same XSD on a WSDL service

    <complexType name="myType">
        <sequence>
    ...
            <element name="myFile" type="xsd:base64Binary"/>
    ...
        </sequence>
    </complexType>
    

    This get generated as by the org.apache.cxf:cxf-codegen-plugin :

    @XmlElement(required = true)
    protected byte[] myFile;
    

    But in order to do the unmarshalling, I had to set a XSLT to jaxws:feature spring client in order to tell CXF to understand the href in the response (content of file transform.xsl as below) :

    <xsl:stylesheet version="1.0"
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" >
    
        <xsl:key name="multiref-by-id" match="multiRef" use="@id"/>
    
        <xsl:template match="/">
            <xsl:copy>
                <xsl:apply-templates select="@*|*"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="*[starts-with(@href, '#')]">
            <xsl:copy>
                <xsl:apply-templates select="@* |
                 key('multiref-by-id', substring-after(@href, '#'))/@* |
                key('multiref-by-id', substring-after(@href, '#'))/node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="@href[starts-with(., '#')] | multiRef[@id] | @soapenc:root"/>
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    And this is the bean defined in XML Spring Config :

        <bean id="xsltFeature" class="org.apache.cxf.feature.transform.XSLTFeature">
            <property name="inXSLTPath" value="transform.xsl" />
            <property name="outXSLTPath" value="transform.xsl" />
        </bean>
    
        <jaxws:client id="myService" serviceClass="my.web.Service" address="myurl">
            <jaxws:features>
                <ref bean="xsltFeature" />
            </jaxws:features>
        </jaxws:client>
    

    By doing so, I'm able to read the content of the attached file by the getter of myFile in Java without additional config.

    I think you can adapt this in order to make it work in your own application context.