J2EE 1.4 eases Web service development

Java's new Web service client and server programming models

At the conclusion of his J2EE (Java 2 Platform, Enterprise Edition) Web services presentation at last year's JavaOne, IBM architect Jim Knutson remarked that "every Web service needs a place to be a service." He then suggested that the most ideal place to be a Web service was inside the J2EE infrastructure. A little more than a year later, the final release of J2EE 1.4 is imminent, and its most significant promise is to deliver on the J2EE Web services vision.

The Web service features in J2EE 1.4 address both the server and client sides of Web services. The features extend J2EE to allow existing server-side enterprise Java components to become Web services and specify how a J2EE client container can invoke Web services. The technologies for both aims have existed for a while, and the new J2EE specs rely on those existing APIs for Web services support. The new specs add to the existing technologies a set of interoperability requirements, and a programming and deployment model for Web service integration.

There are two specifications that explicitly outline those added features: Java Specification Request 151, the umbrella JSR for J2EE 1.4, and JSR 109, Web Services for J2EE. At the time of this writing, JSR 109 has reached its final stage in the JCP (Java Community Process), while JSR 151 is in the last voting phase. Additionally, the JCP amended the final release of JSR 101, Java APIs for XML-based Remote Procedure Call (JAX-RPC), to support J2EE 1.4 interoperation requirements.

J2EE 1.3-level application servers can also implement many of the features prescribed by these JSRs. Indeed, many application server vendors have supported various Web service development and deployment features in their existing products for some time now. JSRs 109 and 151 codify some existing practices and describe new mechanisms with the hope of creating a universal J2EE-Web services integration model. Next-generation application servers will likely follow that unified, standardized model.

Following a brief survey of new Web service-related J2EE features, this article reviews the new client and server programming models, including new J2EE deployment and service management roles associated with Web services support.

Web service-related J2EE extensions

Perhaps the most significant, and most consequential, additions to J2EE are the new interoperation requirements. The requirements prescribe support for SOAP (Simple Object Access Protocol) 1.1 in the J2EE presentation layer to facilitate XML message exchange. J2EE 1.4-compliant containers must also support the WS-I (Web Services Interoperability Consortium) Basic Profile. Since XML message exchange in J2EE depends on JAX-RPC, the JAX-RPC specifications now mandate WS-I Basic Profile support as well.

The result is that a J2EE 1.4-based application can be invoked as a Web service, even from applications not written in the Java programming language. While that is an evolutionary step for J2EE, since the platform has long embraced non-Java based systems, it is possibly the most direct way to facilitate interaction with Windows-based technologies that rely on .Net.

A J2EE-based service's client does not have to be aware of how a service is implemented. Rather, that client can use the service by relying entirely on the service's WSDL (Web Services Description Language) definition. (Previous JavaWorld Web Services columns explain how to discover services based on their WSDL definitions, and how to create and use WSDL definitions. See Resources for links.) While the J2EE specs do not spell out the exact mechanics of such interaction, J2EE 1.4's embrace of the WS-I Basic Profile, which Microsoft also claims to follow, will likely make J2EE-.Net interaction common.

To facilitate access to WSDL definitions, J2EE 1.4 adds support for the JAXR (Java API for XML Registries) standard. The JAXR libraries are now a required part of the J2EE application client, EJB (Enterprise JavaBeans), and Web containers (not the applet container, though). Since WS-I Basic Profile mandates support for UDDI (Universal Description, Discovery, and Integration) 2.0, J2EE clients, as well as EJB components and servlets, can interact with public Web service registries. ("Web Services Take Float with JAXR" (JavaWorld, May 2002) offers a tutorial on JAXR.) Figure 1 illustrates the additional Web service-related libraries supported by J2EE 1.4.

Figure 1. Web service-related library support in J2EE 1.4

Indeed, J2EE takes the view that a Web service is an implementation of one or more interfaces defined by a WSDL document. The operations described in WSDL are first mapped to Java methods following the JAX-RPC specification's WSDL-to-Java mapping rules. Once a Java interface corresponding to a WSDL file is defined, you can implement that interface's methods in one of two ways: as a stateless session bean running in the EJB container or as a Java class running in the J2EE servlet container. Finally, you arrange for the respective container to listen for incoming SOAP requests and map those requests to the respective implementation (EJB or servlet). To process incoming SOAP invocations, J2EE 1.4 mandates the JAX-RPC runtime as an additional J2EE container service.

In keeping with the J2EE architecture, a service implementation's container mediates access to a Web service: if you expose either an EJB component or a servlet as a J2EE Web service, your service's clients can invoke that service only indirectly, via the container. That allows a service implementation to benefit from the container's security, thread management, and even quality-of-service guarantees. In addition, containers let you make important Web service decisions, such as security constraints, at deployment time. Finally, J2EE's container-based model makes Web service deployment portable: you can develop a Java-based Web service using any J2EE tool and expect that service to run in any other compliant container implementation.

A Web service client, on the other hand, remains unaware of a Web service container's presence. Instead, the client sees a port representing a network endpoint instance of a Web service. That endpoint follows the JAX-RPC service endpoint interface (SEI) model and provides an implementation of the service's interface. A client views each J2EE Web service as an SEI and port combination. A single J2EE container can host many such combinations, as Figure 2 illustrates. Each SEI/port combination is an instance of a Web service.

Figure 2. A client's view of a Web service

Note that the client in this architecture can be either a J2EE client, running inside the J2EE client container, or a non-J2EE client. Any WS-I Basic Profile-compliant client can use a J2EE Web service, but each client may follow different programming models. The J2EE Web services specification outlines a programming model for clients that run inside the J2EE application client container and another model—the server programming model—for Web service implementations that execute in the EJB or servlet containers.

The J2EE Web service client programming model

The essence of the Web service client programming model is to streamline the use of APIs defined in JSRs 67 (Java APIs for XML Messaging, JAXM), 93 (JAXR), and 101 (JAX-RPC), and to provide a comprehensive framework for using those APIs together in the J2EE client container.

In keeping with the J2EE client programming model, a Web service client is remotable and provides local/remote transparency. The Web service port provider and the container that the port runs in define how a client sees a Web service. The client always accesses the port and is never passed a direct reference to a Web service's implementation. A J2EE Web service client remains unaware of how a port operates and must concern itself only with the methods a port defines. Those methods constitute a Web service's public interface. In addition, a client must consider access to a Web service port as stateless across service invocations. As far as the client is concerned, a port lacks a unique identity—a client has no way of determining if it communicates with identical ports across service invocations.

The client gains access to a port based on the port's service interface. J2EE Web services rely on JAX-RPC to define the relationship between a port and its service interface. JAX-RPC creates that relationship based on WSDL processing rules. Thus, the Web service's WSDL definition ultimately governs the port's behavior. Based on the JAX-RPC definition, the service interface can either be a generic interface directly implementing the javax.xml.rpc.Service interface, or a "generated service," which is a subtype of that interface. The latter interface type is specific to a Web service's type.

In the J2EE programming model, the client obtains a reference to a Web service's Service object via a JNDI (Java Naming and Directory Interface) lookup operation. The JNDI lookup occurs by a logical name, or service reference, for the Web service. As with all directory-based resources, a client must declare what resources it needs in its deployment descriptor (more on that later).

The Java Web services specification (JSR 109) recommends that all Web services be subsumed under the JNDI service subcontext. The client container binds the service interface described by that reference under the java:comp/env client environment naming context. By declaring a service reference in the client's deployment descriptor, the client container ensures that the referenced service is available in JNDI-aware resources. The following code snippet shows how to obtain a reference to a J2EE-based Web service via JNDI lookup:

 InitialContext ctx = new InitialContext();
Service myService = (Service)ctx.lookup("java:comp/env/services/MyWebService");

The above code obtains a generic service object: an object without a specific type. A JAX-RPC-generated service is accessed the same way, this time casting the service to the specific Web service's interface type:

 InitialContext ctx = new InitialContext();
MyWebService myService =
  (MyWebService)ctx.lookup("java:/comp/env/services/MyWebService");

Note that this code assumes that the MyWebService reference binds to an object that implements the MyWebService interface. Since service-binding is facilitated at a Web service's deployment time, J2EE tools are expected to ensure that consistency. All J2EE 1.4-compliant application servers must support JNDI-based service lookup.

Once a client obtains a Web service's Service object, it can use that object to retrieve a javax.xml.rpc.Call instance that performs the actual service invocation. The client has three options to obtain a Call: via a stub, a dynamic service proxy, or a DII (Dynamic Invocation Interface). I won't discuss in this article the differences between those methods since, regardless of how a Call is created, that Call refers directly back to the service's port—the only object the client must be aware of when invoking the Web service. All J2EE 1.4-compliant containers must support the Service interface methods, and thus allow a client to gain a reference to a Call object for a Web service, and to that service's port, via Call.

Note that in contrast to using JAX-RPC outside J2EE, a client should not use the JAX-RPC ServiceFactory class to obtain a new service. Instead, the client should gain access to the Service from a JNDI-based source, since reference to a service retrieved from JNDI will have all the settings and configurations necessary to invoke the particular service instance. From a client's viewpoint, that difference is somewhat analogous to how a J2EE client retrieves a JDBC DataSource via the JNDI interface to access a database, instead of manually configuring a JDBC Connection instance.

With that Call object in place, the client follows the JAX-RPC semantics of remote procedure calling. For instance, the client might use the invoke() method on that Call to interact with the Web service. (For an example of JAX-RPC-style service invocation, see "I Like Your Type: Describe and Invoke Web Services Based on Service Type" (JavaWorld, September 2002).)

The Web service server programming model

A J2EE-based Web service may follow one of two possible implementations: If the service is implemented as a regular Java class, it must conform to the JAX-RPC servlet container's requirements. Or, if the service is defined to execute in the EJB container, then it must follow the programming model required of stateless EJB session beans. Regardless of implementation method, each container provides the Web service implementation with lifecycle support, concurrency management, and a security infrastructure.

The J2EE server container's primary responsibility is to map and dispatch SOAP requests, in the EJB case, to stateless session beans, and, in the servlet container case, to methods in JAX-RPC service endpoint classes. While the JAX-RPC specification defines a programming model for the latter option, the J2EE Web services JSR (JSR 109) outlines an analogous model for stateless EJB session beans.

Both the servlet and EJB methods focus on defining a port entity. Each port has an SEI and a service implementation bean (SIB). A Web service's SEI results from the mapping of the Web service WSDL document's portType element to a Java representation and binding that port to the WSDL following the JAX-RPC mapping rules. A WSDL port, in essence, defines the address where a service can be contacted. The SIB, in turn, is an object that implements the methods defined by SEI. That implementation, in turn, must follow the rules for the container the SIB object runs in.

Thus, conceptually, a port pairs a service's network address with the implementation object waiting to handle incoming requests. Since a client obtains a reference to a port via a service interface (see above), that client may remain ignorant of either the service's network address or of the bean that implements the service, as the port hides all that information. In other words, the port concept offers a container-level abstraction for a Web service. Figure 3 illustrates the relationship of ports, the SEI, and the SIB.

Figure 3. Ports, SEIs, and SIBs

A port's lifecycle is governed by the container it runs in. The container creates, initializes, destroys, or recreates the port. Since a client's view of the port is a stateless object, the client is unaware of a container's port management actions. For instance, a container may decide to destroy and recreate a port across a single client's invocations. In many cases, implementation beans are shared among ports, allowing one implementation object to service incoming requests from many network addresses.

If an implementation runs inside the EJB container, then it must follow the stateless EJB rules. Specifically, it must:

  • Define a public, no-arg constructor.
  • Be public, not final, and not static.
  • Implement EJB lifecycle-related methods, ejbCreate() and ejbRemove(). In practice, these are often just empty method declarations, since the bean has no persistent state to be concerned about.
  • Not provide a finalize() method.
  • Implement the javax.ejb.SessionBean interface.

In addition to regular EJB requirements, a Web service implementation EJB must also:

  • Implement all the service SEI's method signatures (however, there is no requirement to implement the interface itself)
  • Save no persistent state across method calls
  • The methods defined from the SEI interface must not include Mandatory transaction attributes

As for Web services running inside the J2EE servlet container, the Java Web services specification extends the JAX-RPC programming model and requires an implementation object to do the following:

  • Declare a public, no-arg constructor.
  • Not save persistent state across method calls (must be stateless).
  • Implement all the SEI's methods. There is no requirement to implement that interface itself.
  • Not define a finalize() method.
  • Declare an implementation class as public, not static, and not final.
  • Implement the optional javax.xml.rpc.server.ServiceLifeCycle interface.

Both the EJB and servlet container-based Web service implementations follow the lifecycle of their respective environments.

Web service-related deployment descriptors

Web services not only extend J2EE's programming models, but also the responsibilities of the traditional J2EE roles. The most significant additional tasks occur at deployment time. First, when a new service deploys, that service may also be published in Web service registries via the JAXR API. I do not cover that task here, since J2EE tool vendors will likely automate that publishing process. Second, a set of new deployment descriptors must be specified for both the client and service implementations.

When deploying a Web service, you must provide not only the classes that offer the service's implementation, but also the WSDL file describing the service as well as the deployment descriptors that map the WSDL file's elements to components in your implementation. The service classes, the WSDL file(s), and the Web service-specific deployment descriptors must all be packaged together when deploying your Web service.

The set of Web service implementations that you wish to deploy in either the EJB or servlet containers are defined in a webservices.xml deployment descriptor file. With an EJB-based implementation, that file must be packaged in the EJB jar file's META-INF/webservices.xml. For servlet-based implementations, the Web services descriptor must be at WEB-INF/webservices.xml.

The webservice.xml file's main role is to map the WSDL port entities to a J2EE container's Port. To facilitate that mapping, the webservices.xml file contains the following document elements:

  • component-name: A logical name for the port that is not required to be the same as the port name in the WSDL file.
  • service-impl-bean: Specifies the SIB class (the class that implements the Web service's methods).
  • service-endpoint-interface: The SEI's fully qualified name.
  • wsdl-file: The location of the WSDL document specifying the Web service. The location is relative to the root of the J2EE module.
  • wsdl-port: A QName inside the WSDL file that denotes the WSDL port element.
  • jaxrpc-mapping-file: This element points to a file that, in turn, outlines the mapping of the WSDL file to the JAX-RPC interfaces.

When deploying a Web service client in the J2EE client container, you must provide a webservicesclient.xml client-side deployment descriptor. That file defines references to Web services used by the client, referring to the Web service's logical name (see the webservices.xml file above). For each service the client wishes to use, it must declare a service-ref element in the webservicesclient.xml file with the following mandatory subelements:

  • The component-name element denotes the service's logical name. JNDI lookups occur based on that name.
  • service-interface denotes the name of the class that a JNDI lookup returns following a name-based query.
  • The component-ref element defines the ports the client needs to access the Web services.
  • A component-scoped-refs element must encase the component-ref element in cases when the service reference refers to an EJB jar file.

Ease into Web services development with J2EE

Perhaps the best news about writing a J2EE-based Web service is that you don't need to learn new programming techniques or tools. Rather, you can use your existing J2EE experience and expose your EJB components or servlets as Web services without making programming changes to your existing code. J2EE 1.4-compliant containers ensure that your existing components are available as Web services on the network.

Most of the Java Web services specifications were written with the intent to introduce as few changes as possible to existing J2EE implementations. Indeed, several J2EE 1.3-compliant application servers provide some form of Web service support already. However, the new Web service-related J2EE 1.4 specifications usher in a unified model for Java Web service development and deployment. As that model becomes prevalent in next-generation J2EE products, Java-based Web service implementations will be portable across application servers and, hence, across deployment platforms.

Frank Sommers is founder and CEO of Autospaces, a company focused on bringing Jini technology and Web services to the automotive software market. He is also editor in chief of the Newsletter of the IEEE Task Force on Cluster Computing.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies