Question

I have a soap web service and I want to log the response and the request as xml strings. I know about interceptors but for some reason, that does not matter, I can't use them. How can I construct the xml request (and response) from the object that the xml messages get unmarshaled to? How can I remarshal the object to a xml string?

Thank you for your help.

Was it helpful?

Solution

In order to Marshal/Unmarshall, you need to create a class 'hierarchy' identical to that of the XML document. So, for example:

<tests>
    <lib-data> 
         <library>Library</library> 
         <count>64018</count> 
         <test-data> 
            <title>Book title1</title> 
            <book>Book Name1</book> 
            <count>5</count> 
         </test-data> 
         <test-data> 
            <title>Book title2</title> 
            <book>Book Name3</book> 
            <count>5</count> 
         </test-data> 
         <test-data> 
            <title>Book title3</title> 
            <book>Book Name3</book> 
            <count>4</count> 
         </test-data> 
    </lib-data>
</tests>

I would create a 'tests' class with an single attribute - an ArrayList of 'lib-data' classes.

Within each lib-data class I would then create a String field called library, an int field called count, and an ArrayList of 'test-data' classes.

For each test data class I'd then create String title and book attributes and an int count attribute.

From there, just add the correct @XmlRootElement (before each class) and @XmlElement (before each attribute) annotation. After that point you can use Jaxb to do the actual marshaling and unmarshalling.

Edit:

Sorry, your question was very open ended so I was unsure as to how much you've created.

XML annotation is pretty straight forward, it would look like this for the 'tests' class:

@XmlRootElement
public class Tests {

    @XmlElement(name="lib-data")
    ArrayList<LibData> libData;

    public Tests()
    {
        this.libData = new ArrayList<LibData>()
    }


    public ArrayList<LibData> getLibData() {
        return this.libData;
    }

    public void setLibData(ArrayList<LibData> libData) {
        this.libData = libData;
    }
}

I would also create a 'MarshalHandler' class which hides the implementation details of marshalling and unmarshalling:

public class MarshalHandler {

    private JAXBContext jc;
    private Unmarshaller unmarshaller;
    private Marshaller marshaler;

    public MarshalHandler(){
        jc = JAXBContext.newInstance(Tests.class);
        unmarshaller = jc.createUnmarshaller();
        marshaler = jc.createMarshaller();
    }


    public Test unmarshall(File f){
        return (Tests) unmarshaller.unmarshal(f);
    }

    public void marshal(Tests t, String fileLoc){
        OutputStream os = new FileOutputStream(fileLoc);
        marshaler.marshal(t, os);
        os.flush();
        os.close();
    }
}

(I haven't compiled or tested that code. It likely contains a mistake or two and definitely does not account for exceptions)

Now, how you handle the details of your MarshalHandler are totally up to you and will be influenced by the overall design of your program. For example, you may want your marshal method to return a StreamResult instead of saving to a predetermined fileLoc. Maybe you're grabbing this SOAP response and saving it as a byte array, in which case it would be nice to be able to pass that array directly to your unmarshall method. But, it's hard to say without more details.

OTHER TIPS

You are aware that using an interceptor is by far the easiest way to do this? Logging interceptors are exactly for doing this.

That said, if you cannot do things the easy way then you have to try something else. For example, you can have your own JAXBContext and use that to write the values out for logging purposes; it won't be exactly the same, but it will be pretty close.

// I assume you've already JAXB-annotated these classes anyway.
JAXBContext c = JAXBContext.newInstance(YourInputPOJO.class, YourOutputPOJO.class);

public YourOutputPOJO theMethod(YourInputPOJO input) {
    logMessage("in", "theMethod", input);
    YourOutputPOJO output = ...;
    logMessage("out", "theMethod", output);
    return output;
}

private void logMessage(String dir, String method, Object message) {
    Marshaller m = c.createMarshaller();
    Writer out = new StringWriter();
    m.marshal(message, out);
    log.info("message " + dir + ":" + method + "(" + out + ")");
}

Don't make the JAXBContext on every call — it's quite expensive — but making the Marshaller isn't too bad. Don't share the Marshaller between threads, but after creation the JAXBContext is thread-safe. And I've totally omitted the exception handling…

CXF has built in feature for logging, it just takes a xml entry

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:cxf="http://cxf.apache.org/core"
      xsi:schemaLocation="
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <cxf:bus>
        <cxf:features>
            <cxf:logging/>
        </cxf:features>
    </cxf:bus> 
</beans>

Ref : http://cxf.apache.org/docs/configuration.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top