Java SOAP WebService - How to accept single input parameter - java

I'm developing a SOAP based Webservice API which would be used by a third party system. WSDL file and endpoint URL would be shared with them and they invoke my Web service by passing an input parameter and get the response back
So far, I have created the Web Service API including WSDL, Services Classes, Helper Classes, DAO, Ibatis) and tested using SOAP UI by passing the input parameter, the request hits the databse and returning the response fine. Attached the WSDL file and SOAP request and response.
I got two questions,
The 3rd Party system is asking for an API documentation and SOAP Envelope. What should I give?
The 3rd party system has to invoke my service by passing the input parameter sQuoteRef. How would they usually do it? I'm not concerned about the client code here but how would the request from 3rd party look like and what changes I should make in my WSDL or Java classes to receive the input parameter and pass on to my service class?
Any inputs would be helpful in completing my task. Many Thanks!
WSDL File
<?xml version="1.0" encoding="UTF-8"?><wsdl:definitions name="EmpService" targetNamespace="http://emp.provider.integration.gi.sample.ie/EmpService/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Emp.provider.integration.gi.sample.ie/EmpService/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xsd:schema targetNamespace="http://Emp.provider.integration.gi.sample.ie/EmpService/">
<xsd:element name="EmpRequestVO">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="1" name="sQuoteRef" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="EmpResponseVO">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="0" name="sQuoteStatus" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
<wsdl:message name="GetEmpTransactionSoapIn">
<wsdl:part element="tns:EmpRequestVO" name="parameters"/>
</wsdl:message>
<wsdl:message name="GetEmpTransactionSoapOut">
<wsdl:part element="tns:EmpResponseVO" name="parameters"/>
</wsdl:message>
<wsdl:portType name="EmpServiceSoap">
<wsdl:operation name="getEmpTransaction">
<wsdl:input message="tns:GetEmpTransactionSoapIn"/>
<wsdl:output message="tns:GetEmpTransactionSoapOut"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="EmpServiceSoap" type="tns:EmpServiceSoap">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getEmpTransaction">
<soap:operation soapAction="getEmpTransaction" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="PLUSEmpServices">
<wsdl:port binding="tns:EmpServiceSoap" name="EmpServiceSOAP">
<soap:address location="http://localhost:9080/WebServices/services/EmpServiceSOAP"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
End Point URL
http://localhost:9080/PLUSEmpServices/services/EmpServiceSOAP
SOAP REQUEST in SOAP UI
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:rtw="http://emp.provider.integration.gi.sample.ie/EmpService/">
<soapenv:Header/>
<soapenv:Body>
<rtw:EmpRequestVO>
<sQuoteRef>12123118</sQuoteRef>
</rtw:EmpRequestVO>
</soapenv:Body>
</soapenv:Envelope>
SOAP RESPONSE in SOAP UI
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<p900:EmpResponseVO xmlns:p900="http://emp.provider.integration.gi.sample.ie/EmpService/">
<sQuoteStatus>Active</sQuoteStatus>
</p900:EmpResponseVO>
</soapenv:Body>
</soapenv:Envelope>

The 3rd Party system is asking for an API documentation and SOAP Envelope. What should I give?
To start APIs integration with external parties, your clients require a technical API document that will be a reference to complete APIs integration and a base for any technical communication between the teams. The technical document may includes the following details:
Brief description for different available web-services APIs and their requirements.
APIs communications flow. This will be useful in case the clients need to call more than one services in specific sequence. Including flow diagram will be possible option to explain the system flow.
Test environment details like WSDL URL and access credentials like username, password, agent code, etc.
Detailed description for all fields needed to send a valid request to the server. Fields details may include the following:
a. Filed name.
b. Description.
c. Data Type.
d. Max length.
e. Mandatory, Optional or Conditional.
f. Possible values if any.
All possible response codes a long with their description sent by server API to client API which indicates the successful or failure of transaction process(s). For example (0000 means Success, E001 means validation error, E002 system error, 9999 unknown error, etc.).
Note: Response codes should cover various rejection cases.
Security Section that explain how the data is exchange securely between client and server application.
a. Mention additional Encryption mechanism for part or all request data if any.
b. Communication type with the client wither it is over VPN or accessed public
over https URL. Note the URL you mentioned in your question is deployed in your local machine and it will not be available publicly.
Sample SOAP request(s) and response(s) xml format for all available API’s web methods.
Mention any additional rules or constrains on APIs connectivity.
The 3rd party system has to invoke my service by passing the input parameter sQuoteRef. How would they usually do it? I'm not concerned about the client code here but how would the request from 3rd party look like and what changes I should make in my WSDL or Java classes to receive the input parameter and pass on to my service class?
If I understand you correctly your are asking how should I change WSDL or Java classes to accept client request correctly ? Usually the server side API has the upper hand in XML format need to be exchanged and in your case it should be mentioned in WSDL. So the client will create a valid request as per definition mentioned in server WSDL URL.
Further reading that my be useful on how to generate WSDL from Java classes:
Eclipse WSDL generator (from java class)?
SOAP fault handling in java

Related

How to annotate web service parameters with constraints in java?

I have a GUI application that creates a bunch of XML documents using jdom and these documents conform to their respective schemas. It also has a WSDL creator class (which follows the standard schema provided by W3C) that creates a Services.wsdl. In this WSDL there are 'n' number of service descriptions with their respective entities(port, binding, type and message elements). The parameters of these web services have specific constraints associated with them.
Question 1: How to annotate service parameters with their specific constraints in the WSDL?
Question 2: Should I be using a custom SLAParameter extended from the WSLA Language by IBM or the WS-Policy language to annotate these parameters with their associated constraints?
XML:
<wsdl:message name="request">
<part name="param1" type="xsd:string"/>
<part name="param2" type="xsd:int"/>
</wsdl:message name="response"/>
<wsdl:message name="response"/>
...
<wsdl:operation name="myMethod" parameterOrder="param1, param2">
<input name="input" message="request"/>
<output name="output" message="response"/>
</wsdl:operation>
Java:
void myMethod(String param1, int param2) ...
P.S. The above snippet's source. WSLA and WS-Policy both complement WSDL in their own ways.

Method name in soap message request

I have an interesting problem, I'm using SOAP UI to parse the WSDL. The WSDL from payPal
https://developer.paypal.com/docs/classic/api/PayPalSOAPAPIArchitecture/
When I try to load it in SOAP UI, all the method names are getting created that's fine, but when I look into the Messages, The method name in tree view is BillOutstandingAmount But when I open the request
<soapenv:Body>
<urn:BillOutstandingAmountReq>
....
</urn:BillOutstandingAmountReq>
</soapenv:Body>
How the method name in SOAP request can be changed.? Is there any annotation to do so? When I look into the WSDL,
<wsdl:message name="BillOutstandingAmountRequest">
<wsdl:part name="BillOutstandingAmountRequest" element="ns:BillOutstandingAmountReq"/>
</wsdl:message>
The operation part of WSDL
<wsdl:operation name="BillOutstandingAmount">
<wsdl:input message="ns:BillOutstandingAmountRequest"/>
<wsdl:output message="ns:BillOutstandingAmountResponse"/>
</wsdl:operation>
After many hours I found the answer
#RequestWrapper(localName="localRequestName")
Whatever we're giving in the annotation localName will be the SOAP Request method name.

Java soap calls to pass an .XML file to WebService

So I am new to the whole SOAP and server concept. Ive put together a basic JAX-RPC and JAX-WS. I overall want to pass an .XML file to a web service, receive a response, and write it into a directory. Where do i start, what should I use, and where can find a tutorial/information based on it. Thank you!
Essentially there are two approaches you can take when designing a web service. The top down approach and the bottom up approach. I will give you a brief explanation of both methods and their ups and downs. There will also be links to some tutorials.
Top Down:
In the top down approach you start by modelling a XSD which will contain your request and response messages and the data structures that those requests and responses will use. You then model the operations i.e. the request and response that flows between client and service and finally you put this together into a WSDL. This resulting WSDL is then imported into a IDE such as Netbeans or Eclipse and you then start coding the internals of the service.
For example lets say you have a product service. In this service you want to create a operation that will search for a particular product based on product code. Thus you want to query the product service for product objects. The service will be called ProductService and the operation will be called GetProduct.
To achieve this you need to model a product object which has two string properties called description and code. You will also need to model a GetProductRequest message and a GetProductResponse message.
This might take on the following structures:
The code for the XSD would look like this:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns="http://www.wsexample.com/ProductService_V1/Product"
elementFormDefault="qualified"
targetNamespace="http://www.wsexample.com/ProductService_V1/Product"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="ProductCode_Type">
<xs:annotation>
<xs:documentation>This the product code type. It is based on the string data type it must be 8 characters long.
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:minLength value="8" />
<xs:maxLength value="8" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ProductDescription_Type">
<xs:annotation>
<xs:documentation>This is the base class for the product description field. This is a text field up to 255 characters long.</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:maxLength value="255" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Product_Type">
<xs:annotation>
<xs:documentation>This is the product base class it is used to perform CRUD operations with on all of the product service operations.
</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="ProductCode"
type="ProductCode_Type"
minOccurs="1"
maxOccurs="1" />
<xs:element name="ProductDescription"
type="ProductDescription_Type"
minOccurs="0"
maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="GetProductRequest_Type">
<xs:annotation>
<xs:documentation>This is the base class for the Get Product Request message. In the message you must pass one and only one product code which to search for. </xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="Product"
type="Product_Type" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="GetProductResponse_Type">
<xs:annotation>
<xs:documentation>This is the get product response message and will contain the result of the results of calling the getproductdescription operation on the Product service.
It will contain a product code which was passed in the Get Product Request message and optionally return one description.</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="Product"
type="Product_Type" />
</xs:sequence>
</xs:complexType>
<xs:element name="GetProductRequest"
type="GetProductRequest_Type" />
<xs:element name="GetProductResponse"
type="GetProductResponse_Type" />
</xs:schema>
You will now need to create a new WSDL to describe the service and use this XSD(I called it product.xsd) in this WSDL. As you can see we have modelled data structures to transport the product object and we have modeled the operations used in the service.
Our WSDL might look like this then:
This is the code for the WSDL
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="ProductService"
targetNamespace="http://wsexample.com/ProductService"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns="http://wsexample.com/ProductService"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:ProductData="http://www.wsexample.com/Product/ProductData">
<wsdl:types>
<xs:schema elementFormDefault="qualified"
targetNamespace="http://wsexample.com/ProductService">
<xs:import schemaLocation="Product.xsd"
namespace="http://www.wsexample.com/Product/ProductData" />
</xs:schema>
</wsdl:types>
<wsdl:message name="GetProduct">
<wsdl:part name="in"
element="ProductData:GetProductRequest" />
</wsdl:message>
<wsdl:message name="GetProductRs">
<wsdl:part name="out"
element="ProductData:GetProductResponse" />
</wsdl:message>
<wsdl:portType name="ProductEndPoint">
<wsdl:operation name="GetProduct">
<wsdl:input message="tns:GetProduct" />
<wsdl:output message="tns:GetProductRs" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="ProductServiceBinding"
type="tns:ProductEndPoint">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<wsdl:operation name="GetProduct">
<wsdl:input>
<soap:body parts="in"
use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body parts="out"
use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="ProductEndpointService">
<wsdl:port name="ProductServiceEndPointPort"
binding="tns:ProductServiceBinding">
<soap:address location="http://wsexample.com/ProductService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
So after all that modeling the message that will flow between the client and server will look like this:
Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:prod="http://www.wsexample.com/Product/ProductData">
<soapenv:Header/>
<soapenv:Body>
<prod:GetProductRequest>
<prod:Product>
<prod:ProductCode>12345678</prod:ProductCode>
</prod:Product>
</prod:GetProductRequest>
</soapenv:Body>
</soapenv:Envelope>
Response:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:prod="http://www.wsexample.com/Product/ProductData">
<soapenv:Header/>
<soapenv:Body>
<prod:GetProductResponse>
<prod:Product>
<prod:ProductCode>12345678</prod:ProductCode>
<!--Optional:-->
<prod:ProductDescription>A Basic product for kids to teach them how to count. </prod:ProductDescription>
</prod:Product>
</prod:GetProductResponse>
</soapenv:Body>
</soapenv:Envelope>
Now you can use the WSDL and XSD to implement the web service using this Netbeans tutorial and guide.. Its very simple really just start a new web project in netbeans then right click the project and add new file then just select the web service from WSDL file.
See the screen shot below:
Top down summary:
Top down requires you to do a lot of modelling and planning up front. However you are in complete control. This allows you to decide how the data is going to flow between client and server. It allows you to modify everything to your exact requirements. However it requires a lot of work before hand.
The biggest benefit from me is that I can design a WSDL and XSD that abstracts the data away from the providing systems into something more generic. This become important in integration projects.
Bottom Up:
The bottom up approach allows you to generate the artifacts I created above from java code. There is a excellent tutorial here that will show you all the details. However Java allows you to add annotations to a class that then exposes the class and its method as a web service.
So by taking a class and adding annotations you turn that class into a web service. See the code below for a quick and dirty example:
#Webservice
public Class CalculatorWS
{
#WebMethod
public int add(#WebParam(name = "i") int i, #WebParam(name = "j") int j) {
int k = i + j;
return k;
}
}
If you follow the tutorial you will probably go from code to fully working web service in a couple of minutes. Quick and easy.
Bottom Up Summary:
You have very little control over how your WSDL and XSD will look and behave. In some cases this approach will bind you tightly to the underlying model. It really depends on how you code it. So with bottom up approach you can have a prototype in minutes but the message flowing up and down might not be exactly what you had in mind.
IMPORTANT TIP:
Download a copy of SOAPUI it is really the best tool to use to test and even create mock services. If you are serious about using web services then get it now.
Hope this helps you down the rabbit hole.

SoapUI MockServices returning html rather than xml response

Using the following sample WSDL file, I've generated a new project in SOAP UI (version 3.5), and created the example test suite, test case, and mock service.
WSDL
<definitions name="HelloService"
targetNamespace="http://www.examples.com/wsdl/HelloService.wsdl"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.examples.com/wsdl/HelloService.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<message name="SayHelloRequest">
<part name="firstName" type="xsd:string"/>
</message>
<message name="SayHelloResponse">
<part name="greeting" type="xsd:string"/>
</message>
<portType name="Hello_PortType">
<operation name="sayHello">
<input message="tns:SayHelloRequest"/>
<output message="tns:SayHelloResponse"/>
</operation>
</portType>
<binding name="Hello_Binding" type="tns:Hello_PortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="sayHello">
<soap:operation soapAction="sayHello"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:examples:helloservice"
use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:examples:helloservice"
use="encoded"/>
</output>
</operation>
</binding>
<service name="Hello_Service">
<documentation>WSDL File for HelloService</documentation>
<port binding="tns:Hello_Binding" name="Hello_Port">
<soap:address
location="http://www.examples.com/SayHello/"/>
</port>
</service>
</definitions>
I can start up the mock service and access via the browser, whereby I see a link to the wsdl and can view it.
However, by using the default generated soap request (as follows), it returns an html response (appears to be the web page) rather than the soap response I have configured.
REQUEST
POST http://localhost:8088/SayHello/ HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "sayHello"
User-Agent: Jakarta Commons-HttpClient/3.1
Host: localhost:8088
Content-Length: 467
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:examples:helloservice">
<soapenv:Header/>
<soapenv:Body>
<urn:sayHello soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<firstName xsi:type="xsd:string">James</firstName>
</urn:sayHello>
</soapenv:Body>
</soapenv:Envelope>
RESPONSE
HTTP/1.1 200 OK
Content-Type: text/html; charset=iso-8859-1
Transfer-Encoding: chunked
Server: Jetty(6.1.x)
<html><body><p>There are currently 1 running soapUI MockServices</p><ul><li>Hello_Binding MockService</li></ul></p></body></html>
I've configured a sample response as follows :
SAMPLE RESPONSE ON MOCK
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:examples:helloservice">
<soapenv:Header/>
<soapenv:Body>
<urn:sayHelloResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<greeting xsi:type="xsd:string">?</greeting>
</urn:sayHelloResponse>
</soapenv:Body>
</soapenv:Envelope>
Its configured as the default response, so I have no idea why it is not being returned.
Any suggestions? +1's to anything that helps me progress this.
Thanks
No service is started for the endpoint URL from your request: http://localhost:8088/SayHello/. The only started service is located at URL http://localhost:8088/mockHello_Binding as reported in the service response. SoapUI returns list of all started mock services in HTML page when a non-existed one is requested. Consider fixing endpoint address to resolve this issue.
I have hit the same problem using soap ui 5.4.0. It have happend beacuse the path of created mock service was incorrect.
If you click in the soap UI, on the created mock service - properties button on the bottom - you will see that your path looks like /mockBindingservice, it should say /.
To change it, double click on the created mock service, click stop service than settings button (located next to stop and start buttons).Change Path to / and double check host.
Save, start service. Should work now.
I know it is an old post, but hopefully it will help sombody looking for the anwser.
The Url you are hitting is incorrect. The MockService URL is created while you import the WSDL to SOAP with default port 8088.
Solution:
1)Create new Project
2)Import WSDL
3)Check Create Mockervice
4)Then you will SEE the URL where mockservice will run::-->mockSearchURL(for eg)
5)hit HTTP://{IP}:8088/mockSearchURL
DONE!!
i think is the name in Url and the name in request
Url : SayHello
Request : sayHello S and s
name should be matched
I has similar issue with BizTalk 2010 Send Port and Mock SOAPUI webservice.
I found out that the 2 URL er different
When I open this in IE..I see the HTML response.
http://localhost:8088/MockUpdateBasicHttp
When you opened this in IE I got a blank white screen which normally means success.
http://localhost:8088//MockUpdateBasicHttp
The correct URL is with a single '/' after port number.
http://localhost:8088/MockUpdateBasicHttp

Consume Axis2 Web Services in Visual Studio 2008

I'm having a bit of trouble getting Visual Studio to play nicely with my Axis2 web service. The problem is very strange, although not a show stopper. If anything it is just annoying, and I'd really appreciate a way around this.
The problem is that when using the web service in C# code, none of the parameters or return values are in their native data types. So instead of just being able to call, for example:
string timeDiff = MyWebService.GetServerTimeDifference(LocalTime)
Console.WriteLine(timeDiff);
I have to write
MyWebService.GetServerTimeDifference request = new MyWebService.GetServerTimeDifference();
request.#LocalTime = LocalTime;
Console.WriteLine(MyWebService.GetServerTimeDifference(request).#return);
As you can tell, this gets very annoying very quickly. The strange thing is that when creating the Web Reference to the web service, all of the data types and parameters are correctly shown in the service discovery page. I have tried modifying the WSDL file for the web service to remove anything which may be confusing visual studio, but so far I haven't been able to get this to work as it should.
I read somewhere that this is a Visual Studio and/or .Net problem in the de-serialization process, rather than a problem with the web service itself. I'm thinking this may be true, as the web service can be consumed correctly within NetBeans.
The Web Service is written in Java and hosted on an axis2 / Tomcat server, but the client software will be written in C# .Net 2.0.
Anyway - has anybody experienced this before? I have been unable to find the page where I read about the de-serialization problem again, so if anybody has anything to say which could help me out I'd very much appreciate it.
I suggest you define your WSDL using the document/literal/wrapped style, which as far as I know seems to be the best fit when you want interoperability.
This makes your service implementation behave well with WCF, that is used by Viusual Studio 2008 when you define a Service Reference.
It is possible to modify the WSDL specifikation for your service without breaking an existing implementation, but don't count on it.
The tricky part, though, is that you have to use some special lingo in your WSDL so that WCF will not snap out of generating nice wrappers as you requested. In your case, the automatically generated client code seems to fall back to the document/literal style, where you create and initialize "structs" that you feed to your client service method.
In essence, what you need to do is to:
Define your wsdl:types using XML Schema element.
Stick to a reasonable subset of XML Schema constructs when defining types (see the WCF documentation for a list).
Declare all elements of object-type (such as xsd:token, xsd:NMTOKEN, xsd:base64Binary, ...) as nillable="true" - plain types such as xsd:int should not be nillable.
Be prepared that the first element in a sequence used as an answer will be returned from the service call while the rest will be passed as out parameters - this typically makes an xsd:int status a suitable candidate as first in sequence.
Define your wsdl:message using wsdl:part named parameters (not parameter, not param, it has to be parameters) and using the element attribute
Define the style of the soap:operation in the wsdl:binding as "document".
Declare wsdl:input and wsdl:output to use "literal" SOAP encoding.
Play with your WSDL file and use svcutil to generate the client code. You will get warnings if there are errors in your WSDL file and if you look into the generated code, you will see comments that may point to why the wrapping style fails.
Some code may be of use here. This is a stripped down WSDL describing a service with one method GetVersionInformation that returns the triplet {"1.0", 1, 0} - effectively being the version of the interface using major and minor version numbers.
<wsdl:definitions
targetNamespace="http://tempuri.org"
xmlns:tns="http://tempuri.org"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<xsd:schema
elementFormDefault="qualified"
targetNamespace="http://tempuri.org"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!-- Wrapper elements. Conventions apply to wrapper element names! -->
<xsd:element name="GetVersionInformation" nillable="true" type="tns:VoidType" />
<xsd:element name="GetVersionInformationResponse" nillable="true" type="tns:VersionInformationType" />
<!-- Just a void type -->
<xsd:complexType name="VoidType">
<xsd:sequence />
</xsd:complexType>
<!-- Major and minor version information -->
<xsd:complexType name="VersionInformationType">
<xsd:sequence>
<xsd:element nillable="true" minOccurs="1" maxOccurs="1" name="version" type="xsd:NMTOKEN" />
<xsd:element minOccurs="1" maxOccurs="1" name="major" type="xsd:int" />
<xsd:element minOccurs="1" maxOccurs="1" name="minor" type="xsd:int" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
<!-- GetVersionInformation -->
<wsdl:message name="GetVersionInformationSoapIn">
<wsdl:part name="parameters" element="tns:GetVersionInformation" />
</wsdl:message>
<wsdl:message name="GetVersionInformationSoapOut">
<wsdl:part name="parameters" element="tns:GetVersionInformationResponse" />
</wsdl:message>
<!-- Port type -->
<wsdl:portType name="MyServicePortType">
<wsdl:operation name="GetVersionInformation">
<wsdl:input message="tns:GetVersionInformationSoapIn" />
<wsdl:output message="tns:GetVersionInformationSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="MyServiceSOAP11Binding" type="tns:MyServicePortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
<wsdl:operation name="GetVersionInformation">
<wsdl:input>
<soap:body use="literal" parts="parameters" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="MyServiceSOAP12Binding" type="tns:MyServicePortType">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
<wsdl:operation name="GetVersionInformation">
<wsdl:input>
<soap12:body use="literal" parts="parameters" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="MyService">
<wsdl:port name="MyServiceSOAP11port" binding="tns:MyServiceSOAP11Binding">
<soap:address location="http://localhost:80/mojo/services/MyService" />
</wsdl:port>
<wsdl:port name="MyServiceSOAP12port" binding="tns:MyServiceSOAP12Binding">
<soap12:address location="http://localhost:80/mojo/services/MyService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
You may generate client code for this WSDL, using:
svcutil /t:code /serializer:DataContractSerializer /s /out:MyService.cs /n:*,MyService /ixt MyService.wsdl
Further, calling this code from C# would be like this:
// The endpointConfigurationName must match the corresponding entry
// in app.config, with the following content:
//
// <configuration>
// <system.serviceModel>
// <bindings>
// <basicHttpBinding>
// <binding name="MyServiceSOAP11Binding" ...>
// </binding>
// .../...
// </basicHttpBinding>
// </bindings>
// <client>
// <endpoint
/// ... binding="basicHttpBinding"
// ... bindingConfiguration="MyServiceSOAP11Binding"
// ... name="MyServiceSOAP11port" />
// </client>
// </system.serviceModel>
// </configuration>
//
string endpointConfigurationName = "MyServiceSOAP11port";
string wsEndpoint = "http://localhost/mojo/services/MyService";
MyService.MyServicePortTypeClient wsClient = null;
try
{
wsClient = new MyService.MyServicePortTypeClient(endpointConfigurationName, wsEndpoint);
}
catch (InvalidOperationException ioe)
{
// Possibly a problem with the configuration
// Inform(Logging.LogLevel.WARNING, "Potential problem with configuration: " + ioe.Message);
return;
}
string wsUsername = "John";
string wsPassword = "Doe";
if (!String.IsNullOrEmpty(wsUsername) && !String.IsNullOrEmpty(wsPassword))
{
UserNamePasswordClientCredential credentials = wsClient.ClientCredentials.UserName;
credentials.UserName = wsUsername;
credentials.Password = wsPassword;
}
try
{
int major;
int minor;
string version = wsClient.GetVersionInformation(out major, out minor);
// Inform(Logging.LogLevel.DEBUG, "Service has version " + version);
}
catch (System.ServiceModel.EndpointNotFoundException enfe)
{
// string info = "Could not contact MyService: " + enfe.Message;
// Inform(Logging.LogLevel.ERROR, info);
return;
}
catch (System.ServiceModel.FaultException fe)
{
// string info = "Could not contact MyService: " + fe.Message;
// Inform(Logging.LogLevel.ERROR, info);
return;
}
While we are at it, why not also implement the service using Axis2. First, we need a service specification (services.xml in our AAR):
<serviceGroup name="MyServices">
<service name="MyService" scope="application">
<description>My Service - document/literal wrapped style, suited for .NET integration</description>
<!-- Service methods -->
<operation name="GetVersionInformation">
<messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
<actionMapping>http://tempuri.org/MyServicePortType/GetVersionInformationRequest</actionMapping>
</operation>
<!-- Use WS-Adressing, ... -->
<module ref="addressing" />
<!-- Service implementation -->
<parameter name="ServiceClass">com.mycompany.services.MyService</parameter>
</service>
<service name="MyOtherService" scope="application" >
.../...
</service>
</serviceGroup>
And our server implementation, using AXIOM:
package com.mycompany.services.MyService;
import javax.xml.stream.XMLStreamException;
import javax.xml.namespace.QName;
import org.apache.axiom.om.*;
import org.apache.axis2.context.ServiceContext;
import org.apache.log4j.Logger;
public class MyService {
public static final Integer MAJOR_VERSION = 1;
public static final Integer MINOR_VERSION = 0;
public static final String NAMESPACE = "http://tempuri.org";
public static final String NAMESPACE_ALIAS = "tns";
public static final String GET_VERSION_INFORMATION_RESPONSE_ELEMENT_NAME = "GetVersionInformationResponse";
private ServiceContext serviceContext = null;
private String serviceName = null;
private static final Logger log = Logger.getLogger("SERVICE");
public void init(ServiceContext serviceContext) throws Exception {
this.serviceContext = serviceContext;
serviceName = serviceContext.getName();
}
public OMElement GetVersionInformation(OMElement element) throws XMLStreamException {
// --- Handle request ---
String version = "" + MAJOR_VERSION + "." + MINOR_VERSION;
if (log.isDebugEnabled()) {
log.debug("Retrieving version information: " + version);
}
// --- Prepare response ---
OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace omNs = factory.createOMNamespace(NAMESPACE, NAMESPACE_ALIAS);
//
OMElement response = factory.createOMElement(GET_VERSION_INFORMATION_RESPONSE_ELEMENT_NAME, omNs);
{
OMElement value;
{
value = factory.createOMElement("version", omNs);
value.addChild(factory.createOMText(value, version));
response.addChild(value);
}
{
value = factory.createOMElement("major", omNs);
value.addChild(factory.createOMText(value, "" + MAJOR_VERSION));
response.addChild(value);
}
{
value = factory.createOMElement("minor", omNs);
value.addChild(factory.createOMText(value, "" + MINOR_VERSION));
response.addChild(value);
}
}
return response;
}
}
Axis2 and Axiom are really nice to work with. If you are having problems to generate your C# client, revisit your WSDL - it is unlikely that the problem resides with Axis2. By the way, the "Adressing" module that we refer to in the service configuration is usually added by default, but there are other modules that may be used to handle other parts of the WS-I standard.
I read somewhere that this is a Visual
Studio and/or .Net problem in the
de-serialization process, rather than
a problem with the web service itself.
I'm thinking this may be true, as the
web service can be consumed correctly
within NetBeans.
I have had this problem before - it's a .Net problem. My approach was to take a sledgehammer to the ant as it were and re-write the service in .Net

Categories