0

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.

Wilbur Dsouza
  • 127
  • 1
  • 10
  • You say: "I have tried permutations and combination of xmlMessage but keep ending up with different errors. Any pointers here?" We need to know some examples of your error(s) from the first part of your question. – Greg T. Jul 15 '18 at 07:32
  • @GregT. I have updated my question which some additional info with respect to this request. – Wilbur Dsouza Jul 15 '18 at 07:48

2 Answers2

0

There are multiple potential issues going on with your code. In the future you should stick to one particular problem and/or error in your posts so we can answer clearly.

Implementing SOAP API’s with WSDL can be tricky because there are so many elements involved. Regarding your first error example: “prefix ndf ... is not bound” - This error means you are violating the XML Schema defined for this SOAP API. You can’t do that. You either need to provide/bind the ndf prefix or adjust your schema. This will fix your XML parser error. This problem is common and you can find more info here:

xml schema validation error "prefix is not bound"

The main problem with your code causing the ndf error: This is coming from you removing the ndf schema declaration from:

<soapenv:Envelope ...

in your xmlMessage. If you remove it from there, you can't have it in the tag names, such as where you have:

<ndf:LatLonListCityNames soapenv:encodingStyle ...

You can either tag everything to the proper namespace (ndf, xsd, etc.) or remove them. You could also instruct your code to ignore namespace problems/validation in the parser, but then you can cause problems with your app in other places.

There is more information about this in the provided link. You would also benefit to web search "Java xml namespaces" and do some further reading on this.

Regarding your second error example: You’re receiving a timeout error when your code is trying to connect to the SSL/TLS server at Weather.gov. Are you getting that error consistently? If so, you need to increase your logger verbosity so you can see what’s happening.

Greg T.
  • 115
  • 1
  • 6
  • Hi Greg.T, thank you for the response. I am aware that i am not following the schema in my request is flawed, but i am not sure where it has gone wrong in my code. I will go through the link and see if i can find something that can point me towards the answer. For the second error, i think the proxy is the issue. I tried via direct internet access and it works flawlessly, must look into how to set up proxy for httpconnetion module. Anyhow thanks again for the pointers. – Wilbur Dsouza Jul 15 '18 at 08:41
  • OK, I just edited my answer to give more detailed information about what you're doing wrong regarding your ndf error. Hope that helps. – Greg T. Jul 16 '18 at 01:31
0

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

Wilbur Dsouza
  • 127
  • 1
  • 10