Development Techniques


Now we’re ready to look at how to use Axis to build Web services applications. We’ll begin by giving you a conceptual model of what’s going on inside Axis, which will help you understand how the pieces fit together. From there, we’ll look at how to write Axis deployment descriptors and the administration tools you’ll need in order to deploy a Web service. Then we’ll tackle how to work with WSDL using the tools provided by Axis, so you can be prepared to write the kind of code you saw in the JAX-RPC examples. You’ll have the necessary knowledge to write code for a basic Web service both on the requestor and provider sides, and you’ll know how to deploy it into Axis so it can be called.

We’ll also cover some other techniques. In particular, we’ll revisit the notion of SOAP message handlers and demonstrate how to put them into action. We’ll also look at Axis’s facilities for doing custom type mapping, should you ever need to do that. We’ll wind up this section with a look at some useful tools that are included in the Axis distribution.

Axis Conceptual Model

This section is purposely not called "Axis Architecture." The goal of this section is to help you understand conceptually what’s going on when Axis processes a SOAP message. It isn’t our intent to give you a blow-by-blow rundown on the details of the Axis architecture, although you’ll find that many similarities exist between the model we describe here and the actual architecture of Axis.

When a service requestor invokes an operation on a Web service, the data provided as arguments to the invocation is encoded using the appropriate encoding rules. Most commonly, this is either SOAP encoding for rpc-encoded Web services or no encoding for document-literal Web services. Once this process is complete, you have a complete SOAP envelope that’s ready for delivery to the service provider. Before the envelope is sent to the provider, it must first traverse a pair of handler chains. A handler chain is a list of handlers, each of which is given a chance to process the message in whatever way it sees fit. There are two of these chains, one specific to the particular service (requestor) and one that’s global for the entire Axis runtime on that requestor node. After the envelope has been processed by the two chains, it’s sent to a transport that takes care of delivering the message to the service provider. In many cases, the transport uses HTTP, but the Axis development team is working on support for using a Java Message Service (JMS) package as a transport. SOAP over JMS is a very promising solution for scenarios that involve application integration inside the firewall.

The transport takes care of delivering the SOAP envelope to the provider host. Assuming Axis is also the SOAP runtime being used on the provider, the Axis engine on the provider gets the envelope out of the transport and passes it through a pair of handler chains. This time the order of the handler chains is reversed—that is, the envelope goes through the provider’s global handler chain followed by a service-specific handler chain. After the envelope exits the service-specific chain, the contents of the message undergo type decoding, which creates Java objects that correspond to the data in the envelope. These objects are then passed to the code that implements the service. Once the service code has executed and produced a result, the entire process reverses itself. If you look carefully at the following diagram, you’ll see that the request and response paths are identical if you follow each path from its beginning to its end. This makes sense when you recall that SOAP is a one-way message system.

click to expand

Inside the Axis runtime engine, information describes how to perform the type encoding and decoding, which transport to use, and the configuration of the service-specific and global handler chains. You tell Axis this information via deployment descriptors.

Deployment Descriptors

If you ‘ve worked at all with a servlet or J2EE container, then the deployment descriptors you’ve used are XML files that control various aspects of how your application deploys into the container environment. The Axis deployment descriptors continue in this tradition. The primary configuration for Axis is stored in a file called server-config.wsdd, which is typically found in the WEB-INF directory of the Web application that contains the Axis dispatching servlet, AxisServlet. This file is written using Axis’s Web service deployment descriptor (WSDD) format, which is an XML vocabulary. We’ll teach you about Axis deployment descriptors by walking through this file:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <deployment xmlns="http://xml.apache.org/axis/wsdd/"   3:   xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> 

The root element of a WSDD document is a <deployment> element. As usual, you have a gaggle (okay, two) of namespace declarations. The next element, <globalConfiguration>, contains elements that affect an entire Axis engine. The adminPassword parameter is the password used to secure the AdminService. The administration interface for an Axis engine is itself a Web service:

  4:  <globalConfiguration>   5:   <parameter name="adminPassword" value="admin"/>

If you’re sending messages with attachments using either DIME or SOAP with Attachments, the attachments.Directory specifies the directory where these attachments are stored while being processed:

  6:   <parameter name="attachments.Directory"   7:       value="D:\workspace\apache-xml-book\WEB-INF\attachments"/>

attachments.implementation should contain the name of a class that implements attachments support:

  8:   <parameter name="attachments.implementation"   9:     value="org.apache.axis.attachments.AttachmentsImpl"/>

If sendXsiTypes is true, an xsi:type attribute is added to every part element in the message. If it’s false, no xsi:type attributes are generated:

 10:   <parameter name="sendXsiTypes" value="true"/>

If sendMultiRefs is true, then multiref items are sent:

 11:   <parameter name="sendMultiRefs" value="true"/>

If sendXMLDeclaration is true, then an XML declaration is included in the SOAP message:

 12:   <parameter name="sendXMLDeclaration" value="true"/>

The axis.sendMinimizedElements parameter provides compatibility with .NET. If your .NET client can’t handle a service that returns an empty array inside another object, then set this to "false":

 13:   <parameter name="axis.sendMinimizedElements" value="true"/>

This <requestFlow> establishes a set of handlers that are invoked when a request is processed. Because it’s nested inside the <globalConfiguration>, it applies to all requests processed by this Axis engine:

 14:   <requestFlow>

By default, two handlers are installed in the global request processing flow (<requestFlow>). Both handlers use the class org.apache.axis.handlers.JWSHandler to define their behavior. The first handler takes care of processing .jws Web services (described in detail later), which are handled on a per-session basis. The second handler processes .jwr Web services, which are identical to .jws services except that they’re processed on a per-request basis. Here the <handler> elements are used both to define the existence of the handlers and to indicate the use of the handlers in the <requestFlow>:

 15:    <handler type="java:org.apache.axis.handlers.JWSHandler">  16:     <parameter name="scope" value="session"/>  17:    </handler>  18:    <handler type="java:org.apache.axis.handlers.JWSHandler">  19:     <parameter name="scope" value="request"/>  20:     <parameter name="extension" value=".jwr"/>  21:    </handler>

The three <handler> elements in lines 24-29 simply declare the existence of each of the three handlers and provide a name that can be used to reference them in other parts of the deployment descriptor. All that’s happening here is the predefinition of names for some built-in handlers:

 22:   </requestFlow>  23:  </globalConfiguration>  24:  <handler name="LocalResponder"  25:   type="java:org.apache.axis.transport.local.LocalResponder"/>  26:  <handler name="URLMapper"  27:   type="java:org.apache.axis.handlers.http.URLMapper"/>  28:  <handler name="Authenticate"  29:   type=  30:  "java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>

As we mentioned, the Axis administration service is implemented as a Web service. The <service> element in lines 31-37 contains the complete description of the service. In line 31, the service is given a name (the same name that would appear in the name attribute of the WSDL <service> element), and Axis is told that it should use the MSG provider from the java namespace. A provider is a handler that implements the dispatching to the actual service code. The MSG provider implements message-style services (more on message style in a little bit). The first of the three parameter items specifies which methods can be invoked—in this case, the AdminService method. The second parameter specifies that administration requests can only be made from the same machine the Axis engine is running on. The third parameter specifies the name of the class that implements the service. The <namespace> element specifies the namespace the service is in:

 31:  <service name="AdminService" provider="java:MSG">  32:   <parameter name="allowedMethods" value="AdminService"/>  33:   <parameter name="enableRemoteAdmin" value="false"/>  34:   <parameter name="className"   35:     value="org.apache.axis.utils.Admin"/>  36:   <namespace>http://xml.apache.org/axis/wsdd/</namespace>  37:  </service>

The Version service is described in lines 38-41. This service uses the java:RPC provider, which is the provider that understands how to perform SOAP encoding to and from Java objects. The only method that can be invoked is getVersion, and the class implementing the service is org.apache.axis.Version:

 38:  <service name="Version" provider="java:RPC">  39:   <parameter name="allowedMethods" value="getVersion"/>  40:   <parameter name="className" value="org.apache.axis.Version"/>  41:  </service>

Your first transport, for http, appears in lines 42-48. Transports are also allowed to specify <requestFlow>s and <responseFlow>s. The HTTP transport <requestFlow> uses the handler named URLMapper that was defined earlier in the descriptor, as well as the handler in org.apache.axis.handlers.http.HTTPAuthHandler. URLMapper tries to use any extra path information in the request URI as the service name. HTTPAuthHandler makes authentication information from the HTTP authentication headers available to a service:

 42:  <transport name="http">  43:   <requestFlow>  44:    <handler type="URLMapper"/>  45:    <handler  46:       type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>  47:   </requestFlow>  48:  </transport>

The last entry in the descriptor is the local transport, which can be useful for testing. It defines a <responseFlow> that uses the LocalResponder defined earlier in the descriptor:

 49:  <transport name="local">  50:   <responseFlow>  51:    <handler type="LocalResponder"/>  52:   </responseFlow>  53:  </transport>  54: </deployment>

When you write a Web service, you create a pair of deployment descriptors. One is used for deploying the service into Axis, and the other is used for undeploying the service. By convention, the deployment file is named deploy.wsdd and the undeployment file is named undeploy.wsdd (but that’s just a convention). The root element of the undeployment descriptor is <undeployment>. Typically, your deployment and undeployment descriptors contain only a <service> element as a child of the <deployment> or <undeployment> element.

Deploying Web Services

Once you’ve written your Web service code and created a deployment descriptor, you need a way to perform deployment and undeployment operations. The normal way to do this is to use the AdminClient program that resides in org.apache.axis.client.AdminClient. Your classpath must be set as discussed previously in order to use AdminClient. Typically, you invoke AdminClient by running java, so the command looks like this:

java org.apache.axis.client.AdminClient [options] <descriptor file>

The options available on AdminClient are as follows:

  • -l<url>—AdminClient talks to the AxisServlet. You may need to change the URL to the AxisServlet, depending on how you have installed Axis in your Web application. By default, AdminClient thinks AxisServlet is accessible via http://localhost:8080/axis/servlet/AxisServlet. You can use the -l option to change the URL in one place, or you can use -h, -p, and -s to change the pieces individually.

  • -h<hostname>—Change the hostname part of the default AxisServlet URL.

  • -p<portNumber>—Change the port number part of the default AxisServlet URL.

  • -s<servletPath>—Change the path to the servlet part of the default AxisServlet URL.

  • -f<filename>—Use a simple file-based protocol to talk to the admin service.

  • -u<username>—If the Axis servlet is protected by BASIC authentication, the username is needed to access the Axis servlet.

  • -w<password>—If the Axis servlet is protected by BASIC authentication, the password is needed to access the Axis servlet.

  • -d—Turn on debugging.

  • -t<transport-name>—Communicate with the AdminService using the transport name.

You can also supply one of three commands instead of the descriptor file:

  • list—Display the current contents of the server-config.wsdd file.

  • quit—If talking to the AxisSimpleServer, tell the simple server to shut down.

  • passwd <new-passwd>—Change the administrative password to new-passwd.

You can also use deployment descriptors on the service requestor side. The typical use is to install a requestor-side handler. Once you’ve written the requestor-side deployment descriptor, you can use the org.apache.axis.utils.Admin program to install it. The command looks like this:

java org.apache.axis.utils.Admin client <client-descriptor>

Admin can also be used to install provider-side descriptors by changing the first argument from client to server. Typically, you’ll use AdminClient to work with descriptors on the provider side.

Axis and WSDL

It’s much easier to develop a Web service application using WSDL than without WSDL. If you have a WSDL description of the service, then tools can use that description to help you in various ways. Using a WSDL file allows Axis’s tools to generate requestor-side stubs, provider-side skeleton implementations, and deployment descriptors for the provider side. Axis also provides tools for generating a WSDL file from an existing Java class, so you can gain the benefits of using WSDL even if you aren’t starting from scratch.

Document Styles

Before we get into the tools, we need a bit of discussion regarding the way Axis interprets the WSDL style and use attributes. Recall that the combination of style and use attributes yields four possibilities: rpc-encoded, rpc-literal, document-encoded, and document-literal. We’ll only concern ourselves with how to handle rpc-encoded and document-literal services.

Axis understands four styles of services: RPC, document, wrapped, and message services. This is controlled by the style attribute of the <service> element in a WSDD descriptor for the service. You need to know the relationship between these four styles and the rpc-encoded and document-literal schemes described by WSDL.

The easiest to understand is Axis RPC services. RPC services implement the WSDL rpc-encoded scheme. This means the SOAP body looks like the following listing, where there is a single child element whose name corresponds to the operation to be invoked, and the children of this element are the arguments to the operation. The SOAP encoding is used to encode the arguments:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <soapenv:Envelope   3:  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"   4:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"   5:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   6:  <soapenv:Body>   7:   <ns1:createBook   8:    soapenv:encodingStyle=   9:     "http://schemas.xmlsoap.org/soap/encoding/"  10:    xmlns:ns1="http://BookService.ch9.apachexml.sauria.com">  11:    <author xsi:type="xsd:string">Theodore W. Leung</author>  12:    <title  13:     xsi:type="xsd:string">  14:     Professional XML Development with Apache Tools  15:    </title>  16:    <isbn xsi:type="xsd:string">0-7645-4355-5</isbn>  17:    <month xsi:type="xsd:string">December</month>  18:    <year xsi:type="xsd:int">2003</year>  19:    <publisher  20:     xsi:type="xsd:string">  21:     Wrox  22:    </publisher>  23:    <address xsi:type="xsd:string">Indianapolis, Indiana</address>  24:   </ns1:createBook>  25:  </soapenv:Body>  26: </soapenv:Envelope>

When you use an Axis RPC service, Axis automatically uses the JAX-RPC rules to convert Java objects into the XML for the arguments on the requestor; it reverses the process on the provider to turn the XML back into Java objects that can be passed to the code that implements the service. RPC services set the style attribute of the <service> element to "rpc".

The document, wrapped, and message styles all have to do with handling SOAP messages that aren’t using the SOAP encoding. To see how Axis deals with the different styles, let’s look at a sample literal message. Looking at it, you can’t tell whether it’s using WSDL document or RPC style, because the SOAP Body has only a single child:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <soapenv:Envelope   3:  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"   4:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"   5:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   6:  <soapenv:Body>   7:   <book xmlns="http://sauria.com/schemas/apache-xml-book/book">   8:    <title>Professional XML Development with Apache Tools</title>   9:    <author>Theodore W. Leung</author>  10:    <isbn>0-7645-4355-5</isbn>  11:    <month>December</month>  12:    <year>2003</year>  13:    <publisher>Wrox</publisher>  14:    <address>Indianapolis, Indiana</address>  15:   </book>  16:  </soapenv:Body>  17: </soapenv:Envelope>

For Axis document and wrapped style services, Axis still tries to help you as much as it can. It uses the JAX-RPC type mapping rules to try to automatically convert the elements in the SOAP Body into Java objects. The difference between the two services is in how they try to do this. An Axis document-style service looks at each child of the Body and creates a Java object for each child. So, for the previous listing, a document-style service will create a Book class that contains all the children of the <book> element as fields (Java Bean properties, really) of the class. A wrapped-style service assumes the real data is wrapped up inside a wrapper element (which would be <book> in this example). The wrapped style unwraps the data from inside the wrapper element and deals with it directly. Instead of producing a Book class, a wrapped service will produce an argument list consisting of the title, author, isbn, month, year, publisher, and address data for the book. This is important because the Axis WSDL2Java tool uses the WSDL file to generate stub classes and skeleton implementation classes, and the difference between document and wrapped style services shows up in the argument lists of methods that implement a service’s operations. You might wonder why you have to bother about wrapped services. The answer is that the Microsoft .NET Web services default to a wrapped style of use. The setting for the style attribute of <service> in the WSDD file is "document" for document style and "wrapped" for wrapped style.

The last kind of service is a message-style service. When you specify a message-style service, Axis doesn’t do any type mapping for you. Instead, you get objects corresponding to the actual XML of the SOAP Envelope. When you choose to use a message-style service, you’re asking for full manual control of the process; you don’t get any support from WSDL. On the provider side, there are four possible signatures for a service method. If your operation is called createBook, then here are the four variations:

public Element[] createBook(Element[] bodies);

You’re passed an array of DOM Elements, one Element for each child element of the SOAP body. You must return the result as an array of DOM Elements.

public SOAPBodyElement[] createBook(SOAPBodyElement[] bodies);

You’re passed an array of SOAPBodyElements, one SOAPBodyElement for each child element of the SOAP body. A SOAPBodyElement implements the SOAP Element interface from the Java API for XML Messaging (JAXM/SAAJ). You must return the result as an array of SOAPBodyElements.

public Document createBook(Document body);

You’re passed a DOM Document element containing the entire SOAP body. You must return a DOM Document element containing the SOAP body as the result.

public void createBook(SOAPEnvelope request, SOAPEnvelope response);

You’re passed two SOAPEnvelopes: one for the request and one for the response. You need to fill in the response envelope. This is the only one of the four signatures that gives you access to the header portion of the SOAP Envelope. So, if you need to access or modify the headers, this is the signature you should implement.

WSDL2Java

There are a couple of scenarios in which WSDL2Java can be very helpful. Let’s say you’re writing a program that needs to use an existing Web service. If that Web service has a WSDL description, then your job is pretty easy. You can point WSDL2Java at the WSDL file (even across the Internet) and generate the requestor-side stubs that will allow your application to call the Web service. After you’ve compiled the generated classes, you need to write your code to use them. It doesn’t take long for this to work. This will only get tricky if the WSDL file uses types that can’t be mapped by the JAX-RPC XML-to-Java mapping.

If you’re building a Web service provider from scratch, then the best way to develop it is to start by creating a WSDL file and then generating the code artifacts that implement the service. Once you have those code artifacts, you can fill in the template for the implementation, write some requestor code, and be on your way.

WSDL2Java is a command-line tool that takes a WSDL file as input and produces a set of Java classes and, potentially, a set of deployment descriptors. When all the code-generation options are turned on, WSDL2Java generates classes for the service requestor side and the service provider side. The classes are generated based on a correspondence between WSDL elements and Java classes. We’ll discuss the correspondence and assume that all the code-generation options are turned on.

WSDL2Java generates a Java class for each top-level type declaration element that appears in the <types> section of the WSDL document. If the type or element is used as an in/out parameter, then WSDL2Java generates a JAX-RPC Holder class as well. All these classes are usable on both the requestor and provider sides. Axis can also generate helper classes for each type. These helper classes take the serialization and deserialization code and move them into separate classes. This process allows you to use Axis’s serialization mechanism to easily convert the Java classes back and forth to XML.

A WSDL port type is compiled into a Java interface. In JAX-RPC terms, this is the Service Endpoint Interface (SEI). The name of the interface is taken from the name attribute of the <portType> element.

Each WSDL binding element is translated into several classes. On the requestor side, the binding is used to generate a stub class. The stub class’s name is constructed by concatenating the name attribute of the <service> element that uses the binding and the word SOAPBindingStub. The SOAPBindingStub class implements the SEI. You’ll rarely deal with this class, because you’ll be dealing with the SEI it implements. On the provider side, two possible sets of classes are generated, depending on the value of the --skeletonDeploy flag. Without this flag, WSDL2Java generates an implementation template class that implements the SEI and contains method stubs you can fill in with the code that does the work. This class’s name is constructed from the name attribute of the <service> element using the binding and the word SOAPBindingImpl. If you specify the --skeletonDeploy flag to be true, then WSDL2Java generates an additional skeleton class. The skeleton class implements the SEI and forwards all its methods to the implementation template class. When you’re using a skeleton class, the Axis runtime calls the methods on the skeleton class in order to do its work. The skeleton file is named by taking the name of the <service> and appending the word SOAPBindingSkeleton.

There are a couple of scenarios around the provider-side stubs. In the easy scenario, you starting from scratch and don’t have any of the code that implements the service. In this case, you should omit the --skeletonDeploy flag and allow Axis to generate the implementation template class. You can then edit this file to insert the code to implement the service. In the harder case, you already have some code that implements the service. If by some chance your service code already implements the SEI, then you can slip your class into the deployment descriptors and bypass using any Axis-generated provider-side stubs. You’ll still need to generate them using the --server-side flag to have WSDL2Java generate the deployment descriptors for you. If your service code doesn’t implement the SEI and you’re lazy, you can have WSDL2Java generate the skeleton and template implementation classes for you. You can then throw away the template implementation class and use the skeleton’s forwarding code to get a head start on calling your existing service code. (This is an application of the Adapter design pattern.) You can also do this with the template implementation class, because you can edit the method bodies to forward on to your existing service code.

For each <service> element in the WSDL file, WSDL2Java generates a service interface that extends the JAX-RPC Service interface and adds methods for getting an implementation of the service. This file is named with the name attribute of the <service> element and the word Service. WSDL2Java also generates an implementation of the Service, which is named with the name of the <service> element and the word ServiceLocator.

WSDL2Java generates a pair of deployment and undeployment descriptor files. There’s a WSDD <service> entry for each <service> element in the WSDL file. These descriptors are ready to be used with the AdminClient program.

You should be asking yourself which Java package or packages these generated classes will end up in. The package for the generated classes depends on which namespace the associated WSDL elements appeared in. If the namespace URI associated with a WSDL element is of the form http://host.domain /path1/path2/path3, then WSDL2Java converts it into a Java package of the form domain.host .path1.path2.path3. So, if the portType BookHandler is in the target namespace with URI http://BookService.ch9.apachexml.sauria.com, WSDL2Java will generate a Java Interface in com.sauria.apachexml.ch9.BookService.BookHandler.java. Similarly, the book schema defines its elements to be in the targetNamespace http://sauria.com/schemas/apache-xml-book/book, and book is one of the top-level elements. WSDL2Java will generate a new class called _book in the package com.sauria.schemas.apache-xml-book.book.

Let’s look at the command-line arguments for the WSDL2Java program and then run through a complete example to see what files are generated and where. The full syntax of the WSDL2Java command is

java org.apache.axis.wsdl.WSDL2Java [options] <WSDL URI>

The options are as follows:

  • --verbose—Print informational messages as the file is processed.

  • --noImports—Only generate code for the WSDL document at the URI; don’t process any <wsdl:import> elements.

  • --timeout <seconds>—Set the timeout for the requestor-side stubs in seconds. By default this value is 45 seconds. You can disable the timeout by setting this value to -1.

  • --Debug—Print debugging information.

  • --noWrapped—Don’t perform wrapped document literal processing. Wrapped services are a .NET Web service compatibility mode. When you’re processing a WSDL document, WSDL2Java generates a wrapped-style service if:

    • The input message has a single part.

    • The part is an element.

    • The element has the same name as the operation.

    • The element’s complex type definition has no attributes.

  • --server-side—Generate the provider-side files for the service. The --skeletonDeploy flag controls whether a skeleton class is generated. An implementation template class as well as the pair of deployment descriptors (deploy.wsdd, undeploy.wsdd) is always generated.

  • --skeletonDeploy—If true (it’s false by default), generate a skeleton class for use by the provider and modify the deployment descriptors to make Axis call the skeleton class to perform the service (instead of the implementation template class). If you set this flag true, --server-side is automatically set true, so you don’t need to set --server-side if you set --skeletonDeploy.

  • --NStoPkg <namespace-uri>=<package-name>—This option lets you override WSDL2Java’s rules for generating package names from namespace URIs. To do this, you provide a mapping from a namespace URI to a Java package name.

  • --fileNStoPkg—If you want to override a number of namespace-to-package mappings, you’ll probably want to put them in a file. If you use --fileNStoPkg, you can supply the name of a Java properties file that contains the mappings. Each line in the property file is a mapping of the form <namespace-uri>=<package-name> (note that you’ll have to escape any colons in the namespace URI by prefixing them with \; for example, http://www.sauria.com becomes http\://www.sauria.com). If you use both --fileNStoPkg and --NStoPkg to declare a namespace-to-package mapping, the mapping specified by --NStoPkg takes precedence.

  • --package <package-name>—Force WSDL2Java to put all generated classes in the same package. The danger with this approach is that you could have two types with the same name in different namespaces. By putting them in a single package (WSDL2Java would put them in different packages by default), you’re asking for trouble. You can’t use the --NStoPkg option and the-package option at the same time.

  • --output <directory>—Specify the root directory to be used for output. Any directories needed for packages are created under this directory.

  • --deployScope <scope name>—Axis runs as a servlet, and you can ask for per-scope handling of a particular Web service. The --deployScope flag adds a scope parameter to the deploy.wsdd file. The valid values for the scope name are the three scopes available to servlets: "request", "session", and "application" ("request" is the default).

  • --testCase—Generate a template for a provider-side JUnit test case.

  • --all—Generate code for all the elements in a WSDL document. Normally WSDL2Java only generates code for the elements that are referenced in the document. To determine whether an element is referenced, you must see if it’s reachable from some root element in the document. You’d think this should be the <service> element, but the document you’re processing may not have a <service> element. So, WSDL2Java uses the highest element that appears in the file. <service> is higher than <binding>, <binding> is higher than <portType>, and <portType> is higher than <types>. This order is easy to see because if you start with a <service> and trace all the way through the WSDL grammar to get to <types>, you encounter <service>, <binding>, <portType>, and <types> in that order.

  • --TypeMappingVersion <version>—Specify which version of type mapping to use (SOAP 1.1 or SOAP 1.2). The default is to use the JAX-RPC compliant SOAP 1.2 mapping. You supply the version number, 1.1 or 1.2, as the option value.

  • --factory <classname>—Use the specified classname to extend the behavior of the WSDL emitter. This allows you to get custom generation behavior. The class you supply must extend JavaWriterFactory.

  • --helperGen—Generate separate helper classes for any classes generated in a <types> section.

  • --user <username>—Supply a username for accessing the WSDL file.

  • --password <password>—Supply a password for accessing the WSDL file.

An Example

We’ll show you an example that builds on the document-literal WSDL file from earlier in the chapter. You’ll add a second operation to the <portType>. We’ll also show you how to modularize the WSDL file. You can certainly keep all the WSDL elements in a single file, but doing so means you may have to copy and paste sections of the file if you need to reuse them in other places. Let’s break the single WSDL file into four files (the DL prefix on these files stands for document-literal):

  • book.xsd—This file will contain any XML schema types you use in the Web service. It’s easy to see that you might have a set of common XML schemas you’re using for various purposes throughout your organization. Having a single authoritative copy can prevent copy-and-paste errors. This schema is the same one you’ve been using throughout the book. This file is imported into other WSDL files using the <wsdl:import> element.

  • DLBook.wsdl—This file will contain the <message> and <portType> elements. This is the description of the interface to the service. It doesn’t get into how the service is bound to a transport protocol or serialization format. It’s the most abstract description of the service. This file will import book.xsd, because the message parts will be defined using types and elements from book.xsd. When you split up a WSDL file this way, each file containing WSDL elements must be wrapped in a <wsdl:definitions> element.

  • DLBookBinding.wsdl—We’ve taken the <binding> element and placed it its own file. At the moment the best-defined binding for WSDL is the SOAP/HTTP binding, but the WSDL spec defines several others, and you can expect to see even more (such as SOAP over BEEP or SOAP over JMS). By factoring the <binding> into its own file, you make it easier to add a new binding. The binding file needs to import the interface file.

  • DLBookService.wsdl—This leaves the <service> element to be in a file by itself. This is the implementation file, and it allows you to define groups of services and ports in multiple files and publish them as appropriate.

The first listing contains the book.xsd file. This file should be familiar enough that it doesn’t require any additional explanation:

  1:   <xsd:schema    2:    targetNamespace=   3:       "http://sauria.com/schemas/apache-xml-book/book"    4:    xmlns:book="http://sauria.com/schemas/apache-xml-book/book"    5:    xmlns:xsd="http://www.w3.org/2001/XMLSchema"   6:    elementFormDefault="qualified">   7:    <xsd:element name="address" type="xsd:string"/>   8:    <xsd:element name="author" type="xsd:string"/>   9:    <xsd:element name="book">  10:     <xsd:complexType>  11:      <xsd:sequence>  12:       <xsd:element ref="book:title"/>  13:       <xsd:element ref="book:author"/>  14:       <xsd:element ref="book:isbn"/>  15:       <xsd:element ref="book:month"/>  16:       <xsd:element ref="book:year"/>  17:       <xsd:element ref="book:publisher"/>  18:       <xsd:element ref="book:address"/>  19:      </xsd:sequence>  20:      <xsd:attribute name="version" type="xsd:string"  21:                     use="required"/>  22:     </xsd:complexType>  23:    </xsd:element>  24:    <xsd:element name="isbn" type="xsd:string"/>  25:    <xsd:element name="month" type="xsd:string"/>  26:    <xsd:element name="publisher" type="xsd:string"/>  27:    <xsd:element name="title" type="xsd:string"/>  28:    <xsd:element name="year" type="xsd:short"/>  29:   </xsd:schema>

The WSDL Interface File

The interface file DLBook.wsdl is shown next. In lines 11-13 you import the types from the schema file. You need to specify which namespace the types are imported into and the location of the file to import:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <wsdl:definitions   3:  targetNamespace=   4:    "http://DLBookService.ch9.apachexml.sauria.com"   5:  xmlns="http://schemas.xmlsoap.org/wsdl/"   6:  xmlns:impl="http://DLBookService.ch9.apachexml.sauria.com"   7:  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   8:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"   9:  xmlns:book="http://sauria.com/schemas/apache-xml-book/book" >  10:   11:  <wsdl:import   12:     namespace="http://sauria.com/schemas/apache-xml-book/book"  13:     location="book.xsd"/>  14:   15:  <wsdl:message name="createBookResponse">  16:   <wsdl:part name="createBookReturn" type="xsd:boolean"/>  17:  </wsdl:message>  18:   19:  <wsdl:message name="createBookRequest">  20:   <wsdl:part name="book" element="book:book"/>  21:  </wsdl:message>  22:   23:  <wsdl:message name="getBookRequest">  24:   <wsdl:part name="isbn" element="book:isbn"/>  25:  </wsdl:message>  26:    27:  <wsdl:message name="getBookResponse">  28:   <wsdl:part name="getBookReturn" element="book:book"/>  29:  </wsdl:message>  30:   31:  <wsdl:portType name="DLBookHandler">  32:   <wsdl:operation name="createBook" parameterOrder="book">  33:    <wsdl:input message="impl:createBookRequest"  34:                name="createBookRequest"/>  35:    <wsdl:output message="impl:createBookResponse"  36:                 name="createBookResponse"/>  37:   </wsdl:operation>  38:   39:   <wsdl:operation name="getBook" paramterOrder="isbn">  40:    <wsdl:input message="impl:getBookRequest"  41:                name="getBookRequest"/>  42:    <wsdl:output message="impl:getBookResponse"  43:                 name="getBookResponse"/>  44:   </wsdl:operation>  45:  </wsdl:portType>  46: </wsdl:definitions>

The WSDL Binding File

You add a getBook operation that retrieves a book based on its ISBN number. Here’s the binding file, DLBookBinding.wsdl:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <wsdl:definitions   3:  targetNamespace=   4:     "http://DLBookService.ch9.apachexml.sauria.com"   5:  xmlns="http://schemas.xmlsoap.org/wsdl/"   6:  xmlns:impl="http://DLBookService.ch9.apachexml.sauria.com"   7:  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   8:  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">   9:   10:  <wsdl:import  11:     namespace="http://DLBookService.ch9.apachexml.sauria.com"  12:     location="DLBook.wsdl"/>

The import statement controls which namespace elements go in. It’s possible for the portTypes and the bindings to be in different namespaces. If you’re using an industry-specified WSDL interface, then the <portType> is likely to be in a namespace controlled by an industry consortium or some similar group. When you’re getting ready to deploy your implementation of the service, you’ll probably put your bindings in a namespace related to your company:

 13:   14:  <wsdl:binding name="DLBookServiceSoapBinding"  15:                type="impl:DLBookHandler">  16:   <wsdlsoap:binding style="document"  17:             transport="http://schemas.xmlsoap.org/soap/http"/>  18:   19:   <wsdl:operation name="createBook">  20:    <wsdlsoap:operation soapAction=""/>  21:   22:    <wsdl:input name="createBookRequest">  23:     <wsdlsoap:body  24:      namespace="http://DLBookService.ch9.apachexml.sauria.com"  25:      use="literal"/>  26:    </wsdl:input>  27:   28:    <wsdl:output name="createBookResponse">  29:     <wsdlsoap:body  30:      namespace="http://DLBookService.ch9.apachexml.sauria.com"  31:      use="literal"/>  32:    </wsdl:output>  33:   </wsdl:operation>  34:   35:   <wsdl:operation name="getBook">  36:    <wsdlsoap:operation soapAction=""/>  37:   38:    <wsdl:input name="getBookRequest">  39:     <wsdlsoap:body  40:      namespace="http://DLBookService.ch9.apachexml.sauria.com"  41:      use="literal"/>  42:    </wsdl:input>  43:   44:    <wsdl:output name="getBookResponse">  45:     <wsdlsoap:body  46:      namespace="http://DLBookService.ch9.apachexml.sauria.com"  47:      use="literal"/>  48:    </wsdl:output>  49:   </wsdl:operation>  50:   51:  </wsdl:binding>  52: </wsdl:definitions>

The WSDL Implementation File

Now let’s look at the implementation file, DLBookService.wsdl:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <wsdl:definitions   3:  targetNamespace=   4:    "http://DLBookService.ch9.apachexml.sauria.com"   5:  xmlns="http://schemas.xmlsoap.org/wsdl/"   6:  xmlns:impl="http://DLBookService.ch9.apachexml.sauria.com"   7:  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   8:  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">   9:   10:  <wsdl:import  11:     namespace="http://DLBookService.ch9.apachexml.sauria.com"  12:     location="DLBookBinding.wsdl"/>  13:   14:  <wsdl:service name="DLBookHandlerService">  15:   <wsdl:port binding="impl:DLBookServiceSoapBinding"  16:              name="DLBookService">  17:    <wsdlsoap:address  18:     location="http://localhost:8080/apache-xml-book-web-axis/  19: services/DLBookService"/>  20:   </wsdl:port>  21:  </wsdl:service>  22: </wsdl:definitions>

By creating different versions of DLBookService.wsdl, you can generate different deployable configurations of ports. This approach will be important as applications make increasing use of Web services.

Running WSDL2 Java

Let’s feed DLBookService.wsdl to WSDL2Java and look at the results. You want WSDL2Java to generate metadata helper classes, deployment skeletons, and a JUnit test case, and you want the files to be generated in the src subdirectory of the current directory. The command line looks like this:

java org.apache.axis.wsdl.WSDL2Java –helperGen –SkeletonDeploy=true    --testCase --output src --verbose DLBookService.wsdl

When WSDL2Java finishes, the following files have been written in the src/com/sauria/apachexml/ch9/DLBookService subdirectory of the current directory:

  • deploy.wsdd—Deployment descriptor.

  • DLBookHandler.java—Interface corresponding to the WSDL <portType>. The JAX-RPC SEI.

  • DLBookHandlerService.java—The requestor-side JAX-RPC Service interface.

  • DLBookHandlerServiceLocator.java—The requestor-side JAX-RPC Service implementation.

  • DLBookHandlerServiceTestCase.java—The JUnit test case.

  • DLBookServiceSoapBindingImpl.java—The provider-side template implementation class.

  • DLBookServiceSoapBindingSkeleton.java—The provider-side implementation skeleton class, which forwards methods in DLBookHandler to the same methods in DLBookServiceSoapBindingImpl.java.

  • DLBookServiceSoapBindingStub.java—The provider-side implementation of the JAX-RPC Stub interface.

  • undeploy.wsdd—Undeployment descriptor.

WSDL2Java also write these files in the src/com/sauria/schemas/apache_xml_book/book subdirectory of the current directory:

  • _book.java—The generated Java Bean class corresponding to the <book:book> element

  • _book_Helper.java—The generated helper class that knows how to serialize and deserialize a _book to XML

We want to show you briefly some of the files WSDL2Java generates. You won’t need to look inside most of the files unless you’re curious about the way Axis works. But this example includes four classes you might want to look at: DLBookHandler, _book, DLBookServiceSoapBindingImpl, and DLBookHandlerServiceTestCase. DLBookHandler and _book are important because they provide the API that’s used by a service requestor. DLBookServiceSoapBindingImpl is important because it’s the class where you need to place your service implementation code.

The DLBookHandler Interface

DLBookHandler contains two methods, createBook and getBook, which correspond to the operations defined in the WSDL file. They use _book as both a parameter and a return type. The _book class is basically a Java Bean (it implements java.io.Serializable) and provides a Java Bean property for each subelement of the <book:book> element. So, you have a private field and a pair of public getter and setter methods. Notice that the Java package com.sauria.apachexml.ch9.DLBookService has been derived from the namespace URI for the WSDL document, http://DLBookService.ch9.apachexml.sauria.com:

  1: /**   2:  * DLBookHandler.java   3:  *   4:  * This file was auto-generated from WSDL   5:  * by the Apache Axis WSDL2Java emitter.   6:  */   7:    8: package com.sauria.apachexml.ch9.DLBookService;   9:   10: public interface DLBookHandler extends java.rmi.Remote {  11:     public boolean createBook(  12:         com.sauria.schemas.apache_xml_book.book._book book)  13:         throws java.rmi.RemoteException;  14:   15:     public com.sauria.schemas.apache_xml_book.book._book  16:         getBook(java.lang.String isbn)  17:         throws java.rmi.RemoteException;  18: }

WSDL2Java also generates an equals and hashCode method for completeness. In general, you’ll need to look at any Java classes that were generated for elements inside the <types> section of your WSDL document (the following listing is _book.java generated from the <types> section):

  1: /**   2:  * _book.java   3:  *   4:  * This file was auto-generated from WSDL   5:  * by the Apache Axis WSDL2Java emitter.   6:  */   7:    8: package com.sauria.schemas.apache_xml_book.book;   9:   10: public class _book  implements java.io.Serializable {  11:     private java.lang.String title;  12:     private java.lang.String author;  13:     private java.lang.String isbn;  14:     private java.lang.String month;  15:     private short year;  16:     private java.lang.String publisher;  17:     private java.lang.String address;  18:     private java.lang.String version;  // attribute  19:   20:     public _book() {  21:     }  22:   23:     public java.lang.String getTitle() {  24:         return title;  25:     }  26:   27:     public void setTitle(java.lang.String title) {  28:         this.title = title;  29:     }  30:   31:     public java.lang.String getAuthor() {  32:         return author;  33:     }  34:   35:     public void setAuthor(java.lang.String author) {  36:         this.author = author;  37:     }  38:   39:     public java.lang.String getIsbn() {  40:         return isbn;  41:     }  42:   43:     public void setIsbn(java.lang.String isbn) {  44:         this.isbn = isbn;  45:     }  46:   47:     public java.lang.String getMonth() {  48:         return month;  49:     }  50:   51:     public void setMonth(java.lang.String month) {  52:         this.month = month;  53:     }  54:   55:     public short getYear() {  56:         return year;  57:     }  58:   59:     public void setYear(short year) {  60:         this.year = year;  61:     }  62:   63:     public java.lang.String getPublisher() {  64:         return publisher;  65:     }  66:   67:     public void setPublisher(java.lang.String publisher) {  68:         this.publisher = publisher;  69:     }  70:   71:     public java.lang.String getAddress() {  72:         return address;  73:     }  74:   75:     public void setAddress(java.lang.String address) {  76:         this.address = address;  77:     }  78:   79:     public java.lang.String getVersion() {  80:         return version;  81:     }  82:   83:     public void setVersion(java.lang.String version) {  84:         this.version = version;  85:     }  86:   87:     private java.lang.Object __equalsCalc = null;  88:     public synchronized boolean equals(java.lang.Object obj) {  89:         if (!(obj instanceof _book)) return false;  90:         _book other = (_book) obj;  91:         if (obj == null) return false;  92:         if (this == obj) return true;  93:         if (__equalsCalc != null) {  94:             return (__equalsCalc == obj);  95:         }  96:         __equalsCalc = obj;  97:         boolean _equals;  98:         _equals = true &&   99:             ((this.title==null && other.getTitle()==null) ||  100:              (this.title!=null && 101:               this.title.equals(other.getTitle()))) && 102:             ((this.author==null && other.getAuthor()==null) ||  103:              (this.author!=null && 104:               this.author.equals(other.getAuthor()))) && 105:             ((this.isbn==null && other.getIsbn()==null) ||  106:              (this.isbn!=null && 107:               this.isbn.equals(other.getIsbn()))) && 108:             ((this.month==null && other.getMonth()==null) ||  109:              (this.month!=null && 110:               this.month.equals(other.getMonth()))) && 111:             this.year == other.getYear() && 112:            ((this.publisher==null && other.getPublisher()==null) 113:              || (this.publisher!=null && 114:               this.publisher.equals(other.getPublisher()))) && 115:             ((this.address==null && other.getAddress()==null) || 116:              (this.address!=null && 117:               this.address.equals(other.getAddress()))) && 118:             ((this.version==null && other.getVersion()==null) || 119:              (this.version!=null && 120:               this.version.equals(other.getVersion()))); 121:         __equalsCalc = null; 122:         return _equals; 123:     } 124:  125:     private boolean __hashCodeCalc = false; 126:     public synchronized int hashCode() { 127:         if (__hashCodeCalc) { 128:             return 0; 129:         } 130:         __hashCodeCalc = true; 131:         int _hashCode = 1; 132:         if (getTitle() != null) { 133:             _hashCode += getTitle().hashCode(); 134:         } 135:         if (getAuthor() != null) { 136:             _hashCode += getAuthor().hashCode(); 137:         } 138:         if (getIsbn() != null) { 139:             _hashCode += getIsbn().hashCode(); 140:         } 141:         if (getMonth() != null) { 142:             _hashCode += getMonth().hashCode(); 143:         } 144:         _hashCode += getYear(); 145:         if (getPublisher() != null) { 146:             _hashCode += getPublisher().hashCode(); 147:         } 148:         if (getAddress() != null) { 149:             _hashCode += getAddress().hashCode(); 150:         } 151:         if (getVersion() != null) { 152:             _hashCode += getVersion().hashCode(); 153:         } 154:         __hashCodeCalc = false; 155:         return _hashCode; 156:     } 157:  158: }

The Skeleton Service Class

The implementation template class DLBookServiceSoapBindingImpl contains the same methods that are declared by DLBook handler. The bodies of these methods are stubbed out so that they compile. You’ll need to either modify this class to supply your information or edit DLBookServiceSoapBindingSkeleton to make it forward to an existing class:

  1: /**   2:  * DLBookServiceSoapBindingImpl.java   3:  *   4:  * This file was auto-generated from WSDL   5:  * by the Apache Axis WSDL2Java emitter.   6:  */   7:    8: package com.sauria.apachexml.ch9.DLBookService;   9:   10: public class DLBookServiceSoapBindingImpl   11:    implements  12:    com.sauria.apachexml.ch9.DLBookService.DLBookHandler{  13:    public boolean createBook(  14:            com.sauria.schemas.apache_xml_book.book._book book)  15:              throws java.rmi.RemoteException {  16:        return false;  17:     }  18:   19:     public com.sauria.schemas.apache_xml_book.book._book  20:             getBook(java.lang.String isbn)  21:                 throws java.rmi.RemoteException {  22:         return null;  23:     }  24:   25: }

A Filled-in Skeleton Service Class

Listing 11.23 shows DLBookServiceSoapBindingImpl after a simple object serialization implementation has been plugged in. The implementation of createBook creates a FileOutputStream that writes a file in /tmp. The name of the file is the value of the ISBN property of the book argument suffixed with .ser. The book data is stored by using Java object serialization to serialize the object to the file:

  1: /**   2:  * DLBookServiceSoapBindingImpl.java   3:  *   4:  * This file was auto-generated from WSDL   5:  * by the Apache Axis WSDL2Java emitter.   6:  */   7:    8: package com.sauria.apachexml.ch9.DLBookService;   9:   10: import java.io.FileInputStream;  11: import java.io.FileNotFoundException;  12: import java.io.FileOutputStream;  13: import java.io.IOException;  14: import java.io.ObjectInputStream;  15: import java.io.ObjectOutputStream;  16: import java.rmi.RemoteException;  17:   18: import com.sauria.schemas.apache_xml_book.book._book;  19:   20: public class DLBookServiceSoapBindingImpl   21:     implements DLBookHandler {  22:   23:     public boolean createBook(_book book)  24:         throws java.rmi.RemoteException {  25:         boolean status = false;  26:         String isbn = book.getIsbn();  27:         try {  28:             FileOutputStream fos =   29:                 new FileOutputStream("/tmp/"+isbn+".ser");  30:             ObjectOutputStream oos = new ObjectOutputStream(fos);  31:             oos.writeObject(book);  32:             oos.close();  33:             status  = true;  34:         } catch (FileNotFoundException fnfe) {  35:             fnfe.printStackTrace();  36:         } catch (IOException ioe) {  37:             ioe.printStackTrace();  38:         }  39:         return status;  40:     }

The implementation of getBook takes the isbn string argument and opens a FileInputStream on a file in /tmp whose name is the isbn value suffixed with .ser. The serialized object in the file is deserialized and returned as the value of the getBook operation:

 41:   42:     public _book getBook(java.lang.String isbn)   43:         throws RemoteException {  44:         _book bk = null;  45:         try {  46:             FileInputStream fis =   47:                 new FileInputStream("/tmp/"+isbn+".ser");  48:             ObjectInputStream ois = new ObjectInputStream(fis);  49:             bk = (_book) ois.readObject();  50:         } catch (FileNotFoundException fnfe) {  51:             fnfe.printStackTrace();  52:         } catch (IOException ioe) {  53:             ioe.printStackTrace();  54:         } catch (ClassNotFoundException cnfe) {  55:             cnfe.printStackTrace();  56:         }  57:         return bk;  58:     }  59:   60: }

The Generated Test Case

If you’re using JUnit, the automatically generated test case DLBookHandlerServiceTestCase provides the start of a requestor-side test suite. Here’s a version of the test case that’s been modified to perform a simple test against the DLBookService. We only added code to the generated code—we reformatted the code so it would fit onto book pages, but other than that, this is the code Axis generated:

  1: /**   2:  * DLBookHandlerServiceTestCase.java   3:  *   4:  * This file was auto-generated from WSDL   5:  * by the Apache Axis WSDL2Java emitter.   6:  */   7:    8: package com.sauria.apachexml.ch9.DLBookService;   9:   10: import com.sauria.schemas.apache_xml_book.book._book;  11:   12: public class DLBookHandlerServiceTestCase   13:     extends junit.framework.TestCase {  14:   15:     public DLBookHandlerServiceTestCase(java.lang.String name) {  16:         super(name);  17:     }  18:   19:     public void test1DLBookServiceCreateBook()   20:         throws Exception {  21:         DLBookServiceSoapBindingStub binding;  22:         try {  23:          binding = (DLBookServiceSoapBindingStub)  24:          new DLBookHandlerServiceLocator().getDLBookService();  25:         }  26:         catch (javax.xml.rpc.ServiceException jre) {  27:             if(jre.getLinkedCause()!=null)  28:                 jre.getLinkedCause().printStackTrace();  29:             throw   30:                 new junit.framework.AssertionFailedError(  31:                     "JAX-RPC ServiceException caught: " + jre);  32:         }  33:         assertNotNull("binding is null", binding);  34:   35:         // Time out after a minute  36:         binding.setTimeout(60000);  37:   38:         // Test operation  39:         boolean value = false;

In lines 41-49 you ad code to create a new _book instance:

 40:           41:         _book bk = new _book();  42:         bk.setAuthor("Theodore W. Leung");  43:         bk.setTitle(  44:             "Professional XML Development with Apache Tools");  45:         bk.setIsbn("0-7645-4355-5");  46:         bk.setMonth("December");  47:         bk.setYear((short) 2003);  48:         bk.setPublisher("Wrox");  49:         bk.setAddress("Indianapolis, Indiana");

Change line 51 to use the new _book instance:

 50:           51:         value = binding.createBook(bk);

Add the JUnit assertTrue to make this a complete JUnit test:

 52:         // TBD--validate results  53:         assertTrue(value);  54:     }  55:   56:     public void test2DLBookServiceGetBook() throws Exception {  57:         DLBookServiceSoapBindingStub binding;  58:         try {  59:          binding = (DLBookServiceSoapBindingStub)  60:          new DLBookHandlerServiceLocator().getDLBookService();  61:         }  62:         catch (javax.xml.rpc.ServiceException jre) {  63:             if(jre.getLinkedCause()!=null)  64:                 jre.getLinkedCause().printStackTrace();  65:             throw   66:                 new junit.framework.AssertionFailedError(  67:                     "JAX-RPC ServiceException caught: " + jre);  68:         }  69:         assertNotNull("binding is null", binding);  70:   71:         // Time out after a minute  72:         binding.setTimeout(60000);  73:   74:         // Test operation  75:         _book value = null;  76:         value = binding.getBook("0-7645-4355-5");

For this test, supply an argument for the getBook operation in line 76 and the assertion in line 78 to make this a complete JUnit test:

 77:         // TBD--validate results  78:         assertEquals(value.getAuthor(),"Ted Leung");  79:     }  80:   81: }

The Deployment Descriptor

The last set of files you should look at are the deployment descriptors. The deployment descriptor is in deploy.wsdd and looks like this:

  1: <!--Use this file to deploy some handlers/chains and services   2:  -->   3: <!--Two ways to do this:                                    -->   4: <!--  java org.apache.axis.client.AdminClient deploy.wsdd   -->   5: <!--     after the axis server is running                   -->   6: <!-- or                                                     -->   7: <!-- java org.apache.axis.utils.Admin client|server deploy.wsdd   8: -->   9: <!--     from the same directory that the Axis engine runs -->  10:   11: <deployment  12:  xmlns="http://xml.apache.org/axis/wsdd/"  13:  xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">  14:   15: <!-- Services from DLBookHandlerService WSDL service -->

In line 18, the style and use attributes identify this as a document-literal Web service. You’ll see some additional elements in the deployment descriptor:

 16:   17:  <service name="DLBookService" provider="java:RPC"  18:           style="document" use="literal">

The wsdlTargetNamespace, wsdlServiceElement, and wsdlServicePort parameter values are all taken directly from the WSDL file:

 19:   <parameter name="wsdlTargetNamespace"  20:    value="http://DLBookService.ch9.apachexml.sauria.com"/>  21:   <parameter name="wsdlServiceElement"  22:    value="DLBookHandlerService"/>  23:   <parameter name="wsdlServicePort"  24:    value="DLBookService"/>

The className parameter is the name of the Java class that’s implementing the Web service. Here the value has been set to the generated class com.sauria.apachexml.ch9.DLBookService .DLBookServiceSOAPBindingSkeleton. If you want to use your implementation class directly, change the value of className to be the fully qualified name of your Java class. Be sure any necessary classes are in the classpath of the Web application that’s running the Axis servlet:

 25:   <parameter name="className"  26:    value="com.sauria.apachexml.ch9.DLBookService.  27: DLBookServiceSoapBindingSkeleton"/>

The value of the wsdlPortType parameter is taken directly from the WSDL file:

 28:   <parameter name="wsdlPortType" value="DLBookHandler"/>

There is an <operation> element for each operation defined on the <portType>. The attributes on <operation> define the operation name and the type information for the return type of the operation. So, the qname for the operation is operNS:createBook, and operNS is the targetNamespace defined in the WSDL file. The return message is createBookReturn, and the type is an XML schema boolean:

 29:   <operation name="createBook"  30:    qname="operNS:createBook"  31:    xmlns:operNS="http://DLBookService.ch9.apachexml.sauria.com"  32:    returnQName="createBookReturn"  33:    returnType="rtns:boolean"  34:    xmlns:rtns="http://www.w3.org/2001/XMLSchema" >

There is a <parameter> child element for each parameter of the operation. The <parameter> element sets up the name and type of that parameter:

 35:    <parameter qname="pns:book"  36:     xmlns:pns="http://sauria.com/schemas/apache-xml-book/book"  37:     type="tns:>book"  38:     xmlns:tns="http://sauria.com/schemas/apache-xml-book/book"/>

The allowedMethods parameter says that getBook and createBook are the only operations available to requestors of this Web service:

 39:   </operation>  40:   <operation name="getBook"  41:    qname="operNS:getBook"  42:    xmlns:operNS="http://DLBookService.ch9.apachexml.sauria.com"  43:    returnQName="retNS:book"  44:    xmlns:retNS="http://sauria.com/schemas/apache-xml-book/book"  45:    returnType="rtns:>book"  46:    xmlns:rtns="http://sauria.com/schemas/apache-xml-book/book" >  47:    <parameter qname="pns:isbn"  48:     xmlns:pns="http://sauria.com/schemas/apache-xml-book/book"  49:     type="tns:string"  50:     xmlns:tns="http://www.w3.org/2001/XMLSchema"/>  51:   </operation>  52:   <parameter name="allowedMethods" value="getBook createBook"/>

The <typeMapping> element tells Axis which typeMapping classes to use. The class com.sauria .schemas.apache_xml_book.book._book is a Java Bean, and this is a document service. Axis tries to convert the XML in a document-literal message into a set of Java objects, according the XML-to-Java rules defined by JAX-RPC. Because the element <book:book> generates a Java Bean, you need to use the JAX-RPC rules for serializing and deserializing a Java Bean, which are implemented by the classes specified for the serializer and deserializer attributes. The fact that the encodingStyle attribute is empty signals that the rules for XML schema should be used rather than the rules for the SOAP encoding:

 53:   54:   <typeMapping  55:    xmlns:ns="http://sauria.com/schemas/apache-xml-book/book"  56:    qname="ns:>book"  57:    type="java:com.sauria.schemas.apache_xml_book.book._book"  58:    serializer=  59:       "org.apache.axis.encoding.ser.BeanSerializerFactory"  60:    deserializer=  61:       "org.apache.axis.encoding.ser.BeanDeserializerFactory"  62:    encodingStyle=""  63:    />  64:  </service>  65: </deployment>

The Undeployment Descriptor

The undeployment descriptor is in undeploy.wsdd and is very simple. The content of the <undeployment> element is one <service> element for each service to be undeployed. All you need is a name attribute on the <service> element to tell Axis the name of the service to undeploy:

  1: <!-- Use this file to undeploy some handlers/chains and services   2: -->   3: <!-- Two ways to do this: -->   4: <!--   java org.apache.axis.client.AdminClient undeploy.wsdd -->   5: <!--      after the axis server is running -->   6: <!-- or -->   7: <!-- java org.apache.axis.utils.Admin client|server undeploy.wsdd   8:  -->   9: <!--      from the same directory that the Axis engine runs -->  10:   11: <undeployment  12:     xmlns="http://xml.apache.org/axis/wsdd/">  13:   14:   <!-- Services from DLBookHandlerService WSDL service -->  15:   16:   <service name="DLBookService"/>  17: </undeployment>

Deploying the Web Service

After compiling all of the generated classes, you have a Web service you can deploy into Axis. You do so using the AdminClient program. You need to supply a value for the -l option because you’ve installed the AxisServlet into your own Web application instead of using the Axis-supplied Web application. The command line for deploying this service looks like this:

java org.apache.axis.client.AdminClient   –lhttp://localhost:8080/apache-xml-book-axis-web/services/AxisServlet   deploy.wsdd

Note that we indented the command to make it readable—the entire command should be on a single line in your command or shell window. This is the output you’ll see if the deploy command is successful:

Processing file deploy.wsdd <Admin>Done processing</Admin>

If you want to check that the service is running, you can execute a list command via AdminClient and check the output to see that the <service> entry for DLBookService is present:

java org.apache.axis.client.AdminClient   –lhttp://localhost:8080/apache-xml-book-axis-web/services/AxisServlet   list

You now have a running Web service, so you can run the JUnit test case or write your requestor code and start making service requests. The service will stay deployed until you undeploy it, and that includes restarts of the Web container. If you need to undeploy the service, execute this command:

java org.apache.axis.client.AdminClient   –lhttp://localhost:8080/apache-xml-book-axis-web/services/AxisServlet   undeploy.wsdd

Again, your output will look something like this:

Processing file undeploy.wsdd <Admin>Done processing</Admin>

Java2WSDL

We left one scenario out of our discussion of WSDL2Java, and it’s pretty important: What if you already have working code that implements functionality that you want to expose as a Web service? You may already have a WSDL service description that you need to use—perhaps a description that’s being used in your industry or group of suppliers. If this is the case, your best choice is to use WSDL2Java and generate the provider-side classes, including the skeleton. Then throw away the template implementation class generated by WSDL2Java and use the forwarding code in the skeleton class to forward the service methods to your methods. You may need to do more than forwarding in order to get the skeleton class to talk to your code.

If you don’t already have a WSDL description of the service, then you can use Java2WSDL to generate a WSDL file for you. Java2WSDL takes a Java interface or class and generates a WSDL description of a service whose operations correspond to the methods of the Java interface or class. Once you have the WSDL file, you can take that file and run it through WSDL2Java to generate any classes you need. You can either generate no skeletons and update the deployment descriptor to call your class directly, or you can generate skeleton classes and use them to forward to your class (you’ll throw away the generated template implementation class).

The usage for Java2WSDL looks like this:

java org.apache.axis.wsdl.Java2WSDL [options]    <fully qualified class name>

Java2WSDL needs compiled Java class file to work on, so you need to specify the full name of the class including its package. You also need to make sure this class is on your classpath before you run Java2WSDL. The command-line options for Java2WDSL are as follows:

  • --input <wsdl-file>—Take an existing WSDL file and add any WSDL elements generated to it. This won’t replace an element that’s already there.

  • --output <wsdl-file>—The name of the WSDL file to generate.

  • --location <url>—The URL of the location of the service. The component of the URL should be the name of the service port (name attribute of the <port> element).

  • --portTypeName <name>—The name of the <portType> element. If omitted, the name of the class is used.

  • --bindingName <name>—The name to use for the <binding> element. If omitted, the name is the value of -servicePortName with SOAPBinding appended.

  • --serviceElementName <name>—The name of the <service> element. If omitted, the name is be the value of -portTypeName with Service appended.

  • --servicePortName <name>—The name of the service port. If omitted, the name is the value of the last pathname component of location.

  • --namespace <uri>—The target namespace for the WSDL document.

  • --PkgtoNS <package name>=<namespace uri>—Indicate a mapping from a Java package to a namespace URI. This can be specified multiple times as needed.

  • --methods <list of methods>—A list of methods that will be exported. The list is either space- or comma-separated (you may need to quote the list). If omitted, all the methods in the interface or class are exported.

  • --all—If specified, Java2WSDL looks at base classes or interfaces to determine which methods to export.

  • --outputWsdlMode <mode>—The kind of WSDL being generated. There are three possible values for <mode>:

    • All (the default)—Generate both interface and implementation WSDL elements.

    • Interface—Generate WSDL with interface elements only (excludes the <service> element).

    • Implementation—Generate WSDL with implementation elements only and use <wsdl:import> to import the interface WSDL (the -locationImport option is used to tell how to find the interface WSDL).

  • --locationImport <url>—If you’re generating an implementation-only WSDL file, the corresponding interface WSDL file is at <url>.

  • --namespaceImpl <uri>—The target namespace for the implementation WSDL.

  • --OutputImpl <filename>—The filename for the implementation WSDL. This causes Java2WSDL to generate both interface and implementation WSDL files. Using this option overrides the setting of —outputWSDLMode.

  • --implClass <class name>—Look in the implementation class for additional information.

  • --exclude <list of methods>—Don’t include any methods in the list in the WSDL file. The list is space- or comma-separated.

  • --stopClasses <list of classes>—Java2WSDL should stop searching base classes or interfaces when it encounters a class in the list. The list of classes is space- or comma-separated.

  • --typeMappingVersion <version>—Select the type mapping registry to use. Valid values are 1.1 and 1.2. The default is the JAX-RPC compliant SOAP 1.2 (1.2).

  • --soapAction <value>—Controls how the SOAPAction field is set. There are three options:

    • OPERATION—Set the value of soapAction to the name of the operation.

    • DEFAULT—Set soapAction according to the operation’s metadata (usually "").

    • NONE—soapAction is "".

  • --style <style>—Controls the style attribute of the <binding> element. There are three options:

    • RPC—Use RPC style.

    • DOCUMENT—Use document style.

    • WRAPPED—Use wrapped document-literal style. This forces -use to be literal.

  • --use <use>—Controls the use attribute of the <wsdlsoap:body> element. There are two options:

    • LITERAL—Use XML schema to describe message parts.

    • ENCODED—Message parts are encoded using the SOAP encoding.

  • --extraClasses <list of classes>—A list of classes that should be included in the <types> section of the WSDL. The list is a space- or comma-separated list of classnames.

An Example

Let’s take the DLBookHandler SEI interface and the _book class generated by WSDL2Java and feed them back into Java2WSDL so you can see how Java2WSDL works in a familiar environment:

java2wsdl   -lhttp://locahost:8080/apache-xml-book-web-axis/services/DLBookService  --style document --use literal --output BookServiceIntf.wsdl   --outputImpl BookServiceImpl.wsdl  com.sauria.apachexml.ch9.DLBookService.DLBookHandler

You tell Java2WSDL the URL of the service—this should appear as the value of the location attribute on a <port> in the WSDL. You also ask Java2WSDL to generate a document-literal WSDL file and to output the interface WSDL to BookServiceIntf.wsdl and the implementation WSDL to BookServiceImpl.wsdl. You need to make sure compiled versions of com.sauria.apachexml.ch9.DLBookService.DLBoookHolder and com.sauria.schemas.apache_xml_book.book._book are available in the classpath used by Java2WSDL.

Java2WSDL modularizes WSDL files a little differently than the way we showed in the examples for WSDL2Java. Java2WSDL only separates between an interface WSDL file and an implementation WSDL file. The <types>, <message>, <portType>, and <binding> elements all go in the interface WSDL file, leaving only the <service> element to go in the implementation WSDL file. The BookServiceIntf.wsdl interface file appears in the following listing. The only real difference is in the <types> section. Java2WSDL has created the <complexType> declaration in the namespace that was implicitly specified by the package _book was in. It also created a set of element declarations in the interface WSDL namespace. There is one element name for each message part used by one of the messages. These differences propagate forward to the <part> and <operation> elements:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <wsdl:definitions   3:  targetNamespace="http://DLBookService.ch9.apachexml.sauria.com"   4:  xmlns="http://schemas.xmlsoap.org/wsdl/"   5:  xmlns:apachesoap="http://xml.apache.org/xml-soap"   6:  xmlns:impl="http://DLBookService.ch9.apachexml.sauria.com-impl"   7:  xmlns:intf="http://DLBookService.ch9.apachexml.sauria.com"   8:  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"   9:  xmlns:tns2="http://book.apache_xml_book.schemas.sauria.com"  10:  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"  11:  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"  12:  xmlns:xsd="http://www.w3.org/2001/XMLSchema">  13:  <wsdl:types>  14:   <schema  15:    targetNamespace=  16:     "http://book.apache_xml_book.schemas.sauria.com"  17:    xmlns="http://www.w3.org/2001/XMLSchema">  18:    <complexType name="_book">  19:     <sequence>  20:      <element name="address" nillable="true" type="xsd:string"/>  21:      <element name="author" nillable="true" type="xsd:string"/>  22:      <element name="isbn" nillable="true" type="xsd:string"/>  23:      <element name="month" nillable="true" type="xsd:string"/>  24:      <element name="publisher" nillable="true"  25:       type="xsd:string"/>  26:      <element name="title" nillable="true" type="xsd:string"/>  27:      <element name="version" nillable="true" type="xsd:string"/>  28:      <element name="year" type="xsd:short"/>  29:     </sequence>  30:    </complexType>  31:   </schema>  32:   <schema  33:    targetNamespace=  34:     "http://DLBookService.ch9.apachexml.sauria.com"  35:    xmlns="http://www.w3.org/2001/XMLSchema">  36:    <element name="in0" type="tns2:_book"/>  37:    <element name="createBookReturn" type="xsd:boolean"/>  38:    <element name="in0" type="xsd:string"/>  39:    <element name="getBookReturn" type="tns2:_book"/>  40:   </schema>  41:  </wsdl:types>  42:   43:  <wsdl:message name="getBookResponse">  44:   <wsdl:part element="intf:getBookReturn" name="getBookReturn"/>  45:  </wsdl:message>  46:   47:  <wsdl:message name="createBookResponse">  48:   <wsdl:part element="intf:createBookReturn"  49:              name="createBookReturn"/>  50:  </wsdl:message>  51:   52:  <wsdl:message name="getBookRequest">  53:   <wsdl:part element="intf:in0" name="in0"/>  54:  </wsdl:message>  55:   56:  <wsdl:message name="createBookRequest">  57:   <wsdl:part element="intf:in0" name="in0"/>  58:  </wsdl:message>  59:   60:  <wsdl:portType name="DLBookHandler">  61:   <wsdl:operation name="createBook" parameterOrder="in0">  62:    <wsdl:input message="intf:createBookRequest"  63:                name="createBookRequest"/>  64:    <wsdl:output message="intf:createBookResponse"  65:                 name="createBookResponse"/>  66:   </wsdl:operation>  67:   68:   <wsdl:operation name="getBook" parameterOrder="in0">  69:    <wsdl:input message="intf:getBookRequest"  70:                name="getBookRequest"/>  71:    <wsdl:output message="intf:getBookResponse"  72:                 name="getBookResponse"/>  73:   </wsdl:operation>  74:  </wsdl:portType>  75:   76:  <wsdl:binding name="DLBookServiceSoapBinding"  77:                type="intf:DLBookHandler">  78:   <wsdlsoap:binding style="document"  79:    transport="http://schemas.xmlsoap.org/soap/http"/>  80:   81:   <wsdl:operation name="createBook">  82:    <wsdlsoap:operation soapAction=""/>  83:    <wsdl:input name="createBookRequest">  84:     <wsdlsoap:body  85:      namespace="http://DLBookService.ch9.apachexml.sauria.com"  86:      use="literal"/>  87:    </wsdl:input>  88:   89:    <wsdl:output name="createBookResponse">  90:     <wsdlsoap:body  91:      namespace="http://DLBookService.ch9.apachexml.sauria.com"  92:      use="literal"/>  93:    </wsdl:output>  94:   </wsdl:operation>  95:   96:   <wsdl:operation name="getBook">  97:    <wsdlsoap:operation soapAction=""/>  98:    <wsdl:input name="getBookRequest">  99:     <wsdlsoap:body 100:      namespace="http://DLBookService.ch9.apachexml.sauria.com" 101:      use="literal"/> 102:    </wsdl:input> 103:  104:    <wsdl:output name="getBookResponse"> 105:     <wsdlsoap:body 106:      namespace="http://DLBookService.ch9.apachexml.sauria.com" 107:      use="literal"/> 108:    </wsdl:output> 109:   </wsdl:operation> 110:  </wsdl:binding> 111: </wsdl:definitions>

Aside from the changes in the <types> section, this WSDL file looks extremely similar to the file you’d obtain by combining the books schema, the interface WSDL, and the binding WSDL you saw in the WSDL2Java example. Because this is what you expect to see, it’s a welcome sight.

The implementation WSDL file is also very similar to the implementation file from the WSDL2Java example, so you’re in good shape here also:

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <wsdl:definitions   3:  targetNamespace=   4:   "http://DLBookService.ch9.apachexml.sauria.com-impl"   5:  xmlns="http://schemas.xmlsoap.org/wsdl/"   6:  xmlns:apachesoap="http://xml.apache.org/xml-soap"   7:  xmlns:impl="http://DLBookService.ch9.apachexml.sauria.com-impl"   8:  xmlns:intf="http://DLBookService.ch9.apachexml.sauria.com"   9:  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"  10:  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"  11:  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"  12:  xmlns:xsd="http://www.w3.org/2001/XMLSchema">  13:   14:  <wsdl:import  15:   namespace="http://DLBookService.ch9.apachexml.sauria.com"/>  16:  <wsdl:service name="DLBookHandlerService">  17:   <wsdl:port binding="intf:DLBookServiceSoapBinding"  18:                 name="DLBookService">  19:    <wsdlsoap:address  20:        location="http://locahost:8080/apache-xml-book-web-axis/  21: services/DLBookService"/>  22:   </wsdl:port>  23:  </wsdl:service>  24: </wsdl:definitions>

Accessing the ServletContext

Your Web service may need to get access to information that’s only available via the Java Web container/servlet engine. This might be information carried in the HttpServletRequest or carried via the ServletContext. Axis provides a mechanism for your Web service to access this information: the MessageContext class. Let’s extend the implementation of the BookService to use information from the ServletContext to put the serialized book objects in a directory under the Web application directory:

  1: /**   2:  * DLBookServiceSoapBindingImpl.java   3:  *   4:  * This file was auto-generated from WSDL   5:  * by the Apache Axis WSDL2Java emitter.   6:  */   7:    8: package com.sauria.apachexml.ch9.DLBookService;   9:   10: import java.io.FileInputStream;  11: import java.io.FileNotFoundException;  12: import java.io.FileOutputStream;  13: import java.io.IOException;  14: import java.io.ObjectInputStream;  15: import java.io.ObjectOutputStream;  16: import java.rmi.RemoteException;  17:   18: import javax.servlet.ServletContext;  19: import javax.servlet.http.HttpServlet;  20:   21: import org.apache.axis.MessageContext;  22: import org.apache.axis.transport.http.HTTPConstants;  23:   24: import com.sauria.schemas.apache_xml_book.book._book;  25:   26: public class DLBookServiceSoapBindingImpl   27:     implements DLBookHandler {  28:   29:   30:     private String getPath() {  31:         MessageContext mc = MessageContext.getCurrentContext();  32:         HttpServlet s = (HttpServlet)   33:             mc.getProperty(HTTPConstants.MC_HTTP_SERVLET);

Within a Web service, you get access to the MessageContext by calling the static method getCurrentContext on the org.apache.axis.MessageContext class. Once you have the current MessageContext, you can call the getProperty method with the correct constant name to get the object that has the data you need. In this case, you supply the constant HTTPConstants.MC_HTTP_SERVLET; it returns a reference to the current servlet, which you can use later. The source code for org.apache .axis.transport.http.HTTPConstants contains an exhaustive list of the constants you can use. The ones you’re most likely to use are as follows:

  • MC_HTTP_SERVLET—Get the current servlet.

  • MC_HTTP_SERVLET_REQUEST—Get the current HTTP request.

  • MC_HTTP_SERVLET_RESPONSE—Get the current HTTP response.

You can get to most data from one of these three roots.

The getRealPath method returns the real path of the root of the Web application. Once you have the servlet context, it’s easy to get this information. You just return the results of calling getRealPath on "/":

 34:         ServletContext sCtx = s.getServletContext();  35:         String path = sCtx.getRealPath("/");  36:         return path;

When the getPath method is defined, you use it to compute the prefix of the path for the serialized objects:

 37:     }  38:   39:     public boolean createBook(_book book)  40:         throws java.rmi.RemoteException {  41:         boolean status = false;  42:         String isbn = book.getIsbn();  43:         try {  44:             FileOutputStream fos =   45:                 new FileOutputStream(getPath()+isbn+".ser");  46:             ObjectOutputStream oos = new ObjectOutputStream(fos);  47:             oos.writeObject(book);  48:             oos.close();  49:             status  = true;  50:         } catch (FileNotFoundException fnfe) {  51:             fnfe.printStackTrace();  52:         } catch (IOException ioe) {  53:             ioe.printStackTrace();  54:         }  55:         return status;  56:     }

Of course, you have to modify the getBook method to look in the new location as well:

 57:   58:     public _book getBook(java.lang.String isbn)   59:         throws RemoteException {  60:         _book bk = null;  61:         try {  62:             FileInputStream fis =   63:                 new FileInputStream(getPath()+isbn+".ser");  64:             ObjectInputStream ois = new ObjectInputStream(fis);  65:             bk = (_book) ois.readObject();  66:         } catch (FileNotFoundException fnfe) {  67:             fnfe.printStackTrace();  68:         } catch (IOException ioe) {  69:             ioe.printStackTrace();  70:         } catch (ClassNotFoundException cnfe) {  71:             cnfe.printStackTrace();  72:         }  73:         return bk;  74:     }  75: }

The Axis MessageContext class implements the MessageContext interface, which is part of the JAX-RPC specification. You must use the Axis MessageContext class to perform the functions you’ve seen in this section, because these functions are extensions beyond the JAX-RPC 1.0 functionality. You’ll see the JAX-RPC MessageContext interface again when we talk about handlers.

Message Service

In the section on WSDL and Java, you saw how to write RPC services and document/wrapped services. Now let’s look at writing a message service. This message service will show how to deal with a document-literal style SOAP message. You’ll implement the getBook operation from the document-literal book service using an Axis message-style service.

Recall that when you write a message-style service, you’re just passing the SOAP messages around—the Axis engine barely gets involved. There is no automatic conversion of XML into Java types. Axis does save you from having to parse the XML and gives you a choice of four method signatures with which to implement a message service. Let’s use the signature that requires you to supply a request and response SOAPEnvelope. This signature is the most flexible because it includes the SOAP headers as well as the Body.

The Service Requestor

Let’s look at the service requestor first. It has to create a SOAP Envelope whose Body matches the getBook request used in the document-literal example. That message looks like this:

  1: <soapenv:Envelope   2:  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"   3:  <soapenv:Body>   4:   <isbn xmlns="http://sauria.com/schemas/apache-xml-book/book">   5: 0-7645-4355-5</isbn>   6:  </soapenv:Body>   7: </soapenv:Envelope>

Here’s the requestor:

  1: /*   2:  *    3:  * MessageBookRequestor.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch9.MessageBookService;   9:   10: import java.rmi.RemoteException;  11:   12: import javax.xml.rpc.ServiceException;  13: import javax.xml.soap.Name;  14: import javax.xml.soap.SOAPBody;  15: import javax.xml.soap.SOAPBodyElement;  16: import javax.xml.soap.SOAPException;  17:   18: import org.apache.axis.client.Call;  19: import org.apache.axis.client.Service;  20: import org.apache.axis.message.SOAPEnvelope;  21:   22: public class MessageBookRequestor {  23:   24:     public static void main(String[] args) {  25:         Call call = null;  26:         Service s = new Service();  27:         try {  28:             call = (Call) s.createCall();  29:         } catch (ServiceException e) {  30:             e.printStackTrace();  31:         }

You create a call object. This looks like a DII call, and it is, sort of. There’s no way to generate a message service from WSDL, at least at the moment, so no stubs or dynamic proxies are available.

In lines 33-36, you building the endpoint address for the message-style service provider and set the call’s target endpoint to that address. In line 38, you create an empty SOAPEnvelope that will serve as the argument for your service request:

 32:    33:         String host = "http://localhost:8080";  34:         String path =   35:          "/apache-xml-book-web-axis/services/MessageBookService";  36:         call.setTargetEndpointAddress(host+path);  37:           38:         SOAPEnvelope env = new SOAPEnvelope();

Next you need to add the <isbn> element as the child of the SOAP body. In line 40, you get hold of the SOAP Body. You create a namespace-qualified name for isbn in the http://sauria.com/schemas/apache-xml-book/book namespace, and in line 45 you add a new element to the Body with that qualified name. All that’s left is to add the value of the <isbn> element, which you do by adding a text node as a child of the isbn element. Note that the model for a SOAP message is based around the DOM but extended to cover the functionality needed by SOAP. In particular, the SOAPBody’s addBodyElement adds the element to the Body but also declares the namespace prefix of the element:

 39:         try {  40:             SOAPBody body = env.getBody();  41:             Name isbnName =   42:                env.createName("isbn","",  43:               "http://sauria.com/schemas/apache-xml-book/book");  44:             SOAPBodyElement bodyElt =   45:                 body.addBodyElement(isbnName);  46:             bodyElt.addTextNode("0-7645-4355-5");  47:         } catch (SOAPException se) {  48:             se.printStackTrace();  49:         }

Once you have the complete envelope for the request, you call the version of the invoke method that expects a SOAPEnvelope as its argument and returns a SOAPEnvelope as its result. The Axis engine maps it to the correct signature on the provider side:

 50:   51:         try {  52:             SOAPEnvelope result = call.invoke(env);

To keep the code simple, you just print the contents of the result envelope. You can use the SOAPEnvelope, SOAPBody, SOAPBodyElement, and other related classes to take the envelope apart and perform some processing. You’ll see how the provider uses those classes to do that:

 53:             System.out.println(result);  54:         } catch (RemoteException re) {  55:             re.printStackTrace();  56:         }  57:     }  58: }

The Service Provider

Now let’s turn our attention to the provider side. The provider needs to take a SOAPEnvelope that contains the request, disassemble it, and take some action based on the contents. It needs to take the results and insert them into the response SOAPEnvelope:

  1: /*   2:  *    3:  * MessageBookProvider.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch9.MessageBookService;   9:   10: import java.rmi.RemoteException;  11: import java.util.Iterator;  12:   13: import javax.xml.soap.Name;  14: import javax.xml.soap.SOAPBody;  15: import javax.xml.soap.SOAPElement;  16: import javax.xml.soap.SOAPException;  17:   18: import org.apache.axis.message.SOAPBodyElement;  19: import org.apache.axis.message.SOAPEnvelope;  20: import org.w3c.dom.Element;  21:   22: import   23: com.sauria.apachexml.ch9.DLBookService.DLBookServiceSoapBindingImpl;  24: import com.sauria.schemas.apache_xml_book.book._book;  25:   26: public class MessageBookProvider {  27:     public void getBook(SOAPEnvelope req, SOAPEnvelope res) {

The getBook method has one of the valid signatures for a message-style Axis service: It takes a pair of SOAPEnvelopes as arguments. The request envelope is the contents of the requestor’s request. The Axis engine supplies an empty SOAPEnvelope for the response envelope.

You’re only working with the SOAPBody, so you get the SOAPBody from the request Envelope:

 28:         SOAPBody body = null;  29:         try {  30:             body = req.getBody();  31:         } catch (SOAPException se) {  32:             se.printStackTrace();  33:         }

You iterate over the children of the SOAP body, looking for one whose name is isbn. If you find a child element named isbn, then you have work to do:

 34:         String isbnValue = "";  35:         for (Iterator i = body.getChildElements();  36:              i.hasNext();) {  37:             SOAPBodyElement sbe = (SOAPBodyElement) i.next();  38:             if (sbe.getName().equals("isbn")) {  39:                 Element e = null;

Next you get the content of the isbn element. You can ask for any SOAPBodyElement to be converted to a regular DOM element (lines 40-44). Once you have a DOM, then it’s a matter of straightforward DOM calls to get the content of the element (lines 45-46):

 40:                 try {  41:                     e = sbe.getAsDOM();  42:                 } catch (Exception e1) {  43:                     e1.printStackTrace();  44:                 }  45:                 isbnValue =   46:                     e.getFirstChild().getNodeValue();

Instead of reimplementing the code for getBook, you use the implementation in DLServiceSoapBindingImpl to retrieve a serialized book. Now you’re ready to return the contents of that book as a SOAP message. Again, you want to mimic the message you get back from the document-literal service:

 47:             }  48:         }  49:           50:         DLBookServiceSoapBindingImpl imp =   51:             new DLBookServiceSoapBindingImpl();  52:               53:         try {  54:             _book book = imp.getBook(isbnValue);

After obtaining the SOAPBody from the response envelope (line 56), you create the <books:book> element by creating a qualified name and prefix on the response envelope (lines 58-60) and then creating a SOAPBodyElement with that name:

 55:   56:             SOAPBody b = res.getBody();  57:               58:             Name bkName =   59:               res.createName("book","books",  60:               "http://sauria.com/schemas/apache-xml-book/book");  61:             SOAPBodyElement bk = (SOAPBodyElement)   62:                 b.addBodyElement(bkName);

By adding the children of the <books:book> element, you add the data obtained by the getBook call. You do this by creating a child element of the <books:book> element, remembering what that child is (line 65), and then adding a text node child containing the actual data to that new child (line 66). You then repeat this until all the fields have been added (lines 67-78):

 63:   64:             SOAPElement child;  65:             child = bk.addChildElement("author","books");  66:             child.addTextNode(book.getAuthor());  67:             child = bk.addChildElement("title","books");  68:             child.addTextNode(book.getTitle());  69:             child = bk.addChildElement("isbn","books");  70:             child.addTextNode(book.getIsbn());  71:             child = bk.addChildElement("month","books");  72:             child.addTextNode(book.getMonth());  73:             child = bk.addChildElement("year","books");  74:             child.addTextNode(Short.toString(book.getYear()));  75:             child = bk.addChildElement("publisher","books");  76:             child.addTextNode(book.getPublisher());  77:             child = bk.addChildElement("address","books");  78:             child.addTextNode(book.getAddress());  79:         } catch (RemoteException re) {  80:             re.printStackTrace();  81:         } catch (SOAPException se) {  82:             se.printStackTrace();  83:         }  84:     }  85: }

The Deployment Descriptors

The deployment descriptor for the provider is the last thing you need before you can put your new service into action. The style attribute is "message", indicating that this is an Axis message-style service:

  1: <deployment   2:  name="test" xmlns="http://xml.apache.org/axis/wsdd/"    3:  xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"   4:  xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance">   5:  <service name="MessageBookService" style="message">

You supply the classname of the provider class as the value of the className parameter, so that Axis knows which class is providing the provider implementation:

  6:   <parameter   7:       name="className"   8:       value=   9: "com.sauria.apachexml.ch9.MessageBookService.MessageBookProvider"

You export the getBook method from the service so it can be called:

 10:  />  11:    <parameter name="allowedMethods" value="getBook" />  12:  </service>  13: </deployment>

For the sake of completeness, here’s the undeployment descriptor:

  1: <undeployment   2:  name="test"   3:  xmlns="http://xml.apache.org/axis/wsdd/">   4:   <service name="MessageBookService"/>   5: </undeployment>

The output of the requestor program is as follows:

  1: <soapenv:Envelope   2:  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"   3:  xmlns:xsd="http://www.w3.org/2001/XMLSchema"   4:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   5:  <soapenv:Body>   6:   <books:book   7:    xmlns:books="http://sauria.com/schemas/apache-xml-book/book">   8:    <books:author   9:     xmlns:books=  10:      "http://sauria.com/schemas/apache-xml-book/book">  11:     Ted Leung  12:    </books:author>  13:    <books:title  14:     xmlns:books=  15       "http://sauria.com/schemas/apache-xml-book/book">  16:     Professional XML Development with Apache Tools  17:    </books:title>  18:    <books:isbn  19:     xmlns:books=  20:      "http://sauria.com/schemas/apache-xml-book/book">  21:     0-7645-4355-5  22:    </books:isbn>  23:    <books:month  24:     xmlns:books=  25:      "http://sauria.com/schemas/apache-xml-book/book">  26:     October  27:    </books:month>  28:    <books:year  29:     xmlns:books=  30:      "http://sauria.com/schemas/apache-xml-book/book">  31:     2003  32:    </books:year>  33:    <books:publisher   34:     xmlns:books=  35:      "http://sauria.com/schemas/apache-xml-book/book">  36:     Wrox  37:    </books:publisher>  38:    <books:address  39:     xmlns:books=  40:      "http://sauria.com/schemas/apache-xml-book/book">  41:     Indianapolis, Indiana  42:    </books:address>  43:   </books:book>  44:  </soapenv:Body>  45: </soapenv:Envelope>

This is semantically equivalent to the response message from the document-literal example. The big difference is the declaration of the books namespace on each child of <books:book>.

Handlers

Axis provides an implementation of the JAX-RPC handler facility that allows you to insert handlers that intercept the processing of a message at well-defined points. On the requestor side, interception occurs before the message leaves the requesting host and after the response message has been received (but before it’s processed). Similarly, on the provider side, interception occurs after the request message has been received from the requestor, but before it’s passed to the service code; and after the response message has been generated, but before it’s transmitted back to the requestor.

Let’s look at implementing a simple handler that can be used on both the requestor and the provider sides. This handler logs the SOAP representation of the message using log4j. You can imagine using a handler like this when you need records of every transaction that passes through the system—in particular if you want to prove to the business partner that’s providing or using the Web service that, in fact, a particular request was or wasn’t made or was or wasn’t processed.

The example is based on the document-literal example we’ve used before. Let’s begin by looking at how to register a handler on the requestor side. You’ll build up a pointer to the WSDL file for the document-literal service:

  1: /*   2:  *    3:  * DLBookHandlerClient.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch9.DLBookService;   9:   10: import java.net.MalformedURLException;  11: import java.net.URL;  12: import java.rmi.RemoteException;  13: import java.util.List;  14:   15: import javax.xml.namespace.QName;  16: import javax.xml.rpc.Service;  17: import javax.xml.rpc.ServiceException;  18: import javax.xml.rpc.ServiceFactory;  19: import javax.xml.rpc.handler.HandlerInfo;  20:   21: import   22: com.sauria.apachexml.ch9.DLBookService.handlers.LoggingHandler;  23: import com.sauria.schemas.apache_xml_book.book._book;  24:   25: public class DLBookHandlerClient {  26:     static String router =  27:        "http://localhost:8080/apache-xml-book-web-axis/";  28:     static String service = "DLBookService.wsdl";

You create a ServiceFactory instance (line 32) and use that instance to create a Service instance (line 38) using the WSDL document (line 33) and the QName of the service (lines 34-37):

 29:   30:     public static void main(String[] args) {  31:         try {  32:             ServiceFactory sf = ServiceFactory.newInstance();  33:             URL u = new URL(router + "/" + service);  34:             QName serviceName =  35:                new QName(  36:                "http://DLBookService.ch9.apachexml.sauria.com",  37:                "DLBookHandlerService");  38:             Service s = sf.createService(u,serviceName);

Next you create a QName for the port (lines 40-43), which you need so you can get the handler chain for this port (lines 45-46) (and so you can create the dynamic proxy to the service later):

 39:   40:             QName portName =  41:                new QName(  42:                "http://DLBookService.ch9.apachexml.sauria.com",  43:                "DLBookService");  44:   45:             List handlerChain =   46:             s.getHandlerRegistry().getHandlerChain(portName);

To register the handler in the port’s handler chain, you create an instance of HandlerInfo (line 47-49) that takes the class of the handler you’re registering (in this case, LoggingHandler.class), a Map containing configuration information for the handler (none in this case), and an array of QNames that specifies the headers this handler will process (again, none in this case because you process every message). Once you have the HandlerInfo, you use the regular add method on List to add the handler to the handlerChain:

 47:             HandlerInfo hi =   48:                 new HandlerInfo(LoggingHandler.class,  49:                                 null,null);  50:             handlerChain.add(hi);  51:   52:             DLBookHandler h =  53:                 (DLBookHandler) s.getPort(portName,  54:                                           DLBookHandler.class);  55:   56:             _book bk = new _book();  57:             bk.setAuthor("Theodore W. Leung");  58:             bk.setTitle(  59:                "Professional XML Development with Apache Tools");  60:             bk.setIsbn("0-7645-4355-5");  61:             bk.setMonth("December");  62:             bk.setYear((short) 2003);  63:             bk.setPublisher("Wrox");  64:             bk.setAddress("Indianapolis, Indiana");  65:   66:             boolean value = h.createBook(bk);  67:             System.out.println(value);  68:         } catch (RemoteException re) {  69:             re.printStackTrace();  70:         } catch (ServiceException se) {  71:             se.printStackTrace();  72:         } catch (MalformedURLException mue) {  73:             mue.printStackTrace();  74:         }  75:     }  76: }

Those are the only header-specific lines on the requestor side. Everything else is a regular dynamic proxy invocation, construction of the argument values, and so on. The handlers interact with the requestor code by causing SOAP faults if there’s a problem, so this is handled using the standard mechanism for handling faults (exceptions).

Your handler class needs to implement the JAX-RPC javax.xml.rpc.handler.Handler interface. The interface has init and destroy lifecycle methods for dealing with configuration. The method getHeaders returns an array of the QNames of the headers the handler knows how to process. Most of the work is done by the handleRequest, handleResponse, and handleFault methods. These methods are called when a request, response, or fault message, respectively, passes through the handler. Each of these methods takes a JAX-RPC MessageContext as an argument, which is how the method can access the details of the message being handled. The methods return true if the message should be processed by the next handler in the handler chain, or false if no further handlers in the chain should be executed.

The handler is relatively simple. It has a dedicated log4j Logger channel:

  1: /*   2:  *    3:  * LoggingHandler.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch9.DLBookService.handlers;   9:   10: import javax.xml.namespace.QName;  11: import javax.xml.rpc.handler.Handler;  12: import javax.xml.rpc.handler.HandlerInfo;  13: import javax.xml.rpc.handler.MessageContext;  14: import javax.xml.rpc.handler.soap.SOAPMessageContext;  15: import javax.xml.soap.SOAPException;  16: import javax.xml.soap.SOAPMessage;  17:   18: import org.apache.log4j.Logger;  19:   20: public class LoggingHandler implements Handler {  21:     Logger log;  22:   23:     public void destroy() {  24:     }  25:   26:     public QName[] getHeaders() {  27:         return null;  28:     }

The handleFault, handleRequest, and handleResponse methods log their context to the logger (line 31) and then call getEnvelope to obtain a SOAP representation of the message (line 32). The SOAP representation is also logged (line 32), and then the handler chain is signaled to continue handler processing by returning true (line 33):

 29:       30:     public boolean handleFault(MessageContext mc) {  31:         log.error("requestor fault");  32:         log.error(getEnvelope(mc));  33:         return true;  34:     }  35:   36:     public boolean handleRequest(MessageContext mc) {  37:         log.info("requestor request");  38:         log.info(getEnvelope(mc));  39:         return true;  40:     }  41:   42:     public boolean handleResponse(MessageContext mc) {  43:         log.info("requestor response");  44:         log.info(getEnvelope(mc));  45:         return true;  46:     }

You use the handler’s init method to set up the log4j logger:

 47:   48:     public void init(HandlerInfo hi) {  49:         log = Logger.getLogger(this.getClass());  50:     }

getEnvelope downcasts the MessageContext to a SOAPMessageContext (line 53). This is necessary because only SOAPMessageContext has the method you need in order to obtain the SOAP form of the message (line 54):

 51:   52:     private String getEnvelope(MessageContext mc) {  53:         SOAPMessageContext smc = (SOAPMessageContext) mc;  54:         SOAPMessage msg = smc.getMessage();

Once you have the SOAP message, you get the message as a SOAPPart, obtain the Envelope from the part, and convert the Envelope to a string that can be logged:

 55:         String value = "";  56:         try {  57:             value = msg.getSOAPPart().getEnvelope().toString();  58:         } catch (SOAPException se) {  59:             se.printStackTrace();  60:         }  61:         return value;  62:     }  63: }

The code for the requestor side showed how to install a requestor-side handler. JAX-RPC 1.0 doesn’t specify how to install handlers on the provider side. Axis uses an entry in the deployment descriptor to fulfill this function. This is the deployment descriptor generated by WSDL2Java for the document-literal example—we’ve added the information needed to register the handler:

  1: <!--Use this file to deploy some handlers/chains and services-->   2: <!--Two ways to do this:                                     -->   3: <!--  java org.apache.axis.client.AdminClient deploy.wsdd    -->   4: <!--     after the axis server is running                    -->   5: <!-- or                                                      -->   6: <!-- java org.apache.axis.utils.Admin client|server deploy.wsdd   7:    -->   8: <!--     from the same directory that the Axis engine runs   -->   9:   10: <deployment  11:  xmlns="http://xml.apache.org/axis/wsdd/"  12:  xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">  13:   14: <!-- Services from DLBookHandlerService WSDL service -->  15:   16:  <service name="DLBookService" provider="java:RPC"  17:           style="document" use="literal">  18:   <parameter name="wsdlTargetNamespace"  19:    value="http://DLBookService.ch9.apachexml.sauria.com"/>  20:   <parameter name="wsdlServiceElement"  21:    value="DLBookHandlerService"/>  22:   <parameter name="wsdlServicePort"  23:    value="DLBookService"/>  24:   <parameter name="className"  25:    value="com.sauria.apachexml.ch9.DLBookService.  26: DLBookServiceSoapBindingImpl"/>  27:   <parameter name="wsdlPortType" value="DLBookHandler"/>  28:   <operation name="createBook"  29:    qname="operNS:createBook"  30:    xmlns:operNS="http://DLBookService.ch9.apachexml.sauria.com"  31:    returnQName="createBookReturn"  32:    returnType="rtns:boolean"  33:    xmlns:rtns="http://www.w3.org/2001/XMLSchema" >  34:    <parameter qname="pns:book"  35:     xmlns:pns="http://sauria.com/schemas/apache-xml-book/book"  36:     type="tns:>book"  37:     xmlns:tns="http://sauria.com/schemas/apache-xml-book/book"/>  38:   </operation>  39:   <operation name="getBook"  40:    qname="operNS:getBook"  41:    xmlns:operNS="http://DLBookService.ch9.apachexml.sauria.com"  42:    returnQName="retNS:book"  43:    xmlns:retNS="http://sauria.com/schemas/apache-xml-book/book"  44:    returnType="rtns:>book"  45:    xmlns:rtns="http://sauria.com/schemas/apache-xml-book/book" >  46:    <parameter qname="pns:isbn"  47:     xmlns:pns="http://sauria.com/schemas/apache-xml-book/book"  48:     type="tns:string"  49:     xmlns:tns="http://www.w3.org/2001/XMLSchema"/>  50:   </operation>  51:   <parameter name="allowedMethods" value="getBook createBook"/>  52:   53:   <typeMapping  54:    xmlns:ns="http://sauria.com/schemas/apache-xml-book/book"  55:    qname="ns:>book"  56:    type="java:com.sauria.schemas.apache_xml_book.book._book"  57:    serializer=  58:       "org.apache.axis.encoding.ser.BeanSerializerFactory"  59:    deserializer=  60:       "org.apache.axis.encoding.ser.BeanDeserializerFactory"  61:    encodingStyle=""  62:    />  63: 

WSDD uses the <requestFlow> and <responseFlow> elements to add handlers to the request-handling chain and response-handling chain, respectively. The <handler> elements inside them are the same; the only difference is whether they appear as a child of <requestFlow> or <responseFlow>. Because you want to log requests (to make sure you received them) and responses (to make sure you sent them), you install the logging handler in both flows.

The type attribute specifies that you’re using a JAX-RPC handler. Axis has its own notion of handlers that was developed prior to the introduction of JAX-RPC:

 64:   <requestFlow>  65:    <handler type="java:org.apache.axis.handlers.JAXRPCHandler">

After that are two parameters. The scope parameter says there’s one handler instance per session. The className parameter gives the name of the class that’s providing the handler functionality:

 66:     <parameter name="scope" value="session"/>  67:     <parameter name="className"  68:          value=  69: "com.sauria.apachexml.ch9.DLBookService.handlers.LoggingHandler"/>  70:    </handler>  71:   </requestFlow>  72:   <responseFlow>  73:    <handler type="java:org.apache.axis.handlers.JAXRPCHandler">  74:     <parameter name="scope" value="session"/>  75:     <parameter name="className"  76:          value=  77: "com.sauria.apachexml.ch9.DLBookService.handlers.LoggingHandler"/>  78:    </handler>  79:   </responseFlow>  80:   81:  </service>  82: </deployment>

This is a relatively simple handler. As Web services deployments become more common, and people gain experience with architectures based on networks of Web services, we’ll see the use of SOAP headers to carry information around those networks. That’s when the use of handlers will really take off.

.jws Web Services

Axis provides a mechanism for quickly deploying simple Web services. If you take a service provider class and change the file suffix from .java to .jws, you can then put that file into an appropriately configured Web application directory (the Web application needs to have a url-mapping from .jws files to the AxisServlet). When you access the URL for that file, Axis automatically compiles the file and processes any SOAP request that accompanies the access.

Here’s an example. This is the service code generated by WSDL2Java for the RPC-encoded Web service example. The only change that has been made is to comment out the package declaration in line 8. If you drop this file into a properly configured Web application directory, you’ll have a running Web service, and you can use the various requestors we showed earlier to perform service requests:

  1: /**   2:  * BookServiceSoapBindingImpl.java   3:  *   4:  * This file was auto-generated from WSDL   5:  * by the Apache Axis WSDL2Java emitter.   6:  */   7:    8: //package com.sauria.apachexml.ch9.BookService;   9:   10: public class BookServiceSoapBindingImpl   11:     implements   12:       com.sauria.apachexml.ch9.BookService.BookHandler {  13:     public boolean createBook(java.lang.String author,   14:         java.lang.String title,   15:         java.lang.String isbn,   16:         java.lang.String month,   17:         int year,   18:         java.lang.String publisher,   19:         java.lang.String address)   20:         throws java.rmi.RemoteException {  21:         return author.equals("Ted Leung");  22:     }  23: }

The biggest drawback to .jws Web services is that right now the functionality only works for RPC-encoded Web services. Because the WS-I basic profile mandates document-literal Web services, the usefulness of .jws Web services has been reduced, at least until the Axis team updates them to work with document-literal style. The other drawback is that you don’t have as much control as when you use deployment descriptors, which WSDL2Java can generate for you.

Tools

Let’s look at a pair of tools that can make your life much easier when you’re working with Axis. The first is useful for debugging Web services, and the second will keep you from going typing-crazy.

TCPMon

The first tool is called TCPMon, and it gives you a way to look at the SOAP messages that are exchanged between two hosts. The command line for TCPMon is

java org.apache.axis.utils.tcpmon listenPort targetHost targetPort 

When you execute this command, TCPMon connects the listenPort on the current machine to the targetPort on the target machine. All the arguments are optional, and if you leave them out, TCPMon starts you with a GUI page where you can fill in the values interactively. Here’s a screenshot of TCPMon in action:

click to expand

TCPMon is very useful for telling whether the SOAP that’s being exchanged is the SOAP you think is being exchanged. It’s particularly useful when the requestor and provider are using different SOAP runtimes, because it lets you see what might be causing any interoperability problems.

Ant Tasks

The other tool you’ll want are the Ant tasks for Axis. They’re included in the Axis distribution in the axis-ant.jar file in the lib directory. Tasks are provided for WSDL2Java, Java2WDSL and AdminClient. These tasks can save you lots of typing when you’re working with Axis. Let’s go through each of these tasks.

WSDL2Java

The element name for the WSDL2Java task is <axis-wsdl2java>. The attributes correspond to the command-line options, shown in parentheses, for WSDL2Java. Attributes are optional unless noted:

  • all—Generate all elements. Defaults to false. (--all)

  • debug—Turn on debug output. Defaults to false. (--debug)

  • deployscope—Set the deployment scope: "Application", "Session", or "Request". (--deployScope)

  • factory—Name of the WSDL2Java emitter factory. (--factory)

  • helpergen—Generate helper classes. Defaults to false. (--helperGen)

  • namespacemappingfile—Name of the namespace mapping file. (--filetoNsPkg)

  • noimports—Don’t generate code for elements in imported WSDL. Defaults to false. (--noImports)

  • output—Name of the output directory. (--output)

  • serverside—Generate provider-side files. Defaults to false. (--server-side)

  • skeletondeploy—Generate skeleton files. Defaults to false. (--skeletonDeploy)

  • testcase—Generate a JUnit test case. Defaults to false. (--testcase)

  • timeout—Set the requestor timeout. Defaults to 45 seconds. -1 disables timeouts. (--timeout)

  • typemappingversion—Set the type-mapping version (1.1 or 1.2). Defaults to 1.1. (--typeMappingVersion)

  • url—URL of the WSDL file (required).

  • verbose—Verbose output. Defaults to false. (--verbose)

Java2WSDL

The element name for the Java2WSDL task is <axis-java2wsdl>. The attributes correspond to the command-line options, show in parentheses, for Java2WSDL. Attributes are optional unless noted:

  • bindingname—Name of <binding>. (--bindingName)

  • classname—Name of the class for which to generate WSDL (required).

  • exclude—Methods to exclude. (--exclude)

  • extraclasses—List of extra classes. (--extraClasses)

  • implclass—Implementation class. (--implClass)

  • input—Input WSDL file. (--input)

  • location—Location of the service. (--location)

  • locationimport—Location of the interface WSDL. (--locationImport)

  • methods—Methods to export. (--methods)

  • namespace—Target namespace for the WSDL (required). (--namespace)

  • namespaceimpl—Target namespace for the implementation WSDL. (--namespaceImpl)

  • output—Name of the output WSDL file. (--output)

  • outputimpl—Name of the output implementation WSDL file. (--outputImpl)

  • porttypename—Name of the <portType>. (--portTypeName)

  • serviceelementname—Name of the <service>. (--serviceElementName)

  • serviceportname—Name of the <port>. (--servicePortName)

  • stopclasses—List of classes that stop the inheritance search. (--stopClasses)

  • style—Style of the WSDL document. Valid values are "document", "wrapped", and "rpc". (--style)

  • typemappingversion—Type-mapping version (1.1 or 1.2). Defaults to 1.1. (--typeMappingVersion)

  • use—Set the WSDL use option. Valid values are "encoded" and "literal". (--use)

  • useinheritedmethods—Export inherited methods if true. Defaults to false. (--all)

AdminClient

The element name for the AdminClient task is <axis-admin>. The attributes correspond to the command-line options, shown in parentheses, for AdminClient. Attributes are optional unless noted:

  • debug—Turn on debug output. (-d)

  • failonerror—The Ant build should fail on an error if true. Defaults to true.

  • fileprotocol—If the value is a filename, use a simple file protocol. (-f)

  • hostname—The hostname to connect to. (-h)

  • newpassword—Change the password to the value of this attribute.

  • password—The password needed to log in. (-w)

  • port—The port to connect on. (-p)

  • servletpath—The path to the Axis servlet. (-s)

  • transportchain—The transport chain to use. (-t)

  • url—The full URL to the Axis servlet. (-l)

  • username—The username needed to log in. (-u)

  • xmlfile—The deployment or undeployment descriptor file (required).

Sample build.xml

The following code is a cut-down version of the Ant file used to build the document-literal example in this chapter. Create a property that points to the Axis installation:

  1: <project name="axis-samples" default="wsdl">   2:  <property name="axis.home" value="/work/lib/axis-1_1"/>

Create a classpath that contains the Axis jars:

  3:    4:  <path >   5:   <fileset dir="${axis.home}/lib">   6:    <include name="**/*.jar"/>   7:   </fileset>   8:  </path>

Find the Axis taskdef:

  9:   10:  <taskdef resource="axis-tasks.properties"   11:      classpathref="axis.classpath" />

This target runs Java2WSDL, generating a test case and provider stubs, including skeletons. The output level is verbose, and the Java files are written directly into a directory in the Eclipse project:

 12:    13:  <target name="wsdl-dl">  14:   <axis-wsdl2java url="DLBookService.wsdl"  15:    serverside="true"  16:    skeletondeploy="true"  17:    output=  18:    "/work/workspace-1.4/apache-xml-book-web-axis/WEB-INF/src"  19:    testcase="true"  20:    verbose="true">  21:   </axis-wsdl2java>  22:  </target>

Here you set some properties to make it easier to do administration. You set properties for the port, host, and admin servlet path. You also set a partial path that helps find the generated deployment descriptors:

 23:   24:  <property name="axis.port" value="8080"/>  25:  <property name="axis.host" value="localhost"/>  26:  <property name="axis.adminService"  27:     value="apache-xml-book-web-axis/services/AdminService"/>  28:  <property name="descriptor.path"   29:      value="com/sauria/apachexml/ch9"/>

This task is used to deploy the service. The values of the attributes are supplied by the properties defined earlier:

 30:   31:  <target name="deploy-dl">  32:   <axis-admin  33:    port="${axis.port}"  34:    hostname="${axis.host}"  35:    servletpath="${axis.adminService}"  36:    xmlfile="${descriptor.path}/DLBookService/deploy.wsdd"/>  37:  </target>

This task undeploys the service:

 38:    39:  <target name="undeploy-dl">  40:   <axis-admin  41:    port="${axis.port}"  42:    hostname="${axis.host}"  43:    servletpath="${axis.adminService}"  44:    xmlfile="${descriptor.path}/DLBookService/undeploy.wsdd"/>  45:  </target>  46:   47: </project>

This Ant build file makes it easy to run WSDL2Java and to deploy and undeploy your service. It can save you a lot of typing.




Professional XML Development with Apache Tools. Xerces, Xalan, FOP, Cocoon, Axis, Xindice
Professional XML Development with Apache Tools: Xerces, Xalan, FOP, Cocoon, Axis, Xindice (Wrox Professional Guides)
ISBN: 0764543555
EAN: 2147483647
Year: 2003
Pages: 95

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net