Web Service - SOAP

Content:

  1. Introduction
  2. Concepts
  3. Products
  4. Code first
    1. How it works
    2. Server
    3. Client with generated client stub
    4. Client fully hand coded
    5. Compatibility matrix
  5. WSDL first
    1. How it works
    2. WSDL
    3. Server
    4. Client

Introduction:

Web services are common in most applications today.

Web services provide synchroneous request-response style integration between applications in a convenient way:

Any developer need to have a basic understanding of web services.

SOAP was the de facto standard for web services from 2000 until around 2010 where RESTful web services took over (partly driven by the evolution of smartphones).

But SOAP is still used today. Especially in the hevay enterprise environemnts. The places where they use WebSphere, WebLogic/AquaLogic, BizTalk, Tibco etc..

If asynchroneous integration is required then look at message queues. Read more here.

Concepts:

Web Services:

Web services exist in two types:

RPC style
RPC (Remote Procedure Call) style mean that the web service expose operations with arguments and return values.
RESTful style
REST (Representational state transfer) style mean that the web service expose CRUD for resources.

There are multiple standards for RPC style web services, including:

This article will cover SOAP web services.

For XML-RPC read here.

For RESTful style web services read here.

SOAP web services:

SOAP (Simple Object Access Protocol) is a XML message format for RPC style web services. SOAP was created 1999-2000.

A SOAP message consist of a SOAP envelope containing a SOAP header and a SOAP body.

Usually the transport protocol is HTTP(S). But in theory SMTP and Message Queue can also be used as transport.

SOAP comes in many different flavors:

This article will not go deeper into these flavors. Usually this is hidden for application developers by the frameworks used.

A key concept in SOAP is WSDL (Web Services Description Language). WSDL is a XML format that describe a SOAP API. Basically the WSDL describe the functions, arguments, return values and the above details about SOAP flavor.

There exist a whole bunch of WS standards on top of SOAP including:

WS-Security
Define encryption and signing for SOAP web services
WS-ReliableMessaging
Define reliable delivery of messages for SOAP web services
WS-Coordination and WS-Atomic Transaction
Define atomic transactions for SOAP web services

Products:

Java:

Java defines the following web service standards:

JAX-RPC
JAX-RPC (Java API for XML-based RPC) was defined in 2002. Obsolete and replaced by JAX-WS.
JAX-WS
JAX-WS (Java API for XML Web Services) became standard in 2006 with Java SE 1.6 and Java EE 5.
JAX-RS
JAX-RS (Java API for RESTful Web Services) was defined in 2008 and became standard in 2009 with Java EE 6.

The standards have multiple implementations:

The example code here will use Apache Axis 1.x for JAX-RPC and Apache CXF for JAX-WS (JBoss builtin for server side and standalone for client side, and also Java SE builtin and Axis 2.x for client).

.NET:

.NET comes with two seperate web service frameworks:

old style
.NET 1.0 in 2002 introduced web service support. ASP.NET supports .asmx files for defining server side of SOAP web services and wsdl utility provide the ability to generate stub code for client side.
WCF
.NET 3.0 in 2006 introduced WCF (Windows Communication Foundation). WCF provides a formal framework for defining web services server side both SOAP and RESTful. And WCF also provides a svcutil utility to generate stub code for client side.

PHP:

PHP comes with a Soap extension that just needs to be loaded.

Python:

There are several SOAP libraries available for Python:

The example code here will use Zeep.

Zeep is installed with "pip install Zeep".

Code first:

How it works:

Code first approach works like:

  1. Service developer create service code
  2. When service code is deployed then service host generate WSDL
  3. Client developer use tool to generate client stub from WSDL and call that from client application

For static client language:

Code first - static client language

For dynamic client language:

Code first - dynamic client language

Server:

For JAX-RPC service Axis 1.x is used.

The process to create a web service in Axis 1.x is:

  1. Create normal Java class with methods representing web service with operations
  2. Deploy Axis web application with above class and supporting classes embedded to servlet container.
  3. Create WSDD (web service deployment descriptor)
  4. Send WSDD to Axis admin servlet.

T1.java:

package ws.soap;

public class T1 {
    private int f1;
    private String f2;
    public T1() {
        this(0, "");
    }
    public T1(int f1, String f2) {
        this.f1 = f1;
        this.f2 = f2;
    }
    public int getF1() {
        return f1;
    }
    public void setF1(int f1) {
        this.f1 = f1;
    }
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
}

Test.java:

package ws.soap;

import java.util.ArrayList;
import java.util.List;

public class Test {
    public T1 getOne(int f1) {
        // dummy implementation
        return new T1(f1, "getOne");
    }
    public T1[] getAll() {
        // dummy implementation
        T1[] res = new T1[3];
        res[0] = new T1(1, "getAll #1");
        res[1] = new T1(2, "getAll #2");
        res[2] = new T1(3, "getAll #3");
        return res;
    }
    public boolean save(int f1, String f2) {
        // dummy implementation
        return f1 > 0 && f2.length() > 0;
    }
    public boolean saveObject(T1 o) {
        // dummy implementation
        return o.getF1() > 0 && o.getF2().length() > 0;
    }
    public boolean delete(int f1) {
        // dummy implementation
        return f1 > 0;
    }
}

Test.wsdd:

<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    <service name="TestService" provider="java:RPC">
        <parameter name="wsdlTargetNamespace" value="http://soap.ws"/>
        <parameter name="className" value="ws.soap.Test"/>
        <parameter name="allowedMethods" value="*"/>
        <parameter name="scope" value="application"/>
        <parameter name="sendMultiRefs" value="false"/> <!-- increases portability -->
        <beanMapping xmlns:ns="http://soap.ws"
                     qname="ns:T1"
                     languageSpecificType="java:ws.soap.T1"/>
                     encodingStyle=""/>
        <arrayMapping xmlns:ns="http://soap.ws"
                      qname="ns:ArrayOfT1"
                      languageSpecificType="java:ws.soap.T1[]"
                      innerType="ns:T1"
                      encodingStyle=""/>
      </service>
</deployment>

I will strongly recommend against using JAX-RPC and Axis 1.x. It is using obsolete web service standards that are known to create problems for clients. And the deployment procedure is also funky.

For JAX-WS service JBoss builtin CXF is used.

The process to create a JAX-WS web service within a Java EE container is very simple:

  1. Create normal Java class with methods representing web service with operations
  2. Mark class with @WebService annotation and methods with @WebMethod annoation
  3. Deploy

T1.java:

package ws.soap;

import javax.xml.bind.annotation.XmlType;

@XmlType(name="T1")
public class T1 {
    private int f1;
    private String f2;
    public T1() {
        this(0, "");
    }
    public T1(int f1, String f2) {
        this.f1 = f1;
        this.f2 = f2;
    }
    public int getF1() {
        return f1;
    }
    public void setF1(int f1) {
        this.f1 = f1;
    }
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
}

Test.java:

package ws.soap;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

@WebService(targetNamespace="http://soap.ws")
@SOAPBinding(style=SOAPBinding.Style.RPC)
public class Test {
    @WebMethod
    public T1 getOne(int f1) {
        // dummy implementation
        return new T1(f1, "getOne");
    }
    @WebMethod
    public T1[] getAll() {
        // dummy implementation
        T1[] res = new T1[3];
        res[0] = new T1(1, "getAll #1");
        res[1] = new T1(2, "getAll #2");
        res[2] = new T1(3, "getAll #3");
        return res;
    }
    @WebMethod
    public boolean save(int f1, String f2) {
        // dummy implementation
        return f1 > 0 && f2.length() > 0;
    }
    @WebMethod
    public boolean saveObject(T1 o) {
        // dummy implementation
        return o.getF1() > 0 && o.getF2().length() > 0;
    }
    @WebMethod
    public boolean delete(int f1) {
        // dummy implementation
        return f1 > 0;
    }
}

@SOAPBinding(style=SOAPBinding.Style.RPC) is actually not default, but I believe it is very common.

The process to create an ASP.NET .asmx web service is:

  1. Create normal C# class with methods representing web service with operations
  2. Mark class with [WebService] attribute and methods with [WebMethod] attribute
  3. Create .asmx file referencing class

WsSoap1.cs:

using System;
using System.Web.Services;

namespace Ws.Soap1
{
    public class T1
    {
        public T1() : this(0, "")
        {
        }
        public T1(int f1, string f2)
        {
            this.f1 = f1;
            this.f2 = f2;
        }
        public int f1 { get; set; }
        public string f2 { get; set; }
    }
    [WebService(Name="TestService",Namespace="http://soap.ws")]
    public class Test
    {
        [WebMethod]
        public T1 getOne(int f1)
        {
            // dummy implementation
            return new T1(f1, "getOne");
        }
        [WebMethod]
        public T1[] getAll()
        {
            // dummy implementation
            T1[] res = new T1[3];
            res[0] = new T1(1, "getAll #1");
            res[1] = new T1(2, "getAll #2");
            res[2] = new T1(3, "getAll #3");
            return res;
        }
        [WebMethod]
        public bool save(int f1, string f2)
        {
            // dummy implementation
            return f1 > 0 && f2.Length > 0;
        }
        [WebMethod]
        public bool saveObject(T1 o)
        {
            // dummy implementation
            return o.f1 > 0 && o.f2.Length > 0;
        }
        [WebMethod]
        public bool delete(int f1)
        {
            // dummy implementation
            return f1 > 0;
        }
    }
}

TestService.asmx:

<%@ WebService Class="Ws.Soap1.Test" %>

Even though WCF has replaced .asmx web services, then I would still pick it for a SOAP web service. It is much simpler than WCF.

The process to create an WCF SOAP web service is:

  1. Create normal C# interface and class with methods representing web service with operations
  2. Mark data classes with [DataContract] and [DataMember] attributes
  3. Mark interface with [ServiceContract] and [OperationContract] attributes
  4. Mark class with [ServiceBehavior] attribute
  5. Create .svc file referencing class
  6. Define web service in web.config

WsSoap2.cs:

using System;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace Ws.Soap2
{
    [DataContract(Namespace="http://soap.ws")]
    public class T1
    {
        public T1() : this(0, "")
        {
        }
        public T1(int f1, string f2)
        {
            this.f1 = f1;
            this.f2 = f2;
        }
        [DataMember]
        public int f1 { get; set; }
        [DataMember]
        public string f2 { get; set; }
    }
    [ServiceContract(Name="Test",Namespace="http://soap.ws")]
    public interface ITest
    {
        [OperationContract]
        T1 getOne(int f1);
        [OperationContract]
        T1[] getAll();
        [OperationContract]
        bool save(int f1, string f2);
        [OperationContract]
        bool saveObject(T1 o);
        [OperationContract]
        bool delete(int f1);
    }
    [ServiceBehavior(Namespace = "http://soap.ws/")]
    public class Test : ITest
    {
        public T1 getOne(int f1)
        {
            // dummy implementation
            return new T1(f1, "getOne");
        }
        public T1[] getAll()
        {
            // dummy implementation
            T1[] res = new T1[3];
            res[0] = new T1(1, "getAll #1");
            res[1] = new T1(2, "getAll #2");
            res[2] = new T1(3, "getAll #3");
            return res;
        }
        public bool save(int f1, string f2)
        {
            // dummy implementation
            return f1 > 0 && f2.Length > 0;
        }
        public bool saveObject(T1 o)
        {
            // dummy implementation
            return o.f1 > 0 && o.f2.Length > 0;
        }
        public bool delete(int f1)
        {
            // dummy implementation
            return f1 > 0;
        }
    }
}

Test.svc:

<%@ServiceHost Service="Ws.Soap2.Test"%>

web.config fragment:

<configuration>
...
    <system.serviceModel>
        <services>
            <service behaviorConfiguration="TestBehavior" name="Ws.Soap2.Test">
                <endpoint name="Test" address="/" binding="basicHttpBinding" contract="Ws.Soap2.ITest" bindingNamespace="http://soap.ws"/>
            </service>
        </services>
        <behaviors>
            <serviceBehaviors>
                <behavior name="TestBehavior">
                    <serviceMetadata httpGetEnabled="true"/>
                </behavior>
            </serviceBehaviors>
        </behaviors> 
    </system.serviceModel>
...    
</configuration>

WCF is a very powerful framework. It supports multiple styles (SOAP, RESTful) and it supports multiple protocols (HTTP/HTTPS, TCP socket, MQ) and it supports multiple host models (ASP.NET, standalone program). But it also comes with significant compelxity. Which is why I would go for .asmx for SOAP web services og WEB API for RESTful web services.

Since document style is creating some problems for clients then switching to RPC style is relevant. This can be done via the [XmlSerializerFormat] attribute.

WsSoap2Alt.cs:

using System;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace Ws.Soap2Alt
{
    [DataContract(Namespace="http://soap.ws")]
    public class T1
    {
        public T1() : this(0, "")
        {
        }
        public T1(int f1, string f2)
        {
            this.f1 = f1;
            this.f2 = f2;
        }
        [DataMember]
        public int f1 { get; set; }
        [DataMember]
        public string f2 { get; set; }
    }
    [ServiceContract(Name="Test",Namespace="http://soap.ws")]
    [XmlSerializerFormat(Style=OperationFormatStyle.Rpc,Use=OperationFormatUse.Literal)]
    public interface ITest
    {
        [OperationContract]
        T1 getOne(int f1);
        [OperationContract]
        T1[] getAll();
        [OperationContract]
        bool save(int f1, string f2);
        [OperationContract]
        bool saveObject(T1 o);
        [OperationContract]
        bool delete(int f1);
    }
    [ServiceBehavior(Namespace = "http://soap.ws/")]
    public class Test : ITest
    {
        public T1 getOne(int f1)
        {
            // dummy implementation
            return new T1(f1, "getOne");
        }
        public T1[] getAll()
        {
            // dummy implementation
            T1[] res = new T1[3];
            res[0] = new T1(1, "getAll #1");
            res[1] = new T1(2, "getAll #2");
            res[2] = new T1(3, "getAll #3");
            return res;
        }
        public bool save(int f1, string f2)
        {
            // dummy implementation
            return f1 > 0 && f2.Length > 0;
        }
        public bool saveObject(T1 o)
        {
            // dummy implementation
            return o.f1 > 0 && o.f2.Length > 0;
        }
        public bool delete(int f1)
        {
            // dummy implementation
            return f1 > 0;
        }
    }
}

TestAlt.svc:

<%@ServiceHost Service="Ws.Soap2Alt.Test"%>

Client:

The client stub is generated and the code can be called just like any other code.

Usually it is very easy to call the client stub code.

Note that the specific exposed API in the generated client stub usually vary depending on service technology and generator technology involved.

Axis 1.x comes with a wsdl2java tool that can generate client stub code from WSDL.

The exposed API in the generated client stub varies a little.

Here comes test client code.

For Java JAX-RPC:

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;

import ws.soap.T1;
import ws.soap.Test; // modified
import ws.soap.TestServiceLocator; //modified

public class TestClientOldJ {
    public static void main(String[] args) throws RemoteException, ServiceException {
        TestServiceLocator factory = new TestServiceLocator(); // modified
        Test soap = factory.getTestService();  // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        T1[] all = soap.getAll();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

For Java JAX-WS:

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;

import ws.soap.T1;
import ws.soap.Test; // modified
import ws.soap.TestServiceLocator; // modified

public class TestClientNewJ {
    public static void main(String[] args) throws RemoteException, ServiceException {
        TestServiceLocator factory = new TestServiceLocator(); // modified
        Test soap = factory.getTestPort(); // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        T1[] all = soap.getAll();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

For .NET ASMX:

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;

import ws.soap.T1;
import ws.soap.TestServiceSoap; // modified
import ws.soap.TestServiceLocator; // modified

public class TestClientOldDN {
    public static void main(String[] args) throws RemoteException, ServiceException {
        TestServiceLocator factory = new TestServiceLocator(); // mmodified
        TestServiceSoap soap = factory.getTestServiceSoap(); // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        T1[] all = soap.getAll();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

For .NET WCF:

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;

import ws.soap.T1;
import ws.soap.Test_PortType ; // modified
import ws.soap.Test_ServiceLocator; // modified

public class TestClientNewDN {
    public static void main(String[] args) throws RemoteException, ServiceException {
        Test_ServiceLocator factory = new Test_ServiceLocator(); // modified
        Test_PortType  soap = factory.getTest();  // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        T1[] all = soap.getAll();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

For .NET WCF alternative:

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;

import ws.soap.T1;
import ws.soap.Test_PortType ; // modified
import ws.soap.Test_ServiceLocator; // modified

public class TestClientNewDNAlt {
    public static void main(String[] args) throws RemoteException, ServiceException {
        Test_ServiceLocator factory = new Test_ServiceLocator(); // modified
        Test_PortType  soap = factory.getTestAlt();  // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        T1[] all = soap.getAll();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

To build use (%1 - WSDL URL, %2 = client class):

java -cp C:\Apache\axis-1_4\lib\* org.apache.axis.wsdl.WSDL2Java %1
javac -cp C:\Apache\axis-1_4\lib\* ws\soap\*.java
javac -cp .;C:\Apache\axis-1_4\lib\* %2.java
java -cp .;C:\Apache\axis-1_4\lib\* %2

Java SE comes with a wsimport tool that can generate client stub code from WSDL.

CXF comes with a wsdl2java tool that can generate client stub code from WSDL.

Axis 2.x comes with a wsdl2java tool that can generate client stub code from WSDL.

The exposed API in the generated client stub varies a little.

Note that CXF wsdl2java does not work well with many of the WSDL used here. Also note that Axis 2.x stubs are significant different from the other two.

In my opinion Axis 2.x stubs are different in a bad way.

Here comes test client code.

For Java JAX-WS:

import java.util.List;

import ws.soap.T1;
import ws.soap.Test; // modified
import ws.soap.TestService; // modified

public class TestClientNewJ {
    public static void main(String[] args) {
        TestService factory = new TestService(); // modified
        Test soap = factory.getPort(Test.class); // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        List<T1> all = soap.getAll().getItem(); // modified
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

For .NET ASMX:

import java.util.List;

import ws.soap.T1;
import ws.soap.TestServiceSoap; // modified
import ws.soap.TestService; // modified

public class TestClientOldDN {
    public static void main(String[] args) {
        TestService factory = new TestService(); // modified
        TestServiceSoap soap = factory.getTestServiceSoap(); // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        List<T1> all = soap.getAll().getT1(); // modified
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

For .NET WCF:

import java.util.List;

import ws.soap.T1;
import ws.soap.Test; // modified
import ws.soap.Test_Service; // modified

public class TestClientNewDN {
    public static void main(String[] args) {
        Test_Service factory = new Test_Service(); // modified
        Test soap = factory.getPort(Test.class); // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2().getValue());
        List<T1> all = soap.getAll().getT1(); // modified
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2().getValue());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

For .NET WCF alternative:

import java.util.List;

import ws.soap.T1;
import ws.soap.Test; // modified
import ws.soap.Test_Service; // modified

public class TestClientNewDNAlt {
    public static void main(String[] args) {
        Test_Service factory = new Test_Service(); // modified
        Test soap = factory.getPort(Test.class); // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        List<T1> all = soap.getAll().getT1(); // modified
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

To build with Java SE use (%1 - WSDL URL, %2 = client class):

wsimport -keep %1
javac -cp . %2.java
java -cp . %2

To build with CXF use (%1 - WSDL URL, %2 = client class):

call C:\Apache\apache-cxf-3.2.5\bin\wsdl2java -aer -client %1
javac -cp . %2.java
java -cp . %2

For Java JAX-WS with Axis 2.x in ADB mode:

import ws.soap.TestServiceStub;
import ws.soap.TestServiceStub.T1;

public class TestClientADBNewJ {
    public static void main(String[] args) throws Exception {
        TestServiceStub soap = new TestServiceStub();
        TestServiceStub.GetOne getone = new TestServiceStub.GetOne();
        getone.setArg0(123);
        T1 o = soap.getOne(getone).get_return();
        System.out.println(o.getF1() + " " + o.getF2());
        TestServiceStub.GetAll getall = new TestServiceStub.GetAll();
        T1[] all = soap.getAll(getall).get_return().getItem();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        TestServiceStub.Save save = new TestServiceStub.Save();
        save.setArg0(123);
        save.setArg1("ABC");
        System.out.println(soap.save(save).get_return());
        TestServiceStub.SaveObject saveo = new TestServiceStub.SaveObject();
        saveo.setArg0(o);
        System.out.println(soap.saveObject(saveo).get_return());
        TestServiceStub.Delete del = new TestServiceStub.Delete();
        del.setArg0(123);
        System.out.println(soap.delete(del).get_return());
    }
}

For .NET ASMX with Axis 2.x in ADB mode:

import ws.soap.TestServiceStub;
import ws.soap.TestServiceStub.T1;

public class TestClientADBOldDN {
    public static void main(String[] args) throws Exception {
        TestServiceStub soap = new TestServiceStub();
        TestServiceStub.GetOne getone = new TestServiceStub.GetOne();
        getone.setF1(123);
        T1 o = soap.getOne(getone).getGetOneResult();
        System.out.println(o.getF1() + " " + o.getF2());
        TestServiceStub.GetAll getall = new TestServiceStub.GetAll();
        T1[] all = soap.getAll(getall).getGetAllResult().getT1();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        TestServiceStub.Save save = new TestServiceStub.Save();
        save.setF1(123);
        save.setF2("ABC");
        System.out.println(soap.save(save).getSaveResult());
        TestServiceStub.SaveObject saveo = new TestServiceStub.SaveObject();
        saveo.setO(o);
        System.out.println(soap.saveObject(saveo).getSaveObjectResult());
        TestServiceStub.Delete del = new TestServiceStub.Delete();
        del.setF1(123);
        System.out.println(soap.delete(del).getDeleteResult());
    }
}

For .NET WCF with Axis 2.x in ADB mode:

import java.util.List;

import ws.soap.TestStub;
import ws.soap.TestStub.T1;

public class TestClientADBNewDN {
    public static void main(String[] args) throws Exception {
        TestStub soap = new TestStub();
        TestStub.GetOne getone = new TestStub.GetOne();
        getone.setF1(123);
        T1 o = soap.getOne(getone).getGetOneResult();
        System.out.println(o.getF1() + " " + o.getF2());
        TestStub.GetAll getall = new TestStub.GetAll();
        T1[] all = soap.getAll(getall).getGetAllResult().getT1();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        TestStub.Save save = new TestStub.Save();
        save.setF1(123);
        save.setF2("ABC");
        System.out.println(soap.save(save).getSaveResult());
        TestStub.SaveObject saveo = new TestStub.SaveObject();
        saveo.setO(o);
        System.out.println(soap.saveObject(saveo).getSaveObjectResult());
        TestStub.Delete del = new TestStub.Delete();
        del.setF1(123);
        System.out.println(soap.delete(del).getDeleteResult());
    }
}

To build with Axis 2.x in ADB mode use (%1 - WSDL URL, %2 = client class):

set AXIS2_HOME=C:\Apache\axis2-1.7.8
call %AXIS2_HOME%\bin\wsdl2java -uri %1 -d adb
call ant compile.src
javac -cp .;build\classes;%AXIS2_HOME%\lib\* %2.java
java -cp .;build\classes;%AXIS2_HOME%\lib\* %2

.NET comes with a wsdl tool that can generate client stub code from WSDL.

The exposed API in the generated client stub varies a little.

Here comes test client code.

For all except WCF:

using System;

using ws.soap;

public class Program
{
    public static void Main(string[] args) 
    {
        TestService soap = new TestService(); // modified
        T1 o = soap.getOne(123);
        Console.WriteLine(o.f1 + " " + o.f2);
        T1[] all = soap.getAll();
        foreach(T1 one in all)
        {
            Console.WriteLine(one.f1 + " " + one.f2);
        }
        Console.WriteLine(soap.save(123, "ABC"));
        Console.WriteLine(soap.saveObject(o));
        Console.WriteLine(soap.delete(123));
     }
}

For .NET WCF:

using System;

using ws.soap;

public class Program
{
    public static void Main(string[] args) 
    {
        Test soap = new Test(); // modified
        T1 o = soap.getOne(123, true); // funky
        Console.WriteLine(o.f1 + " " + o.f2);
        T1[] all = soap.getAll();
        foreach(T1 one in all)
        {
            Console.WriteLine(one.f1 + " " + one.f2);
        }
        //  funky:
        bool specified;
        bool res;
        soap.save(123, true, "ABC", out res, out specified);
        Console.WriteLine(res);
        soap.saveObject(o, out res, out specified);
        Console.WriteLine(res);
        soap.delete(123, true, out res, out specified);
        Console.WriteLine(res);
     }
}

For .NET WCF alternative:

using System;

using ws.soap;

public class Program
{
    public static void Main(string[] args) 
    {
        Test soap = new Test(); // modified
        T1 o = soap.getOne(123);
        Console.WriteLine(o.f1 + " " + o.f2);
        T1[] all = soap.getAll();
        foreach(T1 one in all)
        {
            Console.WriteLine(one.f1 + " " + one.f2);
        }
        Console.WriteLine(soap.save(123, "ABC"));
        Console.WriteLine(soap.saveObject(o));
        Console.WriteLine(soap.delete(123));
     }
}

To build use (%1 - WSDL URL, %2 = client class):

wsdl /language:CS /out:TestService.cs /namespace:ws.soap %1
csc %2.cs TestService.cs
%2

.NET comes with a wsdl tool that can generate client stub code from WSDL.

The exposed API in the generated client stub varies a little.

Here comes test client code.

For all except .NET WCF:

Imports System

Imports ws.soap

Public Class Program
    Public Shared Sub Main(args As String())
        Dim soap As New TestService() ' modified
        Dim o As T1 = soap.getOne(123)
        Console.WriteLine(o.f1 & " " & o.f2)
        Dim all As T1() = soap.getAll()
        For Each one As T1 In all
            Console.WriteLine(one.f1 & " " & one.f2)
        Next
        Console.WriteLine(soap.save(123, "ABC"))
        Console.WriteLine(soap.saveObject(o))
        Console.WriteLine(soap.delete(123))
    End Sub
End Class

For .NET WCF:

Imports System

Imports ws.soap

Public Class Program
    Public Shared Sub Main(args As String())
        Dim soap As New Test() ' modified
        Dim o As T1 = soap.getOne(123, True) ' funky
        Console.WriteLine(o.f1 & " " & o.f2)
        Dim all As T1() = soap.getAll()
        For Each one As T1 In all
            Console.WriteLine(one.f1 & " " & one.f2)
        Next
        '  funky:
        Dim specified As Boolean
        Dim res As Boolean
        soap.save(123, True, "ABC", res, specified)
        Console.WriteLine(res)
        soap.saveObject(o, res, specified)
        Console.WriteLine(res)
        soap.delete(123, True, res, specified)
        Console.WriteLine(res)
    End Sub
End Class

For .NET WCF alternative:

Imports System

Imports ws.soap

Public Class Program
    Public Shared Sub Main(args As String())
        Dim soap As New Test() ' modified
        Dim o As T1 = soap.getOne(123)
        Console.WriteLine(o.f1 & " " & o.f2)
        Dim all As T1() = soap.getAll()
        For Each one As T1 In all
            Console.WriteLine(one.f1 & " " & one.f2)
        Next
        Console.WriteLine(soap.save(123, "ABC"))
        Console.WriteLine(soap.saveObject(o))
        Console.WriteLine(soap.delete(123))
    End Sub
End Class

To build use (%1 - WSDL URL, %2 = client class):

wsdl /language:VB /out:TestService.vb /namespace:ws.soap %1
vbc TestClient%2.vb TestService.vb
%2

.NET comes with a svcutil tool that can generate client stub code from WSDL.

The exposed API in the generated client stub varies a little.

Here comes test client code.

For all except .NET ASMX:

using System;

using ws.soap;

public class Program
{
    public static void Main(string[] args) 
    {
        TestClient soap = new TestClient(); // modified
        T1 o = soap.getOne(123);
        Console.WriteLine(o.f1 + " " + o.f2);
        T1[] all = soap.getAll();
        foreach(T1 one in all)
        {
            Console.WriteLine(one.f1 + " " + one.f2);
        }
        Console.WriteLine(soap.save(123, "ABC"));
        Console.WriteLine(soap.saveObject(o));
        Console.WriteLine(soap.delete(123));
     }
}

For .NET ASMX:

using System;

using ws.soap;

public class Program
{
    public static void Main(string[] args) 
    {
        TestServiceSoapClient soap = new TestServiceSoapClient("TestServiceSoap"); // modified
        T1 o = soap.getOne(123);
        Console.WriteLine(o.f1 + " " + o.f2);
        T1[] all = soap.getAll();
        foreach(T1 one in all)
        {
            Console.WriteLine(one.f1 + " " + one.f2);
        }
        Console.WriteLine(soap.save(123, "ABC"));
        Console.WriteLine(soap.saveObject(o));
        Console.WriteLine(soap.delete(123));
     }
}

To build use (%1 - WSDL URL, %2 = client class):

svcutil /language:CS /out:TestService.cs /namespace:http://soap.ws,ws.soap %1
ren output.config %2.exe.config
csc %2.cs TestService.cs
%2

Note that svcutil generate an app.config file and that app.config is required for the client to run properly.

.NET comes with a svcutil tool that can generate client stub code from WSDL.

The exposed API in the generated client stub varies a little.

Here comes test client code.

For all except .NET ASMX:

Imports System

Imports ws.soap

Public Class Program
    Public Shared Sub Main(args As String())
        Dim soap As New TestClient() ' modified
        Dim o As T1 = soap.getOne(123)
        Console.WriteLine(o.f1 & " " & o.f2)
        Dim all As T1() = soap.getAll()
        For Each one As T1 In all
            Console.WriteLine(one.f1 & " " & one.f2)
        Next
        Console.WriteLine(soap.save(123, "ABC"))  
        Console.WriteLine(soap.saveObject(o))
        Console.WriteLine(soap.delete(123))
    End Sub
End Class

For .NET ASMX:

Imports System

Imports ws.soap

Public Class Program
    Public Shared Sub Main(args As String())
        Dim soap As New TestServiceSoapClient("TestServiceSoap") ' modified
        Dim o As T1 = soap.getOne(123)
        Console.WriteLine(o.f1 & " " & o.f2)
        Dim all As T1() = soap.getAll()
        For Each one As T1 In all
            Console.WriteLine(one.f1 & " " & one.f2)
        Next
        Console.WriteLine(soap.save(123, "ABC"))
        Console.WriteLine(soap.saveObject(o))
        Console.WriteLine(soap.delete(123))
    End Sub
End Class

To build use (%1 - WSDL URL, %2 = client class):

svcutil /language:VB /out:TestService.vb /namespace:http://soap.ws,ws.soap %1
ren output.config %2.exe.config
vbc %2.vb TestService.vb
%2

Note that svcutil generate an app.config file and that app.config is required for the client to run properly.

PHP is a dynamic language so it parse WSDL at runtime.

For JAX-RPC:

<?php
function test($url) {
    $soap = new SoapClient($url);
    $o = $soap->getOne(123);
    echo $o->f1 . ' ' . $o->f2 . "\r\n";
    $all = $soap->getAll();
    foreach($all as $one) { // modified
        echo $one->f1 . ' ' . $one->f2 . "\r\n";
    }
    echo $soap->save(123, "ABC") . "\r\n";
    echo $soap->saveObject($o) . "\r\n";
    echo $soap->delete(123) . "\r\n";
}

test('http://localhost:8080/axis/services/TestService?WSDL');
?>

For JAX-WS:

<?php
function test($url) {
    $soap = new SoapClient($url);
    $o = $soap->getOne(123);
    echo $o->f1 . ' ' . $o->f2 . "\r\n";
    $all = $soap->getAll();
    foreach($all->item as $one) { // modified
        echo $one->f1 . ' ' . $one->f2 . "\r\n";
    }
    echo $soap->save(123, "ABC") . "\r\n";
    echo $soap->saveObject($o) . "\r\n";
    echo $soap->delete(123) . "\r\n";
}

test('http://localhost:8080/wssoap/Test?wsdl');
?>

For .NET ASMX and .NET WCF:

<?php
function test($url) {
    $soap = new SoapClient($url);
    $o = $soap->getOne((object)array('f1' => 123))->getOneResult; // funky
    echo $o->f1 . ' ' . $o->f2 . "\r\n";
    $all = $soap->getAll();
    foreach($all->getAllResult->T1 as $one) { // modified
        echo $one->f1 . ' ' . $one->f2 . "\r\n";
    }
    // funky:
    echo $soap->save((object)array('f1' => 123, 'f2' => "ABC"))->saveResult . "\r\n";
    echo $soap->saveObject((object)array('o' => $o))->saveObjectResult . "\r\n";
    echo $soap->delete((object)array('f1' => 123))->deleteResult . "\r\n";
}

test('http://localhost/TestService.asmx?WSDL');
test('http://localhost/test.svc?wsdl');
?>

For .NET WCF alternative:

<?php
function test($url) {
    $soap = new SoapClient($url);
    $o = $soap->getOne(123);
    echo $o->f1 . ' ' . $o->f2 . "\r\n";
    $all = $soap->getAll();
    foreach($all->T1 as $one) { // modified
        echo $one->f1 . ' ' . $one->f2 . "\r\n";
    }
    echo $soap->save(123, "ABC") . "\r\n";
    echo $soap->saveObject($o) . "\r\n";
    echo $soap->delete(123) . "\r\n";
}

test('http://localhost/TestAlt.svc?wsdl')
?>

Python is a dynamic language so it parse WSDL at runtime.

For all except Java JAX-RPC:

from zeep import Client

def test(url):
    soap = Client(url)
    o = soap.service.getOne(123)
    print('%d %s' % (o.f1,o.f2))
    for one in soap.service.getAll():
        print('%d %s' % (o.f1,o.f2))
    print(soap.service.save(123,'ABC'))
    print(soap.service.saveObject(o))
    print(soap.service.delete(123))

test('http://localhost:8080/wssoap/Test?wsdl')
test('http://localhost/TestService.asmx?WSDL')
test('http://localhost/Test.svc?wsdl')
test('http://localhost/TestAlt.svc?wsdl')

For Java JAX-RPC:

from zeep import Client

def test(url):
    soap = Client(url)
    o = soap.service.getOne(123)
    print('%d %s' % (o.f1,o.f2._value_1))
    for one in soap.service.getAll():
        print('%d %s' % (o.f1,o.f2._value_1))
    print(soap.service.save(123,'ABC'))
    print(soap.service.saveObject(o))
    print(soap.service.delete(123))

test('http://localhost:8080/axis/services/TestService?WSDL')

Client fully hand coded

Generating a client stub and calling it normal is sort of the whole point in the model with WSDL. But it is possible to call a web service without generating a stub.

JAX-RPC provides an API for DII (Dynamic Invocation Interface).

That API has severe limitations.

Here comes test client code.

package ws.soap;

import java.rmi.RemoteException;

import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.encoding.XMLType;

import org.apache.axis.encoding.ser.BeanDeserializerFactory;
import org.apache.axis.encoding.ser.BeanSerializerFactory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class JAXRPC {
    public static void test(String url, String opstyle, String encstyle, String actionprefix) throws ServiceException, RemoteException {
        ServiceFactory factory = ServiceFactory.newInstance();
        Service service = factory.createService(new QName("http://soap.ws"));
        Call call = service.createCall();
        call.setTargetEndpointAddress(url);
        call.setProperty(Call.SOAPACTION_USE_PROPERTY, true);
        call.setProperty(Call.OPERATION_STYLE_PROPERTY, opstyle);
        call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY , encstyle);
        BeanSerializerFactory bsf = new BeanSerializerFactory(T1.class, new QName ("http://soap.ws", "T1"));
        BeanDeserializerFactory bdf = new BeanDeserializerFactory(T1.class, new QName ("http://soap.ws", "T1"));
        service.getTypeMappingRegistry().getDefaultTypeMapping().register(T1.class, new QName ("http://soap.ws", "T1"), bsf, bdf);
        // getOne
        call.setOperationName(new QName("http://soap.ws", "getOne"));
        call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "getOne");
        call.removeAllParameters();
        call.addParameter("f1", XMLType.XSD_INT, Integer.class, ParameterMode.IN);
        call.setReturnType(new QName("http://soap.ws", "T1"), T1.class);
        T1 o = (T1)call.invoke(new Object[] { 123 });
        System.out.println(o.getF1() + " " + o.getF2());
        // getAll
        call.setOperationName(new QName("http://soap.ws", "getAll"));
        call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "getAll");
        call.removeAllParameters();
        call.setReturnType(new QName("http://soap.ws", "ArrayOfT1"), T1[].class);
        T1[] all = (T1[])call.invoke(new Object[0]);
        for(T1 one: all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        // save
        call.setOperationName(new QName("http://soap.ws", "save"));
        call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "save");
        call.removeAllParameters();
        call.addParameter("f1", XMLType.XSD_INT, Integer.class, ParameterMode.IN);
        call.addParameter("f2", XMLType.XSD_STRING, String.class, ParameterMode.IN);
        call.setReturnType(XMLType.XSD_BOOLEAN);
        boolean saveres = (Boolean)call.invoke(new Object[] { 123, "ABC" });
        System.out.println(saveres);
        // saveObject
        call.setOperationName(new QName("http://soap.ws", "saveObject"));
        call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "saveObject");
        call.removeAllParameters();
        call.addParameter("o", new QName("http://soap.ws", "T1"), T1.class, ParameterMode.IN);
        call.setReturnType(XMLType.XSD_BOOLEAN);
        boolean saveobjres = (Boolean)call.invoke(new Object[] { o });
        System.out.println(saveobjres);
        // delete
        call.setOperationName(new QName("http://soap.ws", "delete"));
        call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "delete");
        call.removeAllParameters();
        call.addParameter("f1", XMLType.XSD_INT, Integer.class, ParameterMode.IN);
        call.setReturnType(XMLType.XSD_BOOLEAN);
        boolean deleteres = (Boolean)call.invoke(new Object[] { 123 });
        System.out.println(deleteres);
    }
    public static void testWithHandler(String url, String opstyle, String encstyle, String actionprefix) {
        System.out.println(url);
        try {
            test(url, opstyle, encstyle, actionprefix);
        } catch(Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
    public static void main(String[] args) {
        Logger.getRootLogger().setLevel(Level.FATAL);
        testWithHandler("http://localhost:8080/axis/services/TestService", "rpc", "encoded", "");
        //testWithHandler("http://localhost:8080/wssoap/Test", "rpc", "literal", "");
        //testWithHandler("http://localhost/TestService.asmx", "document", "literal", "");
        //testWithHandler("http://localhost/Test.svc", "document", "literal", "Test/");
        //testWithHandler("http://localhost/TestAlt.svc", "rpc", "literal", "Test/");
    }
}

Axis 1.x provides an API for DII (Dynamic Invocation Interface) that extends standard JAX-RPC API.

That API is actually usable.

Here comes test client code.

package ws.soap;

import java.net.MalformedURLException;
import java.rmi.RemoteException;

import javax.xml.namespace.QName;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.encoding.XMLType;

import org.apache.axis.client.Call;
import org.apache.axis.encoding.ser.BeanDeserializerFactory;
import org.apache.axis.encoding.ser.BeanSerializerFactory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class Axis1 {
    public static void test(String url, String opstyle, String encstyle, String actionprefix, String paramns, boolean useaction, boolean usenames) throws MalformedURLException, RemoteException {
        Call call = new Call(url);
        call.setProperty(Call.SOAPACTION_USE_PROPERTY, useaction);
        call.setProperty(Call.OPERATION_STYLE_PROPERTY, opstyle);
        call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY , encstyle);
        BeanSerializerFactory bsf = new BeanSerializerFactory(T1.class, new QName ("http://soap.ws", "T1"));
        BeanDeserializerFactory bdf = new BeanDeserializerFactory(T1.class, new QName ("http://soap.ws", "T1"));
        call.getService().getTypeMappingRegistry().getDefaultTypeMapping().register(T1.class, new QName ("http://soap.ws", "T1"), bsf, bdf);
        // getOne
        call.setOperationName(new QName("http://soap.ws", "getOne"));
        if(useaction) call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "getOne");
        call.removeAllParameters();
        call.addParameter(new QName(paramns, usenames ? "f1" : "arg0"), XMLType.XSD_INT, int.class, ParameterMode.IN);
        call.setReturnType(new QName("http://soap.ws", "T1"), T1.class);
        T1 o = (T1)call.invoke(new Object[] { 123 });
        System.out.println(o.getF1() + " " + o.getF2());
        // getAll
        call.setOperationName(new QName("http://soap.ws", "getAll"));
        if(useaction) call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "getAll");
        call.removeAllParameters();
        call.setReturnType(new QName("http://soap.ws", "ArrayOfT1"), T1[].class);
        T1[] all = (T1[])call.invoke(new Object[0]);
        for(T1 one: all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        // save
        call.setOperationName(new QName("http://soap.ws", "save"));
        if(useaction) call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "save");
        call.removeAllParameters();
        call.addParameter(new QName(paramns, usenames ? "f1" : "arg0"), XMLType.XSD_INT, Integer.class, ParameterMode.IN);
        call.addParameter(new QName(paramns, usenames ? "f2" : "arg1"), XMLType.XSD_STRING, String.class, ParameterMode.IN);
        call.setReturnType(XMLType.XSD_BOOLEAN);
        boolean saveres = (Boolean)call.invoke(new Object[] { 123, "ABC" });
        System.out.println(saveres);
        // saveObject
        call.setOperationName(new QName("http://soap.ws", "saveObject"));
        if(useaction) call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "saveObject");
        call.removeAllParameters();
        call.addParameter(new QName(paramns, usenames ? "o" : "arg0"), new QName("http://soap.ws", "T1"), T1.class, ParameterMode.IN);
        call.setReturnType(XMLType.XSD_BOOLEAN);
        boolean saveobjres = (Boolean)call.invoke(new Object[] { o });
        System.out.println(saveobjres);
        // delete
        call.setOperationName(new QName("http://soap.ws", "delete"));
        if(useaction) call.setProperty(Call.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "delete");
        call.removeAllParameters();
        call.addParameter(new QName(paramns, usenames ? "f1" : "arg0"), XMLType.XSD_INT, Integer.class, ParameterMode.IN);
        call.setReturnType(XMLType.XSD_BOOLEAN);
        boolean deleteres = (Boolean)call.invoke(new Object[] { 123 });
        System.out.println(deleteres);
    }
    public static void testWithHandler(String url, String opstyle, String encstyle, String actionprefix, String paramns, boolean useaction, boolean usenames) {
        System.out.println(url);
        try {
            test(url, opstyle, encstyle, actionprefix, paramns, useaction, usenames);
        } catch(Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
    
    public static void main(String[] args) {
        Logger.getRootLogger().setLevel(Level.FATAL);
        testWithHandler("http://localhost:8080/axis/services/TestService", "rpc", "encoded", "", "http://soap.ws", false, true);
        testWithHandler("http://localhost:8080/wssoap/Test", "rpc", "literal", "", "", false, false);
        testWithHandler("http://localhost/TestService.asmx", "document", "literal", "", "http://soap.ws", true, true);
        testWithHandler("http://localhost/Test.svc", "document", "literal", "Test/", "http://soap.ws", true, true);
        //testWithHandler("http://localhost/TestAlt.svc", "rpc", "literal", "Test/", "", true, true);
    }
}

JAX-WS also provides an API for DII (Dynamic Invocation Interface).

That API is very low level, but it is usable.

Here comes test client code.

package ws.soap;

import java.io.IOException;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class JAXWS {
    public static void test(String url, String actionprefix, String paramns, boolean useaction, boolean usenames) throws SOAPException, IOException {
        Service service = Service.create(new QName("http://soap.ws", "TestService"));
        service.addPort(new QName("http://soap.ws", "TestPort"), SOAPBinding.SOAP11HTTP_BINDING, url);
        Dispatch<SOAPMessage> dispatch = service.createDispatch(new QName("http://soap.ws", "TestPort"), SOAPMessage.class, Service.Mode.MESSAGE);
        dispatch.getRequestContext().put(BindingProvider.SOAPACTION_USE_PROPERTY, useaction);
        MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
        SOAPMessage request;
        SOAPElement operation;
        SOAPMessage response;
        // getOne
        if(useaction) dispatch.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "getOne");   
        request = mf.createMessage();
        operation = request.getSOAPBody().addBodyElement(new QName("http://soap.ws", "getOne"));
        SOAPElement f1getone = operation.addChildElement(new QName(paramns, usenames ? "f1" : "arg0"));
        f1getone.addTextNode(Integer.toString(123));
        request.saveChanges();
        response = dispatch.invoke(request);
        Node xo = response.getSOAPBody().getFirstChild().getFirstChild();
        T1 o = new T1(Integer.parseInt(xo.getFirstChild().getTextContent()), xo.getLastChild().getTextContent());
        System.out.println(o.getF1() + " " + o.getF2());
        // getAll
        if(useaction) dispatch.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "getAll");   
        request = mf.createMessage();
        operation = request.getSOAPBody().addBodyElement(new QName("http://soap.ws", "getAll"));
        request.saveChanges();
        response = dispatch.invoke(request);
        NodeList xall = response.getSOAPBody().getFirstChild().getFirstChild().getChildNodes();
        for(int i = 0; i < xall.getLength(); i++) {
            Node xone = xall.item(i);
            T1 one = new T1(Integer.parseInt(xone.getFirstChild().getTextContent()), xone.getLastChild().getTextContent());
            System.out.println(one.getF1() + " " + one.getF2());
        }
        // save
        if(useaction) dispatch.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "save"); 
        request = mf.createMessage();
        operation = request.getSOAPBody().addBodyElement(new QName("http://soap.ws", "save"));
        SOAPElement f1save = operation.addChildElement(new QName(paramns, usenames ? "f1" : "arg0"));
        f1save.addTextNode(Integer.toString(123));
        SOAPElement f2save = operation.addChildElement(new QName(paramns, usenames ? "f2" : "arg1"));
        f2save.addTextNode("ABC");
        request.saveChanges();
        response = dispatch.invoke(request);
        Node xressave = response.getSOAPBody().getFirstChild().getFirstChild();
        boolean ressave = Boolean.parseBoolean(xressave.getTextContent());
        System.out.println(ressave);
        // saveObject
        if(useaction) dispatch.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "saveObject");   
        request = mf.createMessage();
        operation = request.getSOAPBody().addBodyElement(new QName("http://soap.ws", "saveObject"));
        SOAPElement osaveobj = operation.addChildElement(new QName(paramns, usenames ? "o" : "arg0"));
        SOAPElement f1saveobj = osaveobj.addChildElement(new QName("http://soap.ws", "f1", usenames ? "t1ns" : "")); // has to specify prefix
        f1saveobj.addTextNode(Integer.toString(o.getF1()));
        SOAPElement f2saveobj = osaveobj.addChildElement(new QName("http://soap.ws", "f2", usenames ? "t1ns" : "")); // has to specify prefix
        f2saveobj.addTextNode(o.getF2());
        request.saveChanges();
        response = dispatch.invoke(request);
        Node xressaveobj = response.getSOAPBody().getFirstChild().getFirstChild();
        boolean ressaveobj = Boolean.parseBoolean(xressaveobj.getTextContent());
        System.out.println(ressaveobj);
        // delete
        if(useaction) dispatch.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY, "http://soap.ws/" + actionprefix + "delete");   
        request = mf.createMessage();
        operation = request.getSOAPBody().addBodyElement(new QName("http://soap.ws", "delete"));
        SOAPElement f1delete = operation.addChildElement(new QName(paramns, usenames ? "f1" : "arg0"));
        f1delete.addTextNode(Integer.toString(123));
        request.saveChanges();
        response = dispatch.invoke(request);
        Node xresdelete = response.getSOAPBody().getFirstChild().getFirstChild();
        boolean resdelete = Boolean.parseBoolean(xresdelete.getTextContent());
        System.out.println(resdelete);
    }
    public static void testWithHandler(String url, String actionprefix, String paramns, boolean useaction, boolean usenames) {
        System.out.println(url);
        try {
            test(url, actionprefix, paramns, useaction, usenames);
        } catch(Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
    public static void main(String[] args) {
        testWithHandler("http://localhost:8080/axis/services/TestService", "", "http://soap.ws", false, true);
        testWithHandler("http://localhost:8080/wssoap/Test", "", "", false, false);
        testWithHandler("http://localhost/TestService.asmx", "", "http://soap.ws", true, true);
        testWithHandler("http://localhost/Test.svc", "Test/", "http://soap.ws", true, true);
        testWithHandler("http://localhost/TestAlt.svc", "Test/", "", true, true);
    }
}

Axis 2.x also provides an API for DII (Dynamic Invocation Interface) that is very different from standard JAX-WS API.

Again very low level but usable

Here comes test client code.

package ws.soap;

import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.namespace.QName;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axis2.AxisFault;
import org.apache.axis2.databinding.utils.BeanUtil;
import org.apache.axis2.engine.ObjectSupplier;
import org.apache.axis2.rpc.client.RPCServiceClient;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class Axis2 {
    public static class MyObjectSupplier implements ObjectSupplier {
        @SuppressWarnings("rawtypes")
        @Override
        public Object getObject(Class clz) throws AxisFault {
            try {
                return clz.newInstance();
            } catch (InstantiationException e) {
                throw(new AxisFault(e.getMessage()));
            } catch (IllegalAccessException e) {
                throw(new AxisFault(e.getMessage()));
            }
        }
    }
    public static void testWrapped(String wsdlurl, String service, String port, String paramns, boolean hack) throws AxisFault, MalformedURLException {
        RPCServiceClient client = new RPCServiceClient(null, new URL(wsdlurl), new QName("http://soap.ws", service), port);
        OMFactory factory = OMAbstractFactory.getOMFactory();
        OMElement req;
        OMElement resp;
        // getOne
        req = BeanUtil.getOMElement(new QName("http://soap.ws", "getOne"), new Object[] { 123 }, new QName(paramns, "f1"), false, null);
        resp = client.sendReceive(new QName("http://soap.ws", "getOne"), req);
        T1 o = (T1)BeanUtil.deserialize(resp, new Object[] { T1.class }, new MyObjectSupplier())[0];
        System.out.println(o.getF1() + " " + o.getF2());
        // getAll
        OMElement res = client.invokeBlocking(new QName("http://soap.ws", "getAll"), new Object[0]);
        T1[] all = (T1[])BeanUtil.deserialize(res.getFirstElement(), new Object[] { T1[].class }, new MyObjectSupplier())[0];
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        // save
        OMElement f1 = factory.createOMElement(new QName(paramns, "f1"));
        f1.addChild(factory.createOMText("123"));
        OMElement f2 = factory.createOMElement(new QName(paramns, "f2"));
        f2.addChild(factory.createOMText("ABC"));
        req = factory.createOMElement(new QName("http://soap.ws", "save"));
        req.addChild(f1);
        req.addChild(f2);
        resp = client.sendReceive(new QName("http://soap.ws", "save"), req);
        boolean saveres = (Boolean)BeanUtil.deserialize(resp, new Object[] { Boolean.class }, new MyObjectSupplier())[0];
        System.out.println(saveres);
        // saveObject
        req = BeanUtil.getOMElement(new QName("http://soap.ws", "saveObject"), new Object[] { o }, new QName(paramns, "o"), false, null);
        if(hack) {
            req.getFirstElement().getFirstChildWithName(new QName("", "f1")).setNamespace(factory.createOMNamespace("http://soap.ws", "t1ns"));
            req.getFirstElement().getFirstChildWithName(new QName("", "f2")).setNamespace(factory.createOMNamespace("http://soap.ws", "t1ns"));
        }
        resp = client.sendReceive(new QName("http://soap.ws", "saveObject"), req);
        boolean saveobjres = (Boolean)BeanUtil.deserialize(resp, new Object[] { Boolean.class }, new MyObjectSupplier())[0];
        System.out.println(saveobjres);
        // delete
        req = BeanUtil.getOMElement(new QName("http://soap.ws", "delete"), new Object[] { 123 }, new QName(paramns, "f1"), false, null);
        resp = client.sendReceive(new QName("http://soap.ws", "delete"), req);
        boolean deleteres = (Boolean)BeanUtil.deserialize(resp, new Object[] { Boolean.class }, new MyObjectSupplier())[0];
        System.out.println(deleteres);
    }
    public static void test(String wsdlurl, String service, String port) throws AxisFault, MalformedURLException {
        RPCServiceClient client = new RPCServiceClient(null, new URL(wsdlurl), new QName("http://soap.ws", service), port);
        // getOne
        T1 o = (T1)client.invokeBlocking(new QName("http://soap.ws", "getOne"), new Object[] { 123 }, new Class[] { T1.class })[0];
        System.out.println(o.getF1() + " " + o.getF2());
        // getAll
        OMElement res = client.invokeBlocking(new QName("http://soap.ws", "getAll"), new Object[0]);
        T1[] all = (T1[])BeanUtil.deserialize(res.getFirstElement(), new Object[] { T1[].class }, new MyObjectSupplier())[0];
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        // save
        boolean saveres = (Boolean)client.invokeBlocking(new QName("http://soap.ws", "save"), new Object[] { 123, "ABC" }, new Class[] { Boolean.class })[0];
        System.out.println(saveres);
        // saveObject
        boolean saveobjres = (Boolean)client.invokeBlocking(new QName("http://soap.ws", "saveObject"), new Object[] { o }, new Class[] { Boolean.class })[0];
        System.out.println(saveobjres);
        // delete
        boolean deleteres = (Boolean)client.invokeBlocking(new QName("http://soap.ws", "delete"), new Object[] { 123 }, new Class[] { Boolean.class })[0];
        System.out.println(deleteres);
    }
    public static void testWithHandler(String wsdlurl, String service, String port, String paramns, boolean wrap, boolean hack) {
        System.out.println(wsdlurl);
        try {
            if(wrap) {
                testWrapped(wsdlurl, service, port, paramns, hack);
            } else {
                test(wsdlurl, service, port);
            }
        } catch(Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
    public static void main(String[] args) {
        Logger.getRootLogger().setLevel(Level.FATAL);
        testWithHandler("http://localhost:8080/axis/services/TestService?WSDL", "TestService", "TestService", "", false, false);
        testWithHandler("http://localhost:8080/wssoap/Test?wsdl", "TestService", "TestPort", "", false, false);
        testWithHandler("http://localhost/TestService.asmx?WSDL", "TestService", "TestServiceSoap", "http://soap.ws", true, false);
        testWithHandler("http://localhost/Test.svc?singleWsdl", "Test", "Test", "http://soap.ws", true, false);
        testWithHandler("http://localhost/TestAlt.svc?singleWsdl", "Test", "TestAlt", "", true, true);
    }
}

.NET does not have anything equivalent to Java DII API's - it is necesarry to use WebClient and XmlDocument.

But even though that is a very low level way then it works fine in practice.

Here comes test client code.

using System;
using System.Net;
using System.Xml;

namespace HandCoded
{
    public class T1
    {
        public T1() : this(0, "")
        {
        }
        public T1(int f1, string f2)
        {
            this.f1 = f1;
            this.f2 = f2;
        }
        public int f1 { get; set; }
        public string f2 { get; set; }
    }
    public class Program
    {
        private static XmlDocument Call(string url, Tuple<string,string>[] hdrs, XmlDocument msg)
        {
            string xmlin = msg.OuterXml;
            WebClient wc = new WebClient();
            foreach(Tuple<string,string> hdr in hdrs)
            {
                wc.Headers.Add(hdr.Item1, hdr.Item2);
            }
            string xmlout = wc.UploadString(url, xmlin);
            wc.Dispose();
            XmlDocument res = new XmlDocument();
            res.LoadXml(xmlout);
            return res;
        }
        private static XmlDocument GetEnvelope()
        {
            XmlDocument req = new XmlDocument();
            req.AppendChild(req.CreateElement("soap", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/"));
            req.DocumentElement.AppendChild(req.CreateElement("soap", "Body", "http://schemas.xmlsoap.org/soap/envelope/"));
            return req;
        }
        private static Tuple<string,string>[] GetHeaders(string action, string actionprefix, bool useaction)
        {
            if(useaction)
            {
                return new Tuple<string,string>[] { Tuple.Create("Content-Type", "text/xml"), Tuple.Create("SOAPAction", "http://soap.ws/" + actionprefix + action) };    
            }
            else
            {
                return new Tuple<string,string>[] { Tuple.Create("Content-Type", "text/xml") }; 
            }
        }
        public static void Test(string url, string actionprefix, string paramns, string fldns, bool useaction, bool usenames)
        {
            XmlDocument req;
            XmlElement operation;
            XmlDocument resp;
            // getOne
            req = GetEnvelope();
            operation = req.CreateElement("getOne", "http://soap.ws");
            XmlElement f1getone = req.CreateElement(usenames ? "f1" : "arg0", paramns);
            f1getone.AppendChild(req.CreateTextNode(123.ToString()));
            operation.AppendChild(f1getone);
            req.DocumentElement.LastChild.AppendChild(operation);
            resp = Call(url, GetHeaders("getOne", actionprefix, useaction), req);
            XmlElement xo = (XmlElement)resp.DocumentElement.LastChild.FirstChild.FirstChild;
            XmlNamespaceManager nsmgetone = new XmlNamespaceManager(resp.NameTable);
            nsmgetone.AddNamespace("tns", fldns);
            T1 o = new T1(int.Parse(xo.SelectSingleNode("tns:f1/text()", nsmgetone).Value), xo.SelectSingleNode("tns:f2/text()", nsmgetone).Value);
            Console.WriteLine(o.f1 + " " + o.f2);
            // getAll
            req = GetEnvelope();
            operation = req.CreateElement("getAll", "http://soap.ws");
            req.DocumentElement.LastChild.AppendChild(operation);
            resp = Call(url, GetHeaders("getAll", actionprefix, useaction), req);
            XmlElement xall = (XmlElement)resp.DocumentElement.LastChild.FirstChild.FirstChild;
            XmlNamespaceManager nsmgetall = new XmlNamespaceManager(resp.NameTable);
            nsmgetall.AddNamespace("tns", fldns);
            foreach(XmlElement xone in xall.ChildNodes)
            {
                T1 one = new T1(int.Parse(xone.SelectSingleNode("tns:f1/text()", nsmgetall).Value), xone.SelectSingleNode("tns:f2/text()", nsmgetall).Value);
                Console.WriteLine(one.f1 + " " + one.f2);
            }
            // save
            req = GetEnvelope();
            operation = req.CreateElement("save", "http://soap.ws");
            XmlElement f1save = req.CreateElement(usenames ? "f1" : "arg0", paramns);
            f1save.AppendChild(req.CreateTextNode(123.ToString()));
            operation.AppendChild(f1save);
            XmlElement f2save = req.CreateElement(usenames ? "f2" : "arg1", paramns);
            f2save.AppendChild(req.CreateTextNode("ABC"));
            operation.AppendChild(f2save);
            req.DocumentElement.LastChild.AppendChild(operation);
            resp = Call(url, GetHeaders("save", actionprefix, useaction), req);
            XmlElement xressave = (XmlElement)resp.DocumentElement.LastChild.FirstChild.FirstChild;
            bool ressave = bool.Parse(xressave.FirstChild.Value);
            Console.WriteLine(ressave);
            // saveObject
            req = GetEnvelope();
            operation = req.CreateElement("saveObject", "http://soap.ws");
            XmlElement osaveobj = req.CreateElement(usenames ? "o" : "arg0", paramns);
            operation.AppendChild(osaveobj);
            XmlElement f1saveobj = req.CreateElement("f1", fldns);
            f1saveobj.AppendChild(req.CreateTextNode(123.ToString()));
            osaveobj.AppendChild(f1saveobj);
            XmlElement f2saveobj = req.CreateElement("f2", fldns);
            f2saveobj.AppendChild(req.CreateTextNode("ABC"));
            osaveobj.AppendChild(f2saveobj);
            req.DocumentElement.LastChild.AppendChild(operation);
            resp = Call(url, GetHeaders("saveObject", actionprefix, useaction), req);
            XmlElement xressaveobj = (XmlElement)resp.DocumentElement.LastChild.FirstChild.FirstChild;
            bool ressaveobj = bool.Parse(xressaveobj.FirstChild.Value);
            Console.WriteLine(ressaveobj);
            // delete
            req = GetEnvelope();
            operation = req.CreateElement("delete", "http://soap.ws");
            XmlElement f1delete = req.CreateElement(usenames ? "f1" : "arg0", paramns);
            f1delete.AppendChild(req.CreateTextNode(123.ToString()));
            operation.AppendChild(f1delete);
            req.DocumentElement.LastChild.AppendChild(operation);
            resp = Call(url, GetHeaders("delete", actionprefix, useaction), req);
            XmlElement xresdelete = (XmlElement)resp.DocumentElement.LastChild.FirstChild.FirstChild;
            bool resdelete = bool.Parse(xresdelete.FirstChild.Value);
            Console.WriteLine(resdelete);
        }
        public static void TestWithHandler(string url, string actionprefix, string paramns, string fldns, bool useaction, bool usenames)
        {
            try
            {
                Test(url, actionprefix, paramns, fldns, useaction, usenames);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        public static void Main(string[] args)
        {
            TestWithHandler("http://localhost:8080/axis/services/TestService", "", "http://soap.ws", "", true, false);
            TestWithHandler("http://localhost:8080/wssoap/Test", "", "", "", false, false);
            TestWithHandler("http://localhost/TestService.asmx", "", "http://soap.ws", "http://soap.ws", true, true);
            TestWithHandler("http://localhost/Test.svc", "Test/", "http://soap.ws", "http://soap.ws", true, true);
            TestWithHandler("http://localhost/TestAlt.svc", "Test/", "", "http://soap.ws", true, true);
        }
    }
}

.NET does not have anything equivalent to Java DII API's - it is necesarry to use WebClient and XmlDocument.

But even though that is a very low level way then it works fine in practice.

Here comes test client code.

Imports System
Imports System.Net
Imports System.Xml

Namespace HandCoded
    Public Class T1
        Public Sub New()
            Me.New(0, "")
        End Sub
        Public Sub New(f1 As Integer, f2 As String)
            Me.f1 = f1
            Me.f2 = f2
        End Sub
        Public Property f1() As Integer
        Public Property f2() As String
    End Class
    Public Class Program
        Private Shared Function [Call](url As String, hdrs As Tuple(Of String, String)(), msg As XmlDocument) As XmlDocument
            Dim xmlin As String = msg.OuterXml
            Dim wc As New WebClient()
            For Each hdr As Tuple(Of String, String) In hdrs
                wc.Headers.Add(hdr.Item1, hdr.Item2)
            Next
            Dim xmlout As String = wc.UploadString(url, xmlin)
            wc.Dispose()
            Dim res As New XmlDocument()
            res.LoadXml(xmlout)
            Return res
        End Function
        Private Shared Function GetEnvelope() As XmlDocument
            Dim req As New XmlDocument()
            req.AppendChild(req.CreateElement("soap", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/"))
            req.DocumentElement.AppendChild(req.CreateElement("soap", "Body", "http://schemas.xmlsoap.org/soap/envelope/"))
            Return req
        End Function
        Private Shared Function GetHeaders(action As String, actionprefix As String, useaction As Boolean) As Tuple(Of String, String)()
            If useaction Then
                Return New Tuple(Of String, String)() {Tuple.Create("Content-Type", "text/xml"), Tuple.Create("SOAPAction", "http://soap.ws/" & actionprefix & action)}
            Else
                Return New Tuple(Of String, String)() {Tuple.Create("Content-Type", "text/xml")}
            End If
        End Function
        Public Shared Sub Test(url As String, actionprefix As String, paramns As String, fldns As String, useaction As Boolean, usenames As Boolean)
            Dim req As XmlDocument
            Dim operation As XmlElement
            Dim resp As XmlDocument
            ' getOne
            req = GetEnvelope()
            operation = req.CreateElement("getOne", "http://soap.ws")
            Dim f1getone As XmlElement = req.CreateElement(If(usenames, "f1", "arg0"), paramns)
            f1getone.AppendChild(req.CreateTextNode(123.ToString()))
            operation.AppendChild(f1getone)
            req.DocumentElement.LastChild.AppendChild(operation)
            resp = [Call](url, GetHeaders("getOne", actionprefix, useaction), req)
            Dim xo As XmlElement = DirectCast(resp.DocumentElement.LastChild.FirstChild.FirstChild, XmlElement)
            Dim nsmgetone As New XmlNamespaceManager(resp.NameTable)
            nsmgetone.AddNamespace("tns", fldns)
            Dim o As New T1(Integer.Parse(xo.SelectSingleNode("tns:f1/text()", nsmgetone).Value), xo.SelectSingleNode("tns:f2/text()", nsmgetone).Value)
            Console.WriteLine(o.f1 & " " & o.f2)
            ' getAll
            req = GetEnvelope()
            operation = req.CreateElement("getAll", "http://soap.ws")
            req.DocumentElement.LastChild.AppendChild(operation)
            resp = [Call](url, GetHeaders("getAll", actionprefix, useaction), req)
            Dim xall As XmlElement = DirectCast(resp.DocumentElement.LastChild.FirstChild.FirstChild, XmlElement)
            Dim nsmgetall As New XmlNamespaceManager(resp.NameTable)
            nsmgetall.AddNamespace("tns", fldns)
            For Each xone As XmlElement In xall.ChildNodes
                Dim one As New T1(Integer.Parse(xone.SelectSingleNode("tns:f1/text()", nsmgetall).Value), xone.SelectSingleNode("tns:f2/text()", nsmgetall).Value)
                Console.WriteLine(one.f1 & " " & one.f2)
            Next
            ' save
            req = GetEnvelope()
            operation = req.CreateElement("save", "http://soap.ws")
            Dim f1save As XmlElement = req.CreateElement(If(usenames, "f1", "arg0"), paramns)
            f1save.AppendChild(req.CreateTextNode(123.ToString()))
            operation.AppendChild(f1save)
            Dim f2save As XmlElement = req.CreateElement(If(usenames, "f2", "arg1"), paramns)
            f2save.AppendChild(req.CreateTextNode("ABC"))
            operation.AppendChild(f2save)
            req.DocumentElement.LastChild.AppendChild(operation)
            resp = [Call](url, GetHeaders("save", actionprefix, useaction), req)
            Dim xressave As XmlElement = DirectCast(resp.DocumentElement.LastChild.FirstChild.FirstChild, XmlElement)
            Dim ressave As Boolean = Boolean.Parse(xressave.FirstChild.Value)
            Console.WriteLine(ressave)
            ' saveObject
            req = GetEnvelope()
            operation = req.CreateElement("saveObject", "http://soap.ws")
            Dim osaveobj As XmlElement = req.CreateElement(If(usenames, "o", "arg0"), paramns)
            operation.AppendChild(osaveobj)
            Dim f1saveobj As XmlElement = req.CreateElement("f1", fldns)
            f1saveobj.AppendChild(req.CreateTextNode(123.ToString()))
            osaveobj.AppendChild(f1saveobj)
            Dim f2saveobj As XmlElement = req.CreateElement("f2", fldns)
            f2saveobj.AppendChild(req.CreateTextNode("ABC"))
            osaveobj.AppendChild(f2saveobj)
            req.DocumentElement.LastChild.AppendChild(operation)
            resp = [Call](url, GetHeaders("saveObject", actionprefix, useaction), req)
            Dim xressaveobj As XmlElement = DirectCast(resp.DocumentElement.LastChild.FirstChild.FirstChild, XmlElement)
            Dim ressaveobj As Boolean = Boolean.Parse(xressaveobj.FirstChild.Value)
            Console.WriteLine(ressaveobj)
            ' delete
            req = GetEnvelope()
            operation = req.CreateElement("delete", "http://soap.ws")
            Dim f1delete As XmlElement = req.CreateElement(If(usenames, "f1", "arg0"), paramns)
            f1delete.AppendChild(req.CreateTextNode(123.ToString()))
            operation.AppendChild(f1delete)
            req.DocumentElement.LastChild.AppendChild(operation)
            resp = [Call](url, GetHeaders("delete", actionprefix, useaction), req)
            Dim xresdelete As XmlElement = DirectCast(resp.DocumentElement.LastChild.FirstChild.FirstChild, XmlElement)
            Dim resdelete As Boolean = Boolean.Parse(xresdelete.FirstChild.Value)
            Console.WriteLine(resdelete)
        End Sub
        Public Shared Sub TestWithHandler(url As String, actionprefix As String, paramns As String, fldns As String, useaction As Boolean, usenames As Boolean)
            Try
                Test(url, actionprefix, paramns, fldns, useaction, usenames)
            Catch ex As Exception
                Console.WriteLine(ex.Message)
            End Try
        End Sub
        Public Shared Sub Main(args As String())
            TestWithHandler("http://localhost:8080/axis/services/TestService", "", "http://soap.ws", "", True, False)
            TestWithHandler("http://localhost:8080/wssoap/Test", "", "", "", False, False)
            TestWithHandler("http://localhost/TestService.asmx", "", "http://soap.ws", "http://soap.ws", True, True)
            TestWithHandler("http://localhost/Test.svc", "Test/", "http://soap.ws", "http://soap.ws", True, True)
            TestWithHandler("http://localhost/TestAlt.svc", "Test/", "", "http://soap.ws", True, True)
        End Sub
    End Class
End Namespace

Calling web sservices without generating a client stub from WSDL is extremely cumbersome. In fact it is so difficult due to differences in how web services expect data that often the easiest way to do is to generate a client stub and see how it does it. I will strongly recommend never going the hand coded route.

Compatibility matrix:

This table summarize results from above:

Client Server
Java JAX-RPC Java JAX-WS .NET ASMX .NET WCF .NET WCF alternative
Implementation Axis 1.x JBoss CXF ASP.NET .NET WCF .NET WCF
SOAP flavor SOAP 1.1 RPC/Encoded SOAP 1.1 RPC/Literal SOAP 1.1 & 1.2 Document/Literal-Wrapped SOAP 1.1 Document/Literal-Wrapped SOAP 1.1 RPC/Literal
JAX-RPC Axis 1.x OK OK OK OK OK
JAX-WS Java SE Fail OK Warning OK OK
JAX-WS CXF Fail OK Fail Fail Wrong
JAX-WS Axis 2.x Fail Little funky Little funky Little funky Little funky
.NET old style Warning OK OK Funky OK
.NET WCF OK OK OK OK OK
PHP SoapClient OK OK Funky Funky OK
Python Zeep Little funky OK OK OK OK
JAX-RPC OK Wrong Wrong Wrong Wrong
Axis 1.x OK OK OK OK Wrong
JAX-WS OK OK OK OK OK
Axis 2.x OK OK OK OK Little funky
WebClient and XmlDocument OK OK OK OK OK

Note that for the hand coded examples the reason something does not work can either be a problem in the framework or a problem in the way I coded it.

WSDL first:

How it works:

WSDL first approach works like:

  1. Service developer create WSDL
  2. Service developer create service implementation code
  3. Service is deployed
  4. Client developer use tool to generate client stub from WSDL and call that from client application

For static client language:

WSDL first - static client language

For dynamic client language:

WSDL first - dynamic client language

WSDL:

At first WSDL look like total gibberish and the tougth of writing a WSDL file manually seems very scary. But after looking more closely at it then one realize that it is really very simple. I expect everybody to be able to read and understand my example.

Example WSDL:

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

<!-- define types -->
<wsdl:types>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:tns="http://soap.ws"
                targetNamespace="http://soap.ws"
                elementFormDefault="qualified">
        <xsd:complexType name="T1">
            <xsd:sequence>
                <xsd:element minOccurs="1" maxOccurs="1" name="f1" type="xsd:int"/>
                <xsd:element minOccurs="0" maxOccurs="1" name="f2" type="xsd:string"/>
            </xsd:sequence>
        </xsd:complexType>
        <xsd:complexType name="ArrayOfT1">
            <xsd:sequence>
                <xsd:element minOccurs="0" maxOccurs="unbounded" name="item" nillable="true" type="tns:T1"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:schema>
</wsdl:types>

<!-- define messages -->
<wsdl:message name="getOneRequest">
    <wsdl:part name="f1" type="xsd:int"/>
</wsdl:message>    
<wsdl:message name="getOneResponse">
    <wsdl:part name="return" type="tns:T1"/>
</wsdl:message>
<wsdl:message name="getAllRequest"/>
<wsdl:message name="getAllResponse">
    <wsdl:part name="return" type="tns:ArrayOfT1"/>
</wsdl:message>
<wsdl:message name="saveRequest">
    <wsdl:part name="f1" type="xsd:int"/>
    <wsdl:part name="f2" type="xsd:string"/>
</wsdl:message>    
<wsdl:message name="saveResponse">
    <wsdl:part name="return" type="xsd:boolean"/>
</wsdl:message>
<wsdl:message name="saveObjectRequest">
    <wsdl:part name="o" type="tns:T1"/>
</wsdl:message>    
<wsdl:message name="saveObjectResponse">
    <wsdl:part name="return" type="xsd:boolean"/>
</wsdl:message>
<wsdl:message name="deleteRequest">
    <wsdl:part name="f1" type="xsd:int"/>
</wsdl:message>    
<wsdl:message name="deleteResponse">
    <wsdl:part name="return" type="xsd:boolean"/>
</wsdl:message>

<!-- define operations -->
<wsdl:portType name="TestPort">
    <wsdl:operation name="getOne">
        <wsdl:input message="tns:getOneRequest"/>
        <wsdl:output message="tns:getOneResponse"/>
    </wsdl:operation>
    <wsdl:operation name="getAll">
        <wsdl:input message="tns:getAllRequest"/>
        <wsdl:output message="tns:getAllResponse"/>
    </wsdl:operation>
    <wsdl:operation name="save">
        <wsdl:input message="tns:saveRequest"/>
        <wsdl:output message="tns:saveResponse"/>
    </wsdl:operation>
    <wsdl:operation name="saveObject">
        <wsdl:input message="tns:saveObjectRequest"/>
        <wsdl:output message="tns:saveObjectResponse"/>
    </wsdl:operation>
    <wsdl:operation name="delete">
        <wsdl:input message="tns:deleteRequest"/>
        <wsdl:output message="tns:deleteResponse"/>
    </wsdl:operation>
</wsdl:portType>

<!-- configure SOAP flavor -->
<wsdl:binding name="TestBinding" type="tns:TestPort">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getOne">
        <soap:operation soapAction="http://soap.ws/getOne" style="rpc"/>
        <wsdl:input>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:input>
        <wsdl:output>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="getAll">
        <soap:operation soapAction="http://soap.ws/getAll" style="rpc"/>
        <wsdl:input>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:input>
        <wsdl:output>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="save">
        <soap:operation soapAction="http://soap.ws/save" style="rpc"/>
        <wsdl:input>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:input>
        <wsdl:output>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="saveObject">
        <soap:operation soapAction="http://soap.ws/saveObject" style="rpc"/>
        <wsdl:input>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:input>
        <wsdl:output>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="delete">
        <soap:operation soapAction="http://soap.ws/delete" style="rpc"/>
        <wsdl:input>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:input>
        <wsdl:output>
            <soap:body use="literal" namespace="http://soap.ws"/>
        </wsdl:output>
    </wsdl:operation>
</wsdl:binding>

<!-- define service -->
<wsdl:service name="TestService">
    <wsdl:port name="TestPort" binding="tns:TestBinding" >
        <soap:address location="http://localhost:8080/wssoapx/TestPort"/>
    </wsdl:port>
</wsdl:service>

</wsdl:definitions>

Server:

Server side will be generate with CXF.

The command:

call C:\Apache\apache-cxf-3.2.5\bin\wsdl2java -aer -impl Test.wsdl

That will generate 5 files:

The first 3 are used as is. The last 2 get replaced with the real ones.

T1.java:

package ws.soap;

import javax.xml.bind.annotation.XmlType;

@XmlType(name="T1")
public class T1 {
    private int f1;
    private String f2;
    public T1() {
        this(0, "");
    }
    public T1(int f1, String f2) {
        this.f1 = f1;
        this.f2 = f2;
    }
    public int getF1() {
        return f1;
    }
    public void setF1(int f1) {
        this.f1 = f1;
    }
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
}

TestPortImpl.java:

package ws.soap;

import javax.jws.WebService;

@WebService(serviceName = "TestService",
            portName = "TestPort",
            targetNamespace = "http://soap.ws",
            wsdlLocation = "WEB-INF/Test.wsdl",
            endpointInterface = "ws.soap.TestPort")
public class TestPortImpl implements TestPort {
    public T1 getOne(int f1) {
        // dummy implementation
        return new T1(f1, "getOne");
    }
    public ArrayOfT1 getAll() {
        // dummy implementation
        ArrayOfT1 res = new ArrayOfT1();
        res.getItem().add(new T1(1, "getAll #1"));
        res.getItem().add(new T1(2, "getAll #2"));
        res.getItem().add(new T1(3, "getAll #3"));
        return res;
    }
    public boolean save(int f1, String f2) {
        // dummy implementation
        return f1 > 0 && f2.length() > 0;
    }
    public boolean saveObject(T1 o) {
        // dummy implementation
        return o.getF1() > 0 && o.getF2().length() > 0;
    }
    public boolean delete(int f1) {
        // dummy implementation
        return f1 > 0;
    }
}

The WSDL file get stored in WEB-INF.

Client:

Clients can use generated client stub from WSDL.

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;

import ws.soap.T1;
import ws.soap.TestPort; // modified
import ws.soap.TestServiceLocator; // modified

public class TestClientWSDL {
    public static void main(String[] args) throws RemoteException, ServiceException {
        TestServiceLocator factory = new TestServiceLocator(); // modified
        TestPort soap = factory.getTestPort(); // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        T1[] all = soap.getAll();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

Real JAX-WS:

import java.util.List;

import ws.soap.T1;
import ws.soap.TestPort; // modified
import ws.soap.TestService; // modified

public class TestClientWSDL {
    public static void main(String[] args) {
        TestService factory = new TestService(); // modified
        TestPort soap = factory.getPort(TestPort.class); // modified
        T1 o = soap.getOne(123);
        System.out.println(o.getF1() + " " + o.getF2());
        List all = soap.getAll().getItem(); // modified
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        System.out.println(soap.save(123, "ABC"));
        System.out.println(soap.saveObject(o));
        System.out.println(soap.delete(123));
    }
}

Axis 2.x with ADB:

import ws.soap.TestServiceStub;
import ws.soap.TestServiceStub.T1;

public class TestClientADBWSDL {
    public static void main(String[] args) throws Exception {
        TestServiceStub soap = new TestServiceStub();
        TestServiceStub.GetOne getone = new TestServiceStub.GetOne();
        getone.setF1(123);
        T1 o = soap.getOne(getone).get_return();
        System.out.println(o.getF1() + " " + o.getF2());
        TestServiceStub.GetAll getall = new TestServiceStub.GetAll();
        T1[] all = soap.getAll(getall).get_return().getItem();
        for(T1 one : all) {
            System.out.println(one.getF1() + " " + one.getF2());
        }
        TestServiceStub.Save save = new TestServiceStub.Save();
        save.setF1(123);
        save.setF2("ABC");
        System.out.println(soap.save(save).get_return());
        TestServiceStub.SaveObject saveo = new TestServiceStub.SaveObject();
        saveo.setO(o);
        System.out.println(soap.saveObject(saveo).get_return());
        TestServiceStub.Delete del = new TestServiceStub.Delete();
        del.setF1(123);
        System.out.println(soap.delete(del).get_return());
    }
}
using System;

using ws.soap;

public class Program
{
    public static void Main(string[] args) 
    {
        TestService soap = new TestService(); // modified
        T1 o = soap.getOne(123);
        Console.WriteLine(o.f1 + " " + o.f2);
        T1[] all = soap.getAll();
        foreach(T1 one in all)
        {
            Console.WriteLine(one.f1 + " " + one.f2);
        }
        Console.WriteLine(soap.save(123, "ABC"));
        Console.WriteLine(soap.saveObject(o));
        Console.WriteLine(soap.delete(123));
     }
}
Imports System

Imports ws.soap

Public Class Program
    Public Shared Sub Main(args As String())
        Dim soap As New TestService() ' modified
        Dim o As T1 = soap.getOne(123)
        Console.WriteLine(o.f1 & " " & o.f2)
        Dim all As T1() = soap.getAll()
        For Each one As T1 In all
            Console.WriteLine(one.f1 & " " & one.f2)
        Next
        Console.WriteLine(soap.save(123, "ABC"))
        Console.WriteLine(soap.saveObject(o))
        Console.WriteLine(soap.delete(123))
    End Sub
End Class
using System;
using System.Collections.Generic;

using ws.soap;

public class Program
{
    public static void Main(string[] args) 
    {
        TestPortClient soap = new TestPortClient(); // modified
        T1 o = soap.getOne(123);
        Console.WriteLine(o.f1 + " " + o.f2);
        List all = soap.getAll();
        foreach(T1 one in all)
        {
            Console.WriteLine(one.f1 + " " + one.f2);
        }
        Console.WriteLine(soap.save(123, "ABC"));
        Console.WriteLine(soap.saveObject(o));
        Console.WriteLine(soap.delete(123));
     }
}
Imports System
Imports System.Collections.Generic

Imports ws.soap

Public Class Program
    Public Shared Sub Main(args As String())
        Dim soap As New TestPortClient() ' modified
        Dim o As T1 = soap.getOne(123)
        Console.WriteLine(o.f1 & " " & o.f2)
        Dim all As List(Of T1) = soap.getAll()
        For Each one As T1 In all
            Console.WriteLine(one.f1 & " " & one.f2)
        Next
        Console.WriteLine(soap.save(123, "ABC"))
        Console.WriteLine(soap.saveObject(o))
        Console.WriteLine(soap.delete(123))
    End Sub
End Class
<?php
function test($url) {
    $soap = new SoapClient($url);
    $o = $soap->getOne(123);
    echo $o->f1 . ' ' . $o->f2 . "\r\n";
    $all = $soap->getAll();
    foreach($all->item as $one) { // modified
        echo $one->f1 . ' ' . $one->f2 . "\r\n";
    }
    echo $soap->save(123, "ABC") . "\r\n";
    echo $soap->saveObject($o) . "\r\n";
    echo $soap->delete(123) . "\r\n";
}

test('http://localhost:8080/wssoapx/TestService?wsdl');
?>
from zeep import Client

def test(url):
    soap = Client(url)
    o = soap.service.getOne(123)
    print o.f1,' ',o.f2
    for one in soap.service.getAll():
        print one.f1,' ',one.f2
    print soap.service.save(123,'ABC')
    print soap.service.saveObject(o)
    print soap.service.delete(123)

test('http://localhost:8080/wssoapx/TestService?wsdl');

Article history:

Version Date Description
1.0 July 29th 2018 Initial version

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj