Exposing EJBs Through Web Service Protocols

The types of application functionality exposed through Web Service protocols will be many and varied. The variety of such applications will match, if not exceed, the variety of browser-based applications found on the Web. This means that Web Service applications will have a variety of requirements for such systemic qualities as scalability, availability, performance, and so on. Essentially, Web Service applications are just like any other application. Because of their requirements for scalability and availability, the functionality behind many Java-based Web Services will be implemented using EJBs. What is needed then is a simple way to expose this functionality while keeping overhead to the minimum.

EJBs as Web Service Facades

On Day 18, the role of the facade pattern was discussed as a way of reducing network traffic and creating a simpler, coarse-grained interface from a set of fine-grained interfaces. The use of a Session Facade providing coarse-grained access to entity EJBs has been particularly successful.

As Web Services are intended to be a coarse-grained interface to underlying functionality, the Facade pattern also applies at a Web Service boundary. Indeed, an overarching Session Facade may contain the coarse-grained representation of the business logic implemented by combinations of entity and session EJBs in the application. This overarching Session Facade is ideal to be exposed using Web Service protocols—indeed it may have been created for just such a purpose.

You will now examine an example that uses a stateless session EJB as a Façade for some of the agency functionality from the case study. The façade will provide a list of jobs available at a given location. The stateless session bean will call on underlying entity beans to retrieve the required data. The client will be a standalone Java client just as for the servlet-based Web Service implementation.

Defining the Interface and Implementation

JAX-RPC allows you to expose a stateless session bean so that it can be accessed through Web Service protocols. In developer terms, the main difference between an EJB exposed through JAX-RPC and one exposed through RMI is that you do not provide the same interface files. For an RMI-based EJB, you will provide a home interface and an EJB business interface that extends javax.ejb.EJBObject or javax.ejb.EJBLocalObject. An EJB developed to be exposed as a Web Service does not have a home interface. The relationship between the client and server is different and the client delegates all lookup of services to the proxy as you saw in the GreetingClient class earlier. Similarly, the business interface is defined slightly differently to reflect the different relationship. While the interface for a Web Service EJB is still a Remote interface, it must directly extend java.rmi.Remote rather than one of the EJBObject variants.

NOTE

As it stands, you cannot use the same interface definition for the EJB Remote interface and the Web Service using the tools provided with the J2EE RI (wscompile will generate an error if your Web Service interface extends javax.ejb.EJBObject rather than java.rmi.Remote). This can be seen as a good thing, as it might tempt people to always specify the EJB remote interface as a Web Service interface for the associated EJB. You should not just automatically expose all stateless session EJBs as Web Services. However, if you have a well-crafted, coarse-grained facade implemented as a stateless session EJB, there is no reason you should not expose this as a Web Service.


Listing 20.8 shows an interface that can be used to expose an EJB method that lists the job vacancies registered with the job agency at a particular location. You can see that the interface extends Remote and the findJobsAtLocation() method is labeled as throwing RemoteException. The interface defines a single method that takes a location as a string and returns a string array of job information. Nothing much indicates that this interface is part of an EJB.

Listing 20.8. A Web Service Interface for Agency Functionality (Service.java)
package agency;

import java.rmi.*;

public interface Service extends Remote
{
    public String[] findJobsAtLocation(String location) throws RemoteException;
}

The implementation, on the other hand, looks like a typical session EJB as shown in listing 20.9. Examination of the code reveals that it has the usual lifecycle methods for a stateless session bean and retrieves the local resources it needs in its setSessionContext() method. The only resource it uses is a reference to the home interface of the entity Job EJB.

The bean implements the single business method—findJobsAtLocation()—defined on the Service interface. The implementation of this method uses the findByLocation method on the JobHome interface which returns a list of the jobs at the given location. The method creates a Job EJB instance for each job in the list and calls the getCustomer() and getRef() methods on its JobLocal interface. The strings created from the job information are stored in an array to be returned to the caller.

Listing 20.9. An Enterprise Bean to Underpin a Web Service (ServiceBean.java)
package agency;

import java.util.*;
import java.rmi.*;
import javax.ejb.*;
import javax.naming.* ;

import data.*;

public class ServiceBean implements SessionBean
{
    private JobLocalHome jobHome;

    public String[] findJobsAtLocation(String location)
    {
        String[] jobs = null;
        try
        {
            Collection col = jobHome.findByLocation(location);
            jobs = new String[col.size()];
            Iterator it=col.iterator();
            for (int i=0; i < jobs.length; ++i)
        {
                    JobLocal job = (JobLocal)it.next();
                    jobs[i] = job.getCustomer() + "/" + job.getRef();
            }
        }
        catch (Exception ex)
        {
            // In response to any error just return null
            jobs = null;
        }
        return jobs;
    }

    // EJB methods start here


    public void ejbCreate () throws CreateException {}
    public void ejbActivate(){}

    public void ejbPassivate(){}

    public void ejbRemove(){}

    private SessionContext ctx;

    public void setSessionContext(SessionContext ctx)
    {
        this.ctx = ctx;
        InitialContext ic = null;
        try
        {
            ic = new InitialContext();
            jobHome = (JobLocalHome)ic.lookup("java:comp/env/ejb/JobLocal");
        }
        catch (NamingException ex)
        {
            error("Error looking up java:comp/env/ejb/JobLocal",ex);
            return;
        }
    }

    private void error (String msg, Exception ex)
    {
        String s = "ServiceBean: "+msg + "
" + ex;
        System.out.println(s);
        throw new EJBException(s,ex);
    }
}

The only real difference between this bean implementation and any other stateless session bean is that exceptions are treated differently. When it is exposed through a native RMI interface, the bean implementor can assume that the caller has some way of dealing with any exceptions thrown by the bean, so any errors can be raised as exceptions. However, in Web Service terms, you are less certain how an exception will appear to a client and so it is safer to adopt a less Java-specific stance. In this case, an error is indicated by a null return value. The handling of errors and exceptions in Web Services is discussed later in the section “Web Service Errors and Exceptions.”

Web Service Compiler Configuration File

As with the servlet-based Web Service, you will need to provide additional information in addition to the standard J2EE deployment descriptor for this type of component. In the J2EE Reference Implementation, you again use wscompile to generate this information. The configuration file used to generate the artifacts for the agency Service bean, shown in listing 20.10, is almost identical to that for the servlet-based Web Service, as shown in Listing 20.3 (only the names have changed).

Listing 20.10. wscompile Web Service Configuration File (config-service.xml)
<?xml version="1.0" encoding="UTF-8"?>
<configuration
  xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
  <service
      name="AgencyService"
      targetNamespace="urn:J2EE21Agency"
      typeNamespace="urn:J2EE21AgencyTypes"
      packageName="agency">
      <interface name="agency.Service"/>
  </service>
</configuration>

The wscompile command line is identical to that for the servlet-based Web Service, and the same files will be produced, namely the WSDL description and the mapping.xml file.

As before, the sample code is on the Web site that accompanies this book. As it forms the basis of the exercise, you will find this example in the exercise directory for Day 20. With the code supplied on the accompanying Web site, you can use the following Ant command to build the supplied agency Web Service, which includes the creation of the WSDL and the mapping file:

asant build

The WSDL File

As the interface is different from the greeting service defined earlier, the WSDL generated will also be different. As the findJobsAtLocation() method returns something other than a simple type, a complex type is defined in the types section of the WSDL description:

<types>
    <schema targetNamespace="urn:J2EE21AgencyTypes"
            xmlns:tns="urn:J2EE21AgencyTypes"
            xmlns:soap11-enc="http://schemas.xmlsoap.org/soap/encoding/"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
            xmlns="http://www.w3.org/2001/XMLSchema">
        <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
        <complexType name="ArrayOfstring">
            <complexContent>
                <restriction base="soap11-enc:Array">
                    <attribute ref="soap11-enc:arrayType" wsdl:arrayType="string[]"/>
                </restriction>
            </complexContent>
        </complexType>
    </schema>
</types>

Without going into too much detail, the new type—ArrayOfString— is defined to be an unbounded array of the simple string type. This new type is defined in the scope of the urn:J2EE21Agency namespace specified for types in the wscompile configuration file. This ArrayOfString type can then be used later in the WSDL document to define the return type of the findJobsAtLocation operation:

<message name="Service_findJobsAtLocationResponse">
    <part name="result" type="ns2:ArrayOfstring"/>
</message>

As you can see, the findJobsAtLocation response is defined to contain an ArrayOfString, which is scoped to namespace ns2 (ns2 is defined to be urn:J2EE21Types in the overall definitions element). Other than this, the WSDL document is very similar to the greeting service WSDL described earlier.

The Mapping File

As you would expect, the mapping file is also very similar to that for the greeting service. It contains nothing specific to the service being implemented by an EJB and simply reflects the changes in the interface, such as the use of the array of strings as a return type:

<java-wsdl-mapping version="1.1" ...>
    <service-endpoint-interface-mapping>
    ...
        <service-endpoint-method-mapping>
        ...
            <wsdl-return-value-mapping>
                <method-return-value>java.lang.String[]</method-return-value>
                <wsdl-message xmlns:wsdlMsgNS="urn:J2EE21Agency">
                    wsdlMsgNS:Service_findJobsAtLocationResponse
                </wsdl-message>
                <wsdl-message-part-name>result</wsdl-message-part-name>
            </wsdl-return-value-mapping>
        </service-endpoint-interface-mapping>
    ...
    </service-endpoint-method-mapping>
    ...
</java-wsdl-mapping>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.158.151