<document>
<s1>

JAX-WS (Java API for XML Web Services) offers a powerful new approach to writing SOAP-based services. However because its design is so tightly coupled to WSDL (Web Service Definition Language), getting started writing a simple service has up until now been difficult and confusing. This tutorial shows how to write a simple, straightforward SOAP service starting with Java. We will also see how easy it is to deploy web services using Resin's easy to understand XML configuration.

</s1> <deftable>
The main service implementation.
User class
Configures the environment
Client JSP
</deftable>

SOAP is essentially an XML-based protocol for invoking remote methods. For example, if we have a method `int add(int a, int b)`, the SOAP request and response for this method might look like the following:

<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Body> <m:add xmlns:m="http://example/"> <a>1</a> <b>2</b> </m:add> </soapenv:Body> </soapenv:Envelope> <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Body> <m:addResponse xmlns:m="http://example/"> <result>3</result> </m:addResponse> </soapenv:Body> </soapenv:Envelope>

WSDL is an XML-based language that describes the interface to SOAP services like the one above. It is very flexible and allows for numerous styles of SOAP invocations. Unfortunately, it can be very difficult to read. For example, the above exchange might have the following WSDL:

<?xml version="1.0" standalone="yes"?> <definitions targetNamespace="http://example/" name="AddService" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:m="http://example/"> <types> <xsd:schema version="1.0" targetNamespace="http://example/"> <xsd:element name="add" type="m:add"/> <xsd:complexType name="add"> <xsd:sequence> <xsd:element name="a" type="xsd:int"/> <xsd:element name="b" type="xsd:int"/> </xsd:sequence> </xsd:complexType> <xsd:element name="addResponse" type="m:addResponse"/> <xsd:complexType name="addResponse"/> <xsd:sequence> <xsd:element name="result" type="xsd:int"/> </xsd:sequence> </xsd:complexType> </xsd:schema> </types> <message name="add"> <part name="parameters" element="m:add"/> </message> <message name="addResponse"> <part name="parameters" element="m:addResponse"/> </message> <portType name="Add"> <operation name="add"> <input message="m:add"/> <output message="m:addResponse"/> </operation> </portType> <binding name="AddPortBinding" type="m:Add"> <soap:binding transport="http://schemas.xmlsoap.org/wsdl/soap/" style="document"/> <operation name="add"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="AddService"> <port name="AddPort" binding="m:AddPortBinding"> <soap:address location="http://example/add-service/"/> </port> </service> </definitions>

After looking at this WSDL, which is one of the simpler examples, you might begin to understand why WSDL has become mostly interpreted by programs and not people. If you are a client of another organization and plan to use their SOAP services, you may have to use their WSDL to generate Java code for your client. However if you are designing your own service, especially for intra-organizational use only, there may be no need to use WSDL at all!

JAX-WS is a Java API and architecture for writing SOAP services and clients. Because WSDLs are necessary in some cases, JAX-WS has a lot of functionality that deals with reading and generating them. Unfortunately, if you're simply trying to write your own simple service, dealing with WSDL can be very confusing and complicated. In the example below, we will show how to write a simple, but useful service that uses JAX-WS without ever touching a WSDL!

Our service consists of one method, `getUsers()` that takes a group id and returns all the users in that group. If the group doesn't exist, the service throws an exception.

First we will need an interface for our service. The interface is an actual Java interface. The service implementation will implement this interface and the client will obtain a proxy that exposes it.

package example; import java.util.List; import javax.jws.WebMethod; import javax.jws.WebService; @WebService public interface UserService { @WebMethod public List<User> getUsers(int groupId) throws InvalidGroupIdException; }

Notice the annotations @WebService and @WebMethod. These are not strictly necessary, but help to show that this interface is for a SOAP web service. Other than those annotations, the interface is like any other Java interface with a method that takes arguments, returns a value, and even throws an exception. Within JAX-WS it will perform almost exactly like a local method even if it is really being invoked on a remote machine.

Next we'll look at the User class:

package example; public class User { private String _name; private int _id; private int _groupId; // // JAXB requires a zero-argument constructor // public User() {} public User(int id, int groupId, String name) { _id = id; _groupId = groupId; _name = name; } public String getName() { return _name; } public void setName(String name) { _name = name; } public int getId() { return _id; } public void setId(int id) { _id = id; } public int getGroupId() { return _groupId; } public void setGroupId(int groupId) { _groupId = groupId; } public String toString() { return "User[id=" + _id + ", groupId=" + _groupId + ", name=" + _name + "]"; } }

This is a very simple class with three properties: a user id, a group id, and a user name. Because our service will return a list of these User objects and we're actually using SOAP under the covers, JAX-WS will use another new Java technology: JAXB. JAXB is essentially a way to serialize and deserialize Java to and from XML. So this class needs to be JAXB compatible. In this case, fortunately all that means is that we need a zero-argument constructor.

The InvalidGroupIdException class is just a basic exception:

package example; public class InvalidGroupIdException extends Exception { public InvalidGroupIdException() { super(); } public InvalidGroupIdException(String message) { super(message); } }

These three classes are all that the client needs to use the service. JAX-WS has a way to obtain a client programmatically. However, since we know that this client will be used frequently, we can create a client instance and register it in JNDI using Resin's <web-service-client> configuration tag:

<web-service-client jndi-name="soap/UserService"> <url>soap:${webApp.url}/user/</url> <interface>example.UserService</interface> </web-service-client>

The implementation of this service is as simple as writing a class which implements UserService. In our simple example, we store user groups in a HashMap, but in a more complicated service, we might retrieve the users from a database. The only unusual thing about the implementation is the @WebService annotation which indicates that the UserService interface is the endpointInterface for this service. This simply means that the service will expose that interface as a SOAP service.

package example; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.jws.WebService; @WebService(endpointInterface="example.UserService") public class UserServiceImpl implements UserService { private final HashMap<Integer,List<User>> _userGroupMap = new HashMap<Integer,List<User>>(); public UserServiceImpl() { List<User> group1 = new ArrayList<User>(); group1.add(new User(1, 1, "Bruce")); group1.add(new User(2, 1, "Harvey")); List<User> group2 = new ArrayList<User>(); group2.add(new User(1, 2, "Lois")); group2.add(new User(2, 2, "Lex")); _userGroupMap.put(1, group1); _userGroupMap.put(2, group2); } public List<User> getUsers(int groupId) throws InvalidGroupIdException { List<User> users = _userGroupMap.get(groupId); if (users == null) throw new InvalidGroupIdException("Invalid group id"); return users; } }

Now for the deployment of this service. No WAR file is necessary, just a simple <servlet-mapping> tag in the resin-web.xml file:

<servlet-mapping url-pattern="/user/*" jndi-name="service/UserService" servlet-class="example.UserServiceImpl"> <protocol type="soap"/> </servlet-mapping>

The client can now connect to the UserService by doing a lookup in JNDI. This allows simple access even using JSP:

<%@ page import="java.util.List" %> <%@ page import="javax.naming.*" %> <%@ page import="javax.xml.ws.Holder" %> <%@ page import="example.UserService" %> <%@ page import="example.User" %> <% Context context = (Context) new InitialContext().lookup("java:comp/env"); UserService service = (UserService) context.lookup("soap/UserService"); List<User> users = service.getUsers(2); Exception invalid = null; try { service.getUsers(0); } catch (Exception e) { invalid = e; } %> <pre> UserService.getUsers(1): <%= holder.value %> UserService.getUsers(0): <%= invalid %> </pre> <results> UserService.getUsers(1): [User[id=1, groupId=2, name=Lois], User[id=2, groupId=2, name=Lex]] UserService.getUsers(0): example.InvalidGroupIdException </results>
</document>