Search code examples
javaweb-servicesjax-wscdata

Add CDATA on request's string parameter using only JAX-WS


I have a jax-ws client ganerated with CXF

The request have a string-parameter (MGRequest) that contains an xml, all work's but the generated request is like this:

<S:Body>
   <ns5:MGRequest>&lt;mytag&gt;hello&lt;/mytag&gt;</ns5:MGRequest>
</S:Body>

I need to generate the body like:

<S:Body>
   <ns5:MGRequest><![CDATA[<mytag>hello</mytag>]]></ns5:MGRequest>
</S:Body>

(because i can't control the server..)

The client is like a standard jax-ws:

@WebService(name = "ServiceSoap")
@XmlSeeAlso({ ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface ServiceSoap {

  @WebMethod(operationName = "ProcessMessage")
  @WebResult(name = "MGResponse")
  public String processMessage(
      @WebParam(partName = "input", name = "MGRequest") String input);
}

And i call like this:

Service client = new Service(url);
client.setHandlerResolver(HandlerFactory.build(new LoggerHandler()));

ServiceSoap service = client.getServiceSoap();

String msgToSend = JaxbUtil.jaxbObjToString(xmlObj, false);
String response = service.processMessage(msgToSend);

I have tried adding @XmlJavaTypeAdapter(CDataAdapter.class) before @WebParam but the result was:

<S:Body>
   <ns5:MGRequest>&lt;![CDATA[&lt;mytag&gt;hello&lt;/mytag&gt;]]&gt;</ns5:MGRequest>
</S:Body>

Where CDataAdapter:

public class CDataAdapter extends XmlAdapter<String, String> {

    @Override
    public String marshal(String v) throws Exception {
        return "<![CDATA[" + v + "]]>";
    }

    @Override
    public String unmarshal(String v) throws Exception {
        return v;
    }
}

Any idea how to archive that? Thanks


Solution

  • After a working night i've found the solution: adding a javax.xml.ws.handler.Handler to the client like this:

    client.setHandlerResolver(HandlerFactory.build(new LoggerHandler(), new CDataHandler()));
    

    where my HandlerFactory build a Handler:

    public static HandlerResolver build(final Handler... handlers) {
    
        return new HandlerResolver() {
          @Override
          public List<Handler> getHandlerChain(PortInfo portInfo) {
            List<Handler> handlerChain = new ArrayList<Handler>();
    
            if (handlers != null) {
              for (Handler handler : handlers) {
                handlerChain.add(handler);
              }
            }
            return handlerChain;
          }
        };
      }
    

    import javax.xml.namespace.QName;
    import javax.xml.soap.Node;
    import javax.xml.soap.SOAPMessage;
    import javax.xml.ws.handler.MessageContext;
    import javax.xml.ws.handler.soap.SOAPHandler;
    import javax.xml.ws.handler.soap.SOAPMessageContext;
    
    public class CDataHandler implements SOAPHandler<SOAPMessageContext> {
    
      @Override
      public void close(MessageContext context) {
      }
    
      @Override
      public boolean handleMessage(SOAPMessageContext soapMessage) {
        try {
          SOAPMessage message = soapMessage.getMessage();
          boolean isOutboundMessage = (Boolean) soapMessage
              .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    
          // is a request?
          if (isOutboundMessage) {
            // build a CDATA NODE with the text in the root tag
            Node cddata = (Node) message.getSOAPPart().createCDATASection(
                message.getSOAPBody().getFirstChild().getTextContent());
    
            // add the CDATA's node at soap message
            message.getSOAPBody().getFirstChild().appendChild(cddata);
    
            // remove the text tag with the raw text that will be escaped
            message.getSOAPBody().getFirstChild()
                .removeChild(message.getSOAPBody().getFirstChild().getFirstChild());
    
          }
    
        } catch (Exception ex) {
          // fail
        }
        return true;
      }
    
      @Override
      public boolean handleFault(SOAPMessageContext soapMessage) {
        return true;
      }
    
      @Override
      public Set<QName> getHeaders() {
        return Collections.EMPTY_SET;
      }
    }
    

    This is a simple class, i had only one tag with text, but in more complex scenario you can take the necessary action navigating the DOM.