J2EE and JAX-RPC Web Services

This article was originally published in VSJ, which is now part of Developer Fusion.
Sun has recently released its latest Web Services Toolkit that includes JAX-RPC (Java API for XML Based RPC) v1.0, and mandated the inclusion in J2EE 1.4. The EJB 2.1 specification has also been published in draft final form, and includes mechanisms for using JAX-RPC to expose stateless Session Beans as a Web Service. In this article, we'll examine how to use these tools to publish Web Services, and a future article will show how to use JAX-RPC on the client to consume Web Services.

A quick (re)introduction to Web Services

A Web Service is commonly viewed as an application which exposes its functionality through interfaces that are available over standard protocols. Typically, the web application will be invoked (although it doesn't have to be) using SOAP (Simple Object Access Protocol), which is usually bound to HTTP. In order for this to work, the interfaces must be exposed in a standard manner, and you do this by exposing the interface as a WSDL (Web Services Discovery Language) endpoint. Clients in turn can use this endpoint if they know the URL of the endpoint. Web Service Providers can publish information about their web service in UDDI (Universal Description, Discovery and Integration) registries.

Sun support for synchronous Web Services

DiagramJAX-RPC is an API from Sun that allows this WSDL endpoint to be serviced by either a Servlet or an EJB 2.1 stateless Session Bean. The components need to be stateless as HTTP is essentially a connectionless protocol. See Figure 1 for an overview.

The JAX-RPC API is an integral part of what Sun is calling the Web Services Developer Pack (WSDP). You can download v1.0 of this from www.javasoft.com/webservices, or if you also want EJB support you could download the J2EE 1.4 beta server from www.javasoft.com/j2ee.

To run the sample code you will need to have one of these environments on your machine. Both environments come packaged with Tomcat, in the case of the WSDP Tomcat 4.0.x, and in the beta J2EE server Tomcat 5.0. They also include XML parsers and can cause version control problems in existing systems, so be careful about installing these frameworks onto production boxes.

Once you have a working environment, your first step is to create a simple Web Service that converts Celsius to Fahrenheit, and deploy this into Jakarta-Tomcat.

Creating the interface

The business interface is relatively straightforward; it is simply a remote interface as in RMI. However types in SOAP are always passed by value, therefore the range of types that JAX-RPC supports is somewhat limited, namely:
  • All the primitives, except char
  • All the wrappers for the above primitives
  • java.math.BigDecimal and java.math.BigInteger
  • java.util.Calendar and java.util.Date
  • ArrayList, LinkedList, Stack and Vector
  • HashMap, Hashtable, Properties and TreeMap
  • HashSet and TreeSet
  • Arrays of the above
You can pass your application classes by value also, as long as the type does not implement a Remote interface and all of the fields are a JAX-RPC compliant type. In addition, you are not allowed to declare public fields as final, transient or static.

With that in mind the service endpoint interface is:

package tempconverter;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ConverterIF extends Remote {
	public double cToF(double celsius)
		throws RemoteException;
	public double ftoC(double fahrenheit)
		throws RemoteException;
}
You will now create an implementation class, which is simply a standard Java class that implements the behaviour.
package tempconverter;

public class ConverterImpl implements ConverterIF{
	public double cToF(double celsius) {
		return celsius/5*9+32;
		}

	public double ftoC(double fahrenheit) {
		return (fahrenheit-32)/9*5;
		}
	}
Notice your implementation class does not throw RemoteException. This is the responsibility of the container as this implementation will be deployed in a managed J2EE container. Now deploy the application in Tomcat and connect to the Web Service from any available client. I have used XML spy to create the SOAP request and the response is as shown in Figure 2.

Screen shot

Deploying to a Web Container

The messages passed to the server will be SOAP messages encapsulated in the HTTP protocol, and although servlets understand HTTP they have no SOAP binding. To act as the 'interpreter' the JAX-RPC runtime will create ties from your endpoint interface and implementation. The tie's role is to act as a proxy for the actual implementation class and to call the correct helper objects to (de)serialize the SOAP types to and from Java types.

DiagramThese classes are implementation dependent, but the Tomcat implementation is shown in Figure 3. Much of the detail is left out, but essentially there is a target servlet, of type com.sun.xml.rpc.server.httpJAXRPCServlet, which has an associated Listener. This servlet will use Deserializers to convert arguments to the right type to pass to the Tie object, which will be a subclass of com.sun.xml.rpc.server.TieBase. Similar to tie delegation in CORBA, the actual implementation class you wrote is the actual target of the calls. The response is then created and sent back via SOAP.

It is fairly straightforward to create the WAR file that you must provide to Tomcat. You need to offer deployment information in an XML file called jaxrpc-ri.xml, which the jax-rpc runtime tools will then use to create the tie classes and the WSDL document. This file provides the jax-rpc tools with the namespace for the WSDL document, the name of the Endpoint interface, and which interface and implementation classes provide this mapping.

<?xml version="1.0" encoding="UTF-8"?>
<webServices xmlns=
"http://java.sun.com/xml/ns/jax-rpc/ri/dd"
version="1.0"
targetNamespaceBase="http://www.qa.com/wsdl"
typeNamespaceBase="http://www.qa.com/types"
urlPatternBase="/conv-demo">
<endpoint name="MyTempConverter"
displayName="Temperature Conversion Service"
description="A Simple Temperature Conversion Service"
interface="converter.ConverterIF"
	implementation="converter.ConverterImpl"/>
<endpointMapping endpointName="MyTempConverter"
	urlPattern="/converter"/>
</webServices>
You then compile your classes, and jar them into a WAR file with the deployment descriptor jaxrpc-ri.xml in the web-inf sub-folder. Finally you pass this WAR to a jax-rpc runtime tool called wsdeploy. To simplify the task considerably the jax-rpc runtime comes with a build.xml file for Apache Ant, and all of the tasks can be accomplished by running ant build-service.

Now you have to deploy the war file to tomcat, either by using the ant deploy command or browsing to:

http://localhost:8080/manager/install
?path=/conv-demo 
&war=file:/path/to/war

Consuming the Web Service

To check the service, browse to the endpoint you have just created:
http://localhost:8080/conv-demo/converter.
If everything is working properly the output should be as in Figure 4. Navigate to the WSDL and examine it, and to the model, which is simply an XML description of the Web Service.

Screen shot

I have used XML Spy to create a SOAP request and pass to the server endpoint, but you can use any technology available to you. I exercised the ctoF method with an input of 100.0. Examining Figure 5, you can see the result is 212.0.

Screen shot

More complex types

To pass more complex types as arguments your argument classes have to obey similar rules to JavaBeans:
  1. There must be a public no-arg constructor
  2. The fields must expose getter/setters if you want them exposed
  3. Constants are not allowed so no final static variables
  4. You cannot have transient fields
To illustrate this examine the following class ComplexType:
public class ComplexType{
	private String name;
	private int age;

	public void setAge(int age) {
		this.age=age;
		}
	public int getAge() {
		return age;
		}
	public void setName(String name) {
		this.name = name;
		}
	public String getName() {
		return name;
		}
	}
Running a Web Service that has a single method getComplexType, which returns an instance of ComplexType, results in the SOAP response as shown in Figure 6.

Screen shot

You can still use other application types that don't meet these rules by creating custom Serializer and Deserializer classes.

EJBs as Endpoints

It seems sensible to have all our interactions with servlets, after all these are web services, however applications developed with Enterprise JavaBeans have some significant benefits, chiefly due to their improved lifecycle management and scalability.

Servlets are chiefly designed to service long-lived methods, and the lifecycle feedback to the Servlet instance is limited. EJBs, particularly stateless session beans, have a much richer lifecycle and their scalability is enhanced using object-pooling techniques. Therefore stateless session beans are much better suited for short-lived interactions with clients. The new Web Services specification being implemented for J2EE 1.4 recognises this and has added a lifecycle interface for servlet endpoints javax.xml.rpc.server.ServiceLifecycle.

To support exposing a stateless session bean as a Web Service endpoint, the EJB 2.1 specification has added a new interface type to the ejb-jar.xml deployment descriptor. A stateless session bean can now have three interfaces: a remote interface for normal CORBA clients, a local interface for Java clients in the same JVM and a service endpoint interface, which is a normal JAX-RPC interface.

If you had an existing stateless session bean that already does temperature conversion you could simply add an endpoint interface, or you could create a new stateless session bean that only has a service endpoint interface.

<session>
	<display-name>ConverterEJB</display-name>
	<ejb-name>ConverterEJB</ejb-name>
	<service-endpoint>ejbconv.ConverterIF</service-endpoint>
	<ejb-class>ejbconv.ConverterEJB</ejb-class>
	<session-type>Stateless</session-type>
	<transaction-type>Container</transaction-type>
	<security-identity>
		<use-caller-identity/>
	</security-identity>
</session>
This will have to be mapped somehow to an actual WSDL file, and the EJB spec has added two new deployment descriptors, mapping.xml which describes the type mapping to namespaces and webservices.xml that describes the Web Service in a portable way.
<webservice-description>
	<webservice-description-name>
		ConverterEJB</webservice-description-name>
	<wsdl-file>
MyTempConverter.wsdl</wsdl-file>
	<jaxrpc-mapping-file>
		mapping.xml
</jaxrpc-mapping-file>
	<port-component>
		<port-component-name>
			ConverterIFPort</port-component-name>
		<wsdl-port>
			<namespaceURI>
	http://www.qa.com
	</namespaceURI>
			<localpart>ConverterIFPort
	</localpart>
		</wsdl-port>
		<service-endpoint-interface>
			ejbconv.ConverterIF
			</service-endpoint-interface>
		<service-impl-bean>
			<ejb-link>ConverterEJB
	</ejb-link>
			</service-impl-bean>
		</port-component>
	</webservice-description>
</webservices>
Your bean implementation has access to a new method on the SessionContext interface, getMessageContext (). You can call this method from any method that is in the service endpoint interface. If the method was called from another interface it will return null. In the code example below I have extracted the SOAPMessage from the MessageContext and sent it to standard output.
public double cToF (double celsius) {
	MessageContext ctx = context.getMessageContext();
	try {
		if (ctx instanceof SOAPMessageContext) {
			SOAPMessage message =
			((SOAPMessageContext)ctx).getMessage();
			message.writeTo(System.out);
			}
		} catch (SOAPException e) {
		e.printStackTrace();
		}
	return celsius/5*9+32;
	}
Simply deploy the EJB as you would normally and you have a Web Service running in the EJB container. You can test the implementation by browsing to:
http://localhost:8000/converter?WSDL

Conclusion

The JAX-RPC API and EJB 2.1 specifications are great steps forward for the SUN J2EE platform. Although the large application server vendors have been providing Web Service toolkits for some time, these all suffer from the distinct disadvantage of being proprietary solutions. These new APIs hold the promise of portable solutions for Web Services written in Java. This article has given an introduction to deploying Web Services in your application servers, however much of the advanced material, such as using Handlers to deal with SOAP Attachments and writing your own Serializer and Deserializer classes to deal with complex types, cannot be covered in a single article. All of the source code is available from the VSJ web site, along with some basic instructions for building the examples.


James Winters is a Java specialist responsible for training delivery, course development and product consulting on Java technology at QA. Having published numerous articles, he is currently writing a textbook on Distributed Systems.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Programs must be written for people to read, and only incidentally for machines to execute.”