Novell is now a part of Micro Focus

Multi-tiered Service-Oriented Systems with JBoss, Part 3

Novell Cool Solutions: Feature
By J. Jeffrey Hanson

Digg This - Slashdot This

Posted: 25 Oct 2005
 

J. Jeffrey Hanson
Chief Architect
eReinsure.com, Inc.
jeff@jeffhanson.com

This is the latest article in a series of articles discussing service-oriented systems development on JBoss 4.0. In the previous article, I discussed techniques for adding access control and authentication to services deployed to a JBoss 4.0 server. You may download the code here

In this article, I will discuss techniques to enhance a service-oriented system on a JBoss 4.0 server with dynamic service discovery.

Table of Contents

Review of the JBoss 4.0 Platform Architecture
Introduction to Web Services for J2EE
JAX-RPC's Role within Web Services for J2EE
Service Definition and Implementation Artifacts
JBoss and Web Services for J2EE
The Application Framework Architecture
The Components for the Service-EJB Infrastructure
Defining the Home Interface for the Service EJB
Defining the Service EJB Interface
Specifying the Service EJB Implementation
Defining the Service Endpoint Interface
Defining the Port Component Handler
Specifying the EJB within the jboss.xml Configuration File
Adding Discovery to the UserProfile Service
Deploying the Service EJB to the JBoss Application Server
Testing the Service EJB
Validating the Service EJB's Deployment
Running the Test Client
Conclusion
For Additional Information

Review of the JBoss 4.0 Platform Architecture

For review, the JBoss Application Server version 4.0 provides a comprehensive platform for developing multi-tiered, service-oriented enterprise systems. JBoss 4.0 is J2EE 1.4 compliant and uses a Java Management Extensions (JMX) -based microkernel as its services foundation. Services in the JBoss 4.0 platform are implemented as plain-old Java Objects (POJOs) and can be instrumented easily using the JBoss JMX framework.

The JBoss 4.0 platform is comprised of four main architectural layers:

  • The JBoss Microkernel Layer: This is the JMX-based microkernel layer. This microkernel facilitates a hot-deployable component model with dynamic class-loading features and lifecycle management.
  • The JBoss Service Layer: This layer enables custom services and provides pre-built JBoss services, such as transaction services, messaging services, security services, etc. Each service is packaged as a hot-deployable component known as a "Service ARchive' (SAR).
  • The JBoss Aspect Layer: In this layer, components are embodied in interceptors which enable the system to add service behavior transparently into any object. In JBoss 4.0, aspect-oriented programming (AOP) techniques are employed along with bytecode engineering in order to facilitate custom behavior within this layer.
  • The JBoss Application Layer: This layer is the where end-user applications and systems are deployed.

The following diagram illustrates the relationships between each of the JBoss 4.0 layers.


Figure 1: The JBoss 4.0 Platform Architecture

Introduction to Web Services for J2EE

With the release of J2EE 1.4, the ability of stateless session beans and plain-old Java objects (POJOs) to be exposed and accessed as Web service endpoints has been realized in a standard manner. The ability for clients to interact with Web services in a standard manner has also been added in J2EE 1.4.

In the J2EE environment, developers rely on the Java API for XML-based RPC (JAX-RPC), SOAP, and WSDL to create Web-service components. Web-service clients use remote procedure calls (RPC) and SOAP as the transport and payload-protocol respectively to interact with Web services in J2EE.

JAX-RPC's Role within Web Services for J2EE

JAX-RPC is based on JSR-101 (Java APIs for XML based RPC) and JSR-109 (Implementing Enterprise Web Services). These two JSRs come together in J2EE 1.4 to define Web Services for J2EE, which is a standard mechanism for developing and deploying Web services in the J2EE environment.

With Web Services for J2EE, a remote procedure call is facilitated by transmitting data across an XML-based protocol such as SOAP. The SOAP specification defines calls and responses that are transmitted as XML messages/documents over HTTP. The unique thing about JAX-RPC is its ability to generate SOAP messages and SOAP-endpoint objects for you.

Server-side JAX-RPC development allows a developer to specify remote procedure calls using standard Java interfaces. JAX-RPC client developers create simple local proxies representing a service, which is then used to invoke method calls on the server-side component. The JAX-RPC runtime system further simplifies Web-service development by automatically generating and parsing SOAP messages.

Service Definition and implementation Artifacts

With Web services for J2EE, the server-side artifacts representing a given Web service are declared within and/or implemented by six different files:

  1. The definition/description of a Web service is declared within a WSDL file
  2. The structural information of the Web service is declared within a webservices.xml file
  3. The service-request mappings and the details of how Java interfaces are mapped to and from WSDL elements are defined in the JAX-RPX mapping file. The name of this file must correspond with the name specified by the jaxrpc-mapping-file element within the webservices.xml file. An example of this is:

  4. <jaxrpc-mapping-file>META-INF/my_mappings.xml</jaxrpc-mapping-file>
  5. The WSDL port is embodied within a Java interface known as the "Service Endpoint Interface" (SEI). The concepts of Endpoints and Service Endpoint Interfaces were introduced by the WSDL 1.1 W3C Note (http://www.w3.org/TR/wsdl). In this note, a service is defined as, "a collections of communication endpoints/ports capable of exchanging messages". The SEI must follow the Java programming language and WSDL mapping rules defined by JAX-RPC.
  6. The logic for a Web service is embodied within a standard POJO that implements all the methods defined in the SEI. For an EJB implementation, these methods must also be a subset of the EJB component's remote interface methods.
  7. A port component handler class provides the implementation for the WSDL file's port element

The WSDL file and the jaxrpc-mapping file can be generated using the wscompile tool that ships as part of the Java Web Services Developer Pack (JWSDP).

JBoss and Web Services for J2EE

JBoss Application Server version 4.0 supports Web Services for J2EE. JBossWS is the JBoss module responsible for providing support for Web Services for J2EE. JBossWS is based on Apache Axis (http://ws.apache.org/axis).

The Application Framework Architecture

The application framework used for this article is built upon the JBoss platform in order to take advantage of the JBoss service infrastructure and the JMX management foundation. The following diagram illustrates these relationships:


Figure 2: The Web Application Framework

Notice that the EJB component has been added as an intermediary between a remote client and the service. This allows two modes of interaction between a client and a service: HTTP request/response and SOAP request/response. The components involved in the EJB-service infrastructure application framework are discussed next.

The Components for the Service-EJB Infrastructure

The ancillary classes of the framework facilitate a simple HTTP-based Web application which receives a service request from a user, dispatches the request to the service, and returns an HTTP-based response to the user. SOAP support is added by the interfaces, classes, and deployment artifacts described above.

The components of the Web application framework loosely follow the J2EE blueprints published by Sun. For purpose of this article, a new client-interaction scenario has been added that will allow business services to be invoked from a remote Web-service client, as well as from the application tier of the application framework. The following illustrates the portions of the blueprint defining the ancillary classes used in the framework:


Figure 3: Framework Components

Notice how a Session EJB is used as the intermediary between a remote client and the UserProfileService. This enables the service logic to be called locally by the Business Delegate and remotely by a SOAP client.

The EJB support classes and interfaces that must be added to the framework are discussed next.

Defining the Home Interface for the Service EJB

The user-profile service's EJBHome interface is added to enable the service to be managed by an EJB container. This interface is defined as follows:

package com.jeffhanson.businesstier.ejb;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface UserProfileHome extends EJBHome
{
   public UserProfileRemote create()
      throws CreateException, RemoteException;
}

Defining the Service EJB Interface

The service needs an interface that can be used by an EJB container to expose the service remotely. This interface is embodied within the EJBObject interface, as illustrated in the following listing:

package com.jeffhanson.businesstier.ejb;

import com.jeffhanson.businesstier.UserProfile;

import java.rmi.RemoteException;
import javax.ejb.EJBObject;

public interface UserProfileRemote extends EJBObject
{
   public String[] getUserNames()
      throws RemoteException;

   public UserProfile create(String name,
                             String address,
                             String cityStateZip,
                             String socialSecurityNumber)
      throws RemoteException;
}

Specifying the Service EJB Implementation

Now, the service's EJB implementation class is added. This class provides the implementation for each method defined in the EJBObject interface and interacts directly with the UserProfileService class to expose its business logic to a remote client. This class is illustrated below:

package com.jeffhanson.businesstier.ejb;

import java.rmi.RemoteException;
import javax.ejb.*;

import org.jboss.logging.Logger;

import com.jeffhanson.businesstier.UserProfileService;
import com.jeffhanson.businesstier.UserProfile;

public class UserProfileBean
   implements SessionBean
{
   private static final Logger log =
      Logger.getLogger("com/jeffhanson/businesstier/ejb/UserProfileBean");
   
   private SessionContext ctx;

   public UserProfileBean()
   {
   }

   public String[] getUserNames()
      throws RemoteException
   {
      log.info((new StringBuilder()).append("getUserNames: ").toString());
      UserProfileService userProfileService =
         UserProfileService.getInstance();
      return userProfileService.getUserNames();
   }

   public UserProfile create(String name,
                             String address,
                             String cityStateZip,
                             String socialSecurityNumber)
      throws RemoteException
   {
      log.info((new StringBuilder()).append("create: ").toString());
      UserProfileService userProfileService =
         UserProfileService.getInstance();
      return userProfileService.create(name,
                                       address,
                                       cityStateZip,
                                       socialSecurityNumber);
   }

   public void ejbCreate()
   {
      log.debug("ejbCreate");
   }

   public void setSessionContext(SessionContext ctx)
      throws EJBException, RemoteException
   {
      log.debug("setSessionContext");
      this.ctx = ctx;
   }

   public void ejbRemove()
      throws EJBException, RemoteException
   {
      log.debug("ejbRemove");
   }

   public void ejbActivate()
      throws EJBException, RemoteException
   {
      log.debug("ejbActivate");
   }

   public void ejbPassivate()
      throws EJBException, RemoteException
   {
      log.debug("ejbPassivate");
   }
}

Defining the Service Endpoint Interface

The UserProfile Service Endpoint Interface (SEI) is defined to expose the operations defined within the WSDL file for the UserProfile service. This interface must extend the java.rmi.Remote interface as illustrated in the following listing:

package com.jeffhanson.businesstier;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface UserProfileEndpoint extends Remote
{
   public String[] getUserNames()
      throws RemoteException;

   public UserProfile create(String name,
                             String address,
                             String cityStateZip,
                             String socialSecurityNumber)
      throws RemoteException;
}

Defining the Port Component Handler

SOAP message handlers defined in Web Services for J2EE need to declare a class that implements the WSDL's port element. This class must be an implementation of the javax.xml.rpc.handler.Handler interface. The SOAP message handlers can be "chained" together to act as sophisticated pre-processors and post-processors of SOAP messages. Each handler can be used to enhance or modify SOAP request or response messages for SOAP clients and SOAP services.

The javax.xml.rpc.handler.GenericHandler interface is extended to define the port component handler for the UserProfile service as illustrated in the following listing:

package com.jeffhanson.businesstier.handler;

import javax.xml.namespace.QName;
import javax.xml.rpc.handler.*;
import org.jboss.logging.Logger;

public class PortComponentHandler extends GenericHandler
{
    private static final Logger log =
      Logger.getLogger("com/jeffhanson/businesstier/handler/PortComponentHandler");

    private QName headers[];

    public PortComponentHandler()
    {
    }

    public QName[] getHeaders()
    {
        return headers;
    }

    public void init(HandlerInfo config)
    {
        log.info((new StringBuilder()).append("init: ").
           append(config).toString());
        headers = config.getHeaders();
    }

    public void destroy()
    {
        log.info("destroy");
    }

    public boolean handleRequest(MessageContext msgContext)
    {
        log.info((new StringBuilder()).append("handleRequest: ").
           append(msgContext).toString());
        return true;
    }

    public boolean handleResponse(MessageContext msgContext)
    {
        log.info((new StringBuilder()).append("handleResponse: ").
           append(msgContext).toString());
        return true;
    }

    public boolean handleFault(MessageContext msgContext)
    {
        log.info((new StringBuilder()).append("handleFault: ").
           append(msgContext).toString());
        return true;
    }
}

Specifying the EJB within the jboss.xml Configuration File

JBoss supports vendor-specific configuration options for EJBs in an optional file named jboss.xml. This file is used to specify a different JNDI name for a bean's home interface. The default JNDI name for an EJB is the ejb-name element value found in ejb-jar.xml. Since the UserProfileService is to be accessed from remote Web service clients, the need arises to declare EJB configuration values for the UserProfileBean component. These configuration values are illustrated below:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.0//EN"
                       "http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd">

<jboss>
  <enterprise-beans>
    <session>
      <ejb-name>UserProfileBean
      <jndi-name>ejb/UserProfileBean
    </session>
  </enterprise-beans>
</jboss>

Adding Discovery to the UserProfile Service

In order for the details of the UserProfile service-EJB to be discovered by Web service clients, a WSDL file must be provided to show the abstract operations, types, and concrete bindings of the Web service. The WSDL-to-Java mappings are defined in a file named jaxrpc-mapping.xml. The jaxrpc-mapping deployment descriptor has no standard file name; its name is determined by the jaxrpc-mapping-file element in the webservices.xml file, which is discussed below. The wscompile tool that ships as part of the Java Web Services Developer Pack (JWSDP) is used to generate the jaxrpc-mapping file and the WSDL file. The command-line for the wscompile tool is as follows:

wscompile -gen:server -classpath %CLASSPATH%;.;<APP_JARS>;<JBOSS_JARS> \ -mapping jaxrpc-mapping.xml -d . config.xml

This generates a WSDL file and a JAX-RPC mapping file based on the supplied config.xml and the corresponding classes specified by the CLASSPATH option. The config.xml file for the UserProfile Web service is shown below.

<?xml version="1.0" encoding="UTF-8"?>

<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
   <service name="UserProfileService"
            targetNamespace="http://businesstier.jeffhanson.com/"
            typeNamespace="http://businesstier.jeffhanson.com/types"
            packageName="com.jeffhanson.businesstier">
      <interface name="com.jeffhanson.businesstier.UserProfileEndpoint"/>
   </service>
</configuration>

The service element within the config.xml file defines the interface a Web service provides. The following attributes are required:

  • name: This is the name of the Web service
  • targetNamespace: The namespace for the Web service. Best practices suggest using a namespace URL that corresponds to the Java-package namespace
  • typeNamespace: This specifies the namespace to use for any custom types
  • packageName: This is the base package name for the Web service classes

The WSDL file that wscompile generated for the config.xml file is shown below. Note that the SOAP address isn't provided in the WSDL file. JBoss will insert the correct URL for the WSDL when it deploys the Web service.

The generated WSDL file for the UserProfile service is as follows:

<?xml version="1.0" encoding="UTF-8"?>

<definitions name="UserProfileService"
             targetNamespace="http://businesstier.jeffhanson.com/"
             xmlns:tns="http://businesstier.jeffhanson.com/"
             xmlns="http://schemas.xmlsoap.org/wsdl/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:ns2="http://businesstier.jeffhanson.com/types"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
   <types>
      <schema targetNamespace="http://businesstier.jeffhanson.com/types"
              xmlns:tns="http://businesstier.jeffhanson.com/types"
              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="UserProfile">
            <sequence>
               <element name="address" type="string"/>
               <element name="cityStateZip" type="string"/>
               <element name="name" type="string"/>
               <element name="socialSecurityNumber" type="string"/>
            </sequence>
         </complexType>
         <complexType name="ArrayOfstring">
            <complexContent>
               <restriction base="soap11-enc:Array">
                  <attribute ref="soap11-enc:arrayType"
                             wsdl:arrayType="string[]"/>
               </restriction>
            </complexContent>
         </complexType>
      </schema>
   </types>
   <message name="UserProfileEndpoint_create">
      <part name="String_1" type="xsd:string"/>
      <part name="String_2" type="xsd:string"/>
      <part name="String_3" type="xsd:string"/>
      <part name="String_4" type="xsd:string"/>
   </message>
   <message name="UserProfileEndpoint_createResponse">
      <part name="result" type="ns2:UserProfile"/>
   </message>
   <message name="UserProfileEndpoint_getUserNames"/>
   <message name="UserProfileEndpoint_getUserNamesResponse">
      <part name="result" type="ns2:ArrayOfstring"/>
   </message>
   <portType name="UserProfileEndpoint">
      <operation name="create"
                 parameterOrder="String_1 String_2 String_3 String_4">
         <input message="tns:UserProfileEndpoint_create"/>
         <output message="tns:UserProfileEndpoint_createResponse"/>
      </operation>
      <operation name="getUserNames">
         <input message="tns:UserProfileEndpoint_getUserNames"/>
         <output message="tns:UserProfileEndpoint_getUserNamesResponse"/>
      </operation>
   </portType>
   <binding name="UserProfileEndpointBinding"
            type="tns:UserProfileEndpoint">
      <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
                    style="rpc"/>
      <operation name="create">
         <soap:operation soapAction=""/>
         <input>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               use="encoded"
               namespace="http://businesstier.jeffhanson.com/"/>
         </input>
         <output>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               use="encoded"
               namespace="http://businesstier.jeffhanson.com/"/>
         </output>
      </operation>
      <operation name="getUserNames">
         <soap:operation soapAction=""/>
         <input>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               use="encoded"
               namespace="http://businesstier.jeffhanson.com/"/>
         </input>
         <output>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               use="encoded"
               namespace="http://businesstier.jeffhanson.com/"/>
         </output>
      </operation>
   </binding>
   <service name="UserProfileService">
      <port name="UserProfileEndpointPort"
            binding="tns:UserProfileEndpointBinding">
         <soap:address location="REPLACE_WITH_ACTUAL_URL"/>
      </port>
   </service>
</definitions>

The wscompile tool also generates a jaxrpc-mapping.xml file. This is shown below.

<?xml version="1.0" encoding="UTF-8"?>
<java-wsdl-mapping xmlns="http://java.sun.com/xml/ns/j2ee" version="1.1"
                   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">
   <package-mapping>
      <package-type>com.jeffhanson.businesstier</package-type>
      <namespaceURI>
         http://businesstier.jeffhanson.com/types
      </namespaceURI>
   </package-mapping>
   <package-mapping>
      <package-type>com.jeffhanson.businesstier</package-type>
      <namespaceURI>http://businesstier.jeffhanson.com/</namespaceURI>
   </package-mapping>
   <java-xml-type-mapping>
      <java-type>com.jeffhanson.businesstier.UserProfile</java-type>
      <root-type-qname
         xmlns:typeNS="http://businesstier.jeffhanson.com/types">
         typeNS:UserProfile
      </root-type-qname>
      <qname-scope>complexType</qname-scope>
      <variable-mapping>
         <java-variable-name>address</java-variable-name>
         <xml-element-name>address</xml-element-name>
      </variable-mapping>
      <variable-mapping>
         <java-variable-name>cityStateZip</java-variable-name>
         <xml-element-name>cityStateZip</xml-element-name>
      </variable-mapping>
      <variable-mapping>
         <java-variable-name>name</java-variable-name>
         <xml-element-name>name</xml-element-name>
      </variable-mapping>
      <variable-mapping>
         <java-variable-name>socialSecurityNumber</java-variable-name>
         <xml-element-name>socialSecurityNumber</xml-element-name>
      </variable-mapping>
   </java-xml-type-mapping>
   <service-interface-mapping>
      <service-interface>
         com.jeffhanson.businesstier.UserProfileService
      </service-interface>
      <wsdl-service-name
         xmlns:serviceNS="http://businesstier.jeffhanson.com/">
         serviceNS:UserProfileService
      </wsdl-service-name>
      <port-mapping>
         <port-name>UserProfileEndpointPort</port-name>
         <java-port-name>UserProfileEndpointPort</java-port-name>
      </port-mapping>
   </service-interface-mapping>
   <service-endpoint-interface-mapping>
      <service-endpoint-interface>
         com.jeffhanson.businesstier.UserProfileEndpoint
      </service-endpoint-interface>
      <wsdl-port-type
         xmlns:portTypeNS="http://businesstier.jeffhanson.com/">
         portTypeNS:UserProfileEndpoint
      </wsdl-port-type>
      <wsdl-binding xmlns:bindingNS="http://businesstier.jeffhanson.com/">
         bindingNS:UserProfileEndpointBinding
      </wsdl-binding>
      <service-endpoint-method-mapping>
         <java-method-name>create</java-method-name>
         <wsdl-operation>create</wsdl-operation>
         <method-param-parts-mapping>
            <param-position>0</param-position>
            <param-type>java.lang.String</param-type>
            <wsdl-message-mapping>
               <wsdl-message
                  xmlns:wsdlMsgNS="http://businesstier.jeffhanson.com/">
                  wsdlMsgNS:UserProfileEndpoint_create
               </wsdl-message>
               <wsdl-message-part-name>String_1</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
                  xmlns:wsdlMsgNS="http://businesstier.jeffhanson.com/">
                  wsdlMsgNS:UserProfileEndpoint_create
               </wsdl-message>
               <wsdl-message-part-name>String_2</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.lang.String</param-type>
            <wsdl-message-mapping>
               <wsdl-message
                  xmlns:wsdlMsgNS="http://businesstier.jeffhanson.com/">
                  wsdlMsgNS:UserProfileEndpoint_create
               </wsdl-message>
               <wsdl-message-part-name>String_3</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
                  xmlns:wsdlMsgNS="http://businesstier.jeffhanson.com/">
                  wsdlMsgNS:UserProfileEndpoint_create
               </wsdl-message>
               <wsdl-message-part-name>String_4</wsdl-message-part-name>
               <parameter-mode>IN</parameter-mode>
            </wsdl-message-mapping>
         </method-param-parts-mapping>
         <wsdl-return-value-mapping>
            <method-return-value>
               com.jeffhanson.businesstier.UserProfile
            </method-return-value>
            <wsdl-message
               xmlns:wsdlMsgNS="http://businesstier.jeffhanson.com/">
               wsdlMsgNS:UserProfileEndpoint_createResponse
            </wsdl-message>
            <wsdl-message-part-name>result</wsdl-message-part-name>
         </wsdl-return-value-mapping>
      </service-endpoint-method-mapping>
      <service-endpoint-method-mapping>
         <java-method-name>getUserNames</java-method-name>
         <wsdl-operation>getUserNames</wsdl-operation>
         <wsdl-return-value-mapping>
            <method-return-value>java.lang.String[]</method-return-value>
            <wsdl-message
               xmlns:wsdlMsgNS="http://businesstier.jeffhanson.com/">
               wsdlMsgNS:UserProfileEndpoint_getUserNamesResponse
            </wsdl-message>
            <wsdl-message-part-name>result</wsdl-message-part-name>
         </wsdl-return-value-mapping>
      </service-endpoint-method-mapping>
   </service-endpoint-interface-mapping>
</java-wsdl-mapping>

Once the mapping file and WSDL file are generated, they must be referenced from a webservices.xml file. This file links the WSDL file using a wsdl-file element and the mapping file using a jaxrpc-mapping-file element. In addition, the port-component element is defined to map a port in the WSDL file to the UserProfile Web service implementation. The webservices.xml file for the UserProfile Web service is defined as follows:

<?xml version="1.0" encoding="UTF-8"?>

<webservices xmlns="http://java.sun.com/xml/ns/j2ee"
             xmlns:impl="http://com.jeffhanson.businesstier/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>
         UserProfileServiceEJB
      </webservice-description-name>
      <wsdl-file>META-INF/wsdl/server-ejb.wsdl</wsdl-file>
      <jaxrpc-mapping-file>
         META-INF/jaxrpc-mapping.xml</jaxrpc-mapping-file>
      <port-component>
         <port-component-name>UserProfileEndpoint</port-component-name>
         <wsdl-port>UserProfileEndpointPort</wsdl-port>
         <service-endpoint-interface>
            com.jeffhanson.businesstier.UserProfileEndpoint
         </service-endpoint-interface>
         <service-impl-bean>
            <ejb-link>UserProfileBean</ejb-link>
         </service-impl-bean>
         <handler>
            <handler-name>TestHandler</handler-name>
            <handler-class>
               com.jeffhanson.businesstier.handler.PortComponentHandler
            </handler-class>
            <init-param>
               <param-name>param1</param-name>
               <param-value>value1</param-value>
            </init-param>
            <soap-header>impl:handler-test</soap-header>
            <soap-role>handler-role</soap-role>
         </handler>
      </port-component>
   </webservice-description>
</webservices>

Deploying the Service EJB to the JBoss Application Server

The service EJB classes, configuration files, and deployment descriptor files are packaged as a .jar file and placed inside of the "deploy" directory at the root of the server directory structure. The server configuration, "all", will be used for this application, so the .jar file should be placed inside of the <JBOSS_HOME>/server/all/deploy directory, as shown in the following figure:

Testing the Service EJB

Now that the modifications are made and the deployment of the .jar file is complete, start the JBoss application server by executing the run.bat or run.sh file (depending on your operating system environment) with the command-line options "-c all" as follows:

run.bat -c all

This will execute the server configuration defined at <JBOSS_HOME>/server/all.

Validating the Service EJB's Deployment

Once the server has completed the startup sequence, go to http://127.0.0.1:8080/ws4ee/services and you will see the following response page:


Figure 4: Initial Application View

Running the Test Client

The Service EJB can also be invoked from a client application or component, such as the FrontController servlet. Discovery of the Service EJB is facilitated using JNDI, as shown in the following application client:

package com.jeffhanson.clienttier;

import com.jeffhanson.businesstier.ejb.UserProfileHome;
import com.jeffhanson.businesstier.ejb.UserProfileRemote;

import javax.naming.InitialContext;
import javax.naming.Context;
import java.util.Properties;
import java.util.Hashtable;

public class TestUserProfileService
{
   public static void main(String[] args)
   {
      TestUserProfileService app = new TestUserProfileService();
      try
      {
         app.doTest();
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }

   public void doTest()
      throws Exception
   {
      Hashtable props = new Properties();

      props.put(Context.INITIAL_CONTEXT_FACTORY,
                "org.jnp.interfaces.NamingContextFactory");
      props.put(Context.URL_PKG_PREFIXES,
                "org.jboss.naming:org.jnp.interfaces");
      props.put(Context.PROVIDER_URL,
                "jnp://127.0.0.1:1099");

      InitialContext initCtx = new InitialContext(props);
      Context ejbCtx = (Context)initCtx.lookup("ejb");

      UserProfileHome home =
         (UserProfileHome)ejbCtx.lookup("/ejb/UserProfileBean");
      UserProfileRemote bean = home.create();

      bean.create("John Doe", "123 Anywhere St.",
                  "Buffalo, NY 87654", "123-45-6789");

      String[] userNames = bean.getUserNames();
      System.out.println("User names: ");
      if (userNames != null)
      {
         for (int i = 0; i < userNames.length; i++)
         {
            System.out.println("   " + userNames[i]);
         }
      }
   }
}

To compile the Web services client, enter the ant command as follows:

ant

Execute the Web services client and the follow is displayed:

User names:
John Doe

Conclusion

JBoss 4.0 provides a sophisticated platform for developing multi-tiered, service-oriented, enterprise systems. Its use of plain-old Java Objects and JMX-based management techniques enables service developers to define and implement services that can be deployed and configured in many different configurations to meet business demands. Using the tips discussed in this article, the JBoss 4.0 service-oriented platform architecture and the J2SE access-control-list framework, allow you to build dynamic, sophisticated, and secure services, applications, and systems.

This is the third of a series of articles discussing service-oriented systems development on JBoss 4.0. In this article, I discussed techniques to enhance a service-oriented system on a JBoss 4.0 server with dynamic service discovery. In subsequent articles, I will further discuss how JBoss 4.0 can be used to enhance a service-oriented system with persistence, optimized client-access, etc.

For Additional Information

For more information about the technologies discussed in this Cool Solution for Developers, refer to the following resources:


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© Micro Focus