Getting Started with WebServices and JOnAS 3.X



WebServices and J2EE

    WebServices are fully integrated in J2EE 1.4 compliant Application Servers.
    Web services technologies can be seen as another communication protocol used to interoperate heterogeneous systems. Web services are based on the SOAP protocol. The SOAP mesages can be transported with various transport mode (JMS, HTTP, mail, ...) but the one we will focus on is the HTTP transport (because Web Services for J2EE v1.1 use only SOAP/HTTP).

Early Integration of Axis in JOnAS 3.X series

    JOnAS hosts Apache Axis from version 3.0. That means only that the user no more have to add Axis jars in its webapp.
When you want to use an Axis class (AdminClient, WSDL2Java, ...) from the command line, you just have to use the jclient script in place of the common java executable (jclient create appropriate classpath just for you).

How to use WebServices


Endpoint Creation

    When you want to expose some business methods as web services, you have to use a servlet container such as Tomcat or Jetty to holds the Axis servlet where ends up the SOAP messages of clients.
    The developer has the choice for Axis configuration. He must write a .wsdd file holding Axis specific configuration parameters. It can be of 2 forms : simple wsdd containing only information about the web services the developer wants to expose (see an example here), or a complete wsdd containing exposed web services informations AND Axis base configuration (see an example here).
    The first form is prefered for development purpose because developer can easily change configuration values and submit them again to Axis servlet (Axis servlet will merge the current WEB-INF/server-config.wsdd with this new configuration file). It is usually named deploy.wsdd (its location is free).
    The second form is appropriate when used in production environment when configuration changes are minor (finally the 2 forms results in the same Axis wsdd file). It MUST be named server-config.wsdd and located in WEB-INF directory of the servlet.

Exposing Stateless Session Bean

1. Create an simple WebApp containing just minimal Axis stuff, such as a WEB-INF/web.xml declaring the Axis servlet and a servlet mapping (sample here).
2. Create a wsdd file (use the form you prefer)
3. add mandatory parameters for EJB exposition :
The developer must set AT LEAST local OR remote interfacces names. If local AND remote interfaces are specified, Axis choose to use REMOTE interface even if they are differents.

See "Using Axis WSDD configuration files"  for more informations.

Exposing Simple class (JAX-RPC class)

JAX-RPC class are just normal classes with no particuliar inheritance needs, exposed as a web service.
1. Add the Axis servlet declaration in your web-app descriptor (sample here) with a servlet mapping
2. Create a wsdd file (use the form you prefer)
3. add madatory parameters for JAX-RPC classes exposition :

Client Creation

    Creation of a web services client is heavily based on WSDL knowledge even if it is not mandatory.

WSDL Knowledge

    It's the easiest way to create a web service client.
    The only thing to do is to generate all files needed to access the web service, to compile and to add them into your component archive.
    This can be done from command line using org.apache.axis.wsdl.WSDL2Java tool.

example :
    jclient org.apache.axis.wsdl.WSDL2Java --output <destination directory> --NStoPkg <namespace>=<package> <wsdl url>
--NStoPkg option is used to place generated classes in a convenient package according to the namespace of the WSDL Definition and the namespace(s) of the XML Schema(s).

Axis provides an Ant Task for automated build :
<taskdef name="axis-wsdl2java"
         classname="org.apache.axis.tools.ant.wsdl.Wsdl2javaAntTask">
    <!-- classpath holds jonas.jar, webservices_axis.jar, ... -->
    <classpath refid="base.classpath"/>
</taskdef>

<axis-wsdl2java url="${ws.google.wsdl}/GoogleSearch.wsdl"
                output="${src.dir}">
    <mapping namespace="urn:GoogleSearch"
             package="org.objectweb.wssample.gen.google"/>
</axis-wsdl2java>

code :
import path.to.your.generated.classes.*;
[...]
<ServiceInterface> service = new <Service>Locator();
<PortInterface> port = service.get<PortName>();
<ObjectType> result = port.<methodName>(<arguments>);

No WSDL Knowledge

    When the client does not have the WSDL of the service to access, the developer has to use Service agnostic interfaces.
    With Axis, you have to instanciate an org.apache.axis.client.Service class (implementation of javax.xml.rpc.Service), create a javax.xml.rpc.Call instance from the Service, configure it manually and invoke the Call.

See an example here :

import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.namespace.QName;
import javax.xml.rpc.encoding.TypeMappingRegistry;
import javax.xml.rpc.encoding.TypeMapping;
import org.apache.axis.encoding.ser.BeanSerializerFactory;
import org.apache.axis.encoding.ser.BeanDeserializerFactory;
[...]
// create a Service instance (other constructor usable, see the doc)
Service service = new org.apache.axis.client.Service();

// configure mappings if web service use Complex types
// first get the mapping registry instance
TypeMappingRegistry tmr = service.getTypeMappingRegistry();

// create a QName representing the fully qualified name of the
// XML type to serialize/deserialize (namespace+locapart pair)
QName xmlType =
new QName("namespace-of-complex-type", "complex-type-name");

// get a TypeMapping (default for axis is SOAP encoding TypeMapping)
TypeMapping tm = tmr.getDefaultTypeMapping();

// register the XML type with a Java class by specifying
// Serializer/Deserializer factories to use.
tm.register(your.java.Type.class,
            xmlType,
            new
BeanSerializerFactory(your.java.Type.class, xmlType),
            new
BeanDeserializerFactory(your.java.Type.class, xmlType));

// get a Call instance from the Service
// specify port to use and operation name to be invoked
// see the doc for other createCall methods usage
Call call = service.createCall(new QName("port-name", new QName("operation-name")));

// where is the web service ?
call.
setTargetEndpointAddress("url-address-of-the-endpoint");

// now, we can invoke the web service with its parameters
String result = call.invoke(new Object[] {"Hello, World!"});

Using Axis WSDD Configuration file

This part covers basic functionnalities of the wsdd configuration files.
For detailed informations report to the Axis User Guide and Reference Guide.

Service

<service> is the tag used to declare a web service.
attributes :
provider possible values : java:EJB (used to expose EJB Stateless Session Bean), java:RPC (used to expose Simple classes), java:MSG (used to ???)
style possible values : document, rpc
    With RPC, SOAP Messages must have as their first soap:body element, an element named as the operation to be invoked.
    With Document, SOAP Messages must not have a first element with the operation name.
use possible values : encoded, literal
    With encoded, SOAP refs can be used in the SOAP Message.
    With literal, no SOAP refs in the SOAP Message.
Note : a SOAP ref is something like a pointer in XML SOAP Message : an Element is defined with an ID that can be referenced from somewhere else in the SOAP Message.

See this snippet of Axis javadoc for style and use combinations :

Description of the different styles
style=rpc, use=encoded
First element of the SOAP body is the operation. The operation contains elements describing the parameters, which are serialized as encoded (possibly multi-ref)

   <soap:body>
<operation>
<arg1>...</arg1>
<arg2>...</arg2>
</operation>

style=RPC, use=literal
First element of the SOAP body is the operation. The operation contains elements describing the parameters, which are serialized as encoded (no multi-ref)\
   <soap:body>
<operation>
<arg1>...</arg1>
<arg2>...</arg2>
</operation>

style=document, use=literal
Elements of the SOAP body are the names of the parameters (there is no wrapper operation...no multi-ref)
   <soap:body>
<arg1>...</arg1>
<arg2>...</arg2>

style=wrapped
Special case of DOCLIT where there is only one parameter and it has the same qname as the operation. In such cases, there is no actual type with the name...the elements are treated as parameters to the operation
   <soap:body>
<one-arg-same-name-as-operation>
<elemofarg1>...</elemofarg1>
<elemofarg2>...</elemofarg2>

style=document, use=encoded
There is not an enclosing operation name element, but the parmeters are encoded using SOAP encoding This mode is not (well?) supported by Axis.

Parameter

<parameter> is the tag used to configure a service. It's basically a name-value pair.
attributes :
common parameter :

Optional

    Other options can be specified to a service without parameter tags :

Mappings

    Mappings in WSDD can be set at different level : mappings commons for all services are directly childs of the deployment tag, and mappings specific for a given web service are childs of the service tag.
    The developer can specify to sort of mappings in WSDD configuration file : beanMapping and typeMapping. beanMapping is a write shortcut for typeMapping when mapping bean types.
    It is used to register a java type, with an XML QName and a serializer/deserializer pair.

examples :
<typeMapping xmlns:ns="urn:my.namespace"
             serializer="your.java.serializer.factory"
             deserializer="
your.java.deserializer.factory"
             qname="
ns:my-xml-type-name"
             type="
java:your.java.classname"
             encodingStyle="encoding-namespace"/>
Notes :
type value must be a qname in java prefixed namespace (java:XXX.YYY)
by default encodingStyle is set to http://schemas.xmlsoap.org/soap/encoding/ (SOAP 1.1 Encoding)
When Arrays of Complex Types are serialized and/or deserailized the factories to be used are :
org.apache.axis.encoding.ser.ArraySerializerFactory
org.apache.axis.encoding.ser.ArrayDeserializerFactory

<beanMapping xmlns:ns="urn:my.namespace"
             languageSpecificType="java:your.java.classname"
             qname="ns:my-xml-type-name"/>
Notes :
serializer and deserializer are automatically set to BeanSerializerFactory and BeanDeserializerFactory
encodingStyle is automatically set to null (cannot be overridden)

Deployment

    It's the root Element of any Axis WSDD file. It holds commons namespace definition.

    A normal deployment Element looks like that :

<deployment xmlns="http://xml.apache.org/axis/wsdd/"
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
    <!-- ... services definitions here ... -->
</deployment>

Deploy Created Web Service

    The deployment of the web service in JOnAS depends of the form of your wsdd file :
If you're using full wsdd configuration files, just add it in the WEB-INF/ directory of the servlet with the name server-config.wsdd. Deployment will be done automatically by Axis when the first client will attempt to connect.
If you're using simpler wsdd configuration files, you have to deploy your web-app (or ear) into JOnAS (normal process). And then, use the Axis AdminClient tool :
jclient org.apache.axis.client.AdminClient -lhttp://<hostname>:<port>/<web-app-context>/<servlet-mapping-to-Axis-Servlet> <your-deploy>.wsdd