SCDJWS Study Guide: JAX-RPC
Printer-friendly version |
Mail this to a friend
JAX-RPC Service-Side Programming Models
JAX-RPC defines two server-side programming models for creating J2EE Web service endpoints: JAX-PRC Service Endpoints and Enterprise JavaBeans Endpoints. The service endpoint is deployed in a container-based JAX-RPC runtime system.
- Using a JAX-RPC service endpoint (JSE) — The service implementation is a Java class in the Web container. The service adheres to the Web container's servlet lifecycle and concurrency requirements.
- Using an EJB service endpoint— The service implementation is a stateless session bean in an EJB container. The service adheres to the EJB container's lifecycle and concurrency requirements.
In either case, the service is made portable with the definition of a port component, which provides the service's outside view for Web service implementation. A port component consists of:
- A WSDL document describing the Web service that its clients can use
- A service endpoint
interface defining the Web service's methods that are available to
clients.
- A service implementation bean implementing the business logic of the methods defined in the service endpoint interface. The implementation may be either a Java class in the Web container or a stateless session bean in the EJB container.
JAX-RPC requires the service definition interfaces
must follow RMI conventions, no remote references allowed, and all
objects are passed by copy.
Container-specific service interfaces, created by the J2EE container, provide static stub and dynamic proxies for all ports. A client of a J2EE platform Web service can be a Web service peer, a J2EE component, or a stand-alone application. It is not required that the client be a Web service or application implemented in Java.
Use a stateless Session
Bean to expose Web services if
you:
- Need to expose previously existing stateless Session Beans as Web services
- Need declarative transaction management
- Need the thread management provided by EJB Container
- Need role based security
Use Java classes to
expose your Web services if you:
- Need to expose previously existing Java classes as Web services
- Want a light-weight system, and don't care much about transactional capabilities that an EJB container provides
JAX-PRC Service Endpoint (JSE)
A JSE is easy to develop because it’s just a POJO. A JSE is composed of two parts: a service endpoint interface and the service corresponding implementation class. There are some restrictions for the service interface and the service implementation class:
- The service endpoint interface which MUST extends (directly or indirectly) java.rmi.Remote interface and declares the web service’s publicly accessible methods. All of service endpoint interface’s methods MUST throw the java.rmi.RemoteException type exception.
- The service endpoint interface
may be generated from a WSDL file.
- The service implementation class that implements all methods defined by the service endpoint interface.
- The service implementation class may not directly implement the service endpoint interface by using Java "implements" keyword. In such case, the service implementation class MUST provide all methods, that defined in the servince endpoint interface, with the same signatures.
- The service implementation class' methods MUST NOT throw java.rmi.RemoteException. This is the responsibility of the container as this implementation will be deployed in a managed J2EE container.
Once you have these two parts, you can use them, along with other deployment files, to generate a WSDL document that provides a platform-independent description of the JSE.
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;
}
Second, 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;
}
}
JSEs are deployed into a J2EE servlet container, and have access to the same resources and context information a servlet has. 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.
During the deployment of a service endpoint component on a J2EE servlet container based JAX-RPC runtime system, a service endpoint class is associated with a special JAX-RPC servlet provided by the vendor. The associated servlet class is provided by the JAX-RPC runtime system (not by service endpoint developer) during the deployment. This association is configured in a manner specific to a JAX-RPC runtime system and its deployment tool. For example, a JAX-RPC deployment tool may configure a 1-1 association between a servlet class and service endpoint class. The special JAX-RPC associated servlet class is repsonsible fot responding to HTTP based SOAP requests, parsing the SOAP messages, and invoking the corresponding methods of the JSE implementation object. When the JSE returns a value from the method invocation, the JAX-RPC servlet creates a SOAP message to hold the return value (or a SOAP fault if an exception is thrown) and sends that SOAP message back to the requesting client via an HTTP replay message.
The associated servlet typically takes the responsibility of handling transport specific processing of an RPC request and for initiating dispatch to the target service endpoint instance. Each Servlet.service(...) method maps to a single remote method invocation on the target service endpoint instance. The thread model (whether single threaded or concurrent) for the remote method invocation on the service endpoint instance depends on the runtime system specific servlet associated with the corresponding endpoint class. The Servlet specification provides facility for both concurrent and single threaded model (the latter through the SingleThreadModel interface) for the service(...) method on a servlet.
When processing an incoming SOAP request for a one-way operation, the associated servlet is required to send back an HTTP response code of 200 or 202 as soon as it has identified the incoming request as being one-way and before it dispatches it to the target service endpoint instance.
The term JAX-RPC Service
Endpoint used within the JAX-RPC
specification is somewhat confusing since both Service Implementation
Beans
require the use of a JAX-RPC run time. However, in this case it refers
to the
programming model defined within the JAX-RPC specification that is used
to
create Web services that run within the web container. The requirements
are
repeated here with clarification. Changes from the JAX-RPC defined
programming
model are required for running in a J2EE container-managed environment.
A
JAX-RPC Service Endpoint can be single or multi-threaded. The
concurrency
requirement is declared as part of the programming model. A JAX-RPC
Service
Endpoint must implement javax.servlet.SingleThreadModel if single
threaded
access is required by the component. A container must serialize method
requests
for a Service Implementation Bean that implements the SingleThreadModel
interface. Note, the SingleThreadModel interface has been deprecated in
the
Servlet 2.4 specification.
The Service Implementation Bean must follow
the
Service Developer requirements outlined in the JAX-RPC specification
and are
listed below except as noted:
- The Service Implementation Bean must have a default public constructor.
- The Service Implementation Bean may implement the Service Endpoint Interface as defined by the JAX-RPC Servlet model. The bean must implement all the method signatures of the SEI. In addition, a Service Implementation Bean may be implemented that does not implement the SEI. This additional requirement provides the same SEI implementation flexibility as provided by EJB service endpoints. The business methods of the bean must be public and must not be static. If the Service Implementation Bean does not implement the SEI, the business methods must not be final. The Service Implementation Bean may implement other methods in addition to those defined by the SEI, but only the SEI methods are exposed to the client.
- A Service Implementation must be a stateless object. A Service Implementation Bean must not save client specific state across method calls either within the bean instance's data members or external to the instance. A container may use any bean instance to service a request.
- The class must be public, must not be final and must not be abstract.
- The class must not define the finalize() method.
JAX-RPC Service Endpoint Lifecycle
According to JAX-RPC specification v1.0, if the soap service implements the javax.xml.rpc.server.ServiceLifeycle interface, the servlet container based JAX-RPC runtime system is required to manage the lifecycle of the corresponding service endpoint instances.
Here is javax.xml.rpc.server.ServiceLifeCycle interface:
package javax.xml.rpc.server;
import javax.xml.rpc.ServiceException;
public interface ServiceLifecycle {
void init(Object context) throws ServiceException;
void destroy();
}
The lifecycle of a
service endpoint instance is realized
through the implementation of the init()
and destroy() methods of the ServiceLifecycle
interface. The JAX-RPC
runtime system is responsible for loading and instantiation of service
endpoint
instances. The JAX-RPC runtime system may choose to load and
instantiate a
service endpoint instance during the startup of the runtime system or
when a
service endpoint instance is needed to service an RPC request. The
JAX-RPC
runtime system loads the service endpoint class using the Java class
loading
facility. After service endpoint class is loaded, the JAX-RPC runtime
system
instantiates the class.
After the service endpoint instance is instantiated, the JAX-RPC runtime system is required to initialize the endpoint instance before any requests can be serviced. The JAX-RPC runtime system is required to invoke the ServiceLifecycle.init method (if this interface is implemented by the soap service) for the initialization of the service endpoint instance. The service endpoint instance uses the init method to initialize its configuration and setup access to any external resources. The context parameter in the init method enables the endpoint instance to access the endpoint context provided by the underlying JAX-RPC runtime system. Once a service endpoint instance has been initialized (and in a method ready state), the JAX-RPC runtime system may dispatch multiple remote method invocations to the service endpoint instance. These method invocations must correspond to the remote methods in the service endpoint interface implemented by the service endpoint class. (Notice here, that the container must call ServiceLifecycle.init method before it can start dispatching request to the service endpoint).
The JAX-RPC runtime system is required to invoke the destroy method when the runtime system determines that the service endpoint instance needs to be removed from service of handling remote invocations. A container MAY NOT call the destroy() method while a request is being processed by the bean instance. For example, the JAX-RPC runtime may remove an endpoint instance from service when the runtime system is shutting down or managing memory resources. The service endpoint class releases its resources and performs cleanup in the implementation of the destroy method.
After successful invocation of the destroy method, a service endpoint instance is ready for garbage collection. The JAX-RPC runtime system SHOULD NOT dispatch any remote method invocations to a destroyed endpoint instance. The JAX-RPC runtime systems are required to instantiate and initialize a new service endpoint instance for servicing new remote method invocations.
ServletEndpointContext
JSEs are deployed into a J2EE servlet container, and have access to the same resources and context information a servlet has. JSEs can access to a private set of enviroment variables and resources using JNDI. Also, JSEs can use javax.xml.rpc.server.ServletEndpointContext interface to interact with its enviroment.
For a service endpoint components deployed on a servlet container based JAX-RPC runtime system, the context parameter in the ServiceLifecycle.init method is required to be of the Java type javax.xml.rpc.server.ServletEndpointContext. The ServletEndpointContext provides an endpoint context maintained by the underlying servlet container based JAX-RPC runtime system. The goal of JAX-RPC specification is not to define a more generic abstraction for the endpoint context or session that is independent of any specific component model, container and protocol binding. Such generic abstractions and endpoint model are outside the scope of the JAX-RPC specification.
Here is ServletEndpointContext Interface:
package javax.xml.rpc.server;
public interface ServletEndpointContext {
public java.security.Principal getUserPrincipal();
public boolean isUserInRole(String role);
public javax.xml.rpc.handler.MessageContext
getMessageContext();
public javax.servlet.http.HttpSession
getHttpSession() throws javax.xml.rpc.JAXRPCException;
public javax.servlet.ServletContext
getServletContext();
}
A servlet container based JAX-RPC runtime system is required to implement the ServletEndpointContext interface. The JAX-RPC runtime system is required to provide appropriate session, message context, servlet context and user principal information per method invocation on service endpoint instances.
Creating Session Oriented SOAP Services
- The soap service should implement javax.xml.rpc.server.ServiceLifecycle.
- The soap service should define the init( ), a call back method through which the JAX-RPC runtime system passes the context object to the soap service. destroy( ) method should also be defined.
- The context passed to the soap service from the JAX-RPC runtime system will be of type ServletEndpointContext. ServletEndpointContext.getHttpSession() will get the current http session.
Let’s see some example:
package tempconverter;
public class ConverterImpl implements ConverterIF,
javax.xml.rpc.server.ServiceLifecycle {
ServletEndpointContext
servletEndpointContext;
public void init(Object context)
throws ServiceExcepion {
servletEndpointContext = (ServletEndpointContext)context;
//Open and allocate resources
}
public void destory() {
//Close and Clean up resources
}
public double cToF(double celsius) {
return
celsius/5*9+32;
}
public double ftoC(double fahrenheit) {
return
(fahrenheit-32)/9*5;
}
}
A servlet container based
JAX-RPC runtime system is
required to implement the ServletEndpointContext
interface. The JAX-RPC
runtime
system is required to provide appropriate session, message context,
servlet
context and user principal information per method invocation on service
endpoint instances.
The getHttpSession method returns the current javax.servlet.http.HttpSession. When invoked by the service endpoint instance within a remote method implementation, the getHttpSession returns the HTTP session associated currently with this method invocation. This method is required to return null if there is no HTTP session currently active and associated with this service endpoint instance. An endpoint class should not rely on an active HTTP session being always there; the underlying JAX-RPC runtime system is responsible for managing whether or not there is an active HTTP session.
The getHttpSession method throws JAXRPCException if it is invoked by a non HTTP bound endpoint. The JAX-RPC specification does not specify any transport level session abstraction for non-HTTP bound endpoints.
If the service endpoint
class implements the
ServiceLifecycle interface, the servlet container based JAX-RPC runtime
system
is required to manage the lifecycle of the corresponding service
endpoint
instances. The lifecycle of a service endpoint instance is realized
through the
implementation of the init and destroy methods of the ServiceLifecycle
interface.
Summarize the Servlet-based Endpoint Model:
- The Service implementation class is an ordinary Java calss.
- The invocation of the methods provided the service done inside the servlet container.
- Optioanl ServiceLifecycle interface for initialization and destruction callbacks
- Access all resources in the web application
Statefull Web Service By JAX-RPC
Endpoint Service
In the JAX-RPC 1.1 version, the session management mechanisms require use of HTTP as the transport in the protocol binding. This version of the JAX-RPC specification does not specify (or require) session management using SOAP headers given that there is no standard SOAP header representation for the session information. SOAP based session management may be considered in the future versions of the JAX-RPC specification.
A JAX-RPC runtime system is required to use at least one of the following mechanisms to manage sessions:
- Cookie based mechanism:
On the initial method invocation on a service endpoint, the server side
JAX-RPC runtime system sends a cookie to the service client to initiate
a new session. If service client wants to participate in this session,
the client side JAX-RPC runtime system then sends the cookie for each
subsequent method invocation on this service endpoint. The cookie
associates subsequent method invocations from the service client with
the same session.
- URL rewriting involves adding session related identifier to a URL. This rewritten URL is used by the server-side JAX-RPC runtime to associate RPC invocations to the service endpoint with a session. The URL that is rewritten depends on the protocol binding in use.
- SSL session may be used to associate multiple RPC invocations on a service endpoint as part of a single session.
A session (in JAX-RPC) is initiated by the server-side JAX-RPC runtime system. The server-side JAX-RPC runtime system may use javax.servlet.http.HttpSession (defined in the Servlet specification) to implement support for the HTTP session management.
A service client uses the
javax.xml.rpc.session.maintain
property (set using the Stub or Call interfaces) to indicate whether or
not it
wants to participate in a session with a service endpoint. By default,
this
property is False, so the client does not participate in a session by
default.
However, by setting javax.xml.rpc.session.maintain
property to True, the client
indicates that it wants to join the session initiated by the server. In
the
cookie case, the client runtime system accepts the cookie and returns
the
session tracking information to the server, thereby joining the
session.
The client code by
setting the
javax.xml.rpc.session.maintain property assumes that it would
participate in a session if one is initiated by the server. The actual
session
management happens transparent to the client code in the client-side
runtime
system.
Property javax.xml.rpc.session.maintain
accepts objects of java.lang.Boolean
class. This boolean property is used by a
service client to indicate whether or not it wants to participate in a
session
with a service endpoint. If this property is set to True, the service
client
indicates that it wants the session to be maintained. If set to False,
the session is not maintained.
The default value for this property is False.
package javax.xml.rpc;
public interface Stub {
// ...
void
_setProperty(String name, Object value);
Object
_getProperty(String name);
java.util.Iterator
_getPropertyNames();
}
package
javax.xml.rpc;
public interface Call {
// ...
void
setProperty(String name, Object value);
Object
getProperty(String name);
boolean
removeProperty(String name);
java.util.Iterator
getPropertyNames();
}
The JAX-RPC service endpoint class may implement ServiceLifecycle interface.
To change the default session timeout globally, add the following element in your web.xml (service endpoint implementation deployment descriptor):
<web-app>
...
<session-config>
<!--
set global default timeout to 15 minutes -->
<session-timeout>15</session-timeout>
</session-config>
...
</web-app>
The session-timeout
element defines the default session
timeout interval for all sessions created in this web application. The
specified timeout must be expressed in a whole number of MINUTES. If
the
timeout is 0 or less, the container ensures the default behaviour of
sessions
is never to timeout. If this element is not specified, the container
must set
its default timeout period.
JAX-RPC EJB Endpoints
EJB
components, by design, are meant for distributed computing and are
hence well
suited for exposure as Web services. EJB supports declarative
transactions and
security, and these benefits can also be leveraged if you decide to use
EJB
components as Web services. J2EE 1.4 allows exposing only stateless
session
beans as Web services with JAX-RPC. Any Web service client
can access the
EJB webservice using SOAP 1.1 over HTTP.
You can use a stateless session Web service to accomplish any of these business operations by accessing a persistence layer such as CMP (container-managed persistence) entity beans or sending a message to a JMS (Java Message Service) queue that activates an MDB (message-driven bean) to process the business rule.
The developer can choose a web service endpoint interface for a stateless session bean whenever he wants to expose the functionality of the bean as a web service endpoint through WSDL. The clients for EJB Web Service endpoint may be Java clients and/or clients written in a programming language other than Java. A Java client that accesses the EJB Web Service has to use JAX-RPC client APIs.
Making a stateless session bean accessible as a web service involves basically the same process used to deploy a stateless session bean with a remote or local interface. The only big differece is that you define an endpoint interface that extends javax.rmi.Remote, instead of a remote interface that extends javax.ejb.EJBObject or javax.ejb.EJBLocalObject. In addution, you do not define a home interfacem because an EJB endpoint does not have one.
This is an example shows how can you expose your existing EJB applications as a webservice endpoint and how a pure Java client accesses the ejb webservice. We will use a simple Stateless Session bean TimeBean that displays the current time and locale information. For exposing the webservice endpoint you do not need to have home or remote interfaces for the EJBs, only the end-point interface that extends java.rmi.Remote and bean implementation class is required. Following the code for the service-endpoint for the EJB:
package time;
import
java.rmi.RemoteException;
import java.rmi.Remote;
public interface
TimeService extends Remote {
public
String getDateTime (String name) throws RemoteException;
}
The endpoint interface MUST comply with the rules for WSDL-to-Java mapping defined by the JAX-RPC specification. Every business method defineed in the endpoint interface MUST declare the java.rmi.RemoteException in its throw clause. RemoteException is used to report any networking problems associated with processing a client’s SOAP request. The SOPA client will not use the EJB endpoint interface directly. Instead SOAP clients will use the WSDL document associated with the EJB endpoint to generateee their own service interface.
Once you have defined the endpoint interface, you can define the bean class, which will do the actual implementation of the endpoint interface:
package time;
import
java.rmi.RemoteException;
import
javax.ejb.SessionBean;
import
javax.ejb.SessionContext;
import
java.text.DateFormat;
import java.util.Locale;
import java.util.Date;
public class
TimeServiceBean implements SessionBean
{
public String
getDateTime (String name)
{
System.out.println("TimeServiceBean:getDateTime() invoked");
DateFormat
formater = DateFormat.getDateTimeInstance();
return "Hi "+name +" ! \n today
is "+ formater.format(new Date())+
" in
"+Locale.getDefault().getCountry();
}
public
TimeServiceBean() {}
public void
ejbCreate() {}
public void
ejbRemove() {}
public void
ejbActivate() {}
public void
ejbPassivate() {}
public void
setSessionContext(SessionContext sc) {}
}
Although the bean class MUST implement all methods with the same signature defined in the endpoint interface, it’s not required to explicit implement the interface itself. This is in keeping with the EJB platform convetion of not implementing remote or local interfaces. Each methods in the implementation class MUST throw the same exceptions defined in endpoint interface throw – except one: java.rmi.RemorteException. The endpoint bean’s methods MUST NEVER declare RemoteException.
Then we have to define the end-point interface in ejb-jar.xml as follows:
<session>
<display-name>TimeServiceEJB</display-name>
<ejb-name>TimeServiceEJB</ejb-name>
<service-endpoint>time.TimeService</service-endpoint>
<ejb-class>time.TimeServiceBean</ejb-class>
<session-type>Stateless</session-type>
...
</session>
The WSDL file defines the web services e.g. the following MyTimeService.wsdl describes the Time ejb webservice:
<?xml version="1.0"
encoding="UTF-8"?>
<definitions
name="MyTimeService"
targetNamespace="urn:oracle-ws" xmlns:tns="urn:oracle-ws"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types/>
<message
name="TimeService_getDateTime">
<part
name="String_1" type="xsd:string"/>
</message>
<message
name="TimeService_getDateTimeResponse">
<part
name="result" type="xsd:string"/>
</message>
<portType
name="TimeService">
<operation
name="getDateTime" parameterOrder="String_1">
<input
message="tns:TimeService_getDateTime"/>
<output
message="tns:TimeService_getDateTimeResponse"/>
</operation>
</portType>
<binding
name="TimeServiceBinding" type="tns:TimeService">
<operation
name="getDateTime">
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
use="encoded" namespace="urn:oracle-ws"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
use="encoded" namespace="urn:oracle-ws"/>
</output>
<soap:operation soapAction=""/>
</operation>
<soap:binding
transport="http://schemas.xmlsoap.org/soap/http"
style="rpc"/>
</binding>
<service
name="MyTimeService">
<port
name="TimeServicePort" binding="tns:TimeServiceBinding">
<soap:address
location="REPLACE_WITH_ACTUAL_URL"/></port>
</service>
</definitions>
The mapping.xml file specifies the Java to WSDL mapping i.e. it contains the mapping between package names and XML namespaces, WSDL root types and Java artifacts, and the set of mappings for services. For example we will have the following contents for our mapping.xml:
<package-mapping>
<package-type>time</package-type>
<namespaceURI>urn:oracle-ws</namespaceURI>
</package-mapping>
Deployment of webservices requires a deployment descriptor named webservices.xml in META-INF of the ejb-jar file. This descriptor specifies the set of web service descriptions that are to be deployed into the J2EE Application Server and the dependencies they have on container resources and services:
<webservice-description>
<webservice-description-name>TimeServiceEJB</webservice-description-name>
<wsdl-file>META-INF/MyTimeService.wsdl</wsdl-file>
<jaxrpc-mapping-file>META-INF/mapping.xml</jaxrpc-mapping-file>
<port-component>
<description>port component description</description>
<port-component-name>TimeServicePort</port-component-name>
<wsdl-port>
<namespaceURI>urn:oracle-ws</namespaceURI>
<localpart>TimeServicePort</localpart>
</wsdl-port>
<service-endpoint-interface>time.TimeService</service-endpoint-interface>
<service-impl-bean>
<ejb-link>TimeServiceEJB</ejb-link>
</service-impl-bean>
</port-component>
</webservice-description>