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.
Web services exist in two types:
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 (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:
Java defines the following web service standards:
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 comes with two seperate web service frameworks:
PHP comes with a Soap extension that just needs to be loaded.
There are several SOAP libraries available for Python:
The example code here will use Zeep.
Zeep is installed with "pip install Zeep".
Code first approach works like:
For static client language:
For dynamic client language:
For JAX-RPC service Axis 1.x is used.
The process to create a web service in Axis 1.x is:
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:
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:
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:
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"%>
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')
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.
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 approach works like:
For static client language:
For dynamic client language:
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 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.
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');
Version | Date | Description |
---|---|---|
1.0 | July 29th 2018 | Initial version |
See list of all articles here
Please send comments to Arne Vajhøj