System Implementation


Now that we have seen the user flow for the EPS application, in this section we take a detailed look at each of the implementation source files. We begin by describing how to develop applications and then expose them as Web services. Next, we look at how to implement the client application including the Web service invocations as well as other business logic. And, finally, we describe how to develop presentation code in the form of static HTML pages, J2EE servlets, as well as Java Server Pages (JSPs) that can invoke and leverage Web service functionality.

Figure 12-6 depicts the steps by which a Web service is created, deployed, and registered as well as how a client application access the functionalities of each Web service.

Figure 12-6. Architectural depiction of how a Web service is created and a client application accesses it.

graphics/12fig06.gif

The steps to build and deploy a Web service are:

  1. Implement the business logic of the Web service as an application using your favorite language and platform. In this example, we use Java.

  2. Use tools (that are part of your Web service platform) to deploy the application as a Web service.

  3. Use additional tools (again, that are part of your Web service platform) to generate the WSDL for each deployed Web service.

  4. Register each Web service and WSDL so that potential client applications can locate and understand the usage of the Web service.

The steps to build a client application that consumes Web services is as follows:

  1. Generate a client proxy for each Web service to be used by the application based on each Web service's WSDL.

  2. Within the client application, instantiate each client proxy and call the appropriate methods of the client proxy.

In the next sections, we look at each of these steps in more detail.

VendorAOrdering.java

In this section, we look at the steps involved in developing a Web service. We begin by developing a Java class, which we then expose as a Web service using the tools provided by our Web services platform.

For the sake of brevity, we only discuss the VendorAOrdering Java class and the associated VendorAProcurement Web service. The steps for developing classes and exposing them as Web services are the same for the other two Web services as well.

The source code for VendorAOrdering.java in its entirety follows:

 public class VendorAOrdering {    public VendorAOrdering ()    {    }    public int getPrice ( int partNumber , int quantity )    {       int price = -1;       switch ( partNumber )       {          // part pricing business logic deleted          // for brevity       }       return ( price );    }    public int getLeadTime ( int partNumber , int quantity )    {       int leadtime = -1;       switch ( partNumber )       {          // part leadtime determination business logic          // deleted for brevity       }       return ( leadtime );    }    public int placeOrder ( int partNumber , int quantity )    {       int confirmationOrderNumber = -1;       // order confirmation number generation business       // logic deleted for brevity       return ( confirmationOrderNumber );    }    public String getOrderShipmentInfo ( int orderNumber )    {       String shipDate = "";       // order shipment information business       // logic deleted for brevity       return ( shipDate );    } } 

The code segment is a simple class that implements the following four methods:

  • getPrice: returns the unit price for a component given its part number and the desired purchase quantity.

  • getLeadTime: returns the expected leadtime for delivery of a component given its part number and the desired purchase quantity.

  • placeOrder: returns a confirmation number for an order of the given component part number and purchase quantity.

  • getOrderShipmentInfo: returns the expected delivery date of an order given its order confirmation number.

The VendorAOrdering class is a standard Java class, which can be instantiated and tested within an application. In this case, instead of instantiating it within an application, we wish to expose it as a Web service. There are just a few rules to follow in developing a Java class that will be exposed as a Web service. First, the class must have a public default constructor, and second the methods that will be available as Web service operations must be public.

Once the class file has been tested, deploying it as a Web service is simple. The Web services platform on which the service will be deployed will provide a tool that accepts as input a Java class (or application and fragments implemented in other languages) and deploys it as a Web service. Here we describe the steps required to deploy a Java class as a Web service using Tomcat and Apache Axis. The steps are:

  1. Create a Web Service Deployment Descriptor (WSDD) file for the Web service.

    A WSDD file instructs the Axis engine to make that particular class available as a Web service, and incoming requests for that named Web service are handled by the specified class. Although a WSDD file can specify a rich set of instructions to the Axis engine, in many cases the basic Web service deployment is sufficient. The WSDD file for such a basic deployment is as follows:

     <deployment xmlns="http://xml.apache.org/axis/wsdd/"   xmlns:java=     "http://xml.apache.org/axis/wsdd/providers/java">   <service name="VendorAProcurement" provider="java:RPC">   <parameter name="className" value=" VendorAOrdering"/>   <parameter name="allowedMethods" value="*"/>  </service> </deployment> 

    This file deploys a new Web service called VendorAProcurement using the RPC style. The Web service is based on the Java class VendorAOrdering, and all of the public methods of the class are available as accessible operations of the Web service. Additional information about WSDD files and other parameters supported in the deployment of Web services can be found in the Apache Axis documentation.

  2. Deploy the Web service by using the Axis AdminClient and the created WSDD file.

    Assuming the WSDD file is named VendorA.wsdd, the Web service is deployed onto Axis by executing the following command:

     java org.apache.axis.client.AdminClient VendorA.wsdd 
  3. Place the VendorAOrdering.class Java class file in the system classpath so that it is accessible to Axis.

  4. Verify the deployment by browsing through the WSDL file of the new Web service.

    Upon successful execution of these steps, the VendorAProcurement Web service should be ready for use at the following location:

     http://localhost:8080/axis/services/VendorAProcurement 

    A simple way to verify that the Web service has been deployed to the server is by checking that its WSDL has been properly created and is available at the expected location. The WSDL for our VendorAProcurement Web service should be available at the following URL:

     http://localhost:8080/axis/services/VendorAProcurement?wsdl 

Although we have demonstrated how to deploy a Java class as a Web service on Apache Axis, other Web services platforms also provide similar tools that facilitate the deployment of applications as Web services.

So far, we have made the class available as a Web service, but have not published the WSDL so that client applications can locate and use the service. We demonstrate how to do this in the next section.

VendorAProcurement.wsdl

Figure 12-7 lists the entire WSDL for the VendorAProcurement Web service. Each of the operations supported by the Web service are enumerated together with each operation's arguments, the type of each argument as well as the type of the operation's return value. The endpoint location of the Web service is also specified within the address element.

Figure 12-7. The WSDL for the VendorAProcurement Web service.
 <?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="http://localhost:8080/axis/services/VendorAProcurement" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://localhost:8080/axis/services/VendorAProcurement" xmlns:intf="http://localhost:8080/axis/services/VendorAProcurement" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">   <wsdl:message name="getLeadTimeRequest">     <wsdl:part name="in0" type="xsd:int"/>     <wsdl:part name="in1" type="xsd:int"/>   </wsdl:message>   <wsdl:message name="getOrderShipmentInfoRequest">     <wsdl:part name="in0" type="xsd:int"/>   </wsdl:message>  <wsdl:message name="getOrderShipmentInfoResponse">     <wsdl:part name="getOrderShipmentInfoReturn" type="xsd:int"/>   </wsdl:message>    <wsdl:message name="placeOrderRequest">     <wsdl:part name="in0" type="xsd:int"/>     <wsdl:part name="in1" type="xsd:int"/>   </wsdl:message>   <wsdl:message name="getPriceResponse">     <wsdl:part name="getPriceReturn" type="xsd:int"/>   </wsdl:message>   <wsdl:message name="placeOrderResponse">     <wsdl:part name="placeOrderReturn" type="xsd:int"/>   </wsdl:message>   <wsdl:message name="getLeadTimeResponse">     <wsdl:part name="getLeadTimeReturn" type="xsd:int"/>   </wsdl:message>   <wsdl:message name="getPriceRequest">     <wsdl:part name="in0" type="xsd:int"/>     <wsdl:part name="in1" type="xsd:int"/>   </wsdl:message>   <wsdl:portType name="VendorAProcurement">     <wsdl:operation name="getPrice" parameterOrder="in0 in1">       <wsdl:input message="impl:getPriceRequest" name="getPriceRequest"/>       <wsdl:output message="impl:getPriceResponse" name="getPriceResponse"/>     </wsdl:operation>     <wsdl:operation name="getLeadTime" parameterOrder="in0 in1">       <wsdl:input message="impl:getLeadTimeRequest"        name="getLeadTimeRequest"/>       <wsdl:output message="impl:getLeadTimeResponse"        name="getLeadTimeResponse"/>     </wsdl:operation>     <wsdl:operation name="placeOrder" parameterOrder="in0 in1">       <wsdl:input message="impl:placeOrderRequest" name="placeOrderRequest"/>       <wsdl:output message="impl:placeOrderResponse"        name="placeOrderResponse"/>     </wsdl:operation>     <wsdl:operation name="getOrderShipmentInfo" parameterOrder="in0">       <wsdl:input message="impl:getOrderShipmentInfoRequest"        name="getOrderShipmentInfoRequest"/>       <wsdl:output message="impl:getOrderShipmentInfoResponse"        name="getOrderShipmentInfoResponse"/>     </wsdl:operation>   </wsdl:portType>   <wsdl:binding name="VendorAProcurementSoapBinding"   type="impl:VendorAProcurement">     <wsdlsoap:binding style="rpc" transport="     http://schemas.xmlsoap.org/soap/http"/>     <wsdl:operation name="getPrice">       <wsdlsoap:operation soapAction=""/>       <wsdl:input name="getPriceRequest">         <wsdlsoap:body encodingStyle=         "http://schemas.xmlsoap.org/soap/encoding/"          namespace="http://DefaultNamespace" use="encoded"/>       </wsdl:input>       <wsdl:output name="getPriceResponse">         <wsdlsoap:body encodingStyle=         "http://schemas.xmlsoap.org/soap/encoding/"         namespace=         "http://localhost:8080/axis/services/VendorAProcurement"          use="encoded"/>       </wsdl:output>     </wsdl:operation>     <wsdl:operation name="getLeadTime">       <wsdlsoap:operation soapAction=""/>       <wsdl:input name="getLeadTimeRequest">         <wsdlsoap:body encodingStyle=         "http://schemas.xmlsoap.org/soap/encoding/" namespace=          "http://DefaultNamespace" use="encoded"/>       </wsdl:input>       <wsdl:output name="getLeadTimeResponse">         <wsdlsoap:body encodingStyle=         "http://schemas.xmlsoap.org/soap/encoding/" namespace=         "http://localhost:8080/axis/services/VendorAProcurement"          use="encoded"/>       </wsdl:output>     </wsdl:operation>     <wsdl:operation name="placeOrder">       <wsdlsoap:operation soapAction=""/>       <wsdl:input name="placeOrderRequest">         <wsdlsoap:body encodingStyle=         "http://schemas.xmlsoap.org/soap/encoding/" namespace=          "http://DefaultNamespace" use="encoded"/>       </wsdl:input>       <wsdl:output name="placeOrderResponse">         <wsdlsoap:body encodingStyle=         "http://schemas.xmlsoap.org/soap/encoding/" namespace=         "http://localhost:8080/axis/services/VendorAProcurement"          use="encoded"/>       </wsdl:output>     </wsdl:operation>     <wsdl:operation name="getOrderShipmentInfo">       <wsdlsoap:operation soapAction=""/>       <wsdl:input name="getOrderShipmentInfoRequest">         <wsdlsoap:body encodingStyle=         "http://schemas.xmlsoap.org/soap/encoding/" namespace=          "http://DefaultNamespace" use="encoded"/>       </wsdl:input>       <wsdl:output name="getOrderShipmentInfoResponse">         <wsdlsoap:body encodingStyle=         "http://schemas.xmlsoap.org/soap/encoding/" namespace=         "http://localhost:8080/axis/services/VendorAProcurement"          use="encoded"/>       </wsdl:output>     </wsdl:operation>   </wsdl:binding>   <wsdl:service name="VendorAProcurementService">     <wsdl:port binding="impl:VendorAProcurementSoapBinding"     name="VendorAProcurement">       <wsdlsoap:address location=        "http://localhost:8080/axis/services/VendorAProcurement"/>     </wsdl:port>   </wsdl:service> </wsdl:definitions> 

The WSDL for each deployed Web service is automatically generated by the Web service platform. For example, in the previous section after we deployed and exposed the VendorAOrdering Java class as the Web service VendorAProcurement, the WSDL file VendorAProcurement.wsdl was automatically generated and made available at: http://localhost:8080/axis/services/VendorAProcurement?wsdl.

The WSDL from this location can be saved and distributed as a text file to potential consumers of this Web service. The Web service may be registered at a registry such as the Universal Business Registry (see Chapter 4 for more information about UDDI and the UBR), or the WSDL may be deposited in a public or private WSDL repository.

Now that we have seen how to implement a Web service and make it available for others to use, we next turn our focus to the steps involved in building client applications that use Web services. We start by looking at the client application's user-facing presentation that is implemented as a set of static HTML pages.

EPS.html

EPS.html is the first touch point for users accessing the EPS application. It is a static HTML page that consists of a single form. Each of the form fields is positioned within various rows and columns of a table structure.

What follows are the key segments of the EPS.html source file. The basic parts of the page that are implemented using standard HTML tags are removed for brevity.

 <HTML>   <HEAD>     <TITLE>Enterprise Procurement System</TITLE>   </HEAD>   <BODY bgcolor="#CFFFFF">     <TABLE cellpadding="0" cellspacing="0" border="1">       <TR>         <TD width="375" height="30" colspan="3"        align="center">           <H2>Enterprise Procurement System</H2>         </TD>       </TR>       <TR>         <TD width="345">           <FORM name="form" action=           "http://localhost:8080/epsdemo/ServiceServlet"                 method="post"> 

The details of the HTML form have also been removed for brevity. A standard HTML form is used to collect information on the part number to be purchased as well as the desired quantity and vendor selection metric (e.g., lowest cost or lowest lead time). This user data is passed as HTML form parameters as a HTTP POST to the J2EE Servlet located at http://localhost:8080/epsdemo/ServiceServlet.

           </FORM>         </TD>       </TR>     </TABLE>   </BODY> </HTML> 

The Servlet ServiceServlet services the user requests by invoking the VendorAProcurement and VendorBProcurement and, if necessary, the AltPartService Web services based on the received user input.

As we have seen, the client application, in this case the Web browser, does not access the Web services directly. Instead, the Web service invocations are placed behind an abstraction layer, e.g., a Servlet API. This allows adding Web services to an application without having to modify the entire system. Although this type of abstraction and architecture is useful, there is no reason why a client application could not directly invoke Web services.

EPSCatalog.html

The EPSCatalog.html file is a simple, static HTML file that presents all of the components that are available to be procured through the EPS application. A two-column table structure organizes each component part number together with a brief description of the component.

Since the page is simple and can be developed using standard HTML, we do not list the code here. A screenshot of the page is depicted in Figure 12-4.

A more complex but easier to maintain component catalog page can also be developed by using dynamic pages instead of static ones. Assume that each of the vendor Web services support a component query function that returns a list of component part numbers the vendor carries together with brief descriptions. Whether the component is currently available is an orthogonal issue. A dynamic catalog page can be developed by simply querying this method of each vendor's Web service and presenting to the user the response data.

ServiceServlet.java

ServiceServlet.java is a J2EE Servlet Java source file. It is a controller Servlet that accepts HTTP POST requests from EPS.html, processes the POST parameters, invokes the appropriate Web services, and generates the user response. The response is forwarded to the OutputServlet, which prepares the response for user presentation.

Figure 12-8 depicts the three Web services that are used by the EPS application. The Web services of Vendor A and Vendor B are competitive services that allow a client application to inquire about the price or lead time of a particular component, and then order the component. The Alternate Part Web service is used when the desired part is unavailable from both vendors A and B in order to locate a similar or alternate component.

Figure 12-8. Enterprise procurement application architecture that interfaces with vendor Web services.

graphics/12fig07.gif

The first thing we notice about the Web services from Vendor A and Vendor B is that they expose a different number of methods Vendor A exposes four methods, while Vendor B exposes only three and the names of each method are different. The number, type, and sequencing of arguments for each method may also be different. Although the two Web services provide almost identical functionality, the interfaces they expose are quite different. This difference in API between competing Web services makes building scalable client applications difficult.

The source code for ServiceServlet.java in its entirety with comments and explanations follows:

 /**  *  File: ServiceServlet.java  *  Desc: Controller Servlet that accepts HTTP POSTs from  *        EPS.html, processes the POST parameters, invokes  *        the appropriate Web services, and generates the  *        response, which is forwarded to the  *        OutputServlet.  **/ import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class ServiceServlet extends HttpServlet {    private static final String CONTENT_TYPE = "text/html";    // Individual Web service error codes    private final static int ALTERNATE_PART_ERROR = -1;    private final static int VENDOR_A_ERROR = -1;    private final static int VENDOR_B_ERROR = 0;    public void init ( ServletConfig config ) throws ServletException    {       super.init ( config );    }    // Forward HTTP GET requests to the doPost handler    public void doGet ( HttpServletRequest request , HttpServletResponse response ) throws ServletException , IOException    {       doPost ( request , response );    }    // Process the HTTP POST request    public void doPost ( HttpServletRequest request , HttpServletResponse response ) throws ServletException , IOException    {       // Variables to hold the parameters from the POST       String paramPartNumber = null;       String paramQuantity = null;       String paramOptimizeFor = null;       // Is the form fully completed by the user and the       //  form data ready to be processed? Or, are some       //  fields not completed?       boolean formReady = true;       // Message to return to the user       String responseMessage = null;       // Name of the selected vendor from which the       //  procure the components       String selectedVendorName = null;       // Variables that hold the cost or lead time       //  responses from each vendor Web service       int vendorAResult = 0;       int vendorBResult = 0;       // Value of the best cost or lead time response       int bestResult = 0;       // Was the specified component located or not       boolean componentNotFound = false;       // if the specified component was not located,       //  what is the part number for an alternate component       int alternatePartNumber = 0;       // The Web service client-side binding stub variables       AltPartServiceService apService;       AltPartService apPort;       VendorAProcurementService vapService;       VendorAProcurement vapPort;       VendorBProcurementService vbpService;       VendorBProcurement vbpPort;       // Get the printwriter for the Servlet       response.setContentType ( CONTENT_TYPE );       PrintWriter out = response.getWriter ();       // Get the parameter values from the POST request       paramPartNumber = request.getParameter (           "partnumber" );       paramQuantity = request.getParameter ( "quantity" );       paramOptimizeFor = request.getParameter (           "optimizefor" );       // All of the fields of the HTML form must be filled       //  in. Otherwise, print a message asking the user to       //  fill everything in.       if ( ( paramPartNumber == null ) ||          ( paramQuantity == null ) )       {          // Some of the form fields were not properly filled          //  in. Thus, the form is not ready.          formReady = false;       }       else       {          paramPartNumber = paramPartNumber.trim ();          paramQuantity = paramQuantity.trim ();          paramOptimizeFor = paramOptimizeFor.trim ();          if ( ( paramPartNumber.length () == 0 ) ||             ( paramQuantity.length () == 0 ) ||             ( paramOptimizeFor.length () == 0 ) )          {             // The form fields were properly filled in, and             //  the form is ready to be processed.             formReady = false;          }       }       if ( formReady )       {          // Only if the formReady variable is true, do we          //  invoke the Web services and try to locate the          //  specified part.          try          {             // Vendor A Procurement service             vapService =     new VendorAProcurementServiceLocator ();             // Now use the service to get a stub which             //  implements the SDI.             vapPort = vapService.getVendorAProcurement ();             // Vendor B Procurement service             vbpService =     new VendorBProcurementServiceLocator ();             // Now use the service to get a stub which             //  implements the SDI.             vbpPort = vbpService.getVendorBProcurement ();             if ( paramOptimizeFor.equalsIgnoreCase (             "cost" ) )             {                // optimize for cost                vendorAResult = vapPort.getPrice (      new Integer ( paramPartNumber ).intValue (),                 new Integer ( paramQuantity ).intValue () );                vendorBResult = vbpPort.componentListPrice (      new Integer ( paramQuantity ).intValue () ,                 new Integer ( paramPartNumber).intValue() );             }             else             {                // optimize for lead time                vendorAResult = vapPort.getLeadTime (      new Integer ( paramPartNumber ).intValue (),                 new Integer ( paramQuantity ).intValue () );                vendorBResult = vbpPort.componentLeadtime (      new Integer ( paramQuantity ).intValue () ,                 new Integer ( paramPartNumber ).intValue());             }          }          catch ( Exception e )          {          System.out.println ( "Error in client proxy." );       }       // set bestResult to an error initially       bestResult = VENDOR_A_ERROR;       // -1 returned from Vendor A's Web service means       //  that they do not have the part, or they do not       //  have it in sufficient quantity       if ( vendorAResult != VENDOR_A_ERROR )       {          // if vendorA returned a value, set this to the          //  be bestResult          bestResult = vendorAResult;          selectedVendorName = "Vendor A";       }       // 0 returned from Vendor B's Web service means       //  that they do not have the part, or they do not       //  have it in sufficient quantity       if ( vendorBResult != VENDOR_B_ERROR )       {          if ( vendorBResult < bestResult )          {             bestResult = vendorBResult;             selectedVendorName = "Vendor B";          }       }       // if bestResult is a VENDOR_A_ERROR, then no       //  component was found from any vendor       if ( bestResult == VENDOR_A_ERROR )       {          componentNotFound = true;       }       if ( componentNotFound )       {          // The specified part could not be found by any          //  of the vendor Web services. So, now we use          //  the alternatePart Web service to locate an          //  alternate part, and provide the alternate          //  part number.          try          {             // Alt Part service             apService =    new AltPartServiceServiceLocator ();             // Now use the service to get a stub that        //  implements the SDI.             apPort = apService.getAltPartService ();             alternatePartNumber = apPort.getAltPart (   new Integer ( paramPartNumber ).intValue());          }          catch ( Exception e )          {             System.out.println   ( "Error in AltPartService client proxy" );          }          if ( alternatePartNumber !=ALTERNATE_PART_ERROR)          {             responseMessage =                "The component could not be located. " +                "We found an alternate part   " +                " with part number " +                alternatePartNumber +                " -- that may meet your needs.";          }          else          {             // If no alternate part number is found, then             //  just say so             responseMessage =               "The component could not be located. We               tried to find an alternate part, but we               could not. Good luck!";          }       }       if ( !componentNotFound )       {          responseMessage =           "The component has been successfully located.";          if (paramOptimizeFor.equalsIgnoreCase ("cost") )          {             responseMessage = responseMessage.concat (    "The lowest price is offered by " +               selectedVendorName +               ", and the unit price is $ " +               bestResult + "." );          }          else          {             responseMessage = responseMessage.concat (             "The shortest leadtime is offered by " +             selectedVendorName +             ", and the lead time is " + bestResult +             " days." );          }       }       }       else       {          // If the form is not ready (!formReady) then tell          //  the user to fill in the form completely          responseMessage =           "Please fully complete all of the form fields.";       }       // Use forward or redirect to send the responseMessage       //  data to the OutputServlet   used for simplicity       response.sendRedirect ("http://localhost:8080/epsdemo/        OutputServlet?responseMessage=" + responseMessage );    }    public void destroy ()    {    } } 

Now that we have seen how the ServiceServlet accepts data from an HTML page and invokes Web services to fulfill the specified requests, in the next section we look at one of the Web service binding stubs and how it facilitates client application development.

Client-Side Binding Stubs

Client-side binding stubs are local programs that facilitate invoking the methods of remote Web services. For instance, as we saw above in our discussion of the ServiceServlet servlet, invocations of the remote VendorAProcurement Web service are through the local VendorAProcurement stub. The VendorAProcurement stub is a Java source file that provides the identical API as that of the remote Web service. The client stub is simply a proxy-like means of the application to invoke the methods of a Web service through a local interface. The stub has the same API as that of its corresponding Web service, but the stub does not actually implement any of the business logic of the Web service.

The calling application instantiates the client stub and invokes the appropriate methods. The client stub simply forwards the method invocation using the appropriate protocol to the actual Web service. Upon receiving the request from the client stub, the Web service processes the request and formulates a response. The response is received by the client stub, which then forwards it back to the calling application as the return to the method invocation.

All of the client-side binding stubs are generated based on the Web service's WSDL file. Each Web service platform provides tools that automatically generate these files, further facilitating the job of developers.

Apache Axis provides the tool WSDL2Java for generating client-side binding stubs from Web service WSDL files. The basic invocation is as follows:

 java org.apache.axis.wsdl.WSDL2Java <WSDL filename or URL> 

If we want to generate the client-side stubs for the VendorAProcurement Web service, we execute the following command:

 java org.apache.axis.wsdl.WSDL2Java  http://localhost:8080/axis/services/VendorAProcurement?wsdl 

This command generates the following four files:

  • VendorAProcurement.java

  • VendorAProcurementService.java

  • VendorAProcurementServiceLocator.java

  • VendorAProcurementSoapBindingStub.java

Our discussion in the previous section on invoking Web service operations from the ServiceServlet servlet demonstrated how these generated files are used as a local proxy for the remote Web service and how each actual Web service operation invocation takes place.

Since each of these files is automatically generated, we do not provide source code listings or describe them in further detail.

Now that we have seen how Web services can be used to fulfill user requests, in the next section we finish the enterprise procurement application by formatting and presenting the results to the user.

OutputServlet.java

The OutputServlet.java is a J2EE Servlet Java source file that implements the user's view of the results of the enterprise procurement application. The response generated by the ServiceServlet is presented through the OutputServlet.

The implementation of OutputServlet is simple and straightforward. The key steps are:

  1. Get the responseMessage parameter from the POST request.

  2. Check the validity of the parameter.

  3. Generate the HTML to return to the client browser, including the data from the responseMessage parameter.

The functionality of the Servlet is provided by the doPost handler method. HTTP GET requests are forwarded to the doPost handler as well. The Java source code for the doPost method in its entirety together with comments and explanations follows:

 public void doPost ( HttpServletRequest request ,           HttpServletResponse response )   throws ServletException , IOException {    response.setContentType ( CONTENT_TYPE );    PrintWriter out = response.getWriter ();    String textFieldMessage = request.getParameter            ( "responseMessage" );    if (textFieldMessage != null)    {       textFieldMessage = textFieldMessage.trim();    }    out.println ( "<head>" );    out.println ( "<title>Enterprise Procurement       System</title>" );    out.println ( "</head>" );    out.println ( "<body bgcolor=#cfffff>" );    out.println ( "<table cellpadding=\"0\" cellspacing=\"0\"       border=\"1\">" );    out.println ( "<tr>" );    out.println ( "<td width=\"375\" height=\"30\"       colspan=\"3\" align=\"center\"><h2>       Enterprise Procurement " );    out.println ( "System</h2></td>" );    out.println ( "</tr>" );    out.println ( "<tr>" );    out.println ( "<td width=\"345\" height=\"200\" align=       \"center\"> " );    out.println ( textFieldMessage );    out.println ( "</td>" );    out.println ( "</tr>" );    out.println ( "</table>" );    out.println ( "</body>" );    out.println ( "</html>" ); } 

This completes our discussion of the implementation details of the Enterprise Procurement System application. In the next sections, we discuss how to deploy and then run the application.

Deploying the Application

In this section, we cover how to deploy the application on the Jakarta Tomcat servlet container and the Apache Axis SOAP server. Both Tomcat and Axis are available for free, and can be downloaded from http://jakarta.apache.org/tomcat/ and http://ws.apache.org/axis/, respectively. In our descriptions, we use Tomcat 4.1 and Axis 1.1.

To fully deploy the EPS application, we must simply deploy the EPS application as a Web application onto Tomcat. This assumes that Axis SOAP server as well as each of the Web services that are consumed by the EPS application have been successfully deployed onto Tomcat. Axis can be easily deployed onto Tomcat by simply copying the folder %AXIS_HOME%\webapps\axis\ to the folder %TOMCAT_HOME%\webapps\. We discussed the steps in deploying a Web service from a Java class in the subsection entitled "VendorAOrdering.java".

Before we can deploy the EPS application to Tomcat, we must first create a Web application, which is a collection of files within specific named folders that can be accessed by the servlet container (e.g., Tomcat) during runtime. The files can be left "unpacked" on the file system, or they may be "packed" into what is known as a Web Archive, or WAR file. Unpacked deployments are preferable during development when constant changes are made, but packed WAR files are more convenient when development is finished and the Web application must be distributed.

A Web application is deployed by placing the files required by the servlet container to run the application in the following standard format and locations:

  • %DOCUMENT_ROOT%\: Place HTML, JSP, and other associated files, such as images, in this folder. For larger applications, these files can be better organized and put into subfolders (e.g., for images, html, and so on).

  • %DOCUMENT_ROOT%\WEB-INF\web.xml: This is the Web Application Deployment Descriptor for the application. This file describes all of the components of the Web application.

  • %DOCUMENT_ROOT%\classes\: Place all of the Java classes used by the application in this folder.

  • %DOCUMENT_ROOT%\lib\: Place all JAR files, including third-party libraries, in this folder.

We can create a Web application for the EPS application by organizing all of the files as follows:

  • C:\epsdemo\index.html

  • C:\epsdemo\catalog.html

  • C:\epsdemo\WEB-INF\web.xml

  • C:\epsdemo\WEB-INF\classes\AltPartService.class

  • C:\epsdemo\WEB-INF\classes\AltPartServiceService.class

  • C:\epsdemo\WEB-INF\classes\AltPartServiceServiceLocator.class

  • C:\epsdemo\WEB-INF\classes\AltPartServiceSoapBindingStub.class

  • C:\epsdemo\WEB-INF\classes\OutputServlet.class

  • C:\epsdemo\WEB-INF\classes\ServiceServlet.class

  • C:\epsdemo\WEB-INF\classes\VendorAProcurement.class

  • C:\epsdemo\WEB-INF\classes\VendorAProcurementService.class

  • C:\epsdemo\WEB-INF\classes\VendorAProcurementServiceLocator.class

  • C:\epsdemo\WEB-INF\classes\VendorAProcurementSoapBindingStub.class

  • C:\epsdemo\WEB-INF\classes\VendorBProcurement.class

  • C:\epsdemo\WEB-INF\classes\VendorBProcurementService.class

  • C:\epsdemo\WEB-INF\classes\VendorBProcurementServiceLocator.class

  • C:\epsdemo\WEB-INF\classes\VendorBProcurementSoapBindingStub.class

  • C:\epsdemo\WEB-INF\lib\axis.jar

  • C:\epsdemo\WEB-INF\lib\commons-discovery.jar

  • C:\epsdemo\WEB-INF\lib\jaxrpc.jar

  • C:\epsdemo\WEB-INF\lib\saaj.jar

Each of the JAR files that are placed within the C:\epsdemo\WEB-INF\lib\ folder is available in the Axis distribution (%AXIS_HOME%\lib\). The contents of the web.xml Web Application Deployment Descriptor is shown in Figure 12-9.

The web.xml Web Application Deployment Descriptor specifies that the EPS Web application consists of two servlets, ServiceServlet and OutputServlet. The Java class that implements the servlet as well as the URL pattern that can be used to access the servlet is specified for each servlet.

We have now created a Web application for the EPS application. To deploy this Web application, we simply have to copy the folder in which the Web application lives (C:\epsdemo\) to the deployment folder of Tomcat (%TOMCAT_HOME%\webapps\).

Upon completing this step, we have successfully deployed the EPS application as a Web application onto the Tomcat servlet container. We can access the application by typing the following URL into any browser:

 http://localhost:8080/epsdemo 

The index.html (EPS.html) HTML file should be displayed on typing this URL into your browser. Be sure that Tomcat has successfully started before typing in the URL.

Figure 12-9. The contents of the web.xml Web Application Deployment Descriptor for the EPS application.
 <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet>   <servlet-name>ServiceServlet</servlet-name>   <servlet-class>ServiceServlet</servlet-class> </servlet> <servlet>   <servlet-name>OutputServlet</servlet-name>   <servlet-class>OutputServlet</servlet-class> </servlet> <servlet-mapping>   <servlet-name>ServiceServlet</servlet-name>   <url-pattern>/ServiceServlet</url-pattern> </servlet-mapping> <servlet-mapping>   <servlet-name>OutputServlet</servlet-name>   <url-pattern>/OutputServlet</url-pattern> </servlet-mapping> </web-app> 

This completes the deployment of the EPS application onto Tomcat. In the next section, we run the application and monitor the SOAP messages that are transmitted between the EPS application and each of the Web services it consumes.

Running the Application

In this section, we run the EPS application and also analyze the SOAP messages that are exchanged between the application and each Web service. By analyzing these SOAP messages, we can get a deeper understanding of the entire EPS application, as well as of the client-side binding stubs.

We can run the EPS Web application by simply typing in the appropriate URL into any browser. As we saw in the previous section, the URL for the EPS application is:

 http://localhost:8080/epsdemo 

Once we type in this URL, the EPS.html (index.html) page should be displayed within the browser. This page presents an HTML form prompting the user to enter the desired part number, required quantity, and optimization criteria. Enter the following information:

 Part Number: 15151 Required Quantity: 4 Optimize For: Lead-time 

Based on this information, the application will contact each of the vendor Web services and return the name of the vendor with the lowest lead-time for the component with part number 15151. Assuming that Vendor A has the lowest lead-time with 2 days, the EPS application will return the message: The component has been successfully located. The shortest leadtime is offered by Vendor A, and the lead time is 2 days.

To monitor the messages that are exchanged between the EPS application and each Web service, we use the TCP/IP Monitor utility, tcpmon, which is bundled with the Axis distribution. The tcpmon utility works by listening on a particular port for incoming messages. When an incoming message arrives, tcpmon displays the message and then forwards it to another port for handling.

The default listening port for Tomcat is 8080, and all of our SOAP messages are sent to that port. We can have tcpmon listen on another port, say port 1234, and then forward the message to port 8080.

To make this change, we must modify the port numbers within each of the ServiceLocator files (VendorAProcurementServiceLocator.java, VendorBProcurementServiceLocator.java, AltPartServiceServiceLocator.java) of the client-side binding stubs. For instance, within the VendorBProcurementServiceLocator.java file, we change port 8080 in this line:

 private final java.lang.String VendorBProcurement_address = "http://localhost:8080/axis/services/VendorBProcurement"; 

to

 private final java.lang.String VendorBProcurement_address = "http://localhost:1234/axis/services/VendorBProcurement"; 

This changes the port number to which each SOAP message is sent from port 8080 to port 1234.

Now, we can compile these simple changes into the epsdemo Web application. Next, we run the tcpmon utility by typing:

 java org.apache.axis.utils.tcpmon 1234 localhost 8080 

This instructs the tcpmon utility to run and listen for incoming messages on port 1234 and then to forward those messages to port 8080 on the host localhost. Now, run the EPS application (by typing http://localhost:8080/epsdemo into a browser) and then enter Part Number: 15151, Required Quantity: 4, and Optimize For: Lead-time. The ServiceServlet will send SOAP messages to each of the two vendor Web services, and each of these messages will be displayed by the tcpmon.

Figure 12-10 lists the SOAP request message that is sent from the ServiceServlet to the VendorAProcurement Web service, as well as the response message.

Figure 12-10. The (a) request and (b) response messages between the EPS application's ServiceServlet and the Web service VendorAProcurement.
 POST /axis/services/VendorAProcurement HTTP/1.0 Content-Type: text/xml; charset=utf-8 Accept: application/soap+xml, application/dime, multipart/related, text/* User-Agent: Axis/1.1 Host: 127.0.0.1 Cache-Control: no-cache Pragma: no-cache SOAPAction: "" Content-Length: 484 <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  <soapenv:Body>   <ns1:getLeadTime soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://DefaultNamespace">    <in0 xsi:type="xsd:int">15151</in0>    <in1 xsi:type="xsd:int">4</in1>   </ns1:getLeadTime>  </soapenv:Body> </soapenv:Envelope> 

(a) The SOAP request message from the ServiceServlet to the Web service VendorAProcurement

 HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Date: Sat, 16 Aug 2003 16:17:08 GMT Server: Apache Coyote/1.0 Connection: close <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  <soapenv:Body>   <ns1:getLeadTimeResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://DefaultNamespace">    <ns1:getLeadTimeReturn xsi:type="xsd:int">2</ns1:getLeadTimeReturn>   </ns1:getLeadTimeResponse>  </soapenv:Body> </soapenv:Envelope> 

(b) The SOAP response message from the Web service VendorAProcurement.

By analyzing Figure 12-10, we see that the SOAP message is requesting an invocation of the getLeadTime operation of the VendorAProcurement Web service with the arguments 15151 and 4. The response message listed in Figure 12-10 shows the leadtime is 2 days.

Figure 12-11 lists the SOAP request message that is sent from the ServiceServlet to the VendorBProcurement Web service, as well as the response message.

Figure 12-11. The (a) request and (b) response messages between the EPS application's ServiceServlet and the Web service VendorBProcurement.
 POST /axis/services/VendorBProcurement HTTP/1.0 Content-Type: text/xml; charset=utf-8 Accept: application/soap+xml, application/dime, multipart/related, text/* User-Agent: Axis/1.1 Host: 127.0.0.1 Cache-Control: no-cache Pragma: no-cache SOAPAction: "" Content-Length: 496 <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  <soapenv:Body>   <ns1:componentLeadtime soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://DefaultNamespace">    <in0 xsi:type="xsd:int">4</in0>    <in1 xsi:type="xsd:int">15151</in1>   </ns1:componentLeadtime>  </soapenv:Body> </soapenv:Envelope> 

(a) The SOAP request message from the ServiceServlet to the Web service VendorBProcurement

 HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Date: Sat, 16 Aug 2003 16:17:08 GMT Server: Apache Coyote/1.0 Connection: close <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  <soapenv:Body>   <ns1:componentLeadtimeResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://DefaultNamespace">    <ns1:componentLeadtimeReturn xsi:type="xsd:int">4</ns1:componentLeadtimeReturn>   </ns1:componentLeadtimeResponse>  </soapenv:Body> </soapenv:Envelope> 

(b) The SOAP response message from the Web service VendorBProcurement.

By analyzing request (a), we see that the SOAP message is requesting invocation of the componentLeadtime operation of the VendorBProcurement Web service with the arguments 4 and 15151. The response message (b) lists the lead-time as 4 days.

Based on these two response messages, the business logic of the ServiceServlet is able to select Vendor A as the vendor with the shortest lead-time for procuring 4 units of part 15151. Now we have successfully developed and run our EPS application based on Web services for component ordering and fulfillment.

Direct Web Service Invocations (without Binding Stubs)

In our development of the EPS application, we used client-side binding stubs to facilitate communication between the client-application and the consumed Web services. Although binding stubs are automatically generated from a Web service's WSDL description and support a simple programming model, they do incur some overhead. Each time a Web service is used, all of the binding stub files must be generated, instantiated within the client program, and compiled. Each of the binding stub files must also be co-located with the client application. For applications that consume a large number of Web services, the binding stub files represent a significant footprint. This is especially so for mobile applications.

Client applications can also invoke Web service operations without using binding stubs. In order to invoke Web service operations without binding stubs, the application must simply configure metadata for each Web service operation and then make the actual invocation. The simplest way to do this is to use standard JAX-RPC (Java API for XML-based Remote Procedure Call) objects.

Within the ServiceServlet servlet of the EPS application we used client-side binding stubs to invoke Web service operations as follows:

 try {   // Instantiate the VendorAProcurement Service Locator   VendorAProcurementService vapService =     new VendorAProcurementServiceLocator ();   // Now use the locator to get a stub which   //  implements the SDI.   VendorAProcurement vapPort =     vapService.getVendorAProcurement ();   // Call the getLeadTime operation of the Web service   //  through the client proxy method   int vendorAResult = vapPort.getLeadTime ( 15151, 4);   // Now use the result } catch ( Exception e ) {    System.out.println ( "Error in client proxy." ); } 

The same invocation can be accomplished without using any binding stubs as follows:

 try {   // Specify the Web service endpoint   String endpoint =    "http://localhost:8080/axis/services/VendorAProcurement";   Service service = new Service ();   Call call = ( Call ) service.createCall ();   call.setTargetEndpointAddress (     new java.net.URL ( endpoint ) );   // Specify the Web service operation to   //  invoke - getLeadTime   call.setOperationName ( new QName (     "http://soapinterop.org/" , "getLeadTime" ) );   // Optionally, specify the operation and return type   //  parameter names, types, and modes   call.addParameter ( "partNumber" ,                       org.apache.axis.Constants.XSD_INT ,                       javax.xml.rpc.ParameterMode.IN );   call.addParameter ( "quantity" ,                       org.apache.axis.Constants.XSD_INT ,                       javax.xml.rpc.ParameterMode.IN );   call.setReturnType ( org.apache.axis.Constants.XSD_INT );   // Invoke the Web service operation by providing the   //  actual parameter data of 15151 and 4   Object retObj = call.invoke ( new Object[]{                                   new Integer ( 15151 ),                                   new Integer ( 4 )} );   // Now we can cast the return value into an integer   //  and use it.     .     .     . } catch ( Exception e ) {   System.err.println ( e.toString () ); } 

The imports required for this code segment are:

 import org.apache.axis.client.Call; import org.apache.axis.client.Service; import javax.xml.namespace.QName; 

By simply specifying the metadata for each Web service to invoke, we are able to consume any Web service without generating any client-side binding stubs. Although binding stubs support a simple programming model, direct calls to Web services from the client application support dynamic invocations and are easier to maintain over time.



Developing Enterprise Web Services. An Architect's Guide
Developing Enterprise Web Services: An Architects Guide: An Architects Guide
ISBN: 0131401602
EAN: 2147483647
Year: 2003
Pages: 141

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net