Implementing Web Services With Stateless Session Beans
One of the coolest parts of JSR 109 is that stateless session beans can be
used to quickly and easily develop Web services. A complete review of the EJB
specification as it applies to stateless session beans is appropriate for those
readers with insufficient EJB background. The important aspects of the
specification as it applies to Web services are provided here.
Compliance with JAX-RPC rules: Web services must follow
JAX-RPC rules in order to correctly interact with clients. See the JAX-RPC
page for more information on JAX-RPC.
Stateless and transactionless : Web services implemented
as stateless session EJBs cannot track any state; nor can they require a transactional
context.
Compliance with the SDI for the service: In order to
operate properly, an implementation must implement all of the methods defined by
the SDI for that service.
Service interface: Optionally, the implementation may
implement the service interface; however, in practice, it's more likely that
the implementation will inherit (or have generated for it) the service implementation,
much like an EJB does today. Obviously, the bean may also implement other methods
besides those defined by the SDI.
Comply with the stateless session EJB requirements:
Since stateless session Web services are based on the traditional EJB specification,
such a Web service must comply with the rules for stateless session beans.
All of the aspects defined above apply to new Web services. What about exposing
an existing EJB as a Web service? As long as the EJB follows the rules defined
by the specification and doesn't violate the JAX-RPC specification, it may be
deployed as an implementation of an existing service. In fact, from the perspective of the
EJB container, there is no real difference between the two.
Stateless session beans execute within the context of an EJB container. The
container itself controls the lifecycle of a bean, including creating and destroying
bean instances. Figure 1 shows the lifecycle of a stateless session EJB.
Note that most application servers support a concept
of "pooling" or "pre-creating" instances of objects to improve
application performance. I've added an additional "conceptual"
state representing "pooled."
Figure 1. Stateless Session EJB Lifecycle
Does Not Exist: In this state, no instance
of the bean is available for use. Beans move from this state to the pooled
state or directly to the ready state either because the application server
needs a new instance to satisfy a client request or because a number of "pre-created"
beans have been specified to exist in the pool.
Pooled Unallocated: Not all application servers support
the concept of a readily-available pool, and as such, most specifications don't
describe the optional "Pooled Unallocated" state. Application servers
such as BEA's WebLogic support this intermediate state, where the application
server pre-creates a number of instances of the bean, based on a deployment descriptor,
that are instantly ready for use. When an application releases an instance
of a bean, it can be re-initialized and returned to the pool for reuse.
Ready: Beans in the ready state have been created,
initialized and are method-ready. Beans in the ready state can accept and
process method calls.
Beans move between states for a variety of reasons. Understanding why a bean
transitions from state to state will help us understand how to develop beans
that behave properly.
Transition 1 represents the initial creation of a bean. Normally,
the application server will pre-create some number of beans; however, if the
pool is completely in use, and there is available memory, a new instance of
a bean would normally be created at client-access time. New beans have their
init() method called before they
can be used, as any one-time initialization can be done at this point.
Transition 2 repesents the transition from a pooled state
to a method-ready state. This transition occurs when a pooled instance is
allocated to a client, normally during a get<...>Port() call. Again,
the reason for the pooled state is to improve startup performance.
Transition 3 is simply "business as usual." An instance
of an object is allocated to a client and the client makes one or more method
calls.
Transition 4 occurs when an object is specifically released
(i.e., by setting the variable to null) or by going out of scope.
Depending on the application server, the bean will transition to pooled and
be re-initialized, or go directly to the does-not-exist state.
Transition 5 occurs when the bean is actually de-allocated.
The destroy() method is called,
and the client can then undo anything done during the init() method.
A note on transactions: The Web services of today are not transaction-aware, and any transaction context is suspended before a bean method is
evoked. Practically speaking, this means that beans must not specify or
require a transaction context, and as such, cannot be marked transaction
"Mandatory."
Implementing Web Services With Servlets
There isn't really a significant difference, conceptually, between implementing
Web services using EJBs and implementing with servlets. Choosing servlets as
the implementation vehicle is really a matter of personal preference.
Web services implemented as servlets need to follow
the same guidelines as EJB-based implementations. The real difference between
the two is the behavior of the underlying implementation. Table 1 shows
some of the most obvious differences. It should be noted that these
differences are the opinion of the author and not stated in JSR 109.
Table 1: Differences between EJB and servlet implementations
Servlet
EJB
Lifetime
Long-lived
Normally short-lived, might be longer with pooling
Startup overhead
Can be significant
Normally light
Memory footprint
Large
Small
Container Services support
Some
Significant
Lifecycle-aware
Marginally
Very
Transaction
Unsupported
Must not be required
Must implement
javax.servlet.SingleThreadModel
javax.ejb.SessionBean
Scales
Poorly
Well
When you might choose
When a small number of long-lived methods are required. Startup overhead not an issue.
When a large number of very short-lived calls are required.
The one area where Servlet and EJB behavior differs significantly is in
lifecycle management. As we saw in Figure 1, an EJB is made aware
that is has been created via the init()
call, and removed via the destroy()
call. The servlet specification has no analogous interface; to support
similar behavior, JSR 109 defines an additional interface --
java.xml.rpc.server.ServiceLifeCycle -- that
provides similar behavior.
Deployment solves the problem of isolating the aspects of the Web service
specific to how it is used from the aspects specific to its implementation.
It normally defines both packaging and naming/initialization. The name of the service and what initialization it might require are examples of deployment-specific concerns that the developer might define as required, but the deployer needs to specify in order for the Web service to operate correctly. JSP 109 piggy-backs its deployment onto the deployment of the normal EJB or Web application
via a new deployment description, webservices.xml..
As with so much of JSR 109, deployment of a Web service looks very much like an EJB deployment descriptor. Example 4 shows a sample of how a webservices.xml file
might look, and contains five main elements.
Port Name: A unique port name that is used by the application server to identify the port. Note that the port name is treated like the EJB name
in an EJB deployment descriptor. It is not the actual name of the port, but
is used for identification. Declared using the port-component-name
element.
Port QName: The namespace and WSDL name of the Web service. Specificied
using the port-qname-localname
and port-qname-namespace elements.
WSDL Definition: The path to the WSDL within the packaged .war or
.jar file.
Service Definition Interface class: The name of the class that implements
the service interface functionality of the Web service. Declared using the
service-def-interface element.
Given that there are two possible implementation mechanisms, it's likely that in
practice, this class will be generated, like many
of the EJB interface classes.
Port Service Implementation: The implementation of the bean is specified
with either an EJB-link or servlet-link
element pointing to the actual implementation of the service. Since the webservice.xml
file itself must be packed within the .jar or .war file
containing the implementation
of the service, either of these links must have been previously specified.
That is, you must reference an existing EJB or servlet.
Example 4 shows how a webservices.xml file might look. It
should be noted, however, that this sample was based on the .03 version of the specification,
and is subject to change. The code is simple enough (a complete DTD can be found
in the specification), but bears some explanation.
Lines 12-16 link the Web service description provided by the WSDL on line 05 to the implementation. Either an EJB or a servlet can be specified as the engine
for the Web service, and must be linked back to EJBs or servlets previously defined.
Line 11 defines the service interface for obtaining the port for the Web service.
Lines 08 and 09 qualify the external name of the Web service, so that any
external client might use it.
Example 4. Sample webservices.xml
00 <?xml version="1.0"?>
01 <webservices>
02 <description>My first Web Service using JSP 109</description>
03 <display-name>Sample</display-name>
04 <web-services-description>
05 <wsdl-name>path.to.the.wsdl.representing.the.service</wsdl-name>
06 <port-components>
07 .. various optional elements such as description etc
08 <port-component-name>MyWebService</port-component-name>
09 <port-qname-namespace>qualifying.namespace</port-qname-namespace>
10 <port-qname-localname>name.within.namespace</port-qname-localname>
11 <service-def-interface>com.webservicesareus.webservice.sampleservInt
</service-def-interface>
12 <service-impl-bean>
13 <ejb-link>link to a named ejb</ejb-link>
14 or
15 <servlet-link>link to a named servlet</servlet-link>
16 </service-impl-bean>
17 </port-components>
18 </web-services-description>
19 </webservices>
JSR 109 is an interesting specification that attempts to define how Web
services can be incorporated into an J2EE application server. While there there are
some clear questions, such as why MBeans were omitted, the specification does
cover the major areas required to define how Web services might be piggy-backed
onto existing J2EE applications and within application servers. I'll
be watching this JSR closely, along with many of the similar JSPs on security.
Al Saganich
is BEA Systems' senior developer and engineer for enterprise Java technologies, focused on Java integration and application with XML and Web services.