Search code examples
javaxmlsoapwsdlsoap-client

SOAP Request in JAVA: Cannot add fragments which contain elements which are in the SOAP namespace


i am currently writing a standalone soap request/response application in Java and i am using the following project for it: https://gist.github.com/kdelfour/b2a449a1bac23e3baec8

I am using this sample WSDL for developing the code: https://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl

From the above WSDL i am trying to invoke the operation: LatLonListCityNames which has the below soap request:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ndf="https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl">
   <soapenv:Header/>
   <soapenv:Body>
      <ndf:LatLonListCityNames soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <displayLevel xsi:type="xsd:integer">1</displayLevel>
      </ndf:LatLonListCityNames>
   </soapenv:Body>
</soapenv:Envelope>

This is my code below:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * This is an example of a simple SOAP Client class to send request body to a
 * SOAP Server.
 *
 * Useful when you want to test a SOAP server and you don't want to generate all
 * SOAP client class from the WSDL.
 *
 * @author kdelfour
 */
public class ASimpleSOAPClient {

    // Default logger
    private static final Logger LOG = Logger.getLogger(ASimpleSOAPClient.class);

    // The SOAP server URI
    private String uriSOAPServer;
    // The SOAP connection
    private SOAPConnection soapConnection = null;

    // If you want to add namespace to the header, follow this constant
    private static final String PREFIX_NAMESPACE = "ndf";
    private static final String NAMESPACE = "https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl";

    /**
     * A constructor who create a SOAP connection
     *
     * @param url
     *            the SOAP server URI
     */
    public ASimpleSOAPClient(final String url) {
        this.uriSOAPServer = url;

        try {
            createSOAPConnection();
        } catch (UnsupportedOperationException | SOAPException e) {
            LOG.error(e);
        }
    }

    /**
     * Send a SOAP request for a specific operation
     *
     * @param xmlRequestBody
     *            the body of the SOAP message
     * @param operation
     *            the operation from the SOAP server invoked
     * @return a response from the server
     * @throws SOAPException
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public String sendMessageToSOAPServer(String xmlRequestBody, String operation) throws SOAPException, SAXException, IOException,
            ParserConfigurationException {

        // Send SOAP Message to SOAP Server
        final SOAPElement stringToSOAPElement = stringToSOAPElement(xmlRequestBody);
        final SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(stringToSOAPElement, operation), uriSOAPServer);

        // Print SOAP Response
        LOG.info("Response SOAP Message : " + soapResponse.toString());
        return soapResponse.toString();
    }

    /**
     * Create a SOAP connection
     *
     * @throws UnsupportedOperationException
     * @throws SOAPException
     */
    private void createSOAPConnection() throws UnsupportedOperationException,
            SOAPException {

        // Create SOAP Connection
        SOAPConnectionFactory soapConnectionFactory;
        soapConnectionFactory = SOAPConnectionFactory.newInstance();
        soapConnection = soapConnectionFactory.createConnection();
    }

    /**
     * Create a SOAP request
     *
     * @param body
     *            the body of the SOAP message
     * @param operation
     *            the operation from the SOAP server invoked
     * @return the SOAP message request completed
     * @throws SOAPException
     */
    private SOAPMessage createSOAPRequest(SOAPElement body, String operation)
            throws SOAPException {

        final MessageFactory messageFactory = MessageFactory.newInstance();
        final SOAPMessage soapMessage = messageFactory.createMessage();
        final SOAPPart soapPart = soapMessage.getSOAPPart();

        // SOAP Envelope
        final SOAPEnvelope envelope = soapPart.getEnvelope();
        envelope.addNamespaceDeclaration(PREFIX_NAMESPACE, NAMESPACE);

        // SOAP Body
        final SOAPBody soapBody = envelope.getBody();
        soapBody.addChildElement(body);

        // Mime Headers
        final MimeHeaders headers = soapMessage.getMimeHeaders();
        LOG.info("SOAPAction : " + uriSOAPServer + operation);
        headers.addHeader("SOAPAction", uriSOAPServer + operation);

        soapMessage.saveChanges();

        /* Print the request message */
        LOG.info("Request SOAP Message :" + soapMessage.toString());
        return soapMessage;
    }

    /**
     * Transform a String to a SOAP element
     *
     * @param xmlRequestBody
     *            the string body representation
     * @return a SOAP element
     * @throws SOAPException
     * @throws SAXException
     * @throws IOException
     * @throws ParserConfigurationException
     */
    private SOAPElement stringToSOAPElement(String xmlRequestBody)
            throws SOAPException, SAXException, IOException,
            ParserConfigurationException {

        // Load the XML text into a DOM Document
        final DocumentBuilderFactory builderFactory = DocumentBuilderFactory
                .newInstance();
        builderFactory.setNamespaceAware(true);
        final InputStream stream = new ByteArrayInputStream(
                xmlRequestBody.getBytes());
        final Document doc = builderFactory.newDocumentBuilder().parse(stream);

        // Use SAAJ to convert Document to SOAPElement
        // Create SoapMessage
        final MessageFactory msgFactory = MessageFactory.newInstance();
        final SOAPMessage message = msgFactory.createMessage();
        final SOAPBody soapBody = message.getSOAPBody();

        // This returns the SOAPBodyElement that contains ONLY the Payload
        return soapBody.addDocument(doc);
    }

    public static void main(String[] args) throws SOAPException, SAXException, IOException, ParserConfigurationException {
        String url = "https://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl";
        ASimpleSOAPClient soapClient = new ASimpleSOAPClient(url);
        String xmlMessage = "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ndf=\"https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl\">\r\n" + 
                "   <soapenv:Header/>\r\n" + 
                "   <soapenv:Body>\r\n" + 
                "      <ndf:LatLonListCityNames soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" + 
                "         <displayLevel xsi:type=\"xsd:integer\">1</displayLevel>\r\n" + 
                "      </ndf:LatLonListCityNames>\r\n" + 
                "   </soapenv:Body>\r\n" + 
                "</soapenv:Envelope>";
        String operation = "LatLonListCityNames";
        soapClient.sendMessageToSOAPServer(xmlMessage, operation);

    }
}

I have tried permutations and combination of xmlMessage but keep ending up with different errors. One of the variations that i tried was:

String xmlMessage = "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">\r\n" + 
                "   <soapenv:Header/>\r\n" + 
                "   <soapenv:Body>\r\n" + 
                "      <ndf:LatLonListCityNames soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" + 
                "         <displayLevel xsi:type=\"xsd:integer\">1</displayLevel>\r\n" + 
                "      </ndf:LatLonListCityNames>\r\n" + 
                "   </soapenv:Body>\r\n" + 
                "</soapenv:Envelope>";

Here, i removed the ndf prefix element from the Envelop. This gave me a different error, which is as follows:

[Fatal Error] :4:98: The prefix "ndf" for element "ndf:LatLonListCityNames" is not bound.
Exception in thread "main" org.xml.sax.SAXParseException; lineNumber: 4; columnNumber: 98; The prefix "ndf" for element "ndf:LatLonListCityNames" is not bound.
    at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
    at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
    at javax.xml.parsers.DocumentBuilder.parse(Unknown Source)
    at ASimpleSOAPClient.stringToSOAPElement(ASimpleSOAPClient.java:159)
    at ASimpleSOAPClient.sendMessageToSOAPServer(ASimpleSOAPClient.java:78)
    at ASimpleSOAPClient.main(ASimpleSOAPClient.java:183)

I am not sure what am i doing wrong here. So any pointers here?

Thank you in advance

Also i have tried the below code:

import java.io.*;
import java.net.*;

public class AnotherSoapClient {
    public static void main(String[] args) throws Exception {

        String SOAPUrl      = "https://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl";
        String xmlFile2Send = "<soapenv:Envelope xmlns:xsi=\\\"http://www.w3.org/2001/XMLSchema-instance\\\" xmlns:xsd=\\\"http://www.w3.org/2001/XMLSchema\\\" xmlns:soapenv=\\\"http://schemas.xmlsoap.org/soap/envelope/\\\" xmlns:ndf=\\\"https://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl\\\">\\r\\n\" + \r\n" + 
                "               \"   <soapenv:Header/>\\r\\n\" + \r\n" + 
                "               \"   <soapenv:Body>\\r\\n\" + \r\n" + 
                "               \"      <ndf:LatLonListCityNames soapenv:encodingStyle=\\\"http://schemas.xmlsoap.org/soap/encoding/\\\">\\r\\n\" + \r\n" + 
                "               \"         <displayLevel xsi:type=\\\"xsd:integer\\\">1</displayLevel>\\r\\n\" + \r\n" + 
                "               \"      </ndf:LatLonListCityNames>\\r\\n\" + \r\n" + 
                "               \"   </soapenv:Body>\\r\\n\" + \r\n" + 
                "               \"</soapenv:Envelope>\";\r\n" + 
                "        String operation = \"LatLonListCityNames";

          String SOAPAction = "LatLonListCityNames";

        // Create the connection where we're going to send the file.
        URL url = new URL(SOAPUrl);
        URLConnection connection = url.openConnection();
        HttpURLConnection httpConn = (HttpURLConnection) connection;

        // Open the input file. After we copy it to a byte array, we can see
        // how big it is so that we can set the HTTP Cotent-Length
        // property. (See complete e-mail below for more on this.)

        byte[] b = xmlFile2Send.getBytes();

        // Set the appropriate HTTP parameters.
        httpConn.setRequestProperty( "Content-Length",
                                     String.valueOf( b.length ) );
        httpConn.setRequestProperty("Content-Type","text/xml; charset=utf-8");
        httpConn.setRequestProperty("SOAPAction",SOAPAction);
        httpConn.setRequestMethod( "POST" );
        httpConn.setDoOutput(true);
        httpConn.setDoInput(true);

        // Everything's set up; send the XML that was read in to b.
        OutputStream out = httpConn.getOutputStream();
        out.write( b );    
        out.close();

        // Read the response and write it to standard out.

        InputStreamReader isr =
            new InputStreamReader(httpConn.getInputStream());
        BufferedReader in = new BufferedReader(isr);

        String inputLine;

        while ((inputLine = in.readLine()) != null)
            System.out.println(inputLine);

        in.close();
    }

  // copy method from From E.R. Harold's book "Java I/O"
  public static void copy(InputStream in, OutputStream out) 
   throws IOException {

    // do not allow other threads to read from the
    // input or write to the output while copying is
    // taking place

    synchronized (in) {
      synchronized (out) {

        byte[] buffer = new byte[256];
        while (true) {
          int bytesRead = in.read(buffer);
          if (bytesRead == -1) break;
          out.write(buffer, 0, bytesRead);
        }
      }
    }
  } 
}

For which i get the below error:

Exception in thread "main" java.net.ConnectException: Connection timed out: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.connect(Unknown Source)
    at sun.security.ssl.BaseSSLSocketImpl.connect(Unknown Source)
    at sun.net.NetworkClient.doConnect(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.<init>(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.New(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source)
    at AnotherSoapClient.main(AnotherSoapClient.java:41)

I am able to access the url via browser or soap-ui. I am not sure what i happening here

Thank you again.


Solution

  • I was not able to fix the first code, however the second code I was able to make it work by adding the proxy configuration by following this answer How do I make HttpURLConnection use a proxy? Since I am able to work through my blocker, I am marking this question as answered