Friday

client web services , autogeneration approach

In this post, i mention about the creation of a dynamic web service. Nevertheless, there was mentioned that “One approach would be to use WSDL2Java, the code generation tool provided with Axis2 generate a stub for the web service and use that stub to consume the web service.” So here is this approach. This post could be actually named:

Use Apache Axis tools to generate Web services clients and configure advanced options

Autogenerate your client source code

From the time that you've got a new-and-improved Web service, and you've limited the right methods (or, rather, allowed the right methods), thanks to Axis, you can publish a WSDL file representing the service. Or alternatively, you might just have the necessary url, a position from where you can contact the proper WSDL. From this point, you (or world) can use the Web service. The only problem is that the world (and you :) ) hasn't figured out how to write JAX-RPC style Web service clients.

This isn't an uncommon problem, especially with Web services and related technologies changing at a blistering pace. Fortunately, there's an answer: code autogeneration. Given just a WSDL file, you or another developer can painlessly generate a set of Java client classes that can connect to that service.

Code generation has traditionally been a pretty disappointing exercise, and code-generation tools usually are more pain than help. Thankfully, that's largely not the case with JAX-RPC, and specifically Axis. Axis produces WSDL for your services and can consume that same WSDL to generate usable Java classes.

Get a WSDL file for your service

The first step in code generation is to get a WSDL file for your service. This is easy: just navigate to the URL that your service is deployed at. Suppose that in the case of SomethingSearcher, that's http://localhost:8080/axis/services/SomethingSearcherService. Then, simply tack ?wsdl onto the end of your URL, like this: http://localhost:8080/axis/services/SomethingSearcherService?wsdl. You may get a blank screen from your browser, or a request to download a file, or some sort of pretty-printed XML.

Whatever your browser does, it's trying to deal with what Axis provides: a WSDL file for that service. In Safari, you can view source to see the XML; Firefox is similar, although Firefox tries to display the XML . It's critical that you just get a copy of that WSDL document.

Open up the XML; you should see something that looks like Listing 1 (although the listing only shows part of the WSDL; it might be a very long file):

Listing 1. Axis-generated WSDL for the SomethingSearcherService

   1: <?xml version="1.0" encoding="UTF-8"?>


   2: <wsdl:definitions 


   3:   targetNamespace="http://localhost:8080/axis/services/SomethingSearcherService" 


   4:                   xmlns:apachesoap="http://xml.apache.org/xml-soap" 


   5:                   xmlns:impl="http://localhost:8080/axis/services/SomethingSearcherService" 


   6:                   xmlns:intf="http://localhost:8080/axis/services/SomethingSearcherService" 


   7:                   xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 


   8:                   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 


   9:                   xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 


  10:                   xmlns:xsd="http://www.w3.org/2001/XMLSchema">


  11: <!--WSDL created by Apache Axis version: 1.4


  12: Built on Apr 22, 2006 (06:55:48 PDT)-->


  13:  <wsdl:types>


  14:   <schema targetNamespace="http://localhost:8080/axis/services/SomethingSearcherService" 


  15:           xmlns="http://www.w3.org/2001/XMLSchema">


  16:    <import namespace="http://xml.apache.org/xml-soap"/>


  17:    <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>


  18:    <complexType name="ArrayOf_xsd_anyType">


  19:     <complexContent>


  20:      <restriction base="soapenc:Array">


  21:       <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:anyType[]"/>


  22:      </restriction>


  23:     </complexContent>


  24:    </complexType>


  25:   </schema>


  26:   <schema targetNamespace="http://xml.apache.org/xml-soap" 


  27:           xmlns="http://www.w3.org/2001/XMLSchema">


  28:    <import namespace="http://localhost:8080/axis/services/SomethingSearcherService"/>


  29:    <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>


  30:    <complexType name="Vector">


  31:     <sequence>


  32:      <element maxOccurs="unbounded" minOccurs="0" name="item" type="xsd:anyType"/>


  33:     </sequence>


  34:    </complexType>


  35:   </schema>


  36:  </wsdl:types>


  37:  


  38:    <wsdl:message name="searchResponse">


  39:  


  40:       <wsdl:part name="searchReturn" type="impl:ArrayOf_xsd_anyType"/>


  41:  


  42:    </wsdl:message>


  43:  


  44:    <wsdl:message name="searchRequest">


  45:  


  46:       <wsdl:part name="keyword" type="soapenc:string"/>


  47:  


  48:    </wsdl:message>


  49:  


  50:    <wsdl:message name="getKeywordsResponse">


  51:  


  52:       <wsdl:part name="getKeywordsReturn" type="impl:ArrayOf_xsd_anyType"/>


  53:  


  54:    </wsdl:message>


  55:   ... etc ... 




In these types of actions (meaning the java  and particularly web services), it is in common usage the notion of Stub:



Stubs



Stubs represent remote classes and allow you to work with those remote classes as if they were local.

The scope is to be able to write a client in order to use your stubs.



Build class stubs and skeletons



With a WSDL file in hand, you're ready to use another command-line tool from Axis: WSDL2Java. You can run it and see all the options, as displayed in Listing 2:



Listing 2. WSDL2Java and its output, without supplying a WSDL file





   1: java org.apache.axis.wsdl.WSDL2Java


   2: e wsdl URI was not specified.


   3: age:  java org.apache.axis.wsdl.WSDL2Java [options] WSDL-URI


   4: tions:


   5:   -h, --help


   6:       print this message and exit


   7:   -v, --verbose


   8:       print informational messages


   9:   -n, --noImports


  10:       only generate code for the immediate WSDL document


  11:   -O, --timeout <argument>


  12:       timeout in seconds (default is 45, specify -1 to disable)


  13:   -D, --Debug


  14:       print debug information


  15:   -W, --noWrapped


  16:       turn off support for "wrapped" document/literal


  17:   -q, --quiet


  18:       do not print any informational or debug messages (except err


  19:       ors)


  20:   -s, --server-side


  21:       emit server-side bindings for web service


  22:   -S, --skeletonDeploy <argument>


  23:       deploy skeleton (true) or implementation (false) in deploy.w


  24:       sdd.  Default is false.  Assumes --server-side.


  25:   -N, --NStoPkg <argument>=<value>


  26:       mapping of namespace to package


  27:   -f, --fileNStoPkg <argument>


  28:       file of NStoPkg mappings (default NStoPkg.properties)


  29:   -p, --package <argument>


  30:       override all namespace to package mappings, use this package


  31:        name instead


  32:   -o, --output <argument>


  33:       output directory for emitted files


  34:   -d, --deployScope <argument>


  35:       add scope to deploy.wsdd: "Application", "Request", "Session


  36:       "


  37:   -t, --testCase


  38:       emit junit testcase class for web service


  39:   -a, --all


  40:       generate code for all elements, even unreferenced ones


  41:   -T, --typeMappingVersion <argument>


  42:       indicate 1.1 or 1.2.  The default is 1.1 (SOAP 1.1 JAX-RPC c


  43:       ompliant.  1.2 indicates SOAP 1.1 encoded.)


  44:   -F, --factory <argument>


  45:       name of a custom class that implements GeneratorFactory inte


  46:       rface (for extending Java generation functions)


  47:   -H, --helperGen


  48:       emits separate Helper classes for meta data


  49:   -B, --buildFile


  50:       emit Ant Buildfile for web service


  51:   -U, --user <argument>


  52:       username to access the WSDL-URI


  53:   -P, --password <argument>


  54:       password to access the WSDL-URI


  55:   -X, --classpath


  56:       additional classpath elements


  57:   -i, --nsInclude <argument>


  58:       include namespace in generated code


  59:   -x, --nsExclude <argument>


  60:       exclude namespace from generated code


  61:   -c, --implementationClassName <argument>


  62:       custom name of web service implementation


  63:   -u, --allowInvalidURL


  64:       emit file even if WSDL endpoint URL is not a valid URL


  65:   -w, --wrapArrays


  66:       Prefers building beans to straight arrays for wrapped XML ar


  67:       ray types (defaults to off).




All the options in Listing 2 are a bit confusing, so you can boil it down further: simply run WSDL2Java and supply it the name of the WSDL file you want it to generate code from. So save the WSDL output you got from the SomethingSearcherservice as SomethingSearcher.wsdl (or try to feed it directly from the url: http://localhost:8080/axis/services/SomethingSearcherService?wsdl)



Now, run WSDL2Java and supply the command the WSDL you just saved, as shown bellow:



 WSDL2Java command supplying a WSDL file



[your command prompt]>java org.apache.axis.wsdl.WSDL2Java SomethingSearcherService.wsdl (or if your connection and environment permits: http://localhost:8080/axis/services/SomethingSearcherService?wsdl]


You won't get any visible response at all. But the command creates a new directory, with several nested subdirectories, in your file system. If you're running your service on your local machine, the directory is called localhost.



The topmost folder matches the service's host name (in this case, localhost). Then, you have the path to the service. Because the URL is http://localhost/axis/services/SomethingSearcherService, the file folder structure is localhost/axis/services/SomethingSearcherService. Underneath that last folder are four files:




  • SomethingSearcher.java: An interface defining the methods available on the Web service.


  • SomethingSearcherService.java: An interface defining methods for locating an appropriate Web service.


  • SomethingSearcherServiceLocator.java: An implementation of SomethingSearcherService; it handles service location.


  • SomethingSearcherServiceSoapBindingStub.java: An implementation of SomethingSearcher. This is the class that you'll use to interact with the Web service in your client code.



So, for example, Listing 3 shows the source of SomethingSearcher:



Listing 3. Autogenerated source code for SomethingSearcher





   1: /**


   2:  * SomethingSearcher.java


   3:  *


   4:  * This file was auto-generated from WSDL


   5:  * by the Apache Axis 1.4 Apr 22, 2006 (06:55:48 PDT) WSDL2Java emitter.


   6:  */


   7:  


   8: package localhost.axis.services.SomethingSearcherService;


   9:  


  10: public interface SomethingSearcher extends java.rmi.Remote {


  11:     public java.lang.Object[] getKeywords(java.lang.String title) 


  12:       throws java.rmi.RemoteException;


  13:     public java.lang.Object[] search(java.lang.String keyword) 


  14:       throws java.rmi.RemoteException;


  15: }




Beware of class name collisions



You might have noticed that the generated classes share a lot of names with your existing Java classes. The packaging of the generated code should prevent real problems, but be careful when you compile that you don't overwrite other class files with the same name.



This is the only one of the four classes that is easily decipherable. The others are substantially more complex and full of JAX-RPC syntax and semantics. That's the whole idea, though: let JAX-RPC and Axis deal with all the Web service details, and allow a developer to just write basic code.



Write a client to use your stub class



All that's left is to actually use the generated stubs. That's a matter of compiling them, making sure they're in your classpath, and then knowing which methods to call (and how to call those methods).



Compile all your generated classes



Navigate back to your root directory. That's the directory where you've been keeping all your Java source files and running programs from. You probably have your generated classes off of that directory; if you've been running your service on your local machine, the localhost autogenerated directory should be just under your root. Then, compile all of the generated source code, as shown in Listing 4:



Listing 4. Compiling the auto-generated classes



[your command prompt] 
javac -d . localhost/axis/services/SomethingSearcherService/*.java
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.


If you've already got your classpath set up for Axis and JAX-RPC, you shouldn't need to make any additions or changes to get these classes to compile. If you're running Java SE 5 or later, you'll probably get warnings similar to those shown in Listing 4; that's because the code doesn't use parameterized types. Don't worry about those warnings; they won't affect your Web service work.



Use the generated classes in a client class



Next, you need to build a client. Here are the basic steps you'll follow any time you use generated classes from Axis:




  1. Instantiate an implementation of your service class, using the generated locator class.


  2. Get an instance of the class that represents your service.


  3. Invoke methods on that class as if the service were local.



That's all a bit abstract; look at Listing 5, which puts these steps into action:



Listing 5. Client class that uses the autogenerated source code





   1: import localhost.axis.services.SomethingSearcherService.*;


   2:  


   3: public class WSDLClient {


   4:  


   5:   public static void main(String[] args) {


   6:     if (args.length != 1) {


   7:       System.err.println("Usage: java WSDLClient [search keyword]");


   8:       return;


   9:     }


  10:     String keyword = args[0];


  11:  


  12:     try {


  13:       SomethingSearcherService service = new SomethingSearcherServiceLocator();


  14:       localhost.axis.services.SomethingSearcherService.SomethingSearcher searcher =


  15:         service.getSomethingSearcherService();


  16:       Object[] results = searcher.search(keyword);


  17:       System.out.println("Returned something for keyword '" + keyword + "':");


  18:       for (int i = 0; i<results.length; i++) {


  19:         System.out.println("  " + results[i]);


  20:       }


  21:     } catch (Exception e) {


  22:       e.printStackTrace(System.err);


  23:     }


  24:   }


  25: }




Here's how the code maps to the three steps I listed above:





  1. Instantiate an implementation of your service class, using the generated locator class: You need a connection to the Web service. The auto-generated classes take care of all the semantics, so all you need is a single line:



     SomethingSearcherService service = new SomethingSearcherServiceLocator(); 


    This creates a new class that knows how to locate your Web service.





  2. Get an instance of the class that represents your service: This class is called a stub. Stubs represent remote classes and allow you to work with those remote classes as if they were local. Here's how to get a stub for your Web service:



    localhost.axis.services.SomethingSearcherService.SomethingSearcher searcher =
    service.getSomethingSearcherService();


    I've prefixed SomethingSearcher with its complete package name to avoid any confusion about which SomethingSearcher is being used. Recall that the actual Java class used to create the Web service was called SomethingSearcher, and you may even have that source and class file in your current directory. By using a full package name, you eliminate the risk of name collisions.





  3. Invoke methods on that (stub) class as if the service were local: The wonderful thing about a stub class is that it protects your code from network and Web service syntax. You just call methods on the stub as if it were the Web service:



    Object[] results = searcher.search(keyword);




The think here is that you must have in mind, is there's no JAX-RPC code that the client needs to deal with. All that code is abstracted away in the autogenerated source code.



Run the client class



All that's left to do is run your client. Compile it and then run the class, as shown in Listing 6:



Listing 6. Running the WSDL client




[your command prompt]>> java WSDLClient marketing
Returned something for keyword 'searchterm':
Result 1
Result 2
Result 3



That's a huge improvement, and you can teach anyone these three steps. The result? Your Web service is now available to people who know JAX-RPC ... and to those who don't. That's priceless.

Analyze WSDL to understand a service

you've probably got some good ideas about exactly what the WSDL describes. But keep in mind that in many cases, you won't have the source code for a service you want to use. All you'll have is the WSDL to get by with. At those times, your understanding of WSDL is critical to using an RPC service (or any other type of Web service) correctly and effectively.

WSDL is namespace-heavy

Most of the time, programmers and even document authors quickly scan through — or outright skip over — the root element declaration in a WSDL file. In WSDL, though, that declaration contains a lot of information, as shown in Listing 1:

Listing 1. Root element declaration

   1: <wsdl:definitions targetNamespace="http://localhost:8080/axis/BookSearcher.jws" 


   2:    xmlns:apachesoap="http://xml.apache.org/xml-soap" 


   3:   xmlns:impl="http://localhost:8080/axis/BookSearcher.jws" 


   4:   xmlns:intf="http://localhost:8080/axis/BookSearcher.jws" 


   5:   xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 


   6:   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 


   7:   xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 


   8:   xmlns:xsd="http://www.w3.org/2001/XMLSchema">




Each of the xmlns: attributes defines a namespace and associated prefix. So there's an Apache SOAP namespace, a SOAP encoding namespace, both a WSDL and WSDL SOAP namespace, the XML Schema namespace ... and the list goes on. The target namespace is also set, and its Uniform Resource Identifier (URI) is the JWS file representing your published service.



The good news is that although these namespaces are important to SOAP, RPC, Axis, XML parsers, and almost every other technology used in Web services, you needn't worry much about them. It's enough to know that most of the elements in the WSDL are defined by a WSDL specification and associated with the wsdl prefix, and that the XML Schema namespace (and its associated types, which will become important soon) is associated with the xsd prefix. The rest is useful, but not something you'll need to write effective Web service clients.







WSDL defines object-based types



The next key section of the WSDL is contained within the <wsdl:types> element, shown in Listing 2:



Listing 2. The <wsdl:types> element





   1: <wsdl:types>


   2: space="http://xml.apache.org/xml-soap" 


   3: p://www.w3.org/2001/XMLSchema">


   4: ="http://localhost:8080/axis/BookSearcher.jws"/>


   5: ="http://schemas.xmlsoap.org/soap/encoding/"/>


   6: ="mapItem">


   7:     <sequence>


   8: key" nillable="true" type="xsd:anyType"/>


   9: value" nillable="true" type="xsd:anyType"/>


  10:     </sequence>


  11:    </complexType>


  12: ="Map">


  13:     <sequence>


  14: urs="unbounded" minOccurs="0" 


  15: item" type="apachesoap:mapItem"/>


  16:     </sequence>


  17:    </complexType>


  18: ="Vector">


  19:     <sequence>


  20: urs="unbounded" minOccurs="0" 


  21: item" type="xsd:anyType"/>


  22:     </sequence>


  23:    </complexType>


  24:   </schema>


  25: space="http://localhost:8080/axis/BookSearcher.jws" 


  26: p://www.w3.org/2001/XMLSchema">


  27: ="http://xml.apache.org/xml-soap"/>


  28: ="http://schemas.xmlsoap.org/soap/encoding/"/>


  29: ="ArrayOf_xsd_anyType">


  30:  


  31: se="soapenc:Array">


  32: ="soapenc:arrayType" wsdl:arrayType="xsd:anyType[]"/>


  33:  


  34: >


  35:    </complexType>


  36:   </schema>


  37: </wsdl:types>




WSDL and Web services know about some basic types, such as string and int and float, but aren't very smart about types more complex than what amount to Java primitives. However, the BookSearcher class has methods that take in or return both lists and maps. To handle these complex, object-based types, the WSDL must define them in terms of XML Schema types. This section of the document defines all of those types. For example, Listing 3 shows the definition of a map that RPC clients and services can understand:



Listing 3. Definition of a map RPC clients and services can understand





   1: <complexType name="mapItem">


   2:     <sequence>


   3: "key" nillable="true" type="xsd:anyType"/>


   4: "value" nillable="true" type="xsd:anyType"/>


   5:     </sequence>


   6:    </complexType>


   7: e="Map">


   8:     <sequence>


   9: curs="unbounded" minOccurs="0" 


  10: "item" type="apachesoap:mapItem"/>


  11:     </sequence>


  12: </complexType>




The Vector type is used to represent lists in the same fashion, providing upper and lower bounds for the lists. These are of limited use, because once you've written a few services and clients, you'll become familiar with the basic mappings between Java objects and custom WSDL types. Still, if you see a method that takes in a Vector, you'll know to look for the <wsdl:types> element to find out more about that type, including any constraints it might have for the values it can contain.







Every send and return is a message



The next elements are represented by <wsdl:message>. Here's where things deviate from Java terms and focus instead on network- and SOAP-specific concepts. When you send a request to a method on a service, you're sending a message. If the method you're requesting has no parameters, your message doesn't have any data within it for the method to operate on. If the method does require parameter data, you must send that data as part of the message.



The same is true when the service returns from a method: it either contains no data from the method or one piece of data. But the key is that the send and the receive are separate messages. One is a message from a client to a service, and the other is a message from a service back to a client. The two are logically related, but they're not connected in a programming or technological sense.



So each of these messages must be declared and defined. Take the getKeywords() method of BookSearcher. It takes as a parameter a string title, and it returns a list. Each of these two messages must be represented in the WSDL:



Listing 4. Messages in WSDL





   1: <wsdl:message name="getKeywordsRequest">


   2: e="title" type="xsd:string"/>


   3:    </wsdl:message>


   4:  


   5: e="getKeywordsResponse">


   6: e="getKeywordsReturn" type="impl:ArrayOf_xsd_anyType"/>


   7: </wsdl:message>




The name of each message is simply the method name, plus either Request or Response. Each has a nested <wsdl:part> element that defines a parameter name and a type (a string title for the request, and a generically named array for the response). This enables you or a code-generation tool to figure out what a request to getKeywords() looks like, as well as what to expect in return.



If no parameter is sent, or if there's no return value, then there's no <wsdl:part> child element:



Listing 5. No <wsdl:part> child element





   1: <wsdl:message name="addKeywordResponse">


   2:    </wsdl:message>




The addKeyword() method has no return value, so it is represented by an empty addKeywordResponse() element.







A service is represented by a port type



With the possible messages delineated, the WSDL can now describe the entire Web service, via the <wsdl:portType> element, shown in Listing 6:



Listing 6. The <wsdl:portType> element





   1: <wsdl:portType name="BookSearcher">


   2: n name="setBooks" parameterOrder="books">


   3:  message="impl:setBooksRequest" name="setBooksRequest"/>


   4: t message="impl:setBooksResponse" name="setBooksResponse"/>


   5: on>


   6: n name="addBook" parameterOrder="title keywords">


   7:  message="impl:addBookRequest" name="addBookRequest"/>


   8: t message="impl:addBookResponse" name="addBookResponse"/>


   9: on>


  10: n name="addKeyword" parameterOrder="title keyword">


  11:  message="impl:addKeywordRequest" name="addKeywordRequest"/>


  12: t message="impl:addKeywordResponse" name="addKeywordResponse"/>


  13: on>


  14: n name="getKeywords" parameterOrder="title">


  15:  message="impl:getKeywordsRequest" name="getKeywordsRequest"/>


  16: t message="impl:getKeywordsResponse" name="getKeywordsResponse"/>


  17: on>


  18: n name="search" parameterOrder="keyword">


  19:  message="impl:searchRequest" name="searchRequest"/>


  20: t message="impl:searchResponse" name="searchResponse"/>


  21: on>


  22: </wsdl:portType>




Each operation maps to a method, and the various input and output messages are connected to each operation. The ordering of parameters is also supplied. These completely describe an exposed method, and when laid out like this, should make perfect sense to you.



WSDL provides some lower-level SOAP-specific information



As a programmer, you have everything you need, but some encoding and SOAP-specific details still need to be handled. These are wrapped into the <wsdl:binding> element, which maps to the port type already defined. Most of <wsdl:binding> is concerned with encodings and namespaces. For example, Listing 7 shows further SOAP-specific information related to how the getKeywords() method and operation is to be handled:



Listing 7. SOAP-specific information on handling getKeywords()





   1: <wsdl:operation name="getKeywords">


   2: peration soapAction=""/>


   3:  name="getKeywordsRequest">


   4: p:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 


   5:        namespace="http://DefaultNamespace" use="encoded"/>


   6: t>


   7: t name="getKeywordsResponse">


   8: p:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 


   9:        namespace="http://localhost:8080/axis/BookSearcher.jws" 


  10:        use="encoded"/>


  11: ut>


  12: </wsdl:operation>




 



Who is the WSDL for?



WSDL serves a dual role:




  • WSDL lets Web service and code-generation tools know the semantics and SOAP specifics that are required to connect to a service.


  • WSDL lets programmers know what methods are available and the data those methods expect and provide in return.



As you may know, JAX-RPC and the Axis framework provide tools for reading WSDL and then handling the building and connectivity of code that consumes (uses) an RPC service. So the WSDL is integral for that support code, and it allows you to avoid hours of menial SOAP-encoding semantics. But the WSDL is also essential for you, particularly when you don't have the source code for the service you're connecting to. You must use the WSDL to find out what to send, and what you'll get back.



You've probably already gotten a strong sense of how WSDL could tell you what you need to know about almost any set of methods and those methods' return types. Now it's time to put this knowledge to use and connect and interact with a Web service.