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 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 SomethingSearcher
service 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:
- Instantiate an implementation of your service class, using the generated locator class.
- Get an instance of the class that represents your service.
- 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:
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.
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 whichSomethingSearcher
is being used. Recall that the actual Java class used to create the Web service was calledSomethingSearcher
, 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.
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:
Post a Comment