wsdlpull is a C++ web services client library. It includes a WSDL Parser,a XML Schema parser and validator and an XML Parser and serializer. It has an API and command line tool for dynamic WSDL inspection and invocation.The command line tool is a generic web service client using which you can invoke most web services from command line without writing any code.
wsdl http://www.ignyte.com/webservices/ignyte.whatsshowing.webservice/moviefunctions.asmx?wsdl GetTheatersAndMovies 90210 3
The above command gets all movies showing within 3 miles of Beverly Hills,CA,USA
The Web service Invocation API allows you to dynamically inspect and invoke a web service.
Unlike other toolkits there is no need to generate stubs or type serializers.
You can use the dynamic invocation API to write your own web service clients in one step.
Writing a web service client involves providing the WSDL URL,setting the operation,the input types and getting back the results.
No knowledge of SOAP is needed to write a web service client using wsdlpull.
The invocation API is quite protocol independent and focusses on presenting a simple and intutive interface and abstracts away protocol specific details.
wsdlpull comes with a generic web service invocation tool wsdl which uses the above API. Both the API and the tool are very dynamic and allow invocation of WSDLs which have complex types and simple types in the schema.Using the wsdl tool you can invoke complex web services from command line without writing any code.
The WSDL parser provides APIs to access various WSDL elements such as operations,messages,bindings,port types.Although wsdlpull is based on the pull parsing style of xml parsing the API is meant to be semantically as close as possible to the standard API for parsing WSDL,namely WSDL4J.
The schema parser and validator provides an API for parsing XSD schema documents and validating their xml instance documents. The schema parser is used to parse the types section of the WSDL document.The Xml parser and serializer provide a C++ implementation of the XML Pull API.WSDL processing,XSD schema processing,sending and receiving SOAP messages use the parser and serializer.
Download wsdlpull and follow the instructions below. The package has detailed doxygen documentation for all the APIs
Linux/Unix Installing from source
Unpack the archive and give the following commands ./configure --help ./configure --prefix=%{prefix} [other options] make make installThis will compile all the targets excluding examples. If you want to compile the examples use the --enable-examples option with configure.
Normally ,g++ does optimization of c++ code and it becomes impossible to debug.If you wish to debug then do --disable-opt with configure.
For logging related code to be present add --with-log with configure.
To write a web service client using wsdlpull make sure to add the $(prefix)/include in your include path and add $(prefix)/libs and -lwsdl -lschema -lxmlpull to your LDFLAGS .
Installing from rpms
RPMs for Fedora core 3 are available on the Sourceforge site. Dag Wieers's RPM repository has RPMs for many platforms.
Windows
Win32 users can find MSVC++ project files in the win32 directory.Open wsdlpull.dsw in MSVC++ and do a clean build.
Additionally makefiles are provided for DLL and single/multi threaded builds.
For logging related code to be present add LOGGING in the preprocessor defines of the MSVC++ project.
The path to the SOAP schemas required for parsing is set to "src/schemas" by default.This can be overriden at compile time
by defining the SCHEMADIR flag.
If try to execute the wsdl tool from another location,you would have to recompile with the new SCHEMADIR.
wsdlpull has a tool called wsdl,a generic web service invocation tool which uses the invocation API.Using this command you can see the web service operations,their descriptions and provide operation's inputs and get the output.
A way to use it is like this.
[user@localhost]wsdl http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl getQuote XYZW 27.101
If you do not specify the operation name or any of the parameters,the tool gives a choice of operations to invoke and prompts for the operation's parameters.With the -d option you can also see the operation's documentation.
[user@localhost]wsdl http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl 1.getRate : Choose one of the above operations [1-1] :1 country1:Japan country2:India 46.23If you just want it in one shot then you can do
[user@localhost]wsdl http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl getRate "United States" India 43.81
If the parameter is a space separated string make sure you enclose it in " " or else the shell would treat it as 2 arguments.
The -l option simply lists the operations of the web service.The below command shows how you can see a list of operations available for the MovieSearch Web service from www.ignyte.com along with a description of what each operation does.
[user@localhost]wsdl -l -d http://www.ignyte.com/webservices/ignyte.whatsshowing.webservice/moviefunctions.asmx?wsdl 1.GetTheatersAndMovies(This method will retrieve a list of all theaters and the movies playing today.) 2.GetUpcomingMovies(This method will retrieve a list of all movies that are scheduled to be released in the future and their anticipated release dates.)
The -v options runs the tool in the verbose mode. It displays wsdl operations' documentation and also logs the SOAP requests and response.
Invoking the tool with no arguments shows the usage of the command.The wsdl invocation tool can invoke web services which accept and/or return complex types and simple types in their description.If a web service needs a complex type then only its constituent simple type particles,are passed as inputs. Simple types are validated for basic checks and also against any schema provided restrictions such as enumeration. For example invoking the instant messenger service from http://www.bindingpoint.com/ws/imalert/ involves sending a complex type defined as below
<s:element name="SendYahoo"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="LKEY" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="FromName" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="ToUserID" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Message" type="s:string" /> </s:sequence> </s:complexType>When you run the wsdl tool on the IM web service you will have to just enter the 4 simple constituent elements.
[user@localhost wsdls]$wsdl im.wsdl 1.SendAIM 2.SendICQ 3.SendMSN 4.SendYahoo Choose one of the above operations [1-4] :4 LKEY: ****** FromName: Foo ToUserID: bar666 Message:Message1 ### or a one liner [user@localhost wsdls]$wsdl im.wsdl SendYahoo ***** Foo bar666 "This is a test message"
Sometimes a schema can specify how many times or a range(minOccurs..maxOccurs) a particle can occur inside its content model. This is often used to implement arrays by specifiying the maxOccurs as unbounded. By using the -o options the wsdl tool prompts for occurrences of input parameters.
Notes
Obviously there is a limit on the expressive power of providing parameters from command line for wsdl invocation. Here are a few notes.
You can use the invocation API to write a web service client. Unlike many other toolkits you don't need to generate skeleton stubs or write type serializers or even have any knowledge of SOAP.The below example illustrates how to write a client for the StockQuotes.wsdl web service included in the distribution.
<s:element name="GetQuotes">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="QuoteTicker" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="GetQuotesResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="GetQuotesResult" type="s0:ArrayOfQuote" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="ArrayOfQuote">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="Quote" type="s0:Quote" />
</s:sequence>
</s:complexType>
<s:complexType name="Quote">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="CompanyName" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="StockTicker" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="StockQuote" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="LastUpdated" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Change" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="OpenPrice" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="DayHighPrice" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="DayLowPrice" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Volume" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="MarketCap" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="YearRange" type="s:string" />
</s:sequence>
</s:complexType>
<s:element name="ArrayOfQuote" nillable="true" type="s0:ArrayOfQuote" />
</s:schema><message name="GetQuotesSoapIn">
<part name="parameters" element="s0:GetQuotes" />
</message>
<message name="GetQuotesSoapOut">
<part name="parameters" element="s0:GetQuotesResponse" />
</message><portType name="StockQuotesSoap">
<operation name="GetStockQuotes">
<input name="GetQuotes" message="s0:GetQuotesSoapIn" />
<output name="GetQuotes" message="s0:GetQuotesSoapOut" />
</operation>
</portType>
First,include the relevant header file.
#include <wsdlparser/WsdlInvoker.h>
The convention while including files from wsdlpull is to specify the root include directory in the include path and then specify the actually directory while #include -ing. Files belonging to the schema parser would be included like
#include <schemaparser/SchemaParser.h>
Create an instance of the WsdlInvoker.
WsdlInvoker invoker;
if (!invoker.setWSDLUri("StockQuotes.wsdl")) {
std::cerr<<invoker.errors()<<std::endl; return 2;
}
Now set the operation .Before that you can also get a list of operations available
std::vector<std::string> ops;
invoker.getOperations(ops);
if(!invoker.setOperation("GetStockQuotes")){
std::cerr<<"Error calling GetStockQuotes "<<std::endl;
return 2;
}
Set the input values. Setting the input values can be done by passing a void* or by passing a string representation of the value.
The string representation is provided for convinience so that you can just read something from the standard input using
scanf or cin and pass it on to the WsdlInvoker to do the type validation check.You dont have to bother about reading an int or a
float or a string.Just read the data from the stream as a string and allow the invoker to do all the validation.
if (invoker.status()){
std::string ticker("XYZ");
if (!invoker.setValue("QuoteTicker",(void*)(&ticker))){
std::cerr<<"Incorrect input value "<<ticker<<std::endl;
return 2;
}
if (invoker.invoke()){
Schema::Type type;
void *val = invoker.getValue("OpenPrice",type);
//type is a string
cout<<*((std::string*)val)<<std::endl;
}
The above example is illustrates the simple API usage.
However you can get more control over the input and output with more of the API.
You can set the occurrence constraints like this.
After invoking the web service ,you can
get the individual elements using getValue() like above.
std::vector<std::string> stocks
stocks.push_back("IBM");
stocks.push_back("YHOO");
stocks.push_back("MSFT");
stocks.push_back("MOT");
//4 occurrences of the <QuoteTicker> element
invoker.setValue("QuoteTicker",stocks);
The getValue() method returns a type via reference ,which
can be used to typecast the return void*. xsdType:string is serialized as
std::string and others are fairly obvious such as int and float for xsdType:int
and xsdType:float respectively.
See examples/stocks/stocks.cpp to see the above code in action.
Apart from the simple style above
there are apis to examine all the inputs of a web service and set values with occurrence constraints
for each of them.
The above API exposes all the simple types that need to be passed as input.Even if a web service accepts
a complex type ,calls to getNextInput exposes the constituent particles which are of a simple type.
You can either use this style or directly set the value using setValue() as shown earlier.
int getNextInput(std::string & param ,Schema::Type & type,int & min,int & max);
Outputs can be read either using getValue() or by getting the TypeContainer for all the message response parts using getNextOutput().The TypeContainer interface is more complicated but allows better access to the XML structure,such as reading attributes,multiple occurrences etc. The api is in the schemaparser/TypeContainer.h
The generic web service invocation tool 'wsdl' in wsdlparser/wsdl.cpp uses the complex style.Check that for an example
wsdlpull has a WSDL Parser which can be used to parse and examine the WSDL.
Create an instance of the WSDLParser object passing in the url
of the wsdl
file.
WsdlParser wp (url, cout);
Make sure you include the Wsdlparser header files.
#include <wsdlparser/WsdlParser.h>
The parsing process progresses whenever the you start
calling WSDLParser::getNextElement (). It parses a top
level WSDL element and returns the kind of element just parsed ,which
can be one of
WsdlParser::START,
Using the return value you can use any of the various getter methods
to the get the WSDL elements just parsed. example getBinding() returns
a pointer to the binding just parsed.
At any time the state of the parser can be queried using
WSDLParser::getEventType ,The parsing has ended when the method
returns WSDLParser::END.
WsdlParser::DEFINITION,
WsdlParser::DOCUMENTATION,
WsdlParser::ANNOTATION,
WsdlParser::TYPES,
WsdlParser::MESSAGE,
WsdlParser::PORT_TYPE,
WsdlParser::BINDING,
WsdlParser::SERVICE,
WsdlParser::IMPORT,
WsdlParser::END
The Error status can be be queried using
while (wp.getEventType () != WsdlParser::END){
switch (wp.getNextElement ()) {
case WsdlParser::DOCUMENTATION:
cout << wp.getDocumentation () << endl;
break;
case WsdlParser::TYPES:
cout << wp.getNumSchemas () << " schema(s) found" << endl;
break;
case WsdlParser::MESSAGE:
cout <<"Message :"<<(wp.getMessage())->getName () << endl;
break;
case WsdlParser::PORT_TYPE:
const PortType * p = wp.getPortType ();
cout <<"PortType:"<<p->getName () << " has " <<
p->getNumOps () << " operations "<<endl;
Operation::cOpIterator from,to;
p->getOperations(from,to);
while(from!=to){
cout<<(*from)->getName()<<endl;
from++;
}
break;
}
}
WsdlParser::status()
Even after parsing is over you can use the getter methods to get
all the WSDL Elements or query using a valid Qname,for
example getPortType(Qname(myNamespace:portType1))
would return reference to the port type whose name is portType1.
The below code prints out all the operations in the WSDL
#include <wsdlparser/WsdlParser.h>
PortType::cPortTypeIterator p1,p2;
wParser.getPortTypes(p1,p2);
while(p1!=p2){
Operation::cOpIterator op1,op2;
(*p1)->getOperations(op1,op2);
while(op1!=op2){
cout<<(*op1)->getName();
op1++;
}
p1++;
}
See the doxygen documentation of the API that comes with the package
std::string
MyExtension::getNamespace()const
{
return "my-namespace";
}
std::string
MyExtension::getExtensibilitySchema()const
{
return "http://tempuri.org/myschema.xsd";
}
SOAP binding has been parsed this way,so check it out for an
example.
m_soap = new Soap();
wParser.addExtensibilityHandler (m_soap);
//Soap derives from WSDLExtension
wsdlpull also includes a schema parser. The Schema
parser itself can be used in isolation to parse xsd schemas. The schema parser
can validate an instance document of the xsd schema just parsed.
Create a SchemaParser object
#include <schemaparser/SchemaParser.h>
#include <schemaparser/SchemaValidator.h>
...
SchemaParser sParser= new SchemaParser(uri,"target namespace");
Calling SchemaParser::parseSchemaTag() parses the entire
document. Use getter methods to get
types/elements and attributes .Use SchemaValidator to parse an instance of a
type or element by
calling
The TypeContainer returned is a recursvive
container structure holding the values of xml elements and attributes in the
instance.
See src/schemaparser/schema.cpp to
understand how to do this.
SchemaValidator::validate(int xType, XmlPullParser* ,TypeContainer*)
See the doxygen documentation of the API that comes with the package
wsdlpull includes a schema validation tool 'schema' Take the schema below which has a bunch of restricted schema types
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:simpleType name="USState"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="AK"/> <xsd:enumeration value="AL"/> <xsd:enumeration value="AR"/> <xsd:enumeration value="TX"/> <xsd:enumeration value="IL"/> <xsd:enumeration value="FL"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="myInteger"> <xsd:restriction base="xsd:integer"> <xsd:minExclusive value="10000"/> <xsd:maxInclusive value="99999"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="listOfMyIntType"> <xsd:list itemType="myInteger"/> </xsd:simpleType> <xsd:simpleType name="listOfInt"> <xsd:list itemType="xsd:int"/> </xsd:simpleType> <xsd:simpleType name="USStateList"> <xsd:list itemType="USState"/> </xsd:simpleType> <xsd:simpleType name="SixUSStates"> <xsd:restriction base="USStateList"> <xsd:length value="8"/> </xsd:restriction> </xsd:simpleType> <xsd:element name="listOfMyInt" type="listOfMyIntType"/> <xsd:element name="sixStates" type="SixUSStates"/> </xsd:schema>
<listOfMyInt>20003 15037 95977 95945</listOfMyInt> <listOfMyInt>10000 10001</listOfMyInt> <sixStates>PA NY CA NY LA AK</sixStates> <sixStates>FL IL</sixStates> <listOfMyInt>3 4 5</listOfMyInt> <sixStates>FL IL AK</sixStates> <sixStates>PA IL AK</sixStates>
The output below catches the errors that some of the data types have wrt the schema,including violation of enumeration,length,max and min facets.
[user@localhost]schema list.xsd -i list.xml Successfully parsed schema :listOfMyInt 20003 15037 95977 95945 :listOfMyInt 10000 10001 -->Invalid value for data type :sixStates PA NY CA NY LA AK -->Invalid value for data type :sixStates FL IL -->Invalid value for data type :listOfMyInt 3 4 5 -->Invalid value for data type :sixStates FL IL AK :sixStates PA IL AK -->Invalid value for data typeThe tool can flag many other validation errors including violation of occurrence constraints,and missing attributes
Invoking the popular stock quotes web service and the currency exchange services
wsdl http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl getQuote SYMBOL
wsdl http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl getRate "United States" India
wsdl http://www.nanonull.com/TimeService/TimeService.asmx?wsdl getCityTime Tokyo
Or a more complex google search whose wsdl is included in the test directory.Get the first 5 google search results for 'wsdlpull'wsdl GoogleSearch.wsdl doGoogleSearch "****license-key****" wsdlpull 1 5 0 1 1 1 1 1
Get a dictionary/thesarus on your command line
wsdl http://services.aonaware.com/DictService/DictService.asmx?WSDL DefineInDict wn thesaurus
wsdl http://services.aonaware.com/DictService/DictService.asmx?WSDL DefineInDict moby-thes thesaurus
schema http://api.search.yahoo.com/VideoSearchService/V1/VideoSearchResponse.xsd -i "http://api.search.yahoo.com/VideoSearchService/V1/videoSearch?appid=YahooDemo&query=madonna&results=1"
The below code returns a list of movies running within a radius of 3 miles from Beverly Hills,CA.The result is a huge list but takes hardly a few seconds on a high speed connection to parse the wsdl,send requests and get back the response!wsdl http://www.ignyte.com/webservices/ignyte.whatsshowing.webservice/moviefunctions.asmx?wsdl GetTheatersAndMovies 90209 3
Or get all the web services summaries from xmethods.net.The response is a huge list but hardly takes a few seconds.wsdl http://www.xmethods.net/wsdl/query.wsdl getAllServiceSummaries
XML parser and serializer can be used to handle XML .The api is the xml pull api and can be used in the same fashion.The roundtrip example in examples/xml gives a demonstration.
Send a bug report to http://groups.yahoo.com/group/wsdlpull
or to the author.
You may also use the sourceforge.net tracker.
If you are submitting a patch then do send the diffs either on the mailing list or to the author.Also send a
brief description of the patch and whether you tested it or if it needs testing.
If you need to check in to the CVS contact the author for developer access.
Before submitting a patch ,please perform some unit tests to ensure if the fix hasnt broken any existing functionality.