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.

No comments: