Search code examples
javaxmljaxbcxfjax-ws

How to make changes in marshalled output in Java web service client


I am trying to interact with a third party web service, who requires me to send a security token as a part of each request. The token is a node by itself, and I acquire it from the response of an initial call. The web service endpoint is dotNet, and I have a Java client.

Apparently, the server side expects me to send the security token exactly like it was provided to me: literally the same string: so it won't do if its content has a different size, order, etc.

So, in SoapUI, everything works fine. There is a token in the response of the initial 'startSession' call, which I copy into the request of a next call.

But in Java (I tried JAX-WS and CXF generated code, both rely on JAXB) it doesn't work. I receive the token as an object after it is unmarshalled, and I use this object in the next call. When marshalled and send, it is missing a namespace attribute in a subnode. The server side says it won't continue because the token is incorrect. So, by using JAXB outbound logical handler functionality, I am able to add the missing namespace without any problems in the DOM source (I was also able to achieve this with a CXF interceptor).

The problem now is, that the attributes, when marshalled, are ordered in such a way that the result still not matches the provided token as it was before it was unmarshalled. Alhough it should not matter, the order of these attributes is crucial.

I have no idea how to solve this, unless it is possible to actually modify the output XML string. I even tried a dirty hack by removing all attributes from the subnode and replacing them with one attribute that visually looks the same; but then the outer two double quotes become single quotes...

I hope anyone has an idea. Because I have none.

Cheers.

UPDATE: I should have mentioned that the attributes in question are namespace(d) attributes. The node should look like this:

<HawanedoSessionInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c">

However, after using outbound JAXB handler to add the missing xmlns="...", my result looks like this:

<HawanedoSessionInfo xmlns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

In the HawanedoSessionInfo class, I used XmlType.proporder and @XmlAttribute like so:

@XmlType(name = "HawanedoSessionInfo", propOrder = {
"xsd",
"xsi",
"xmlns", 

and some other non-attribute sub-elements..

    private String xsd;
private String xsi;
private String xmlns;

    @XmlAttribute(ns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c")
public String getXsd() {
    return xsd;
}

public void setXsd(final String xsd) {

    this.xsd = xsd;
}

@XmlAttribute(ns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c")
public String getXsi() {
    return xsi;
}

public void setXsi(final String xsi) {

    this.xsi = xsi;
}

@XmlAttribute
public String getXmlns() {
    return xmlns;
}

public void setXmlns(final String xmlns) {

    this.xmlns = xmlns;
}

So apparently the proporder option does not help in this case?

UPDATE 2:

Like I wrote in my answer, it now works. Based on this LINK, in the HawanedoSessionInfo class I added:

@XmlCustomizer(HawanedoSessionInfoCustomizer.class)

I created the customizer class exactly as described in the linked page, and I added the jaxb.properties.

So I did two things:

1) I added my attributes to (the top of the already existing) propOrder attribute. I added the attributes as instance variables and created the getters/setters. I annotated the getters with XmlAttribute.

2) I implemented the XmlCustomizer solution.

Now comes the strange part. According to Fiddler, the order of the attributes is still not changed! But I must stress that this is now working, ONLY after implementing the Customizer. What is happening here? :)


Solution

  • So in principle you cannot control order of attributes in a standard way, but ....

    1. Depending on jaxb /java version the order can be determined by alphabetical order of the names, the order of declaration. You could try in your code if a) moving the fields around changes anything, b) renaming the fields (the XMLAttribute than have to map to original name).

    If you are lucky, it will work. But of course it is a hack and will work till next jaxb/java update.

    1. The JAXB providers (the actuall implementation can have extra features), that can be used to customized the marshalling process). For example I found that: https://community.oracle.com/thread/977397 abut eclipselink.

    2. I am sure there was a way of intercepting the soap body before it is send or governing the data serialization before it is send. I can think how it was called but try to google the jaxws client customization. If you capture the whole soap message simple xslt transforamation could fix the attributes order.

    I feel your pain. The whole point of using xml, jaxws and such is to make our life easier and then someone providers decide not to follow standards and you end up with a mess that you were trying to clean for few days. Good luck and maybe try to contact xml gurus from Eclipse Moxy