Version 2

    EJB Server Side Programming Model

     

    The EJB server side programming model is about exposing Stateless Session Beans (SLSB) as service endpoints.

    According to the spec, Stateful Session Beans, Message Driven Beans, or Entity Beans cannot be exposed as

    Web Services. The EJB service endpoint is deployed as a standard ejb archive plus an additional

    webservices.xml deployment descriptor.

     

    Service Endpoint Interface

     

    The EJB-2.1 specification introduces a new type of interface for a SLSB, the service endpoint interface (SEI).

    The SEI declares the endpoint implementation methods that are to be exposed as Web Service operations.

     

     

     

    I would like to discuss the concepts using the example of a Crime Portal, which you might be familiar with

    from the JBoss Advanced Training.

     

    In times of a recession the boss' of various gangster organizations, decide to expose some information about their

    organizations using a Web Service. When a potential customer needs to get a job done, the boss' hope that the customer

    will find this portal useful when choosing between the various organizations.

     

     

     

    The gangster CTO Roberto Amazzo decides to expose the following information about an organization.

     

     

     

       package com.underworld.crimeportal;
    
       public interface OrganizationEndpoint extends java.rmi.Remote
       {
          public String getContactInfo(String organization) throws java.rmi.RemoteException;
       }
    

     

     

    Because webservices are all about interoperability, we need a description language that tells the

    (possibly non java) world about our new webservice. This language is WSDL, the webservice description language.

     

    Web Service Description Language

     

    To describe the functionality of a service endpoint, we use an interoperable document format, the WSDL.

    The Java Web Service Development Pack comes

    with the tools needed to generate the WSDL from the service endpoint interface.

     

     

     

    To generate a WSDL that uses rpc/literal, you would use something similar to:

       wscompile -cp ../../output/classes -gen:server -f:rpcliteral -d META-INF/wsdl -mapping META-INF/OrganizationServiceEJB_Mapping.xml config.xml
    
       <configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
        <service name="OrganizationService"
          targetNamespace="http://com.underworld.crimeportal"
          typeNamespace="http://com.underworld.crimeportal/types"
          packageName="com.underworld.crimeportal">
          <interface name="com.underworld.crimeportal.OrganizationEndpoint"></interface>
        </service>
       </configuration>
    

     

    The options to wscompile are described as part

    of the jaxrpc documentation that comes with the JWSDP.

     

    The webservice  tutorial is also a good starting point.

     

     

     

     

    Here is a WSDL, that corresponds to the service endpoint interface from above.

     

     

      <definitions name="OrganizationServiceEJB" targetNamespace="http://com.underworld.crimeportal"
         xmlns:tns="http://com.underworld.crimeportal" xmlns="http://schemas.xmlsoap.org/wsdl/"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
    
        <types></types>
    
        <message name="OrganizationEndpoint_getContactInfo">
          <part name="String_1" type="xsd:string"></part>
        </message>
        <message name="OrganizationEndpoint_getContactInfoResponse">
          <part name="result" type="xsd:string"></part>
        </message>
    
        <portType name="OrganizationEndpoint">
          <operation name="getContactInfo" parameterOrder="String_1">
            <input message="tns:OrganizationEndpoint_getContactInfo"/>
            <output message="tns:OrganizationEndpoint_getContactInfoResponse"></output>
          </operation>
        </portType>
    
        <binding name="OrganizationEndpointBinding" type="tns:OrganizationEndpoint">
          <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"></soap:binding>
          <operation name="getContactInfo">
            <soap:operation soapAction=""></soap:operation>
            <input>
              <soap:body use="literal" namespace="http://com.underworld.crimeportal"></soap:body>
            </input>
            <output>
              <soap:body use="literal" namespace="http://com.underworld.crimeportal"></soap:body>
            </output>
          </operation>
        </binding>
    
        <service name="OrganizationServiceEJB">
          <port name="OrganizationEndpointPort" binding="tns:OrganizationEndpointBinding">
            <soap:address location="REPLACE_WITH_ACTUAL_URL"></soap:address>
          </port>
        </service>
      </definitions>
    

     

     

     

    Note, the server side WSDL may define a dummy webservice location. JBoss will replace the location attribute

    after successful deployment.

     

    JAX-RPC Mapping Files

     

    In addition to a service endpoint interface and a WSDL file, the Web Service must include a JAX-RPC mapping file.

    It maps endpoint interfaces, methods, and parameters to WSDL portType, operation, and message definitions.

    It also maps a JAX-RPC service interface to a particular WSDL service definition.

     

     

    JAX-RPC mapping can get quite complicated, but Roberto opts for simplicity and only provides mapping for the java

    package name. Remember WSDL is a portable format and therefore has no notion of the java package structure.

    Mr. Amazzos simplistic JAX-RPC mapping file would look like this.

     

       <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.underworld.crimeportal</package-type>
           <namespaceURI>http://com.underworld.crimeportal</namespaceURI>
         </package-mapping>
    
       </java-wsdl-mapping>
    

     

    Note, the mapping file generated by wscompile is more complete.

     

    Packaging the service endpoint

     

    The standard deployment descriptor for EJB components ejb-jar.xml, declares the service endpoint.

     

       <ejb-jar 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://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"
         version="2.1">
         <enterprise-beans>
           <session>
             <ejb-name>OrganizationBean</ejb-name>
             <service-endpoint>com.underworld.crimeportal.OrganizationEndpoint</service-endpoint>
             <ejb-class>com.underworld.crimeportal.ejb.OrganizationSLSB</ejb-class>
             <session-type>Stateless</session-type>
             <transaction-type>Container</transaction-type>
           </session>
         </enterprise-beans>
       </ejb-jar>
    

     

    Additionally, we need the J2EE Web Service deployment descriptor, webservices.xml

     

       <webservices xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:impl="http://com.underworld.crimeportal/ws4ee"
         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_web_services_1_1.xsd"
         version="1.1">
    
         <webservice-description>
           <webservice-description-name>OrganizationServiceEJB</webservice-description-name>
           <wsdl-file>META-INF/wsdl/OrganizationServiceEJB.wsdl</wsdl-file>
           <jaxrpc-mapping-file>META-INF/OrganizationServiceEJB_Mapping.xml</jaxrpc-mapping-file>
           <port-component>
             <port-component-name>Organization</port-component-name>
             <wsdl-port>OrganizationEndpointPort</wsdl-port>
             <service-endpoint-interface>com.underworld.crimeportal.OrganizationEndpoint</service-endpoint-interface>
             <service-impl-bean>
               <ejb-link>OrganizationSLSB</ejb-link>
             </service-impl-bean>
           </port-component>
         </webservice-description>
       </webservices>
    

     

    Deploy the Web Service to JBoss

     

    When you deploy the jar that contains all the artifacts we just taked about, you should see something like this

    in the JBoss console

     

       14:49:49,656 INFO  [EjbModule] Deploying OrganizationSLSB
       14:49:49,906 INFO  [EJBDeployer] Deployed: file:/.../server/default/deploy/samples-server-ejb.jar
       14:49:49,921 INFO  [WSDLFilePublisher] WSDL published to: file:/.../server/default/data/wsdl/samples-server-ejb.jar/OrganizationServiceEJB.wsdl
       14:49:50,062 INFO  [AxisService] WSDD published to: ...\server\default\data\wsdl\samples-server-ejb.jar\Organization.wsdd
       14:49:50,062 INFO  [AxisService] Web Service deployed: http://TDDELL:8080/samples-server-ejb/Organization
    

     

    To see the WSDL that describes this service, point your browser to

     

       http://localhost:8080/samples-server-ejb/OrganizationServiceEJB?wsdl
    

     

    you will see the WSDl from above, with a corrected location.

     

    Accessing the Service from Java

     

    This service can now be accessed from any webservice client. Here is a simple test case, that shows how a java client

    would first obtain the WSDL from the remote location, and then invoke getContactInfo on the service endpoint

    interface.

     

       public void testWebServiceAccess() throws Exception
       {
          URL url = new URL("http://localhost:8080/samples-server-ejb/Organization?wsdl");
          QName serviceName = new QName("http://com.underworld.crimeportal", "OrganizationServiceEJB");
    
          ServiceFactory factory = ServiceFactory.newInstance();
          Service service = factory.createService(url, serviceName);
          
          OrganizationEndpoint endpoint = (OrganizationEndpoint)service.getPort(OrganizationEndpoint.class);
          String info = endpoint.getContactInfo("mafia");
          assertEquals("The 'mafia' boss is currently out of office, please call again.", info);
       }