ArticlesProjectsCredentialsAbout
soapxmlweb-services

SOAP Web Services: When XML Was Going to Save Enterprise Computing

·4 min read

SOAP Web Services: When XML Was Going to Save Enterprise Computing

By 1999 every enterprise software vendor was talking about web services. The promise was compelling: a universal integration standard built on HTTP and XML, replacing proprietary middleware like CORBA and DCOM. SOAP — the Simple Object Access Protocol — was the core of this vision.

At Motorola we integrated our NMS with an enterprise asset management system using SOAP. Here is what building a SOAP service looked like before frameworks made it invisible.

What SOAP Is

SOAP is a protocol for exchanging structured information between services using XML. A SOAP message is an XML document with a defined envelope structure:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:nms="http://motorola.com/nms/ws">

  <soap:Header>
    <!-- optional metadata: auth tokens, routing, transaction IDs -->
    <nms:Authentication>
      <nms:Username>integration</nms:Username>
      <nms:Token>a3f8c921d4</nms:Token>
    </nms:Authentication>
  </soap:Header>

  <soap:Body>
    <!-- the actual operation call -->
    <nms:GetDeviceStatus>
      <nms:DeviceIp>192.168.1.10</nms:DeviceIp>
    </nms:GetDeviceStatus>
  </soap:Body>

</soap:Envelope>

The server responds with another SOAP envelope:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:nms="http://motorola.com/nms/ws">
  <soap:Body>
    <nms:GetDeviceStatusResponse>
      <nms:Status>UP</nms:Status>
      <nms:Uptime>1234567</nms:Uptime>
      <nms:LastPolled>1999-09-08T14:32:00Z</nms:LastPolled>
    </nms:GetDeviceStatusResponse>
  </soap:Body>
</soap:Envelope>

SOAP ran over HTTP (or SMTP, or any transport, in theory — in practice almost always HTTP). The HTTP method was irrelevant; everything meaningful was in the XML body.

WSDL: Describing the Service

A WSDL (Web Services Description Language) file described what operations a service offered, what parameters they took, and what they returned. It was the IDL of the SOAP world:

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="NmsService"
             targetNamespace="http://motorola.com/nms/ws"
             xmlns:tns="http://motorola.com/nms/ws"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns="http://schemas.xmlsoap.org/wsdl/">

  <types>
    <xsd:schema targetNamespace="http://motorola.com/nms/ws">
      <xsd:element name="GetDeviceStatus">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="DeviceIp" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="GetDeviceStatusResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="Status"      type="xsd:string"/>
            <xsd:element name="Uptime"      type="xsd:long"/>
            <xsd:element name="LastPolled"  type="xsd:dateTime"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </types>

  <message name="GetDeviceStatusRequest">
    <part name="parameters" element="tns:GetDeviceStatus"/>
  </message>
  <message name="GetDeviceStatusResponse">
    <part name="parameters" element="tns:GetDeviceStatusResponse"/>
  </message>

  <portType name="NmsPortType">
    <operation name="GetDeviceStatus">
      <input  message="tns:GetDeviceStatusRequest"/>
      <output message="tns:GetDeviceStatusResponse"/>
    </operation>
  </portType>

  <binding name="NmsSoapBinding" type="tns:NmsPortType">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="GetDeviceStatus">
      <soap:operation soapAction="http://motorola.com/nms/GetDeviceStatus"/>
      <input>  <soap:body use="literal"/> </input>
      <output> <soap:body use="literal"/> </output>
    </operation>
  </binding>

  <service name="NmsService">
    <port name="NmsPort" binding="tns:NmsSoapBinding">
      <soap:address location="http://nms-primary:8080/soap/nms"/>
    </port>
  </service>
</definitions>

Handling a Request in Java (Without a Framework)

Before Axis and JAX-WS, handling SOAP in Java meant parsing XML manually:

import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.*;
import java.net.*;

public class SoapServlet extends HttpServlet {

    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws IOException {

        // Parse the incoming SOAP envelope
        Document doc = parseXml(req.getInputStream());

        // Extract the operation and parameters
        NodeList body = doc.getElementsByTagNameNS(
            "http://schemas.xmlsoap.org/soap/envelope/", "Body");
        Element bodyEl    = (Element) body.item(0);
        Element operation = (Element) bodyEl.getFirstChild();
        String  opName    = operation.getLocalName();

        String responseXml;
        if ("GetDeviceStatus".equals(opName)) {
            String ip = getTextContent(operation, "DeviceIp");
            DeviceStatus status = deviceRegistry.get(ip);
            responseXml = buildStatusResponse(status);
        } else {
            responseXml = buildFaultResponse("Unknown operation: " + opName);
        }

        res.setContentType("text/xml; charset=UTF-8");
        res.getWriter().write(responseXml);
    }

    private String buildStatusResponse(DeviceStatus s) {
        return "<?xml version="1.0"?>" +
               "<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"" +
               "               xmlns:nms="http://motorola.com/nms/ws">" +
               "<soap:Body><nms:GetDeviceStatusResponse>" +
               "<nms:Status>" + s.getStatus() + "</nms:Status>" +
               "<nms:Uptime>" + s.getUptime() + "</nms:Uptime>" +
               "</nms:GetDeviceStatusResponse></soap:Body></soap:Envelope>";
    }

    private String buildFaultResponse(String message) {
        return "<?xml version="1.0"?>" +
               "<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">" +
               "<soap:Body><soap:Fault>" +
               "<faultcode>soap:Client</faultcode>" +
               "<faultstring>" + message + "</faultstring>" +
               "</soap:Fault></soap:Body></soap:Envelope>";
    }
}

Why SOAP Lost to REST

Verbosity. A SOAP envelope for a simple status check was 800 bytes. The equivalent JSON over HTTP was 40 bytes.

Tooling complexity. WSDL files were hundreds of lines for modest APIs. Generating client stubs required specialised tools. Every interoperability issue traced back to someone's WSDL parser behaving differently.

Over-engineering. WS-Security, WS-Transaction, WS-ReliableMessaging — the WS-* stack grew into a cathedral of specifications that almost nobody used correctly.

REST showed a simpler path. GET /devices/192.168.1.10/status returning JSON is self-explanatory. It does not require a schema file, a stack of middleware, or a code generator. Roy Fielding's REST dissertation landed in 2000. By 2005 the industry had made its choice.

The SOAP era produced genuinely useful ideas — explicit contracts, schema validation, structured error responses. REST kept the ideas and discarded the ceremony.