Axis User's Guide
Beta 1 Version
Table of Contents
Introduction
Welcome to Axis, the third generation of Apache SOAP! This is the beta 1 version. Please note that Axis is a work in progress, and although
the basic functionality is there, there are still some unfinished areas
and rough edges. That said, we're very psyched about the package so far
and would love to get your take on how we can make it better.
What is SOAP?
SOAP is an XML-based communitcation protocol and encoding format
for inter-application communication. Originally conceived by Microsoft
and Userland software, it has evolved through several generations and the
current spec, SOAP 1.1, is fast growing
in popularity and usage. The W3C's XML Protocol working group is in the
process of turning SOAP into a true open standard, and as of this writing
has released a working draft of SOAP 1.2, which cleans up some of the more
confusing areas of the 1.1 spec.
SOAP is widely viewed as the backbone to a new generation of cross-platform
cross-language distributed computing applications, termed Web Services.
What is Axis?
Apache SOAP began at IBM as "SOAP4J" and then became Apache SOAP version
2. The committers on the v2 project began some conversations in late 2000
about making the engine much more flexible, configurable, and able to handle
both SOAP and the upcoming XML Protocol specification from the W3C.
After a little while, it became clear that a ground-up rearchitecture
was the way to go. Several of the v2 committers proposed very similar designs,
all based around configurable "chains" of message "handlers" which would
implement small bits of functionality in a very flexible and composable
manner. Axis is the result of months of continued discussion and coding
effort in this direction. Some of the key Axis features include the following:
-
Speed. Axis uses SAX (event-based) parsing to acheive significantly
greater speed than earlier versions of Apache SOAP.
-
Flexibility. The Axis architecture gives the developer complete
freedom to insert extensions into the engine for custom header processing,
system management, or anything else you can imagine.
-
Stability. Axis defines a set of
published
interfaces which change relatively slowly compared to the rest of Axis.
-
Component-oriented deployment. You can easily define reusable networks
of Handlers to implement common patterns of processing for your applications,
or to distribute to partners.
-
Transport framework. We have a clean and simple abstraction for
designing transports (i.e. senders and listeners for SOAP over various
protocols such as SMTP, FTP, message-oriented middleware, etc), and the
core of the engine is completely transport-independent.
We hope you enjoy using Axis. Please note that this is an open-source effort
- if you feel the code could use some new features or fixes, please get
involved and lend a hand! The Axis developer community welcomes your participation.
Let us know what you think!
Please send feedback about the package to "axis-user@xml.apache.org".
Also, Axis is regsitered in bugzilla,
the Apache bug tracking and feature-request database.
What's in this release?
This release includes the following features:
- SOAP 1.1 compliant engine
- Flexible configuration / deployment system
- Support for "drop-in" deployment of SOAP services (JWS)
- Support for all basic types, and a type mapping system for defining new
serializers/deserializers
- Automatic serialization/deserialization of Java Beans
- Automatic two-way conversions between Java "List" collections and SOAP
Arrays
- Providers for RPC and message based SOAP services
- Automatic WSDL generation from deployed services
- WSDL2Java tool for building Java proxies and skeletons from WSDL documents
- Java2WSDL tool for building WSDL from Java classes.
- Preliminary security extensions, which can integrate with Servlet 2.2 security/roles
- Preliminary support for the SOAP with Attachments specification
- An EJB provider for accessing EJB's as Web Services
- HTTP servlet-based transport
- Standalone version of the server (with HTTP support)
- Examples, including a client and server for the soapbuilders community
interoperability tests
What's missing?
TBD
Installing Axis and Using this Guide
See the Axis Installation Guide
for instructions on installing Axis as a web application on your J2EE server.
Before running the examples in this guide, you'll need to make sure
that your CLASSPATH includes (note: if you build axis from a CVS checkout, these will be in xml-axis/java/build/lib instead of xml-axis-beta1/lib):
- xml-axis-beta1/lib/axis.jar
- xml-axis-beta1/lib/jaxrpc.jar
- xml-axis-beta1/lib/clutil.jar
- xml-axis-beta1/lib/commons-logging.jar
- xml-axis-beta1/lib/tt-bytecode.jar
- xml-axis-beta1/lib/wsdl4j.jar
- xml-axis-beta1/ # for the sample code
- A JAXP-1.1 compliant XML parser such as xerces or crimson
Consuming Web Services with Axis
Basics - Getting Started
Let's take a look at an example Web Service client that will call the echoString
method on the public Axis server at Apache.
1 import org.apache.axis.client.Call;
2 import org.apache.axis.client.Service;
3 import javax.xml.rpc.namespace.QName;
4
5 public class TestClient {
6 public static void main(String [] args) {
7 try {
8 String endpoint =
9 "http://nagoya.apache.org:5049/axis/servlet/AxisServlet";
10
11 Service service = new Service();
12 Call call = (Call) service.createCall();
13
14 call.setTargetEndpointAddress( new java.net.URL(endpoint) );
15 call.setOperationName( new QName("http://soapinterop.org/", "echoString") );
16
17 String ret = (String) call.invoke( new Object[] { "Hello!" } );
18
19 System.out.println("Sent 'Hello!', got '" + ret + "'");
20 } catch (Exception e) {
21 System.err.println(e.toString());
22 }
23 }
24 }
(You'll find this file in samples/userguide/example1/TestClient.java)
Assuming you have a network connection active, this program can be run
as follows:
% java samples.userguide.example1.TestClient
Sent 'Hello!', got 'Hello!'
%
So what's happening here? On lines 11 and 12 we create new Service and
Call objects. These are the standard JAX-RPC objects that are used to store
metadata about the service to invoke. On line 14, we set up our endpoint
URL - this is the destination for our SOAP message. On line 15 we define
the operation (method) name of the Web Service. And on line 17 we actually invoke
the desired service, passing in an array of parameters - in this case just
one String.
You can see what happens to the arguments by looking at the SOAP request
that goes out on the wire (look at the colored sections, and notice they
match the values in the call above):
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:echoString xmlns:ns1="http://soapinterop.org/">
<arg0 xsi:type="xsd:string">Hello!</arg0>
</ns1:echoString>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The String argument is automatically serialized into XML, and the server
responds with an identical String, which we deserialize and print.
Note: to actually watch the XML flowing back and forth between a
SOAP client and server, you can use the included tcpmon tool. See the appendix
for an overview.
Naming Parameters
In the above example, the parameters are in the order in which we sent
them, but since we only passed the objects themselves, Axis automatically
named the XML-encoded arguments in the SOAP message "arg0", "arg1", etc.
If you want to change this, it's easy! Before calling invoke()
you need to call addParameter for each parameter, like so:
call.addParameter("testParam",
org.apache.axis.encoding.XMLType.XSD_STRING,
javax.xml.rpc.ParameterMode.PARAM_MODE_IN);
This will assign the name testParam to the 1st (and only) parameter
on the invoke call. This will also define the type of the parameter (org.apache.axis.encoding.XMLType.XSD_STRING)
and whether it is an input, output or inout parameter - in this case its
an input parameter. Now when you run the program you'll get a message that
looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:echoString xmlns:ns1="http://soapinterop.org/">
<testParam xsi:type="xsd:string">Hello!</testParam>
</ns1:echoString>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Note that the param is now named "testParam" as expected.
Interoperating with "untyped" servers
In the above examples, we've been casting the return type of invoke(),
which is Object, to the appropriate "real" type - for instance, we know
that the echoString method returns a String, so we expect to get one back
from client.invoke(). Let's take a moment and investigate how this happens,
which sheds light on a potential problem (to which, of course, we have
a solution - so don't fret :)).
Here's what a typical response might look like to the echoString method:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:echoStringResponse xmlns:ns1="http://soapinterop.org/">
<result xsi:type="xsd:string">Hello!</result>
</ns1:echoStringResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Take a look at the section which we've highlighted in red
- that attribute is a schema type declaration, which Axis uses to figure
out that the contents of that element are, in this case, deserializable into a
Java String object. Many toolkits put this kind of explicit typing information
in the XML to make the message "self-describing". On the other hand, some toolkits
return responses that look like this:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:echoStringResponse xmlns:ns1="http://soapinterop.org/">
<result>Hello, I'm a string!</result>
</ns1:echoStringResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
There's no type in the message, so how do we know what Java object we should
deserialize the <result> element into? The answer is metadata
- data about data. In this case, we need a description of the service
that tells us what to expect as the return type. Here's how to do it on
the client side in Axis:
call.setReturnType( org.apache.axis.encoding.XMLType.XSD_STRING );
This method will tell the Axis client that if the return element is not
typed then it should act as if the return value has an xsi:type attribute
set to the predefined SOAP String type. (You can see an example of this
in action in the interop echo-test client - samples/echo/TestClient.java.)
OK - so now you know the basics of accessing SOAP services as a client.
But how do you publish your own services?
Publishing Web Services with Axis
Let's say we have a simple class like the following:
public class Calculator {
public int add(int i1, int i2)
{
return i1 + i2;
}
public int subtract(int i1, int i2)
{
return i1 - i2;
}
}
(You'll find this very class in samples/userguide/example2/Calculator.java.)
How do we go about making this class available via SOAP? There are a
couple of answers to that question, but we'll start with the easiest way
Axis provides to do this, which takes almost no effort at all!
JWS (Java Web Service) Files - Instant Deployment
OK, here's step 1 : copy the above .java file into your webapp directory,
and rename it "Calculator.jws". So you might do something like this:
% copy Calculator.java <your-webapp-root>/axis/Calculator.jws
Now for step 2... hm, wait a minute. You're done! You should now be able
to access the service at the following URL (assuming your Axis web application
is on port 8080):
http://localhost:8080/axis/Calculator.jws
Axis automatically locates the file, compiles the class, and converts
SOAP calls correctly into Java invocations of your service class. Try it
out - there's a calculator client in samples/userguide/example2/CalcClient.java,
which you can use like this:
% java samples.userguide.example2.CalcClient -p8080 add 2 5
Got result : 7
% java samples.userguide.example2.CalcClient -p8080 subtract 10 9
Got result : 1
%
(note that you may need to replace the "-p8080" with whatever port your
J2EE server is running on)
Custom Deployment - Introducing WSDD
JWS files are great quick ways to get your classes out there as Web Services,
but they're not always the best choice. For one thing, you need the source
code - there might be times when you want to expose a pre-existing class
on your system without source. Also, the amount of configuration you can
do as to how the service gets accessed is pretty limited - you can't specify
custom type mappings, or control which Handlers get invoked when people
are using your service.
Deploying via descriptors
To really use the flexibility available to you in Axis, you should get
familiar with the Axis Web Service Deployment Descriptor (WSDD)
format. A deployment descriptor contains a bunch of things you want to
"deploy" into Axis - i.e. make available to the Axis engine. The most common
thing to deploy is a Web Service, so let's start by taking a look at a
deployment descriptor for a basic service (this file is samples/userguide/example3/deploy.wsdd):
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="MyService" provider="java:RPC">
<parameter name="className" value="samples.userguide.example3.MyService"/>
<parameter name="allowedMethods" value="*"/>
</service>
</deployment>
Pretty simple, really - the outermost element tells the engine that this
is a WSDD deployment, and defines the "java" namespace. Then the service
element actually defines the service for us. A service is a targeted chain (see the Architecture Guide), which means it may have
any/all of: a request Handler, a pivot Handler (which for a service is
called a "provider"), and a response Handler. In this case, our provider
is "java:RPC", which is predefined to indicate a Java RPC service.
We need to tell the RPCDispatcher that it should instantiate and call
the correct class (e.g. samples.userguide.example3.MyService), and we do
so by including a <parameter> tag, giving the service one parameter
to configure the class name, and another to tell the engine that any public
method on that class may be called via SOAP (that's what the "*" means;
we could also have restricted the SOAP-accessible methods by using a space
or comma separated list of available method names).
Using the AdminClient
Once we have this file, we need to send it to an Axis server in order to
actually deploy the described service. We do this with the AdminClient,
or the "org.apache.axis.client.AdminClient" class. An invocation of the
AdminClient looks like this:
% java org.apache.axis.client.AdminClient deploy.wsdd
<Admin>Done processing</Admin>
This command has now made our service accessible via SOAP. Check it out
by running the Client class - it should look like this:
% java samples.userguide.example3.Client "test me!"
You typed : test me!
%
If you want to prove to yourself that the deployment really worked, try
undeploying the service and calling it again. There's an "undeploy.wsdd"
file in the example3/ directory which you can use just as you did the deploy.wsdd
file above. Run the AdminClient on that file, then try the service
Client again and see what happens.
You can also use the AdminClient to get a listing of all the deployed
components in the server:
% java org.apache.axis.client.AdminClient list
<big XML document returned here>
In there you'll see services, handlers, transports, etc. Note that this
listing is an exact copy of the server's "server-config.wsdd" file, which
we'll talk about in more detail a little later.
More deployment - Handlers and Chains
Now let's start to explore some of the more powerful features of the Axis
engine. Let's say you want to track how many times your service has been
called. We've included a sample handler in the samples/log directory to
do just this. To use a handler class like this, you first need to deploy
the Handler itself, and then use the name that you give it in deploying
a service. Here's a sample deploy.wsdd file (this is example 4 in samples/userguide):
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<!-- define the logging handler configuration -->
<handler name="track" type="java:samples.userguide.example4.LogHandler">
<parameter name="filename" value="MyService.log"/>
</handler>
<!-- define the service, using the log handler we just defined -->
<service name="LogTestService" provider="java:RPC">
<requestFlow>
<handler type="track"/>
</requestFlow>
<parameter name="className" value="samples.userguide.example4.Service"/>
<parameter name="allowedMethods" value="*"/>
</service>
</deployment>
The first section defines a Handler called "track" that is implemented
by the class samples.userguide.example4.LogHandler. We give this Handler
an option to let it know which file to write its messages into.
Then we define a service, LogTestService, which is an RPC service just
like we saw above in our first example. The difference is the <requestFlow>
element inside the <service> - this indicates a set of Handlers that
should be invoked when the service is invoked, before the provider. By
inserting a reference to "track", we ensure that the message will be logged
each time this service is invoked.
Remote Administration
Note that by default, the Axis server is configured to only accept administration
requests from the machine on which it resides - if you wish to enable remote
administration, you must set the "enableRemoteAdmin" property of the AdminService
to true. To do this, find the "server-config.wsdd" file in your
webapp's WEB-INF directory. In it, you'll see a deployment for the AdminService.
Add an option as follows:
<service name="AdminService" provider="java:MSG">
<parameter name="className" value="org.apache.axis.util.Admin"/>
<parameter name="allowedMethods" value="*"/>
<parameter name="enableRemoteAdmin" value="true"/>
</service>
WARNING: enabling remote administration may give unauthorized parties
access to your machine. If you do this, please make sure to add security
to your configuration!
XML <-> Java Data Mapping in Axis
Encoding Your Beans - the BeanSerializer
Axis includes the ability to serialize/deserialize, without writing any
code, arbitrary Java classes which follow the standard JavaBean pattern
of get/set accessors. All you need to do is tell Axis which Java classes
map to which XML Schema types. Configuring a bean mapping looks like this:
<beanMapping qname="ns:local" xmlns:ns="someNamespace"
languageSpecificType="java:my.java.thingy"/>
The <beanMapping> tag maps a Java class (presumably a bean) to an XML
QName. You'll note that it has two important attributes, qname and
languageSpecificType.
So in this case, we'd be mapping the "my.java.thingy" class to the XML
QName [someNamespace]:[local].
Let's take a look at how this works in practice. Go look at samples/userguide/example5/BeanService.java. The key
thing to notice is that the argument to the service method is an Order
object. Since Order is not a basic type which Axis understands by default,
trying to run this service without a type mapping will result in a fault
(if you want to try this for yourself, you can use the bad-deploy.wsdd
file in the example5 directory). But if we put a beanMapping into our deployment,
all will be well. Here's how to run this example (from the example5 directory):
% java org.apache.axis.client.AdminClient -llocal:///AdminService deploy.wsdd
<Admin>Done processing</Admin>
% java samples.userguide.example5.Client -llocal://
Hi, Glen Daniels!
You seem to have ordered the following:
1 of item : mp3jukebox
4 of item : 1600mahBattery
If this had been a real order processing system, we'd probably have charged you about now.
%
When Beans Are Not Enough - Custom Serialization
Just as JWS deployment is sometimes not flexible enough to meet all needs,
the default bean serialization model isn't robust enough to handle every
case either. At times there will be non-bean Java classes (especially in
the case of pre-existing assets) which you need to map to/from XML, and
there also may be some custom XML schema types which you want to map into
Java in particular ways. Axis gives you the ability to write custom serializers/deserializers,
and some tools to help make your life easier when you do so.
TBD - this section will be expanded in a future
version! For now, take a look at the ArraySerializer, the BeanSerializer
(both in org.apache.axis.encoding), and the DataSer example (in samples/encoding)
to see how custom serializers work.
Deploying custom mappings - the <typeMapping> tag
Now that you've built your serializers and deserializers, you need to tell
Axis which types they should be used for. You do this with a typeMapping
tag in WSDD, which looks like this:
<typeMapping qname="ns:local" xmlns:ns="someNamespace"
languageSpecificType="java:my.java.thingy"
serializer="my.java.Serializer"
deserializer="my.java.DeserializerFactory"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
This looks a lot like the <beanMapping> tag we saw earlier, but there
are three extra attributes. One, serializer, is the Java class name
of the Serializer class which should be used to write the specified Java
class (i.e. my.java.thingy) into XML. Two, deserializer, is
the class name of a Deserializer factory that generates Deserializers
which can be used to unmarshall XML into the correct Java class. Finally, the encoding style, which is SOAP encoding.
(The <beanMapping> tag is really just shorthand for a <typeMapping>
tag with serializer="org.apache.axis.encoding.BeanSerializer", deserializer="org.apache.axis.encoding.BeanSerializer$BeanDeserFactory", and encodingStyle="http://schemas.xmlsoap.org/soap/encoding/",
but clearly it can save a lot of typing!)
Using WSDL with Axis
The Web Service Description Language
is a specification authored by IBM and Microsoft, and supported by many
other organizations. WSDL serves to describe Web Services in a structured
way. A WSDL description of a service tells us, in a machine-understandable
way, the interface to the service, the data types it uses, and where the
service is located. Please see the spec (follow the link in the first sentence)
for details about WSDL's format and options.
Axis supports WSDL in three ways:
-
When you deploy a service in Axis, users may then access your service's
URL with a standard web browser and by appending "?WSDL" to the end of
the URL, they will obtain an automatically-generated WSDL document which
describes your service.
-
We provide a "WSDL2Java" tool which will build Java proxies and skeletons
for services with WSDL descriptions.
-
We provide a "Java2WSDL" tool which will build WSDL from Java classes.
?WSDL: Obtaining
WSDL for deployed services
When you make a service available using Axis, there is typically a unique
URL associated with that service. For JWS files, that URL is simply the
path to the JWS file itself. For non-JWS services, this is usually the
URL "http://<host>/axis/services/<service-name>".
If you access the service URL in a browser, you'll see a message indicating
that the endpoint is an Axis service, and that you should usually access
it using SOAP. However, if you tack on "?wsdl" to the end of the URL, Axis
will automatically generate a service description for the deployed service,
and return it as XML in your browser (try it!). The resulting description
may be saved or used as input to proxy-generation, described next. You
can give the WSDL-generation URL to your online partners, and they'll be
able to use it to access your service with toolkits like .NET, SOAP::Lite,
or any other software which supports using WSDL.
You can also generate WSDL files from existing Java classes (see Java2WSDL:
Building WSDL from Java ).
WSDL2Java:
Building stubs, skeletons, and data types from WSDL
Client-side bindings
You'll find the AXIS WSDL -> Java tool in "org.apache.axis.wsdl.WSDL2Java".
The basic invocation form looks like this:
% java org.apache.axis.wsdl.WSDL2Java
(WSDL-file-URL)
This will generate only those bindings necessary for the client.
AXIS follows the JAX-RPC specification when generating Java client bindings
from WSDL. For this discussion, assume we executed the following:
% cd samples/addr
% java org.apache.axis.wsdl.WSDL2Java
AddressBook.wsdl
The generated files will reside in the directory "AddressFetcher2".
They are put here because that is the target namespace from the WSDL and namespaces map to Java packages.
Namespaces will be discussed in detail later.
WSDL clause |
Java class(es) generated |
For each entry in the type section |
A java class |
|
A holder if this type is used as an inout/out parameter |
For each portType |
A java interface |
For each binding |
A stub class |
For each service |
A service interface |
|
A service implementation (the locator) |
Types
The Java class generated from a WSDL type will be named from the WSDL type.
This class will typically, though not always, be a bean. For example,
given the WSDL (the WSDL used throughout the WSDL2Java discussion is from
the Address Book sample):
<xsd:complexType
name="phone">
<xsd:all>
<xsd:element name="areaCode" type="xsd:int"/>
<xsd:element name="exchange" type="xsd:string"/>
<xsd:element name="number" type="xsd:string"/>
</xsd:all>
</xsd:complexType>
WSDL2Java will generate:
public class Phone implements java.io.Serializable {
public Phone() {...}
public int getAreaCode() {...}
public void setAreaCode(int areaCode) {...}
public java.lang.String getExchange() {...}
public void setExchange(java.lang.String exchange) {...}
public java.lang.String getNumber() {...}
public void setNumber(java.lang.String number) {...}
}
Holders
This type may be used as an inout or out parameter. Java does not
have the concept of inout/out parameters. In order to achieve this
behavior, JAX-RPC specifies the use of holder classes. A holder class
is simply a class that contains an instance of its type. For example,
the holder for the Phone class would be:
public final class PhoneHolder implements
javax.xml.rpc.holders.Holder {
public Phone value;
public PhoneHolder()
{
}
public PhoneHolder(Phone value) {
this.value = value;
}
}
A holder class is only generated for a type if that type is used as
an inout or out parameter.
The holder classes for the primitive types can be found in javax.xml.rpc.holders.
PortTypes
The Service Definition Interface (SDI) is the interface that's derived
from a WSDL's portType. This is the interface you use to access the
operations on the service. For example, given the WSDL:
<message name="empty">
<message name="AddEntryRequest">
<part name="name" type="xsd:string"/>
<part name="address" type="typens:address"/>
</message>
<portType name="AddressBook">
<operation name="addEntry">
<input message="tns:AddEntryRequest"/>
<output message="tns:empty"/>
</operation>
</portType>
WSDL2Java will generate:
public interface AddressBook extends
java.rmi.Remote {
public void addEntry(String
name, Address address) throws java.rmi.RemoteException;
}
A note about the name of the SDI. The name of the SDI is typically the name of the portType. However, to construct the SDI, WSDL2Java needs information from both the portType and the binding. (This is unfortunate and is a topic of discussion for WSDL version 2.)
JAX-RPC says (section 4.3.3): "The name of the Java interface is mapped
from the name attribute of the wsdl:portType element. ... If the mapping to
a service definition interface uses elements of the wsdl:binding ..., then
the name of the service definition interface is mapped from the name of the
wsdel:binding element."
Note the name of the spec. It contains the string "RPC". So this spec,
and WSDL2Java, assumes that the interface generated from the portType is an
RPC interface. If information from the binding tells us otherwise (in
other words, we use elements of the wsdl:binding), then the name of the
interface is derived instead from the binding.
Why? We could have one portType - pt - and two bindings - bRPC and
bDoc. Since document/literal changes what the interface looks like, we cannot use a single interface for both of these bindings, so we end up with 2 interfaces - one named pt and another named bDoc - and two stubs - bRPCStub (which implements pt) and bDocStub (which implements bDoc).
Ugly, isn't it? But you can see why it's necessary. Since document/literal changes what the interface looks like, and we could have more than one binding referring to a single portType, we have to create more than one interface, and each interface must have a unique name.
Bindings
A Stub class implements the SDI. Its name is the binding name with
the suffix "Stub". It contains the code which turns the method invocations
into SOAP calls using the Axis Service and Call objects. It stands
in as a proxy (another term for the same idea) for the remote service,
letting you call it exactly as if it were a local object. In other words,
you don't need to deal with the endpoint URL, namespace, or parameter arrays
which are involved in dynamic invocation via the Service and Call objects.
The stub hides all that work for you.
Given the following WSDL snippet:
<binding name="AddressBookSOAPBinding"
type="tns:AddressBook">
...
</binding>
WSDL2Java will generate:
public class AddressBookSOAPBindingStub
extends org.apache.axis.client.Stub implements AddressBook {
public AddressBookSOAPBindingStub()
throws org.apache.axis.AxisFault {...}
public AddressBookSOAPBindingStub(URL
endpointURL, javax.xml.rpc.Service service) throws org.apache.axis.AxisFault
{..}
public AddressBookSOAPBindingStub(javax.xml.rpc.Service
service) throws org.apache.axis.AxisFault {...}
public void addEntry(String
name, Address address) throws RemoteException{...}
}
Services
Normally, a client program would not instantiate a stub directly.
It would instead instantiate a service locator and call a get method which
returns a stub. This locator is derived from the service clause in
the WSDL. WSDL2Java generates two objects from a service clause.
For example, given the WSDL:
<service name="AddressBookService">
<port name="AddressBook" binding="tns:AddressBookSOAPBinding">
<soap:address location="http://localhost:8080/axis/services/AddressBook"/>
</port>
</service>
WSDL2Java will generate the service interface:
public interface AddressBookService
extends javax.xml.rpc.Service {
public String getAddressBookAddress();
public AddressBook
getAddressBook() throws javax.xml.rpc.ServiceException;
public AddressBook
getAddressBook(URL portAddress) throws javax.xml.rpc.ServiceException;
}
WSDL2Java will also generate the locator which implements this interface:
public class AddressBookServiceLocator
extends org.apache.axis.client.Service implements AddressBookService {
...
}
The service interface defines a get method for each port listed in the
service element of the WSDL. The locator is the implementation of this
service interface. It implements these get methods. It serves as
a locator for obtaining Stub instances. The Service class will by default
make a Stub which points to the endpoint URL described in the WSDL file,
but you may also specify a different URL when you ask for the PortType.
A typical usage of the stub classes would be as follows:
public class Tester
{
public static void main(String
[] args) throws Exception {
// Make a service
AddressBookService
service = new AddressBookServiceLocator();
// Now use the service
to get a stub which implements the SDI.
AddressBook port
= service.getAddressBook();
// Make the actual call
Address address = new Address(...);
port.addEntry("Russell Butek", address);
}
}
Server-side bindings
Just as a stub is the client side of a Web Service represented in Java,
a skeleton is a Java framework for the server side. To make
skeleton classes, you just specify the "--skeleton" option to WSDL2Java.
For instance, using the AddressBook.wsdl as we had above:
% java org.apache.axis.wsdl.WSDL2Java
--skeleton AddressBook.wsdl
You will see that WSDL2Java generates all the classes that were generated
before for the client, but it generates a few new files:
WSDL clause |
Java class(es) generated |
For each binding |
A skeleton class |
|
An implementation template class |
For each service |
deploy.wsdd |
|
undeploy.wsdd |
Bindings
The skeleton class is the class that sits between the AXIS engine and the
actual service implementation. Its name is the binding name with
suffix "Skeleton". For example, for the AddressBook binding, WSDL2Java
will generate:
public class AddressBookSOAPBindingSkeleton
implements AddressBook,
org.apache.axis.wsdl.Skeleton {
private AddressBook impl;
public AddressBookSOAPBindingSkeleton() {
this.impl = new AddressBookSOAPBindingImpl();
}
public AddressBookSOAPBindingSkeleton(AddressBook impl) {
this.impl = impl;
}
public void addEntry(java.lang.String
name, Address address) throws java.rmi.RemoteException
{
impl.addEntry(name, address);
}
}
(The real skeleton is actually much richer. For brevity I'm just
showing you the basic skeleton.)
The skeleton contains an implementation of the AddressBook service.
This implementation is either passed into the skeleton on construction,
or an instance of the generated implementation is created. When the
AXIS engine calls the skeleton's addEntry method, it simply delegates the
invocation to the real implementation's addEntry method.
Besides the skeleton, WSDL2Java also generates an implementation template
from the binding:
public class AddressBookSOAPBindingImpl
implements AddressBook {
public void addEntry(String
name, Address address) throws java.rmi.RemoteException {
}
}
This template could actually be used as a test implementation but, as
you can see, it doesn't do anything. It is intended that the service
writer fill out the implementation from this template.
When WSDL2Java is asked to generate the implementation template (via
the --skeleton flag), it will ONLY generate it if it does not already exist.
If this implementation already exists, it will not be overwritten.
Services
The tool also builds you a "deploy.wsdd" and an "undeploy.wsdd" for each
service for use with the AdminClient. These files may be used to deploy
the service once you've filled in the methods of the Implementation class,
compiled the code, and made the classes available to your Axis engine.
Other WSDL2Java details
WSDL2Java has a number of options besides --skeleton.
Usage: java org.apache.axis.wsdl.WSDL2Java [options] WSDL-URI
Options:
-h, --help
print this message and exit
-v, --verbose
print informational messages
-s, --server-side
emit server-side bindings for web service
-S, --skeletonDeploy <argument>
deploy skeleton (true) or implementation (false) in deploy.wsdd.
Default is true. Assumes --server-side.
-N, --NStoPkg <argument>=<value>
mapping of namespace to package
-f, --fileNStoPkg <argument>
file of NStoPkg mappings (default NStoPkg.properties)
-p, --package <argument>
override all namespace to package mappings, use this package
name instead
-o, --output <argument>
output directory for emitted files
-d, --deployScope <argument>
add scope to deploy.xml: "Application", "Request", "Session"
-t, --testCase
emit junit testcase class for web service
-n, --noImports
only generate code for the immediate WSDL document
-a, --all
generate code for all elements, even unreferenced ones
-D, --Debug
print debug information
-T, --typeMappingVersion
<argument>
indicate 1.1 or 1.2. The default is 1.2 (SOAP 1.2 JAX-RPC c
ompliant)
-h, --help
Print the usage statement and exit
-v, --verbose
See what the tool is generating as it is generating it.
-s, --server-side
Emit the server-side bindings for the web service:
-
a skeleton class named <bindingName>Skeleton. This may or may
not be emitted (see -S, --skeletonDeploy).
-
an implementation template class named <bindingName>Impl. Note
that, if this class already exists, then it is not emitted.
-
deploy.wsdd
-
undeploy.wsdd
-S, --skeletonDeploy <argument>
Deploy either the skeleton (true) or the implementation (false) in deploy.wsdd.
In other words, for "true" the service clause in the deploy.wsdd file will
look something like:
<service name="AddressBook"
provider="java:RPC">
<parameter name="className" value="samples.addr.AddressBookSOAPBindingSkeleton"/>
...
</service>
and for "false" it would look like:
<service name="AddressBook" provider="java:RPC">
<parameter name="className" value="samples.addr.AddressBookSOAPBindingImpl"/>
...
</service>
The default for this option is true. When you use this option, the --server-side option is assumed, so you don't have to explicitly specify --server-side as well.
-N, --NStoPkg <argument>=<value>
By default, package names are generated from the namespace strings in the
WSDL document in a magical manner (typically, if the namespace is of the
form "http://x.y.com" or "urn:x.y.com" the corresponding package will be
"com.y.x"). If this magic is not what you want, you can provide your
own mapping using the --NStoPkg argument, which can be repeated as often
as necessary, once for each unique namespace mapping. For example,
if there is a namespace in the WSDL document called "urn:AddressFetcher2",
and you want files generated from the objects within this namespace to
reside in the package samples.addr, you would provide the following option
to Wsdl2java:
--NStoPkg urn:AddressFetcher2=samples.addr
(Note that if you use the short option tag, "-N", then there must not be
a space between "-N" and the namespace.)
-f, --fileNStoPkg <argument>
If there are a number of namespaces in the WSDL document, listing a mapping
for them all could become tedious. To help keep the command line
terse, WSDL2Java will also look for mappings in a properties file.
By default, this file is named "NStoPkg.properties" and it must reside
in the default package (ie., no package). But you can explicitly
provide your own file using the --fileNStoPkg option.
The entries in this file are of the same form as the arguments to the
--NStoPkg command line option. For example, instead of providing
the command line option as above, we could provide the same information
in NStoPkg.properties:
urn\:AddressFetcher2=samples.addr
(Note that the colon must be escaped in the properties file.)
If an entry for a given mapping exists both on the command line and
in the properties file, the command line entry takes precedence.
-p, --package <argument>
This is a shorthand option to map all namespaces in a WSDL document to
the same Java package name. This can be useful, but dangerous.
You must make sure that you understand the effects of doing this.
For instance there may be multiple types with the same name in different
namespaces. It is an error to use the --NStoPkg switch and --package
at the same time.
-o, --output <argument>
The root directory for all emitted files.
-d, --deployScope <argument>
Add scope to deploy.wsdd: "Application", "Request", or "Session".
If this option does not appear, no scope tag appears in deploy.wsdd, which
the AXIS runtime defaults to "Request".
-t, --testCase
Generate a client-side JUnit test case. This test case can stand
on its own, but it doesn't really do anything except pass default values
(null for objects, 0 or false for primitive types). Like the generated
implementation file, the generated test case file could be considered a
template that you may fill in.
-n, --noImports
Only generate code for the WSDL document that appears on the command line.
The default behaviour is to generate files for all WSDL documents, the
immediate one and all imported ones.
-a, --all
Generate code for all elements, even unreferenced ones. By default,
WSDL2Java only generates code for those elements in the WSDL file that
are referenced.
A note about what it means to be referenced. We cannot simply
say: start with the services, generate all bindings referenced by
the service, generated all portTypes referenced by the referenced bindings,
etc. What if we're generating code from a WSDL file that only contains
portTypes, messages, and types? If WSDL2Java used service as an anchor,
and there's no service in the file, then nothing will be generated.
So the anchor is the lowest element that exists in the WSDL file in the
order:
types
portTypes
bindings
services
For example, if a WSDL file only contained types, then all the listed
types would be generated. But if a WSDL file contained types and
a portType, then that portType will be generated and only those types that
are referenced by that portType.
-D, --Debug
Print debug information, which currently is WSDL2Java's symbol table.
-T, --typeMappingVersion <argument>
Indicate 1.1 or 1.2. The default is 1.2 (SOAP 1.2 JAX-RPC compliant).
Java2WSDL: Building WSDL
from Java
The Java2WSDL and WSDL2Java emitters make it easy to develop a new web service. The following sections describe the steps in building a web service from a Java interface.
Step 1: Provide a Java interface or class
Write and compile a Java interface (or class) that describes the web service
interface. Here is an example interface that describes a web services
that can be used to set/query the price of widgets (samples/userguide/example6/WidgetPrice.java):
package samples.userguide.example6;
/**
* Interface describing a web service
to set and get Widget prices.
**/
public interface WidgetPrice {
public void setWidgetPrice(String
widgetName, String price);
public String getWidgetPrice(String
widgetName);
}
Note: If you compile your class with debug information, Java2WSDL
will use the debug information to obtain the method parameter names.
Step 2: Create WSDL using Java2WSDL
Use the Java2WSDL tool to create a WSDL file from the interface above.
Here is an example invocation that produces the wsdl file (wp.wsdl)
from the interface described in the previous section:
% java org.apache.axis.wsdl.Java2WSDL -o wp.wsdl -l"http://localhost:8080/axis/services/WidgetPrice" -n "urn:Example6" -p"samples.userguide.example6" "urn:Example6" samples.userguide.example6.WidgetPrice
Where:
-
-o indicates the name of the output WSDL file
-
-l indicates the location of the service
-
-n is the target namespace of the WSDL file
-
-p indicates a mapping from the package to a namespace.
There may be multiple mappings.
-
the class specified contains the interface of the webservice.
The output WSDL document will contain the appropriate WSDL types, messages,
portType, bindings and service descriptions to support a SOAP rpc, encoding
web service. If your specified interface methods reference other
classes, the Java2WSDL tool will generate the appropriate xml types to
represent the classes and any nested/inherited types. The tool supports
JAX-RPC complex types (bean classes), extension classes, enumeration classes,
arrays and Holder classes.
The Java2WSDL tool has many additional options which are detailed in
the next section.
Java2WSDL Details
Here is the help message generated from the current tool:
Java2WSDL emitter
Usage: java org.apache.axis.wsdl.Java2WSDL
[options] class-of-portType
Options:
-h, --help
print this message and exit
-o, --output <argument>
output WSDL filename
-l, --location <argument>
service location url
-s, --service <argument>
service name (obtained from --location if not specified)
-n, --namespace <argument>
target namespace
-p, --PkgtoNS <argument>=<value>
package=namespace, name value pairs
-m, --methods <argument>
space or comma separated list of methods to export
-a, --all
look for allowed methods in inherited class
-w, --outputWsdlMode <argument>
output WSDL mode: All, Interface, Implementation
-L, --locationImport <argument>
location of interface WSDL
-N, --namespaceImpl <argument>
target namespace for implementation WSDL
-O, --outputImpl <argument>
output Implementation WSDL filename, setting this causes --outputWsdlMode
to be ignored
-f, --factory <argument>
name of the Java2WSDLFactory class for extending WSDL generation functions
-i, --implClass <argument>
optional class that contains implementation of methods in
class-of-portType. The debug information in the class is used
to obtain the method parameter names, which are used to set
the WSDL part names.
Details:
portType name=
<class-of-portType name>
binding
name= <--service value>SoapBinding
service
name= <--service value>Service
port
name= <--service value>
address location= <--location
value>
-h , --help
Prints the help message.
-o, --output <WSDL file>
Indicates the name of the output WSDL file. If not specified,
a suitable default WSDL file is written into the current directory.
-l, --location <location>
Indicates the url of the location of the service. The name after
the last slash or backslash is the name of the service port (unless overriden
by the -s option). The service port address location attributed is
assigned the specified value.
-s, -service <name>
Indicates the name of the service. If not specified, the service
name is derived from the --location value. The names of the WSDL
binding, service, and port elements are derived from the service name as
indicated in the Details section
above.
-n, --namespace <target namespace>
Indicates the name of the target namespace of the WSDL.
-p, --PkgToNS <package> <namespace>
Indicates the mapping of a package to a namespace. If a package
is encountered that does not have a namespace, the Java2WSDL emitter will
generate a suitable namespace name. This option may be specified
multiple times.
-m, --methods <arguments>
If this option is specified, only the indicated methods in your interface
class will be exported into the WSDL file. The methods list must
be comma separated. If not specified, all methods declared in the
interface class will be exported into the WSDL file.
-a, --all
If this option is specified, the Java2WSDL parser will look into extended
classes to determine the list of methods to export into the WSDL file.
-w, --outputWSDLMode <mode>
Indicates the kind of WSDL to generate. Accepted values are:
-
All --- (default) Generates wsld containing both interface and implementation
WSDL constructs.
-
Interface --- Generates a WSDL containing the interface constructs (no
service element).
-
Implementation -- Generates a WSDL containing the implementation.
The interface WSDL is imported via the -L option.
-L, --locationImport <url>
Used to indicate the location of the interface WSDL when generating
an implementation WSDL.
-N, --namespaceImpl <namespace>
Namespace of the implementation WSDL.
-O, --outputImpl <WSDL file>
Use this option to indicate the name of the output implementation WSDL
file. If specified, Java2WSDL will produce interface and implementation
WSDL files. If this option is used, the -w option is ignored.
-f, --factory <class>
Use this expert option to extend and customize the WSDL2Java tool.
-i, --implClass <impl-class>
The Java2WSLD tool uses method parameter names to construct the WSDL
message part names. The message names are obtained from the debug
information of the <class-of-portType> class file. If that
class file was compiled without debug information or if <class-of-portType>
is an interface, the method parameter names are not available. In
these cases, you can use the --implClass option to provide an alternative
class from which to obtain method parameter names. The <impl-class>
could be the actual implementation class, a stub class or a skeleton class.
Step 3: Create Bindings using WSDL2Java
Use the generated WSDL file to build the appropriate client/server bindings
for the web service (see WSDL2Java):
% java org.apache.axis.wsdl.Wsdl2java -o . -d session -s -Nurn:Example6 samples.userguide.example6 ws.wsdl
This will generate the following files:
-
WidgetPriceSoapBindingImpl.java : Java file containing
the default server implementation of the WidgetPrice web service.
You will need to modify the *SoapBindingImpl file to add your implementation
(see ../samples/userguide/example6/WidgetPriceSoapBindingImpl.java
).
-
WidgetPrice.java: New interface file that contains
the appropriate java.rmi.Remote usages.
-
WidgetPriceService.java: Java file containing the client
side service class.
-
WidgetPriceSoapBindingSkeleton.java: Server side skeleton.
-
WidgetPriceSoapBindingStub.java: Client side stub.
-
deploy.xml: Deployment descriptors
-
undeploy.xml: Undeploy descriptors
-
(data types): Java files will be produced for all of the other types
and holders necessary for the web service. There are no additional
files for the WidgetPrice web service.
Now you have all of the necessary files to build your client/server side
code and deploy the web service!
Published Axis Interfaces
Although you may use any of the interfaces and classes present in Axis, you need to
be aware that some are more stable than others since there is a continuing
need to refactor Axis to maintain and improve its modularity.
Hence certain interfaces are designated as published, which means
that they are relatively stable. As Axis is refactored, the Axis developers
will try to avoid changing published interfaces unnecessarily and will
certainly consider the impact on users of any modifications.
So if you stick to using only published interfaces, you'll minimise
the pain of migrating between releases of Axis. On the other hand, if you
decide to use unpublished interfaces, migrating between releases could
be an interesting exercise! If you would like an interface to be published,
you should make the case for this on the
axis-user
mailing list.
The current list of published interfaces is as follows:
-
JAX-RPC interfaces. At the time of this writing, JAX-RPC is still in the public review stage of its existence, meaning that it may still change. As it does, the JAX-RPC interfaces may also change. JAX-RPC is expected to become official some time during the summer of 2002.
- javax.xml.messaging.Endpoint
- javax.xml.messaging.URLEndpoint
- javax.xml.rpc.Call
- javax.xml.rpc.FaultException
- javax.xml.rpc.JAXRPCException
- javax.xml.rpc.ParameterMode
- javax.xml.rpc.Service
- javax.xml.rpc.ServiceException
- javax.xml.rpc.ServiceFactory
- javax.xml.rpc.Stub
- javax.xml.rpc.encoding.DeserializationContext
- javax.xml.rpc.encoding.Deserializer
- javax.xml.rpc.encoding.DeserializerFactory
- javax.xml.rpc.encoding.SerializationContext
- javax.xml.rpc.encoding.Serializer
- javax.xml.rpc.encoding.SerializerFactory
- javax.xml.rpc.encoding.TypeMapping
- javax.xml.rpc.encoding.TypeMappingRegistry
- javax.xml.rpc.handler.Handler
- javax.xml.rpc.handler.HandlerChain
- javax.xml.rpc.handler.HandlerInfo
- javax.xml.rpc.handler.HandlerRegistry
- javax.xml.rpc.handler.MessageContext
- javax.xml.rpc.handler.soap.SOAPMessageContext
- javax.xml.rpc.holders.BigDecimalHolder
- javax.xml.rpc.holders.BigIntegerHolder
- javax.xml.rpc.holders.BooleanHolder
- javax.xml.rpc.holders.BooleanWrapperHolder
- javax.xml.rpc.holders.ByteArrayHolder
- javax.xml.rpc.holders.ByteHolder
- javax.xml.rpc.holders.ByteWrapperArrayHolder
- javax.xml.rpc.holders.ByteWrapperHolder
- javax.xml.rpc.holders.CalendarHolder
- javax.xml.rpc.holders.DateHolder
- javax.xml.rpc.holders.DoubleHolder
- javax.xml.rpc.holders.DoubleWrapperHolder
- javax.xml.rpc.holders.FloatHolder
- javax.xml.rpc.holders.FloatWrapperHolder
- javax.xml.rpc.holders.Holder
- javax.xml.rpc.holders.IntHolder
- javax.xml.rpc.holders.IntegerWrapperHolder
- javax.xml.rpc.holders.LongHolder
- javax.xml.rpc.holders.LongWrapperHolder
- javax.xml.rpc.holders.ObjectHolder
- javax.xml.rpc.holders.QNameHolder
- javax.xml.rpc.holders.ShortHolder
- javax.xml.rpc.holders.ShortWrapperHolder
- javax.xml.rpc.holders.StringHolder
- javax.xml.rpc.namespace.QName
- javax.xml.rpc.server.ServiceLifecycle
- javax.xml.rpc.soap.SOAPFault
- javax.xml.rpc.soap.SOAPHeaderFault
- javax.xml.transform.Source
- AXIS interfaces:
- org.apache.axis.AxisFault
- org.apache.axis.Handler
- org.apache.axis.DefaultEngineConfigurationFactory
- org.apache.axis.EngineConfiguration
- org.apache.axis.EngineConfigurationFactory
- org.apache.axis.Message
- org.apache.axis.MessageContext
- org.apache.axis.SOAPPart
- org.apache.axis.client.Call
- org.apache.axis.client.Service
- org.apache.axis.client.ServiceFactory
- org.apache.axis.client.Stub
- org.apache.axis.client.Transport
- org.apache.axis.description.TypeDesc
- org.apache.axis.description.AttributeDesc
- org.apache.aixs.description.ElementDesc
- org.apache.axis.encoding.SerializationContext
- org.apache.axis.session.Session
- org.apache.axis.transport.http.SimpleAxisServer
- org.apache.axis.wsdl.WSDL2Java
- org.apache.axis.wsdl.Java2WSDL
Using the Axis TCP Monitor (tcpmon)
The included "tcpmon" utility can be found in the org.apache.axis.utils
package. To run it from the command line:
% java org.apache.axis.utils.tcpmon [listenPort targetHost targetPort]
Without any of the optional arguments, you will get a gui which looks like
this:
To use the program, you should select a local port which tcpmon will
monitor for incoming connections, a target host where it will forward such
connections, and the port number on the target machine which should be
"tunneled" to. Then click "add". You should then notice another tab appearing
in the window for your new tunneled connection. Looking at that panel,
you'll see something like this:
Now each time a SOAP connection is made to the local port, you will
see the request appear in the "Request" panel, and the response from the
server in the "Response" panel. Tcpmon keeps a log of all request/response
pairs, and allows you to view any particular pair by selecting an entry
in the top panel. You may also remove selected entries, or all of them,
or choose to save to a file for later viewing.
The "resend" button will resend the request you are currently viewing,
and record a new response. This is particularly handy in that you can edit
the XML in the request window before resending - so you can use this as
a great tool for testing the effects of different XML on SOAP servers.
Note that you may need to change the content-length HTTP header value
before resending an edited request.
-
Glossary
-
Handler
-
<definition>
-
SOAP
-
The Simple Object Access Protocol (yes, despite the fact that it sometimes
doesn't seem so simple, and doesn't have anything to do with objects...
:)). You can read the SOAP 1.1 specification at http://www.w3.org/TR/SOAP.
The W3C is currently in the midst of work on SOAP 1.2, under the auspices
of the XML Protocol Group.
-
Provider
-
<definition>
-