Chapter 15. EJB 2.1 and Web Services

Support for web services in EJB 2.1 is based on the web services for the J2EE 1.1 (WS-J2EE) specification. This specification includes the Java API for XML-based RPC (JAX-RPC), SOAP with Attachments API for Java (SAAJ), and the Java API for XML Registries (JAXR). JAX-RPC is basically Java RMI over SOAP; SAAJ is an API for manipulating the structure of a SOAP message; and JAXR allows you to access web service registries, usually UDDI (Universal Description, Discovery and Integration).

While this chapter and the one before it provide you with a launching pad for learning about web services in J2EE (specifically EJB), the subject is too huge to cover in a book about EJB. In order to cover J2EE web services comprehensively we would have needed another 500 pages—since you’ll need to lift this book to read it, I wrote a lighter approach to the subject. This chapter provides you with an introduction to JAX-RPC, but it should not be considered a comprehensive guide to the API.

If you are interested in learning more about the standard web services technologies (XML, SOAP 1.1, WSDL, and UDDI) and J2EE APIs (JAX-RPC, SAAJ, and JAXR), you might want to read J2EE Web Services (Addison-Wesley) by the author of this book, for a complete and thorough coverage of these topics.

The main purpose of JAX-RPC is to describe the relationship between WSDL 1.1, XML, SOAP 1.1, and Java. JAX-RPC provides EJB with a client-side programming model for accessing remote web services, as well as a server-side programming model for deploying EJBs as web services.

Accessing Web Services with JAX-RPC

JAX-RPC provides a client-side programming model based on Java RMI that allows you to access web services on other platforms from your EJBs. In other words, by using JAX-RPC, EJBs can access web services across the network hosted on Java and non-Java platforms (Perl, .NET, C++, and so on) alike. There are three APIs for accessing web services: generated stubs, dynamic proxies, and the DII (Dynamic Invocation Interface). Of these three APIs, the one you are most likely to use is the Generated Stubs programming model, which is the primary focus of this chapter.

Generated stubs are based on the classic Java RMI programming model, where the client accesses a remote service via a Java RMI remote interface implemented by a network stub. The stub translates calls made on the remote interface into network messages sent to the remote service. It’s pretty much the same as using an EJB remote reference, except the protocol is SOAP over HTTP rather than CORBA IIOP. Figure 15-1 illustrates the RMI loop executed with a JAX-RPC generated stub.

The JAX-RPC RMI Loop

Figure 15-1. The JAX-RPC RMI Loop

The RMI loop in JAX-RPC is basically the same as any other RMI loop. In step 1, the client invokes a method on the JAX-RPC generated stub. The method invocation is transformed into a SOAP message that is sent to the server in step 2. In step 3 the web service process the request and send the results back as a SOAP response message in step 4. In step 5, the SOAP response messages is transformed into either a return value or an exception (if it was a SOAP Fault) and returned to the client.

Generating JAX-RPC Stubs from WSDL

Generated stubs get their name because the remote interface, called an endpoint interface , and the network stub are generated at deployment time. A JAX-RPC-compliant compiler generates the endpoint interface and stub from a WSDL document. The WSDL <portType> is used to create an endpoint interface, while the WSDL <binding> and <port> definitions are used to create the stub. The WSDL document is provided by the organization that hosts the web service. The JAX-RPC compiler reads the WSDL document and translates it into an endpoint interface and stub that you can use at runtime to send and receive SOAP messages.

Imagine that Titan Cruises subcontracts a company, Charge-It, Inc., to process payments made by customers using credit cards. Charge-It runs a system based on .NET and exposes its credit card processing application to clients via a web service. The web service is described by a WSDL document. The WSDL document for Charge-It’s web service looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://charge-it.com/Processor"
    targetNamespace="http://charge-it.com/Processor">

<message name="chargeRequest">
  <part name="name" type="xsd:string"/>
  <part name="number" type="xsd:string"/>
  <part name="exp-date" type="xsd:dateTime"/>
  <part name="card-type" type="xsd:string"/>
  <part name="amount" type="xsd:float"/>
</message>
<message name="chargeResponse">
  <part name="return" type="xsd:int"/>
</message>
<portType name="Processor">
  <operation name="charge">
    <input message="tns:chargeRequest"/>
    <output message="tns:chargeResponse"/>
  </operation>
</portType>
<binding name="ProcessorSoapBinding" type="tns:Processor">
  <soap:binding style="rpc"
      transport="http://schemas.xmlsoap.org/soap/http"/>
  <operation name="charge">
    <soap:operation soapAction="" style="rpc"/>
    <input>
      <soap:body use="literal" 
          namespace="http://charge-it.com/Processor"/>
    </input>
    <output>
      <soap:body use="literal" 
          namespace="http://charge-it.com/Processor"/>
    </output>
  </operation>
</binding>
<service name="ProcessorService">
   <port name="ProcessorPort" binding="tns:ProcessorSoapBinding">
      <soap:address  
       location="http://www.charge-it.com/ProcessorService"/>
   </port>
</service>
</definitions>

The endpoint interface is based on the WSDL <portType> and its corresponding <message> definitions. Based on these definitions, a JAX-RPC compiler would generate the following interface:

package com.charge_it;

public interface Processor extends java.rmi.Remote {
    public int charge(String name, String number, java.util.Calendar expDate, 
                      String cardType, float amount) 
    throws java.rmi.RemoteException;
}

An endpoint interface is a Java RMI remote interface that extends the java.rmi.Remote type. Its methods must throw the java.rmi.RemoteException and, optionally, application exceptions. The interface name, method names, parameters, and exceptions are all derived from the WSDL document. Figure 15-2 shows the mapping between the <portType> and <message> definitions and the endpoint interface.

Mapping a WSDL <portType> to a JAX-RPC endpoint interface

Figure 15-2. Mapping a WSDL <portType> to a JAX-RPC endpoint interface

The name of the endpoint interface comes from the name of the <portType>, which is Processor. The methods defined by the endpoint interface are derived from the <operation> elements declared by the WSDL <portType>. In this case, there is one <operation> element, which maps a single method, charge( ). The parameters of the charge( ) method are derived from <operation> element’s input message. For each <part> element of the input message, there will be a corresponding parameter in the charge( ) method. The output message, in this case, declares a single <part> element, which maps to the return type of the charge( ) method.

The JAX-RPC specification defines an exact mapping between many of the XML Schema built-in types and Java. This is how the XML Schema types declared by the WSDL <part> elements are mapped to the parameters and the return type of an endpoint method. Table 15-1 shows the mapping between XML Schema built-in types and Java primitives and classes.

Table 15-1. XML Schema built-in types and their corresponding Java types

XML Schema built-in type

Java type

xsd:byte

byte

xsd:boolean

boolean

xsd:short

short

xsd:int

int

xsd:long

long

xsd:float

float

xsd:double

double

xsd:string

java.lang.String

xsd:dateTime

java.util.Calendar

xsd:integer

java.math.BigInteger

xsd:decimal

java.math.BigDecimal

xsd:QName

java.xml.namespace.QName

xsd:base64Binary

byte [ ]

xsd:hexBinary

byte [ ]

JAX-RPC also maps nillable types (types that can be null), based on XML Schema built-in types, to Java primitive wrappers. For example, a nillable xsd:int type would map to a java.lang.Integer type and a nillable xsd:double would map to a java.lang.Long type.

In addition, JAX-RPC defines a mapping between complex types defined in the WSDL <types> element and Java bean classes. Complex types are addressed later in this chapter.

The stub, which implements the endpoint interface, is generated from the <binding> and <port> definitions. The JAX-RPC compiler translates the messaging style specified by the <binding> definition into a marshalling algorithm for converting method calls made on the endpoint stub into SOAP request and reply messages. Charge-It’s WSDL document defines the following <binding> element:

<binding name="ProcessorSoapBinding" type="tns:Processor">
  <soap:binding style="rpc"
      transport="http://schemas.xmlsoap.org/soap/http"/>
  <operation name="charge">
    <soap:operation soapAction="" style="rpc"/>
    <input>
      <soap:body use="literal" 
          namespace="http://charge-it.com/Processor"/>
    </input>
    <output>
      <soap:body use="literal" 
          namespace="http://charge-it.com/Processor"/>
    </output>
  </operation>
</binding>

According to the <binding> element, the web service employs RPC/Literal SOAP 1.1 messages with a request-response style operation. When the JAX-RPC compiler reads this <binding>, it generates a corresponding stub that implements the endpoint interface. The stub is responsible for converting method calls made on the endpoint interface into SOAP messages sent to the web service. It’s also responsible for converting SOAP response messages sent back to the stub into a return value—or, if it’s a SOAP fault message, into an exception thrown by the endpoint method.

The stub is also based on a particular <port> definition, which declares the Internet address where the web service is located. The Charge-It WSDL document defines the following <port> element:

<service name="ProcessorService">
   <port name="ProcessorPort" binding="tns:ProcessorSoapBinding">
                  <soap:address  
                  location="http://www.charge-it.com/ProcessorService"/>
                  </port>
</service>

Based on this <port> definition, the JAX-RPC compiler generates the stub that exchanges SOAP messages with the URL indicated by the address attribute (http://www.charge-it.com/ProcessorService). Figure 15-3 illustrates how the Processor endpoint interface and stub are used to access the Charge-It credit card processing web service.

The JAX-RPC RMI loop for the Charge-It web service

Figure 15-3. The JAX-RPC RMI loop for the Charge-It web service

In addition to the endpoint interface and its stub, the JAX-RPC compiler also creates a Service interface, which is used to get an instance of the generated stub at runtime. The Service interface is based on the <service> element of the WSDL document and declares methods for obtaining a live endpoint stub. Here’s the definition of the ProcessorService interface generated from Charge-It’s WSDL document:

package com.charge_it;

public interface ProcessorService extends javax.xml.rpc.Service {
   public com.charge_it.Processor getProcessorPort( ) 
      throws javax.xml.rpc.ServiceException;
   public java.lang.String getProcessorPortAddress( );
   public com.charge_it.Processor getProcessorPort(java.net.URL portAddress) 
      throws javax.xml.rpc.ServiceException;
}

The getProcessorPort( ) method returns a live endpoint stub that is ready to invoke methods on the web service. The getProcessPortAddress( ) method returns the URL that the stub accesses by default. The getProcessorPort(URL) method allows you to create an endpoint stub that accesses a URL that is different from the default URL defined in the WSDL document.

The JAX-RPC compiler also generates a class that implements the Service interface. This class is tightly bound to the EJB Container system and manufactures endpoint stubs at runtime.

Using JAX-RPC Generated Stubs

Just like other resources (JDBC, JMS, and so on) the JAX-RPC Service is bound to a specific namespace in the JNDI ENC at deployment time. To get a reference to a stub at runtime, therefore, the EJB requests a specific JAX-RPC Service from the JNDI ENC. The stub is then used to execute operations on the remote web service.

To illustrate how stubs are used by EJBs, we will modify the bookPassage( ) method of the TravelAgentBean defined in Chapter 11. Instead of using the ProcessPayment EJB to process credit cards, the TravelAgent EJB will use the Charge-It’s Processor web service. The following code shows the changes to the TravelAgentBean class:

package com.titan.travelagent;
import com.charge_it.ProcessorService;
                  import com.charge_it.Processor;
...
public class TravelAgentBean implements javax.ejb.SessionBean {
    public CustomerRemote customer;
    public CruiseLocal cruise;
    public CabinLocal cabin;
    public javax.naming.Context jndiContext;
    ...
    public TicketDO bookPassage(CreditCardDO card, double price)
        throws IncompleteConversationalState {
                   
        if (customer == null || cruise == null || cabin == null) 
        {
            throw new IncompleteConversationalState( );
        }
        try {
            ReservationHomeLocal resHome = (ReservationHomeLocal)            
                jndiContext.lookup("java:comp/env/ejb/ReservationHomeLocal");
             
            ReservationLocal reservation =
                resHome.create(customer, cruise, cabin, price, new Date( ));
                
            ProcessorService webService = (ProcessorService) jndiContext.lookup(
                                  "java:comp/env/service/ChargeItProcessorService");

                              Processor endpointStub = webService.getProcessorPort( );

            String customerName = customer.getFirstName( )+" "+
                                  customer.getLastName( );
            java.util.Calandar expDate = new Calandar(card.date);
 
            endpointStub.charge(customerName, card.number, 
                                                  expDate, card.type, price);

            TicketDO ticket = new TicketDO(customer, cruise, cabin, price);
            return ticket;
        } catch(Exception e) {
            throw new EJBException(e);
        }
    }
    ...
}

As you can see, the EJB uses the JAX-RPC endpoint stub much like it would any other resource. It obtains a reference to a resource factory from the JNDI ENC, uses that to obtain the stub, and uses the stub to invoke operations on the web service—in this case, Charge-It’s Processor web service.

The stub, however, presents some problems in a transactional environment. If the stub encounters a networking problem or SOAP processing error, it throws a JAXRPCException, which is caught and rethrown as an EJBException, causing the entire transaction to roll back. However, if an error occurs after the web service has executed but before the EJB method successfully returns, a partial rollback occurs: the reservation would be rolled back, but the charge made using the Charge-It web service would not.

The <service-ref> Deployment Element

EJB 2.1 includes a new element, <service-ref>, which binds a JAX-RPC Service to the JNDI ENC. The modified TravelAgent EJB declares a <service-ref> element that looks like this:

<?xml version='1.0' encoding='UTF-8' ?>
<ejb-jar 
 xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:chargeIt="http://charge-it.com/Processor"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                    http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"
 version="2.1">
   <enterprise-beans>
     <session>
       <ejb-name>TravelAgentEJB</ejb-name>
       ...
       <service-ref>
                  <service-ref-name>service/ChargeItProcessorService</service-ref-name>
                  <service-interface>com.charge_it.ProcessorService</service-interface>
                  <wsdl-file>META-INF/wsdl/ChargeItProcessor.wsdl</wsdl-file>
                  <jaxrpc-mapping-file>META-INF/mapping.xml</jaxrpc-mapping-file>
                  <service-qname>chargeIt:ProcessorService</service-qname>
                         </service-ref>
      ...
    </session>
  </enterprise-beans>
  ...
</ejb-jar>

The <service-ref-name> element declares the name of the JAX-RPC Service in the JNDI ENC—it’s always relative to the "java:comp/env" context. The <service-interface> identifies the JAX-RPC Service interface, which is implemented by a JAX-RPC service object. The <wsdl-file> identifies the location of the WSDL document that describes the Charge-It web service. The WSDL document must be packaged in the same EJB-JAR file as the EJB that is making the web service call. The path is always relative to the root of the EJB-JAR file. In this case, a copy of the Charge-It WSDL document, ChargeItProcessor.wsdl, is stored in the META-INF directory of the EJB-JAR file. The <jaxrpc-mapping-file> identifies the location of the JAX-RPC mapping file relative to the root of the EJB-JAR file. In this case, it’s also located in the META-INF directory. (The JAX-RPC mapping file is an additional deployment file that helps the EJB container system understand the mapping between the WSDL document and the endpoint service interfaces.) The <service-qname> identifies the fully qualified XML name of the WSDL <service> definition to which this reference pertains. The qualified service name is relative to the WSDL document identified by the <wsdl-file> element.

The JAX-RPC Mapping File

A JAX-RPC mapping file is required if an EJB is to use JAX-RPC to access web services. The mapping file conforms to a specific XML Schema defined by the Web Services for J2EE 1.1 specification. This file helps the deployment tools and EJB container understand the relationship between a JAX-RPC service and endpoint interfaces, and their corresponding WSDL document, allowing the deployment tools to generate a proper stub: one that uses the correct protocols and messaging modes.

At a bare minimum, the JAX-RPC mapping file must specify the mapping between the WSDL XML namespace of a <service> element and a Java package name. Example 15-1 is a perfectly legal JAX-RPC mapping file for the <service-ref> used by the TravelAgent EJB.

Example 15-1. EJB 2.1: Lightweight JAX-RPC mapping file

<?xml version='1.0' encoding='UTF-8' ?>
<java-wsdl-mapping
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
               http://www.ibm.com/webservices/xsd/j2ee_jaxrpc_mapping_1_1.xsd"
version="1.1">
 <package-mapping>
    <package-type>com.charge_it</package-type>
    <namespaceURI>http://charge-it.com/Processor</namespaceURI>
  </package-mapping>
</java-wsdl-mapping>

The JAX-RPC mapping shown in the previous listing is as simple as it gets. Only under very specific conditions can a JAX-RPC mapping file be this simple; the TravelAgent EJB happens to use a web service that qualifies. Here’s a brief list of the attributes a WSDL document must have in order to qualify for a package-only JAX-RPC mapping file:

  1. It has only one <service> element, which contains one <port> element.

  2. The <service>, <binding>, <portType>, and all custom XML types (complexType and simpleType) have unique names.

  3. The <binding> definition uses the RPC messaging style (style="rpc“) and SOAP 1.1 Encoding (encodingStyle="http://schemas.xmlsoap.org/soap/encoding/“) for all input, output, and fault message parts.

  4. No header blocks or header faults are specified in the <binding> definition; the parts attribute of input and output elements must be omitted or, if the parts attribute is declared, it must list all parts.

  5. Each <operation> within a <portType> definition must:

    • Have a unique name.

    • Include exactly one <input> element, zero or one <output> elements, and zero or more <fault> elements.

    • Omit the parameterOrder attribute. If the parameterOrder is declared, the <operation> must specify all parts from the input message in the order they are originally declared in the corresponding <message> definition.

  6. A fault <message> definition has one part named "message" of type "xsd:string“.

  7. The input <message> definition may declare zero or more <part> elements, and the output <message> definition may declare zero or one <part> elements.

  8. Every <part> definition is defined with a name attribute and a type attribute; the element attribute is not used. The type attribute may be one of the following:

    • A standard XML Schema built-in type

    • An XML Schema-based complex type, which uses either the xsd:sequence or xsd:all compositor and can be easily mapped to Java beans according to the JAX-RPC specifications

    • A WSDL-restricted SOAP Encoded array

The ChargeItProcessor.wsdl document meets all these requirements; as a result, it only needs to have a package mapping. It’s not difficult to create WSDL documents that meet these requirements; however, if you are attempting to access a web service defined by someone else, you’re likely to run into WSDL documents that do not adhere to the criteria for a lightweight mapping file. In that case, you’ll have to create a heavyweight mapping file. Example 15-2 is a heavyweight mapping file for the ChargeItProcessor.wsdl document.

Example 15-2. Heavyweight JAX-RPC mapping file

<?xml version='1.0' encoding='UTF-8' ?>
<java-wsdl-mapping
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:chargeIt="http://charge-it.com/Processor" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
            http://www.ibm.com/webservices/xsd/j2ee_jaxrpc_mapping_1_1.xsd"
  version="1.1">

  <package-mapping>
   <package-type>com.charge_it</package-type>
   <namespaceURI>http://charge-it.com/Processor</namespaceURI>
  </package-mapping>
  <service-interface-mapping>
    <service-interface>com.charge_it.ProcessorService</service-interface>
    <wsdl-service-name>chargeIt:ProcessorService</wsdl-service-name>
    <port-mapping>
      <port-name>chargeIt:ProcessorPort</port-name>
      <java-port-name>ProcessorPort</java-port-name>
    </port-mapping>
  </service-interface-mapping>
  <service-endpoint-interface-mapping>
    <service-endpoint-interface>com.charge_it.Processor
    </service-endpoint-interface>
    <wsdl-port-type>chargeIt:Processor</wsdl-port-type>
    <wsdl-binding>chargeIt:ProcessorSoapBinding</wsdl-binding>
    <service-endpoint-method-mapping>
      <java-method-name>charge</java-method-name>
      <wsdl-operation>chargeIt:charge</wsdl-operation>
      <method-param-parts-mapping>
        <param-position>0</param-position>
        <param-type>java.lang.String</param-type>
        <wsdl-message-mapping>
          <wsdl-message>chargeIt:chargeRequest</wsdl-message>
          <wsdl-message-part-name>name</wsdl-message-part-name>
          <parameter-mode>IN</parameter-mode>
        </wsdl-message-mapping>
      </method-param-parts-mapping>
      <method-param-parts-mapping>
        <param-position>1</param-position>
        <param-type>java.lang.String</param-type>
        <wsdl-message-mapping>
          <wsdl-message>chargeIt:chargeRequest</wsdl-message>
          <wsdl-message-part-name>number</wsdl-message-part-name>
          <parameter-mode>IN</parameter-mode>
        </wsdl-message-mapping>
      </method-param-parts-mapping>
      <method-param-parts-mapping>
        <param-position>2</param-position>
        <param-type>java.util.Calandar</param-type>
        <wsdl-message-mapping>
          <wsdl-message>chargeIt:chargeRequest</wsdl-message>
          <wsdl-message-part-name>exp-date</wsdl-message-part-name>
          <parameter-mode>IN</parameter-mode>
        </wsdl-message-mapping>
      </method-param-parts-mapping>
      <method-param-parts-mapping>
        <param-position>3</param-position>
        <param-type>java.lang.String</param-type>
        <wsdl-message-mapping>
          <wsdl-message>chargeIt:chargeRequest</wsdl-message>
          <wsdl-message-part-name>card-type</wsdl-message-part-name>
          <parameter-mode>IN</parameter-mode>
        </wsdl-message-mapping>
      </method-param-parts-mapping>
      <method-param-parts-mapping>
        <param-position>4</param-position>
        <param-type>float</param-type>
        <wsdl-message-mapping>
          <wsdl-message>chargeIt:chargeRequest</wsdl-message>
          <wsdl-message-part-name>amount</wsdl-message-part-name>
          <parameter-mode>IN</parameter-mode>
        </wsdl-message-mapping>
      </method-param-parts-mapping>
      <wsdl-return-value-mapping>
        <method-return-value>int</method-return-value>
        <wsdl-message>chargeIt:chargeResponse</wsdl-message>
        <wsdl-message-part-name>return</wsdl-message-part-name>
      </wsdl-return-value-mapping>
    </service-endpoint-method-mapping>
  </service-endpoint-interface-mapping>
</java-wsdl-mapping>

The complete JAX-RPC mapping file is too complicated to discuss in detail. Suffice it to say, the heavyweight mapping file is complex and provides elements for mapping every aspect of the service and endpoint interfaces to a WSDL document. The service interface is mapped to a WSDL <service> element, the endpoint interface is mapped to a WSDL <portType>, each method is mapped to a WSDL <operation>, and every parameter and return value is mapped to a specific WSDL <part> of a specific WSDL <message> definition.

It seems to me that a JAX-RPC compiler should be able to interpret a far broader set of WSDL definitions than the very narrow criteria required for a lightweight mapping. The Web Services for J2EE specification requires a complete mapping for any JAX-RPC resource that strays even a little from the minimum criteria for a lightweight mapping. In my opinion, the criteria should be broadened. Only the nonconforming aspects of the WSDL document should be mapped; conforming elements should not require documentation in the mapping file.

Exercise 15.1 in the Workbook shows how to deploy these examples.

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

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