Introduction
There is not much information on sending SOAP messages between .NET clients and web services written in Java and if it exists, it is based on sending and receiving primitive data information such as numbers and small text strings.
But what if we want to interchange complex objects between two platforms that in theory should interchange information in a transparent way? The fact is that a client sends a SOAP request message (SOAP provides a standard and simple way to present messages in an XML format) to a web service, the web service receives the XML document, verifies that it has the SOAP message characteristics (Envelope, Header, Body), processes the message and returns a SOAP response message.
Any object (no matter how sophisticated) will be serialized, and after that it will be incorporated in the SOAP message and sent to the web service. If the structure of the SOAP message is the same in the client as in the web service, the only thing that we would have to worry about is that there exists a correct object deserialization in the web service, in order for the message to be interpreted correctly.
The objective of this article is to show how to send and receive complex SOAP messages between .NET client and Java Axis web service and it is not to explain how the transmission of SOAP messages works in each platform. That information can be found very easily on the Web.
Using the Code
The example here consists in sending an XML document from a client written in C# to a web service written in Java. Apache Axis as implementation tool of SOAP messages in an Apache Tomcat web server. This is the structure of the XML document to send:
* Table Order Details of the base of Northwind data coming with SQL Server 2000.
The web service processes the purchase orders of the XML documents requests and returns the accumulated of the unit price multiplied by for the quantity minus the discount. The return structure is as follow:
Apache Axis Web Service
Start by building a web service with a method that receives an XML document as a parameter. Previously, we needed to install Tomcat Apache and the necessary files in order for the Axis Apache to work (for example the JAVA JRE and JDK). Building an Axis web service is very simple. We have to follow the steps given below:
- First, deploy the web service through a describer file. To do this, create the following document and name it deploy.wsdd.
<deployment xmlns="http ://xml.apache.org/axis/wsdd/"
xmlns:java="http ://xml.apache.org/axis/wsdd/providers/java"
xmlns:xsi="http ://www.w3.org/2000/10/XMLSchema - instance">
<service name="wsOrderSummary" provider="java:RPC">
<parameter name="className" value="wsOrderSummary">
<parameter name="allowedMethods" value="*">
<operation name="getOrderSummary">
<parameter name="XMLdoc" mode="IN">
</operation>
</service>
</deployment>
- Next, execute the deploy file to update the Axis server. Execute the following code in a DOS command window. Perhaps you'll have to change the paths and jar files versions, if it's necessary.
java - classpath "C:\Program Files\Foundation software Apache\
Tomcat 5.5\webapps\axis\WEB-INF\lib\axis.jar";
"C:\Program Files\Foundation software Apache\
Tomcat 5.5\webapps\axis\WEB-INF\lib\axis-ant.jar";
"C:\Program Files\Foundation software Apache\
Tomcat 5.5\webapps\axis\WEB-INF\lib\jaxrpc.jar";
"C:\Program Files\Foundation software Apache\
Tomcat 5.5\webapps\axis\WEB-INF\lib\commons,logging,1.0.4.jar";
"C:\Program Files\Foundation software Apache\
Tomcat 5.5\webapps\axis\WEB-INF\lib\commons,discovery,0.2.jar";
"C:\Program Files\Foundation software Apache\
Tomcat 5.5\webapps\axis\WEB-INF\lib\saaj.jar";
"C:\Program Files\Foundation software Apache\
Tomcat 5.5\webapps\axis\WEB-INF\lib\activation.jar"
org.apache.axis.client.AdminClient deploy.wsdd
- Then, build the following two classes in Java. The first class will be named
WsOrderSummary
, that it's the web service in Java properly. Add the following method to your wsOrderSummary
class:
public Document getOrderSummary(Document XMLdoc )
{
try
{
NodeList XMLrows = XMLdoc.getElementsByTagName("row");
ClassOrderSummary array =
new ClassOrderSummary[1];
for( int row=0; row < XMLrows.getLength() ; row++)
{
NodeList XMLcolumns = XMLrows.item( row ).getChildNodes();
int orderId = Integer.parseInt
(XMLcolumns.item(0).getTextContent().trim());
float unitPrice = Float.valueOf
(XMLcolumns.item(2).getTextContent().trim()).floatValue();
int Quantity=Integer.parseInt
(XMLcolumns.item(3).getTextContent().trim());
float Discount = Float.valueOf
(XMLcolumns.item(4).getTextContent().trim()).floatValue();
float Total = (unitPrice*Quantity)*( 1 - Discount);
int idx=existsOrder( array, orderId );
if( idx >= 0)
{
array[idx].OrderTotal += Total;
}
else
{
array[array.length-1] = new ClassOrderSummary();
array[array.length-1].OrderID = orderId;
array[array.length-1].OrderTotal = total;
array=(ClassOrderSummary ) resizeArray( array, array.length + 1);
}
}
array=(ClassOrderSummary ) resizeArray
( array, array.length -1);
Document XMLResponse = createXMLOrders( array );
return(XMLResponse );
}
catch(Exception ex )
{
System.out.println ( " error:" + ex.getMessage ());
ex.printStackTrace ();
return( null );
}
}
And the second class will be used to build the XML SOAP response. This class will be named ClassOrderSummary
. See ClassOrderSummary.java file:
public class ClassOrderSummary {
public int OrderID;
public float OrderTotal;
public ClassOrderSummary()
{
}
}
- After we have the two Java files, compile them by using the Java JDK compiler. Execute the following code in a DOS command window. Don't forget to modify javac.exe path file if it's necessary, also the two Java class files.
C:\Program Files\Java\jdk1.5.0_05\bin\javac.exe
wsOrderSummary.java ClassOrderSummary.java
- To finish the deploy, the compiler creates 2 class files, copy those files to the axis' classes folder:
C:\Program Files\Foundation software Apache\
Tomcat 5.5\webapps\axis\WEB-INF\classes
- Restart the Tomcat Apache service. Test the web service opening a web browser on the following URL http://localhost:8080/axis/services/wsOrderSummary?wsdl. Change the server name if the web service isn't on your local host machine. If you have an IDE (eclipse, JDeveloper, etc.), one of the recover steps mentioned will be carried out by the IDE.
JAVA Tools used |
Type | Version |
JRE | 1.5.0.06 |
JDK | 1.5.0.05 |
Apache Tomcat | 5.5 |
Apache Axis | 1.4 |
C# Client
To send a SOAP message in the .NET Framework, we use WSE (Web Services Enhancements) 3.0. We start by analyzing the SOAP message structure that the Axis web service publishes.
="1.0"="utf-8"
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:impl="http://localhost:8080/axis/services/wsOrderSummary"
xmlns:apachesoap="http://xml.apache.org/xml-soap">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<impl:getOrderSummary>
<XMLdoc xsi:type="apachesoap:Document"></XMLdoc>
</impl:getOrderSummary>
</soap:Body>
</soap:Envelope>
There are two important namespaces, the first one is "xmlns:impl
" that qualifies the name space for this particular web service and the second one is "xmlns:apachesoap
" that identifies Java special class types. Inside the Body
is the method "impl:getOrderSummary
", which we want to invoke. Inside this method is the parameter named "XMLDoc
" that it's an "apachesoap:Document
" type. It's equivalent to the XmlDocument
object in .NET.
- First, create a console application. After this, create a derived class from the
SoapClient
object, in order to access its properties and mainly to invoke the SendRequestResponse
method, it permits sending and receiving SOAP messages. It will be named ClassSoapClient
.
public class ClassSoapClient : SoapClient
{
private String soapAction;
public ClassSoapClient(string soapAction, EndpointReference ref1) : base(ref1)
{
this.soapAction = soapAction;
}
public SoapEnvelope SendEnvelope(XmlDocument xmlDoc)
{
SoapEnvelope soapRequest = new SoapEnvelope();
soapRequest.CreateBody();
soapRequest.Body.AppendChild
(soapRequest.ImportNode(xmlDoc.DocumentElement, true));
return base.SendRequestResponse(soapAction, soapRequest);
}
}
Note that SendEnvelope
method receives an XML document as a parameter; it has inside an object serialized that we want to send to the Java web service. It inserts the XML document in SoapEnvelope (part of WSE) type object. Next, it calls SendRequestResponse
method which sent the SOAP message through a protocol that usually is HTTP. SendRequestResponse
won't return the control until an answer message is received or timeout is reached. Then the answer message is returned as a SoapEnvelope
object again.
- Next, update the
Program
class that is in the file Program.cs, which is part of the console application that it has created previously.
static void Main( string args )
{
Run();
}
In the Run
method, write the following:
String _UrlWebService =
"http://localhost:8080/axis/services/wsOrderSummary";
String _pathFiles = "C:\\NET_with_WS_AXIS\\DotNetClient\\Xml\\";
int _minutes = 3;
"_UrlWebservice
" is the URL of the location of the web service; the variable "_pathFiles
" is the path in which the XML for sending is found and where we save the soap message received from the Java web service. Create an instance of ClassSoapClient
class and set the timeout in 3 minutes, also create the Address
object, which represents a URL object from where the web service will send the SOAP message.
ClassSoapClient sc = new ClassSoapClient
("http ://inApacheAxisSoapActionDoesnotCare.org",
sc.Timeout = _minutes * 60000;
sc.Destination.Address = new Microsoft.Web.Services3.Addressing.Address
(new Uri(_UrlWebService));
- Next create and load the XML document for sending.
XmlDocument docXML = new XmlDocument();
docXML.Load ( _pathFiles + " Orders.xml ");
It's necessary to serialize the method and the parameter of the web service that we want to access. Without this, the SendEnvelope
method doesn't know which method to access. It loads all this in an XmlDocument
object adding as a child parameter of the method the previous XML document with the orders purchase information.
String soapString = "<impl:getOrderSummary xmlns:
xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
" xmlns:impl=\"http://localhost:8080/axis/services/wsOrderSummary\" >" +
"<XMLdoc xsi:type=\"apachesoap:Document\" />" +
"</impl:getOrderSummary>";
XmlDocument requestXML = new XmlDocument();
requestXML.LoadXml(soapString);
requestXML.FirstChild.FirstChild.AppendChild(requestXML.ImportNode
(docXML.DocumentElement, true));
- Finally, the SOAP Request is sent, the method gets back the SOAP Response, the SOAP Response is saved into a file for later analysis. Also we extract the "
ORDERS
" node; it contains the answer from the web service. At this point, we can deserialize this XML document in an object, for example load it into a Dataset
object or save it into a database.
SoapEnvelope soapResponse = new SoapEnvelope();
soapResponse = sc.SendEnvelope(requestXML);
soapResponse.Save(_pathFiles + "soapResponse.xml");
docXML.InnerXml = soapResponse.Body.GetElementsByTagName
("ORDERS")[0].ParentNode.InnerXml;
docXML.Save(_pathFiles + "OrdersSummary.xml");
.NET Tools used |
Type | Version |
Microsoft.NET Framework | 2.0 |
WSE | 3.0 |
IDE | Microsoft Visual C# 2005 Express Edition |
History
- 2nd August, 2007: Initial post