2. The global.request chain is invoked.
3. The transport chain is invoked. Notice that here the entire chain is invoked, not just the request side. There is no need to split the request and response invocations because they would be called one right after the other anyway. Also note that at the pivot point of the transport specific chain is a transport sender ; more on this later.
4. The global.response chain is invoked.
5. The response handlers of the Web service–specific response chain are invoked.
As mentioned in Step 3, there is something new here: a transport sender. A transport sender is a handler that is responsible for taking the request SOAP message and sending it to a SOAP server. For example, one of the transport senders that is shipped with Axis is an HTTP transport sender that will take the request SOAP message, open an HTTP socket connection to the specified HTTP server, do a POST of the SOAP message, and wait for a response. The response is then placed in the response message portion of the MessageContext object.
Because transport senders are just handlers, they can be placed in any chain in any configuration. So, it is technically possible that the transport chain shown in Figure 4.7 could have multiple transport senders if you desire a multicast scenario. It is also possible that a transport sender could be placed in one of the chains on the server such that the response message could be sent over a different transport than it was received on (for example, the transport listener could be an HTTP servlet, but the SOAP response message could be sent back via SMTP).
Locating the Service Chain
We've talked about how there are service-specific chains, but we haven't yet touched on how the service chain is selected by the Axis engine. An interesting aspect of SOAP is that it doesn't mandate how to determine the exact Web service to invoke. This might seem odd—but it is accurate. Many different common practices have been established, and each one is valid. Here are just three of the more common ones:
• SOAPAction— The value of the SOAPAction HTTP header is used to match the service name.
• URL— The URL of the incoming HTTP request is used to match the service name.
For example, if the URL used to access Axis was
http://localhost:8080/axis/servlet/AxisServlet/MyService, then the SOAP engine would look for a service chain called myService.
• Namespace— Perhaps the most used method. It takes the namespace of the first XML element in the SOAP Body block and tries to find a service chain that
matches.
Axis could have chosen one method, most likely the namespace approach, but doing so would have limited the options available to its users. Instead, Axis lets you choose how services are determined. A handler can be written and placed in the transport chain or
global chain that can determine which service chain to choose. This handler is free to use any algorithm it wants to make this determination. However, if a service chain has not been selected by the time the Axis engine gets to the point in its processing that it wants to invoke the service specific chain, it will default to the namespace selection method.
XML Parsing
Axis has been designed and coded with a watchful eye towards performance. For this reason, when tackling the problem of how to parse an XML stream efficiently, a SAX- based approach was chosen over a DOM-based one. As we discussed in Chapter 2, "XML Primer," SAX XML parsing does not read the entire XML stream into memory; rather, it triggers callbacks based on the types of XML tokens that are encountered. It then becomes the responsibility of those callback routines to do any buffering of data or saving of state that is needed so that subsequent callbacks still have access to any previously seen data. Even though SAX is used for parsing, a DOM-based representation of XML sometimes is used when passing around XML blocks—as will be seen in the
"Document-Centric Services" section. In various discussions and examples in this
chapter, we'll need to talk about concepts and features in terms of using a SAX parser or writing SAX callback routines. We assume that you are familiar with these concepts, and we will not explain the specific details of how to write code utilizing a SAX parser in great detail.
With that overview of what Axis is and how it works, now we can move on to actually using it.
Installing Axis
Installing an Axis server is relatively simple. In the servlet engine's directory structure is a webapps directory. This directory contains the various Web applications that are
deployed. The Axis distribution includes a webapps directory as well, and contains a directory named axis. Copy the axis directory into the servlet engine's webapps directory. In this new axis directory should be a WEB-INF/lib directory. There, you should place all the JAR files Axis will need to run. You should already see axis.jar.
Currently, Axis will need one only additional JAR file: xerces.jar (available from the Apache Xerces distribution at http://xml.apache.org) or any other JAXP-compliant parser.
Copy the parser's JAR file into the WEB-INF/lib directory.
When services are deployed, the Java class files should be placed in the axis/WEB-INF/
classes directory; or if there are JAR files, they should be placed in the axis/WEB- INF/lib directory. If you plan to use the JWS feature of Axis (see the "Simple Web Services" section), you will also need to make sure that the Java tools.jar file is either in the axis/ WEB-INF/lib directory or in your servlet engine's classpath.
Installing the client is simply a matter of making sure the axis.jar and xerces.jar files are in your classpath.
Configuring Axis
There are four different types of configuration files. Each will contain the same type of data, but the outermost XML element will vary:
• client-config.xml — This is the configuration information for the Axis engine when it is invoked on the client. This file needs to be in the current working directory of the client application. This file has the format
• <engineConfig>
• …configuration XML…
• </engineConfig>
• server-config.xml — This is the configuration information for the Axis engine when it is invoked on the server. This file needs to be in the servlet engine's axis/WEB-INF directory in the HTTP case, or in the current working directory for other transports. This file has the format
• <engineConfig>
• …configuration XML…
• </engineConfig>
• deploy.xml — This is the file used by the AdminClient to deploy new handlers, chains, and services. The AdminClient is an administrative tool used for deploying and undeploying Axis resources (such as handlers and chains). The AdminClient uses a document-centric Web service—it takes the XML file passed to it and sends it to the Axis server for processing (more later). Unlike the first two configuration files, the name of this file can be anything. This file has the format
• <m:deploy xmlns:m="AdminService">
• …configuration XML…
• </m:deploy>
• undeploy.xml — This is the file used by the AdminClient to undeploy resources. The AdminClient will remove any named resources listed as immediate child elements of the undeploy XML element. Unlike the first two configuration files, the name of this file can be anything. This file has the format
• <m:undeploy xmlns:m="AdminService">
• …resources to be removed…
• </m:undeploy>
In the first three cases, ...configuration XML... section will contain one of the following XML elements:
• handler
• <handler name="handler_name" class="class_name">
• [<option name="option_name" value="option_value"/>]…
• </handler>
•
• Example:
• <!-- Define an EMail handler -->
• <handler name="EMail" class="com.skatestown.services.EMailHandler" />
• Defines a single handler named handler_name whose class is given by
class_name.The optional name/value pairs are defined by each specific handler and will be available to the handler at runtime.
• chain
• <chain name="chain_name" { flow="list_of_handlers" |
• request="list_of_handlers" pivot="handler_name"
• response="list_of_handlers"} >
• [<option name="option_name" value="option_value"/>]…
• </chain>
•
• Example:
• <!--Define a chain consisting of 2 handlers, a Java RPC Dispatcher
• and the Email handler from chapter 3 -->
• <chain name="myChain" flow="RPCDispatcher, Email" />
• Defines a chain called chain_name that consists of either the list of handlers specified in the flow attribute or the handlers specified by the request pivot and response attributes combined. The optional name/value pairs will be available to the chain at runtime. There are two reserved chain names: global.request and global.response. These names should be used when defining the global chains.
• service
• <service name="service_name" [request="list_of_handlers"]
• [pivot="handler_name"]
• [response="list_of_handlers"]>
• [<option name="option_name" value="option_value"/>]…
• </service>
• Example:
• <!-- Define a service chain with the "RPCDispatcher" at the pivot-point
• and Email handler to mail the response. The class and method name
• that the RPCDispatcher will use are passed in as options -->
• <service name="InventoryCheck" pivot="RPCDispatcher" response="Email">
• <option name="className"
• value="com.skatestown.services.InventoryCheck"/>
• <option name="methodName" value="doCheck"/>
• </service>
Defines a service chain consisting of the list of handlers specified in the request, pivot, and response attributes combined. The optional name/value pairs will be available to the service at runtime. The name specified here should match the namespace URI of the first body entry of the incoming SOAP request message.
• transport
• <transport name="transport_name" [request="list_of_handlers"]
• [pivot="handler_name"]
• [response="list_of_handlers"]>
• [<option name="option_name" value="option_value"/>]…
• </transport>
• Example:
• <!-- Define a transport specific chain that will invoke a "uudecode"
• handler on the request message and the "uuencode" handler on the
• response message – these will only be invoked for those messages
• that come in using the "file" transport -->
• <transport name="file" request="uudecode" response="uuencode"/>
Defines a transport chain consisting of the list of handlers
specified in the request, pivot, and response attributes combined.
The optional name/value pairs will be available to the transport chain at runtime. The name specified on this definition must match
the name of the transport specified by the transport listener on the
setTransportName() method.
• beanMappings
• <beanMappings>
• <x:name xmlns:x="namespace_uri" classname="class_name" />
• </beanMappings>
• Example:
• <beanMappings>
• <x:po xmlns:x="http://www.skatestown.com/ns/po"
• classname="www.skatestown.com.data.PO" />
• </beanMappings>
Defines the bean serializer/deserializer class (class_name) to be used for the bean named name in the namespace namespace_uri. This is a convenient way of using the default Java bean serializer and
deserializer for your Java beans. We'll give more details about serializers and deserializers in the "Data Encoding/Decoding"
section.
• typeMappings
• <typeMappings>
• <x:name xmlns:x="namespace_uri" type="soap_type" serializer="class_name"
• deserializerFactory="class_name" />
• </typeMappings>
•
• Example:
• <typeMappings>
• <x:PO xmlns:x="http://www.skatestown.com/ns/po" type="po"
• serializer="serializePO"
• deserializerFactory="deserializePOFactory" />
• </typeMappings>
• Defines a mapping between the type found in the SOAP message (soap_type) in the specified snamespace namespace_uri with the
specified serializer and deserializer. We'll give more details about serializers and deserializers in the "Data Encoding/Decoding"
section.
The fourth configuration file, undeploy.xml, has a slightly different format. In this file, you just list the types of resources to be undeployed and their names. For example:
<m:undeploy xmlns:m="AdminService">
<handler name="logger" />
<service name="DoCheck" />
<transport name="file" />
</m:undeploy>
In this example, three resources will be undeployed: a handler named logger, a service chain named DoCheck, and a transport chain named file.
Configuration Methods
You can configure the Axis server two ways: You can modify the server-config.xml file directly by adding or removing the XML configuration data, or you can use the
AdminClient tool. This tool lets you remotely modify the server's configuration. By default, Axis will only allow the AdminClient to be run from the same machine as the server (for security reasons), but this is easily changed (see the "Security" section).
To run the AdminClient, you must first create an XML file with the list of changes to be made. Inside this XML file should be just the list of resources (handlers, chains, services, and so on) that should be deployed or undeployed. As previously shown, the XML's root element should be named deploy in the case where new resources are being added and undeploy when they are being removed. Once all the deployment information is placed in an XML file, you can invoke AdminClient:
> java org.apache.axis.client.AdminClient
-l http://localhost:8080/axis/servlet/AxisServlet deploy.xml
Note that this assumes an HTTP transport and that the Axis servlet is available and waiting for requests on the specified URL
(http://localhost:8080/axis/servlet/AxisServlet). This invocation will work for deploying new resources to an Axis engine running as a server. To add new resources to an Axis client engine, you'll need to modify the client-config.xml file used by the client Axis engine. This file will reside in the current working directory. Following is one of the sample XML files from Chapter 3 that is used as input to the AdminClient:
<m:deploy xmlns:m="AdminService">
<handler name="URLMapper" class="org.apache.axis.handlers.http.URLMapper"/>
<handler name="ActionHandler"
class="org.apache.axis.handlers.http.HTTPActionHandler"/>
<transport name="http" request="URLMapper"/>
<!-- Chapter 3 example 3 services -->
<handler name="EMail" class="com.skatestown.services.EMailHandler"/>
<service name="InventoryCheck" pivot="RPCDispatcher" response="EMail">
<option name="className" value="com.skatestown.services.InventoryCheck"/>
<option name="methodName" value="doCheck"/>
</service>
<!-- Chapter 3 example 4 services -->
<service name="POSubmission" pivot="MsgDispatcher">
<option name="className" value="com.skatestown.services.POSubmission"/>
<option name="methodName" value="doSubmission"/>
</service>
</m:deploy>
Although it is possible to run the AdminClient tool to change the configuration information of the Axis client engine, it is easier to simply modify the client- config.xml file that resides in the current working directory.
Once a Web resource is deployed, either to the client or the server, it will be available until it is undeployed—even across servlet engine restarts.
At the time of publication, Axis' deployment XML files use a very simple XML definition format. Although this format works for now, it doesn't quite support the full features that Axis is planning to have. Eventually, Axis will switch to a new XML format called Web Services Deployment Descriptor (WSDD) . Although WSDD is still under
development, it should be more robust than the current XML deployment file. One other advantage is that WSDD will have a WSDL flavor that should allow for Web services that are defined in WSDL to be deployed by having the WSDD point to their WSDL file. But all of this is still being designed.
By default, Axis will have several handlers, chains, and services automatically deployed.
If a server-config.xml file is not found in the appRoot/WEB-INF directory by the Axis server, Axis will default to have the following pre-deployed:
• JWSProcessor service— Looks for and processes JWS files (JWS was briefly
discussed in Chapter 3, and will be discussed in more detail later in this chapter).
• RPCProvider handler— Locates and invokes a Java class method.
• AdminService service— Takes as input an XML document that will be interpreted as a deployment data XML file containing the list of new handlers, chains, and services to deploy (or undeploy).
• HTTPSender handler— Is used by an Axis client to send a SOAP request to a SOAP server. It can also be used on the server side when a message should be sent to another SOAP server using HTTP (for example, an intermediary).
If a client-config.xml file is not found, then Axis will default to having just one handler pre-deployed: the HTTPSender, for use in sending a SOAP request over HTTP.
Security
Currently, Axis includes only one minor security feature. By default, the AdminService will only allow deployment of new resources from the same machine running the Axis server. By using the enableRemoteAdmin option on the AdminService, resources can be deployed from any other machine as well. The server-config.xml file should be changed as follows:
<service name="AdminService" pivot="RPCDispatcher">
<option name="className" value="org.apache.axis.util.Admin"/>
<option name="methodName" value="AdminService"/>
<option name="enableRemoteAdmin" value="true"/>
</service>
Note, however, that security can be added to Axis through the development of handlers that perform the desired security checks. This addition is being planned for development in time for Axis' first release.
Simple Web Services
By far the easiest and quickest way to deploy a Java Web service is through Axis' Java Web Service (JWS) facility. JWS lets you place a Java file in your Web application
directory structure, and Axis will automatically find it, compile it, and deploy the methods automatically. Using the example from Chapter 3, we have the JWS (or Java) file shown in Listing 4.2.
Listing 4.2 InventoryCheck.jws
import org.apache.axis.MessageContext;
import bws.BookUtil;
import com.skatestown.data.Product;
import com.skatestown.backend.ProductDB;
/**
* Inventory check Web service */
public class InventoryCheck {
/**
* Checks inventory availability given a product SKU and * a desired product quantity.
*
* @param msgContext This is the Axis message processing context * BookUtil needs this to extract deployment * information to load the product database.
* @param sku product SKU * @param quantity quantity desired
* @return true|false based on product availability * @exception Exception most likely a problem accessing the DB */
public static boolean doCheck(MessageContext msgContext, String sku, int quantity) throws Exception
{
ProductDB db = BookUtil.getProductDB(msgContext);
Product prod = db.getBySKU(sku);
return (prod != null && prod.getNumInStock() >= quantity);
} }
All you need to do is place this file in the Axis webapps directory structure with a .jws extension instead of .java. So, to access this example on the CD, because it is in the ch3/ex2 directory, the URL for this Web service would be
http://localhost:8080/bws/ch3/ex2/InventoryCheck.jws. It's as easy as that.
One important thing to remember is that all public methods will be available as Web services. So, use JWS files with care.
Client-Side Programming
Accessing a Web service from the client can be (almost) as easy. In the simplest case of wanting to access an RPC SOAP service, let's take a closer look at the example from Chapter 3, shown in Listing 4.3.
Listing 4.3 InventoryCheckClient.java package ch3.ex2;
import org.apache.axis.client.ServiceClient;
/*
* Inventory check web service client */
public class InventoryCheckClient { /**
* Service URL