I’m using the apache cxf library as our SOAP client to transmit SOAP requests to a specific endpoint. Unfortunately it seems that this endpoint do have some trouble to correctly parse the data if it comes in one line (which is pretty much the standard). I’ve written a simple Test-Client which sends RAW Strings to that endpoint and with that I was able to find out that the requests could be processed if the SOAP-Message is pretty printed. If I send the very same request in one line the server respond with a HTTP/1.1 500 Internal Server Error.
I’ve already submitted a bug report but I’m afraid that the receiving company will ask me to transmit my data pretty printed.
Is that possible with apache cxf?
Advertisement
Answer
I’ve found another question which helped me to find a solution for my needs.
Going from that post I was able to adopt the following code which can do what I need
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; import java.io.StringReader; import java.io.StringWriter; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.message.Message; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; public class PrettyPrintingOutInterceptor extends AbstractPhaseInterceptor<Message> { public PrettyPrintingOutInterceptor(int indent) { super(Phase.PRE_STREAM); addBefore(SoapPreProtocolOutInterceptor.class.getName()); } @Override public void handleMessage(Message message) throws Fault { boolean isOutbound = false; isOutbound = message == message.getExchange().getOutMessage() || message == message.getExchange().getOutFaultMessage(); if (isOutbound) { OutputStream os = message.getContent(OutputStream.class); CachedStream cs = new CachedStream(); message.setContent(OutputStream.class, cs); message.getInterceptorChain().doIntercept(message); try { cs.flush(); CachedOutputStream csnew = (CachedOutputStream) message .getContent(OutputStream.class); // get current payload String soapMessage = IOUtils.toString(csnew.getInputStream()); // manipulate payload soapMessage = prettyPrint(soapMessage, 3); // Write new data into the OutputStream from above ByteArrayInputStream bin = new ByteArrayInputStream(soapMessage.getBytes()); CachedOutputStream.copyStream(bin, os, 1024); os.flush(); } catch (IOException | TransformerException e) { // error handling } finally { // Important! Close streams! try { cs.close(); } catch (IOException e) { } try { os.close(); } catch (IOException e) { } } } } private String prettyPrint(String xml, int indent) throws TransformerException { Source xmlInput = new StreamSource(new StringReader(xml)); StringWriter stringWriter = new StringWriter(); StreamResult xmlOutput = new StreamResult(stringWriter); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "" + indent); transformer.transform(xmlInput, xmlOutput); return xmlOutput.getWriter().toString(); } private class CachedStream extends CachedOutputStream { public CachedStream() { super(); } protected void doFlush() throws IOException { currentStream.flush(); } protected void doClose() throws IOException { } protected void onWrite() throws IOException { } } }
and adding an object from this class to the client with client.getOutInterceptors().add(new PrettyPrintingOutInterceptor());