not bad post on Document/Literal Wrapper web service

from http://labs.icodeon.com/projects/spd/html/index.html

<div style="overflow: scroll;height: 300px">
This document was created using the &gt;e-novative&gt; DocBook Environment (eDE)
Building Document Literal Wrapped Web Services with Java

Copyright © 2007 Icodeon Ltd

July 2007

Table of Contents

1. Introduction

    1.1. Project
    1.2. Web Services by Design
    1.3. The Document Literal Wrapped Design Pattern
    1.4. Project Context

2. Conceptual Overview

    2.1. Document Based SOAP Web Services
    2.2. Wrapped Documents

3. Web Service Design in XML

    3.1. WSDL Design
    3.2. XSD Design

4. Code Generation

    4.1. Code Generation from WSDL
    4.2. Code Generation from XSD

5. Web Service Deployment

    5.1. Deployment Descriptors
    5.2. Deployment

6. Web Service Testing

    6.1. Self-Description Testing
    6.2. Functional Testing
    6.3. Load Testing
    6.4. Conformance Testing

7. Web Service Clients

    7.1. Clients
    7.2. Generic AJAX Base Client Class
    7.3. Specialized AJAX Client SubClass

8. Conclusion

    8.1. Project Outcomes

Chapter 1. Introduction

Table of Contents

1.1. Project
1.2. Web Services by Design
1.3. The Document Literal Wrapped Design Pattern
1.4. Project Context

1.1. Project

This project describes an approach to designing, building and testing a particular style of web service: the document literal wrapped style. Web services built using this approach provide the benefit of complete self-description, standards conformance, and robustness in the face of changing requirements.

This work has been funded under the JISC e-Learning Program as the "Saving Private Data" project
1.2. Web Services by Design

This project describes an approach to designing, building and testing a SOAP style web service, using the document literal design pattern. The web service is built using code produced using code generation techniques. The approach is to use XML design documents as the foundation and to generate code as an artefact of the design. The web service code is generated from web services definition language (WSDL) design documents and XML schema (XSD) documents:
WSDL to code

Web services code generated from WSDL design

This approach contrasts with the common practice of generating WSDL documents from web service code that has been written by hand:
Code to WSDL

WSDL document generated from web services code

By reversing this common practice of generating WSDL from a hand written code base, and instead using XML documents as a foundational design document, we gain more control over the web service design. This extra control of design then enables the benefits of the document literal wrapped pattern to be realised.
1.3. The Document Literal Wrapped Design Pattern

The document literal wrapped web service produced as a result of this attention to design affords the following benefits:

    *

      Web service code is generated from XML design documents, ensuring design led development.
    *

      With XML documents as the foundational design documents, web services can be generated in different programming languages. The approach is platform neutral. Icodeon have used the techniques described in this project with both Java and C# programming languages.
    *

      Web service operations and the service messages are completely self-describing. This ensures that the web service can be discovered and automatically conmsumed by tools and clients.
    *

      The web service is standards compliant against the Web Services Interoperability profile. This means that the web service can be integrated with other web services technologies, within service oriented architectures and web services orchestrations.
    *

      The web service is robust against future requirements changes. Changes can be made to the messages that the service sends or receives without breaking exisiting clients and consumers.

1.4. Project Context

The project describes an approach to web service design for a service that sends and recieves valid XML documents. The project uses a large and complex schema called the Content Object Communication Datamodel schema published by the IEEE under the IEEE 1484.11.3 specification.

The IEEE schema is an XML binding for the Computer Managed Instruction Datamodel used in the widely deployed SCORM profile of e-Learning specifications.

The project, and this narrative, have the following structure:

   1.

      First, an overview is given of the conceptual overview around SOAP messaging with valid XML documents. See Chapter 2.
   2.

      Second, an XML description for the operations of the web service is designed. The XML document will be used later for code generation of the web service, using a WSDL-to-Java code generation tool. See Chapter 3.
   3.

      Third, schema descriptions for the documents that the web service will send and recieve are defined in XML. The XML schemas will be used later for code generation of the data model used by the web service, using an XSD-to-Java code generation tool. See Chapter 4.
   4.

      Fourth, the code is generated from the XML design documents. The generated Java code from different design documents is combined, edited and compiled. See Chapter 5.
   5.

      Fifth, the compiled code is packaged and deployed using the Apache AXIS web services framework. See Chapter 6.
   6.

      Then, the web service is tested using functional testing, load/stress testing and conformance testing. Testing is carried out by using the Apache JMeter tool to send and receive documents from the web service. See Chapter 7.
   7.

      Finaly, a generic AJAX client for document literal wrapped style is built as a Java Script class using object oriented techniques. The generic client class is sub-classed to a specific client for the particular web service. See Chapter 8.

Chapter 2. Conceptual Overview

Table of Contents

2.1. Document Based SOAP Web Services
2.2. Wrapped Documents

2.1. Document Based SOAP Web Services

SOAP based web services receive messages from clients as SOAP envelopes.
SOAP messaging

Messages sent from client to web service as a SOAP envelope

These envelopes contain message headers and a message body:
SOAP Envelope

SOAP envelope with header and body

In the case of document based services, we can be much more specific about the contents of the message body. The body of the message will hold a valid XML document whose schema is exposed in the WSDL description of the web service. We will sometimes refer to this XML document as the document payload of the envelope: it is this document inside the envelope that provides the value of the message:
SOAP Envelope with document

Valid XML document as the payload of a SOAP message

With the schema for the document embedded in the WSDL description of the web service, introspection tools can query the web service to determine the structure and types for the information carried in the document. The web service is self-describing for the documents it processes.

So already we have an intelligence heavy message: information can be added to the headers, and the document in the message body is described by a schema available by inspecting the web service WSDL.
2.2. Wrapped Documents

However, the message remains lacking in an important feature: the message does not carry with it any instructions about what the web service is to do with the information contained in the document. The message does not contain details of what operation the web service is to invoke on the document. The document literal wrapped design patterm resolves this issue by adding a wrapper around the document that contains the name of the operation to be trageted on the web service.
Wrapped document

Document payload wrapped with the name of the target web service operation

The message we have built has information rich headers, a self-describing, valid, intelligence heavy payload, and carries with it an operation name to tell the web service what to do with the payload. In Icodeon's experience however, this level of information is not yet sufficient. The document remains anonymous: we know what the document is, and how it should be processed, but who is the document from?

To resolve this final issue, Icodeon have found it necessary to nest in-between the wrapper and document a inner-wrapper to carry contextual informaction such as a user ID or a session ID that has no other logical home elsewhere in the message:
Wrapped document

Document payload wrapped with the contextual information, such as a user ID

This completes the conceptual design for the messaging in a document literal wrapped web service. This design translates technically to the following elements:

    *

      A header with descriptive information about the message, such as the content type.
    *

      A root wrapper element with the name of the target operation. We will be persisting the document so we will choose a meaningful verb name for the operation: "Commit".
    *

      A nested inner wrapper element to hold contextual information such as user ID or session ID. We can define this element however we like, so lets use something that refers to the context of the message: "ContextDataModel".
    *

      The document payload itself. This is the Content Object Communication Datamodel document, which has a root element called "cocd".

The four elements make up the content of the SOAP message element we will be sending to the "Commit" operation of the web service:

          
          Content-Type: text/xml; charset=utf-8
          Content-Length: length
          SOAPAction: "http://www.icodeon.com/services/cmi/Commit"
          
          
          
          
          
          
          
          ...
          
          
          
          
          
          
        

Chapter 3. Web Service Design in XML

Table of Contents

3.1. WSDL Design
3.2. XSD Design

3.1. WSDL Design

We are going to use the document/literal wrapped design pattern for the WSDL. A key point of this design is that a new XML element is "wrapped" around the document payload, and that this new element has the same name as the web service operation.

So to send a document to the "Commit" operation of the web service, we first need to wrap the document in an new XML element called "Commit" and define this element in the WSDL:

          
            
                
                ...
                
            
            
        

Now that we have defined the new XML element to wrap the document payload, we need to use the element to define the input to the "Commit" operation. The output of the operation is equivalent to a void return and this is defined also in the WSDL:

          
              
                
               
          
        

With the wrapping element defined, and used as a message part, we are ready to define a web service operation with the same name as the wrapping element:

          
              
                
                  
                
          
        

This abstract descritpion of an operation needs an implementation, so we add this to our design within the binding element of the WSDL:

        
            
            
            
              
              
                  
              
              
                  
              
            
          
      

3.2. XSD Design

We have completed the design of the WSDL, but one practical issue remains. A constraint of the document/literal wrapped design pattern is that the wrapping element's complex type may have no attributes. This is slightly awkward from a practical point of view as we often need to send some contextual information with the request (for example, a userID or sessionID) as attribute values along with the document payload itself (i.e the document that is being sent to the service is associated with a user and session).

To include this contextual information in the request, as well as the document payload, Icodeon have found that a good technique is to nest another element below the wrapping element but above the document wrapper - a kind of inner wrapper. In this example, we have called the inner wrapper "ContextDataModel" as it will hold information about the context. This element is then nested below the wrapping element in the WSDL:

        
            
                
                    
                        
                    
                
            
          
      

Now that we have a element that is free of any constraints from the document/literal wrapped design pattern, we can add whatever contextual information we like to accompany the main document payload. In our example, we will just define a userID and sessionID as attributes:

        
          
            
            
          
          
      

The element that we defined above is not only being used to carry around a few useful identifiers. It is also a parent to the document payload that is the principal meesage that we are sending to the web service operation. So to complete the newly defined element, we need to nest as a child element the root element of the document payload. In our case, the root element of the IEEE 1484.11.3 schema for the Content Object Communication Datamodel is called cocd:

        
          
            
              
            
            
            
          
          
      

We then reference the IEEE 1484.11.3 schema for the Content Object Communication Datamodel and it's root element and namespace with an import statement:

        
          
          
      

The final touch is to separate the schema for this extra contextual information into it's own XSD file and reference the file from within the WSDL with an inclusion statement:

        
            
          
      

This may seem like a lot of hoops to jump through, but the end result is a very powerful design pattern. This is what all the effort we have put into WSDL design now buys us:

    *

      Everything that appears in the SOAP message's body is defined by a schema. The document payload needs no out of band agreement to describe the documents that may be sent to and received from the web service.
    *

      In addition to a defined schema for the document, we have defined a schema to carry small bits of contextual information, such as user identifiers.
    *

      The service WSDL is robust. If we need to add new elements to any of the schemas in the future, we can simply add a new namespaced element. Existing implementations of service consumers will not break, but new implementations can take advantage of additions to the service.
    *

      Not only does the WSDL provide a description of the documents that can be sent to it and recived from it, the WSDL describes the available operations. So we have a fully self-describing service interface that can dynamically tell clients and tools what operations are availale, what documents can be sent, and what documents can be recieved.
    *

      We have separated out WSDL definitions and document schema definitions into their own files for easier maintainability. The files are referenced by inclusion and import statements.,
    *

      Last, but not least, we have created a WSDL design that is WS-I compliant. The wrapped pattern meets the WS-I restriction that the SOAP message's body has only one child. In our case, this single child is the new XML element used as the wrapper. 

Now that the WSDL design is complete, we are in a position to use the WSDL to drive code generation of the method stubs for each of the web service operations.
Chapter 4. Code Generation

Table of Contents

4.1. Code Generation from WSDL
4.2. Code Generation from XSD

4.1. Code Generation from WSDL

In the preceeding section on WSDL design, we created a WSDL that was self-describing, robust and WS-I compliant. In this section we will use tools to generate Java code from the design.

The Apache AXIS project includes a WSDL to Java generator (called wsdl2java) for generating code from WSDL designs. This is great as far as it goes, but our WSDL includes not only WSDL definitions but also references to schema definitions in external XSD files. The AXIS wsdl2java tool does a great job with the WSDL definitions, but a poor job with the referenced XSD files. Writing a tool that can correctly handle highly completx schema is no trivial task and it alone represents an effort as potentially difficult as the whole of the AXIS project itself.

A solution to this issue that Icodeon have found to be helpful is to use "best of breed" tools for each task: to use wsdl2java for generating Java code for the web service operations, and a different (and superior) toolset for generaing Java code from referenced XSD files. In this example, we will use the Castor Code Generator.

So the approach is to first use Apache AXIS wsdl2java to generate Java code for web service operations and referenced XSD files, then throw away the code generated from XSD and replace it with code generated with the (superior) Castor tools. Here is the process:

   1.

      Use the Apache AXIS wsdl2java tool to generate generate Java code for web service operations and referenced XSD files. A useful way to do this is from an ANT build file task:

                    
                    
                      
                          
                      
                    
                  
                  

      A useful point to note is that the AXIS wsdl2java tool will try to "guess" package naming for the generated Java code from the namespacing in the WSDL file. This is usually NOT what we want, so we reference a "namespace mapping" properties file (called NStoPkg.properties) which tells AXIS wsdl2java how to map namespaces to package names. In our case we have defined an Icodeon namespace:

                    
                    http://www.icodeon.com/services/cmi
                  
                  

      and we map this to a package hierachy in the NStoPkg.properties file, using escape characters as needed:

                    
                    http\://www.icodeon.com/services/cmi=com.icodeon.services.cmi
                  
                  

   2.

      The Apache AXIS wsdl2java tool generates the Java code for the web service from the WSDL file and document code from referenced XSD files. By looking at the generated code, we can soon see that wsdl2java is not doing a very good job with the referenced XSD files, and we are getting some strange naming conventions as an artefact of code generation. For example, a generated file called:

                    
                    CmiServiceSoapBindingStub.java
                  
                  

      This file contains the code for serializing an element called "mode" in the the IEEE 1484.11.3 schema for the Content Object Communication Datamodel. But the code generation has incorrectly added an angle bracket to the element name:

                    
                    new javax.xml.namespace.QName("http://ltsc.ieee.org/xsd/1484_11_3", "&gt;mode");
                  
                  

      So we need to recognise that Apache AXIS wsdl2java tool needs to be replaced with a superior tool when it comes to code generation from XSD files. Later on we will use Castor Code Generator, but for the moment we will delete all generated code and classes that come from XSD files, including the strange code artefacts with angle brackets.

      There are two steps to the deletion. The first step is to delete the entries with the strange angle brackets. All code sections that contain the angle brackets in references to a qualified name are deleted:

                    
                  qName = new javax.xml.namespace.QName("http://ltsc.ieee.org/xsd/1484_11_3", "&gt;mode");
                  cachedSerQNames.add(qName);
                  cls = com.icodeon.services.cmi.Mode.class;
                  cachedSerClasses.add(cls);
                  cachedSerFactories.add(enumsf);
                  cachedDeserFactories.add(enumdf);
                  
                  

      The second step is to delete all files that have been generated from the XSD schemas, as we will replace these with files generated by a superior tool later on.

      This leaves us with only the code and classes representing the web service, the web service operations and the wrapper elements that have the same names as the web service operations. After deletion we are left with the files for the web service:

                    
                  CmiService.java
                  CmiServiceLocator.java
                  CmiServicePortType.java
                  CmiServiceSoapBindingImpl.java
                  CmiServiceSoapBindingStub.java
                  
                  

      and also the files for wrapper elements that have the same names as the web service operations:

                    
                  Initialize.java
                  Commit.java
                  Terminate.java
                  
                  

      We also need to keep hold of two other files that come from the code generation also that will be used for setting up the way the web service serializes XML to Java. These are known as web service deployment descriptors (.wsdd file type):

                    
                  deploy.wsdd
                  undeploy.wsdd
                  
                  

Now that the code generation for the web service from the WSDL design file is complete, we need to generate the code to represent the document payload that is sent to the web service. That is, we need to run code generation from XSD files to replace the malformed code we deleted that was emitted by the Apache AXIS wsdl2java tool.
4.2. Code Generation from XSD

In the preceeding section we looked at the IEEE 1484.11.3 Schema, a fairly large and complex schema. The Apache AXIS project WSDL to Java generator (called wsdl2java) was not able to generate code from this schema without error, so we need to use a superior tool for working with XSDs. Fortuneately such as toolset exists and is available from the Castor project at Codehaus.

The Castor project includes an XSD to Java generator for generating code from XSD designs. We will use this tool to generate the Java code and replace the incorrectly generated code from AXIS wsdl2java. Here is the process:

   1.

      Use the Castor toolset to generate Java code for the XSD files. A useful way to do this is from an ANT build file task:

                    
      	          
      	          
      	          
      	          
      	          
      	          
                  
                  

   2.

      Castor toolset generates the Java code from the XSD files. By looking at the generated code, we can soon see that the code generation from Castor is not perfect either, with some elements being mapped to Java Objects rather than Java Strings, with invalid constructors, such as:

                    
                    new java.lang.Object("4000");
                  
                  

      Luckily this can be simply corrected with and extensive search and replace for the correct, valid String constructors, such as:

                    
                    new java.lang.String("4000");
                  
                  

Now that the code generation for the web service from the WSDL design file is complete, AND the code generation from XSD is complete, we have a complete code set for the document literall wrapped web service derived from "best of breed" tools for WSDL and for XSD. The code can now be compiled packaged into a .jar file ready for deployment as a web service.
Chapter 5. Web Service Deployment

Table of Contents

5.1. Deployment Descriptors
5.2. Deployment

5.1. Deployment Descriptors

Java based web services that consume or produce documents need to know how to convert XML elements to instances of Java objects: that is, to managed the serialization/deserialization from XML to Java and back again. In the Apache AXIS web services framework, thus serialization/deserialization is managed by web service deployment descriptors, or .wsdd files.

We noted earlier that code generation from XSDs by Apache AXIS wsdl2java tool produced errors, and that the code had to be replaced by code generated from the Castor toolset. The same errors that appeared in the Java code also appear in the web service deploment descriptors (.wsdd file types) generated by Apache AXIS also. For example, in the deployment descriptor called:

        
            deploy.wsdd
        
      

angle brackets have been incorrectly added to many of the element names in the IEEE 1484.11.3 schema, such as the mode element:

        
          mode"
          type="java:com.icodeon.services.cmi.Mode"
          serializer="org.apache.axis.encoding.ser.EnumSerializerFactory"
          deserializer="org.apache.axis.encoding.ser.EnumDeserializerFactory"
          encodingStyle=""
          /&gt;
       
      

So in the same way that we had to replace code generated by Apache AXIS wsdl2java with code generated by the Castor toolset, we now have to replace the AXIS serializers/deserializers with their Castor equivalents. For example, the root element of the IEEE 1484.11.3 schema, an element called cocd, had a serializer/deserializer generated by Apache AXIS that looked like this:

        
          
       
      

This serializer/deserializer now needs to be replaced with it's Castor equivalent:

        
          
       
      

In addition to detailing the serializers/deserializers that the web service will use, the deployment descriptor includes other details, such as which web service operations are to be exposed, and the name to be used for the service. In this example, we'll use the name "CmiService" (which later on will become part of the URL of the service) and expose the three operations called "Initialize", "Commit" and "Terminate":

        
          
            ...
            
            ...
          
          /&gt;
       
      

With the deployment descriptors now modified to support the Java objects generated by the Castor toolset, we are ready to deploy the web service.
5.2. Deployment

Our document literal web service code has been generated from design files, WSDL and XSD. The generated code has been compiled and packaged into a .jar file, and deployment descriptors have been modified to support Java objects generated by the Castor toolset. We are now ready to deploy to an application server.

Apache AXIS is servlet based, and so a servlet container such as the popular Jakarta Tomcat is required. Icodeon used a recent build, version 5.5. The web service was deployed to a web application under the Tomcat servlet container. Here are the steps:

   1.

      Set up a web application in Apache Tomcat with a simple web.xml file to support Apache Axis. Icodeon used a web application name of "cmi", but any name could be chosen. Within this web application we will need to set up the Apache AXIS servlet, and mappings for this servlet to URL patterns:

                  
                  
                    AxisServlet
                    org.apache.axis.transport.http.AxisServlet
                  
                  
                    AxisServlet
                    /servlet/AxisServlet
                    
                    
                    AxisServlet
                    /services/*
                  
                
                

   2.

      Copy to the lib directory of this web application all the supporting .jar files requried for Apache AXIS and Castor. For this example, Icodeon used:

                  
                  axis.jar
                  jaxrpc.jar
                  saaj.jar
                  wsdl4j-1.5.1.jar
                  castor-1.1.1-xml.jar
                  commons-discovery-0.2.jar
                  
                

      The result is that we now have an "empty" web application within Tomcat that has all the infrastruture to support a web service, but no web service yet deployed! You can check that the AXIS administration service is running (ready for the next step) by pointing your browser to:

                  
                    http://localhost:8080/cmi/services/AdminService?wsdl
                  
                

   3.

      The next step is to deploy the .jar file of the document literal wrapped web service we have generated using the modified deployment descriptors and a helpful deployment task from an ANT build file.

      Apache AXIS comes with ANT build file "administration" tasks for deploying and undeploying web services to servlet containers such as Tomcat. Here is the "axis-admin" deployment task which contains a reference to our modified .wsdd file and a reference to the web application (called cmi) that we have set up within Tomcat:

                  
                    
                    
                    
                  
                

      When this task runs a new file is created within the web application called:

                  
                    server-config.wsdd
                  
                

      This file should contain the details of serializers/deserializers that were added to the web service deployment descriptors. It is Icodeon's experience however, that this file is not always complete after the first deployment using the axis-admin task: the first invocation of the task appears to generate the file, and once the file has been "seeded", then a second run of the deployment using the axis-admin task adds the details of serializers/deserializers.
   4.

      Finally, point your browser to the location of the web service, and if all is well, we have a live web service generated entirely from WSDL and XSD design files:

                  
                    http://localhost:8080/cmi/services/CmiService?wsdl
                  
                

Chapter 6. Web Service Testing

Table of Contents

6.1. Self-Description Testing
6.2. Functional Testing
6.3. Load Testing
6.4. Conformance Testing

6.1. Self-Description Testing

One of the benefits of the document literal wrapped design style is that the resulting web service is completely self-describing: not only are the operations of the web service described (by the WSDL file), but also the documents that the web service sends or receives are described (by the associated XSD files). There are a number of tools that can bind to the web service, use this self-describing behaviour and query the service properties: Icodeon have used the SOAP Analyser in the Oxygen XML Editor.

The screen shot below shows the SOAP Analyser in the Oxygen XML Editor querying the "Commit" operation of the web service that was deployed above. The SOAP Analyser is showing that, as expected, the "Commit" operation of the web service is expecting a document with an element called "Commit" as a single child of SOAP message's body which is parent to an element called "ContextDataModel" that will be the parent to the document payload.
SOAP Analyser in the Oxygen XML Editor

SOAP Analyser in the Oxygen XML Editor querying the "Commit" operation of the web service [full size]
6.2. Functional Testing

Now that the web service is deployed, and we have checked that tools can dynamically read the self-description of the web service, we are ready to check the functionality of the web service. Icodeon have successfully used the Apache JMeter load testing tool for this purpose. Apache JMeter has traditonally been used for load testing web applications by sending URL parameters to web application URLs, and then examine the response mark up. However, the software can also be configured to send whole documents to a document style web service, and then query the response document. So we can use this approach to test that the web service operations have the behaviour we are expecting. Here are the steps:

   1.

      To set up Apache JMeter for web service testing, a couple of extra .jar files need to be added to the classpath of JMeter. We need to add in the mail.jar and activation.jar available from Sun Microsystems to the lib directory of Apache JMeter.
   2.

      Next, we need to author aTest Plan, and the first step is to set up a group of users/clients each making many independent requests to the web service. To get JMeter to represent these many concurrent users/clients accessing the web service, we need to set up a Thread Group. We may also need tp represent each of these users/clients making many requests within a session, so we add a Loop Controller as a child to the Thread group. During the functionality testing we can simply leave the defaults set of a single user/client making a single request.
   3.

      Now that we have a single user/client set up, we need to specifiy the operation that the user/client will invoke. This is an easy step because the document literal web service we have designed is self-describing: we simply have to point JMeter at out web service WSDL and JMeter will dynamically configure itself for all of the operations exposed on the web service. Add a Web Service (SOAP) Request Sampler to as a child of the Loop Controller element, enter the URL for the WSDL in the in the WSDL URL field, and press the Configure button. JMeter queries the WSDL and then configures itself for each of the web service operations. We will test the "Commit" operation, so select that from the drop down list of "Web Methods" that JMeter has discovered from the WSDL.
   4.

      We have configured a single user/clien and the web service operation to invoke. The final step in out preparation is define the document that will be sent to the web service. At this point, we can use a tool like the SOAP Analyser in the Oxygen XML Editor to generate the document that will be sent in the invocation of the web service. Paste the document into SOAP Data field, and everything is prepared.
   5.

      To visualise, log and test the invocation and response from the web service operation, JMeter offers a number of tools. We will use a Summary Report Listener and a Save Resonses to a File Post Processor. The Summary Report will give us a table showing request and response statistics; the Post Processor will write the web service operation to a file. The final set up is shown in the screen shot below:
      JMeter Test Plan

      Apache JMeter Test Plan for the "Commit" operation of the web service [full size]
   6.

      All that remains now is to Run the Test Plan (which by default is for one user/client making a single request) and check the results. We check the results in two ways. First, we check the file from the Post Processor to ensure that we get the expected document as the response, and second we check the report from the Summary Report Listener. For functional testing, the most imporant result is the document that is returned and written to file. However, the report shows that for one user making a single request, we have a response time of about 30 ms. which will be useful bench mark for the load testing which we work on next.
      Summary Report

      Apache JMeter Summary Report for a Single Client Making a Single Web Service Invocation[full size]

6.3. Load Testing

The functional testing enabled us to send documents to a chosen web service operation and examine the response. We can use this as a foundation to build a test plan for load testing the web service. Here are the steps:

   1.

      In the Save Resonses to a File Post Processor, check to make only failed responses saved to disc: we only want to know about web service faults now that the functional testing is complete.
   2.

      Add more samplers to create a more realistic test plan. In our example, we can add an invocation for each of the web service operations: "Initialize", "Commit" and Terminate".
   3.

      Next we have to set up simulated group of concurrent users making multiple requests within a session by modifying the three parameters within the thread group: the number of users/clients, the ramp-up period and the number of requests each user will exceute within the test session. The number of users parameter is self-explanatory (we will use 100) and the number of times they make a request are self explanatory, but the ramp-up period takes some care to set up.
   4.

      Ramp-up is the time period over which all users/client join. By default, ramp-up is set to one second, so for our scenario of 100 users, we have a default join rate of 1 user/client thread every 10 ms. To refine this figure we can look at the JMeter log (located in JMeter_Home_Directory/bin) and tune the ramp up period so that the first thread does not finish before the last thread has started. This ensures that we do not have too long a ramp-up. In our example, Icodeon found that this conditon could be achieved with one new user/client thread joining every 1ms, tuning the ramp-up to a duration of 0.1 seconds:

                  
                  10:09:51 INFO  - jmeter.threads.JMeterThread: Thread Thread Group 1-1 started
                  ...
                  10:09:51 INFO  - jmeter.threads.JMeterThread: Thread Thread Group 1-100 started 
                  ...
                  10:09:52 INFO  - jmeter.threads.JMeterThread: Thread Thread Group 1-1 is done
                  
                

   5.

      Although our ramp-up is quite steep (one new user/client joining very 1ms), we need to also reflect the rate at which users/client continue to invoke service operations once they have joined. To do this we can use a Gaussian Random Timer to manage the pause that users/clients have between each web service request. We will use the JMeter defaults of a pause of 100ms and a deviation of 300ms.
   6.

      All that remains now is to Run the Test Plan and check the results. The screen shot below shows the finished test plans and the results of the load test.
      Summary Report

      Apache JMeter Summary Report for a 100 Clients Making a Web Service Invocations to Three Operations [full size]
   7.

      The summary tells us that our web service handled 300 requests at a throughput of 187 requests per second. The fastest response time was 5 ms, the slowest response was 352 ms, and the average response time was 149ms. Studies in cognitive science suggest that users users lose focus and start doing something else if the response time is greater than 10 seconds. Under our conditions of a steep ramp-up to 100 users, followed by a "user click rate" every 100+/-300ms, our web service is providing acceptable performance.

6.4. Conformance Testing

Chapter 7. Web Service Clients

Table of Contents

7.1. Clients
7.2. Generic AJAX Base Client Class
7.3. Specialized AJAX Client SubClass

7.1. Clients

A key benefit of the designing web services with the document literal wrapped design pattern is that the web service is completely self-describing. This means that all sorts of tools and consumers that have the ability to introspect the web service WSDL can automatically generate intefaces and code to interact with the web service.

Icodeon have found that the Macromedia/Adobe authoring tools for Flash have particularly good support for rapid development of clients to web services.

In this project, a client based on asynchronous Javascript and XML (AJAX) is built, rather than using the Flash tools - the approach requires no special authoring environment or browser plugins. The approach is to use object oriented (OO) techniques to build a generic client base "class" for interacting with document literal web services in general, and then "sub-class" this functionality to build a specific client for interacting with the web service built in this project.

The approach of using object oriented techniques with the JavaScript programming language has been possible for many years but recently a diverse set to tools has made this approach more viable than it has been previously. Icodeon have used this toolset to build re-usable JavaScript "class libraries" that make use of OO techniques such as inheritence, encapsulation, package namepsacing and private/public variable scope. The available tools now mean that unit testing, logging and automated generation of documentation is available for these "classes" also. also.

Altough it is not strictly true to say that JavaScript supports class-based inheritence (like Java or C#), because it supports a prototyped-based inheritence instead. However, we can use the term "class" and "sub-class" informally as we are simulating the featues of object orientation that appear in true class-based languages.
7.2. Generic AJAX Base Client Class

The first task required before building a base class and any sub-classes is to simulate the package namespacing found in languages such as Java and C#. Our classes will be part of a unqiue namespace to distinguish them from any other classes of the same class name that may be present in a browser window. We will use the common practivce of reversing a domaon name to use a unique namespac: "com.icodeon" will be used, and build our base class called "AjaxClient" class within this namespace. The fully qualified class name is then "com.icodeon.AjaxClient".

First, we set up the "package" namespace, checking that the namespace is truly unique.

        var com;
        if(!com){com = {};}
        else if(typeof com != "object")
        {throw new Error("namepsace com exists");}
        
        if(!com.icodeon){com.icodeon = {};}
        else if(typeof com.icodeon != "object")
        {throw new Error("namepsace com.icodeon exists");}
      

The we set up the "class" namespace, again checking that there are no namspace collisions:

        if(!com.icodeon.AjaxClient){com.icodeon.AjaxClient = {};}
        else if(typeof com.icodeon.AjaxClient != "object")
        {throw new Error("namepsace com.icodeon.AjaxClient exists");}
      

No that we have a unique namespace, we can declare a constructor:

        icodeon.AjaxClient = function(){
        ...
        }
      

Public methods to be inherited or overidden in sub-classes can be declared:

        this.methodName = function(){
        ...
        }
      

Public inherited properties can be declared:

        this.inheritedPropertyName = ;
      

Private properties that will not be inherited are declared within the constructor

        var privatePropertyName = ;
      

The base class has two just two important methods - other methods are just utilities. The two important methods are the methods that call the web service with a document and then process any document that is returned. We will call these methods "request" and "response", and both need to be inherited by specialist sub-classes:

          this.request = function(){
          ...
          }
          this.response = function(){
          ...
          }
      

The "response" method will be unimplemented in the base class (the base class does not know what to do with a specific response) but will be overidden with an implementation in a sub-class:

        /**
        * Public inherited method that is called after 
        * a web service operation. Sub-classes implement this.
        * 
        * @param {Object} obj_SoapEnvelope the SOAP envelope response
        * @param {Error} obj_Error application specific error or undefined
        */
        this.response = function(obj_SoapEnvelope, obj_Error){
        ...
        }
      

The "request" method takes a number of arguments necessary to call the web service operation. In particular is it passed a DOM object that represents the SOAP envelope and wrapped document:

        /**
        * Public method to invoke a web service operation.
        * 
        * @param {String} str_HttpAction the GET or POST action
        * @param {String} str_ServiceEndPoint the service endpoint URL
        * @param {Boolean} b_Async flag for asynchronous call
        * @param {String} str_SoapActionHeader the SOAP action request header
        * @param {Object} obj_SoapEnvelope the SOAP envelope request and payload
        * @param {Function} fn_CallBack the function to be called on response
        */
        this.request = function(
          str_HttpAction,
          str_ServiceEndPoint,
          b_Async,
          str_SoapActionHeader,
          obj_SoapEnvelope){
          ...
      }
      

7.3. Specialized AJAX Client SubClass

Having built a generic client class to invoke document services in general, we now need to subclass the functionality to build a client that will send and receive documents from a web service in particular. In the case of this project, we will build a client for the web service we have generated from the WSDL and XSD design documents.

The key featues of this client will be:

    *

      A constructor or method that enables us to specify the URL of the web service.
    *

      Semantics to derive a sub-class from the base class.
    *

      A method that enables us to build a wrapped document to send to a particular operation of the web service.
    *

      An overriden implementation of the "reponse" method of the base class. This is where we can implement specific handling of the response from the operation that was invoked.

These 4 key featrues are implemented by the following code:

First, the constructor will specify the URL end point for the web service:

        /**
        * Construct a new CmiServiceClient class.
        * 
        * @class This class represents an instance of CmiServiceClient .
        * @extends AjaxClient
        * @constructor
        * @param {String} str_HttpAction the GET or POST action
        * @param {String} str_ServiceEndPoint the web service endpoint URL
        * @param {Boolean} b_Async flag for asynchronous call
        * @return A new instance of com.icodeon.CmiServiceClient 
        */
        com.icodeon.CmiServiceClient = function(
          str_HttpAction,
          str_ServiceEndPoint,
          b_Async
        ){
        ...
        }
      

Second, to support inheritence the sub-class constructor will ensure there is a call to the super-class constructor and the inheritence relationship will be explicitly defined:

        com.icodeon.AjaxClient.call(this);
        ...
        com.icodeon.CmiServiceClient.prototype = new com.icodeon.AjaxClient();
        com.icodeon.CmiServiceClient.prototype.constructor = com.icodeon.CmiServiceClient;
      

Third, we will build the wrapped document as DOM object, using the Sarissa utility library to help with cross-browser neutral implementation of DOM functionality:

        // Create the SOAP Envelope
        var obj_DomDoc = Sarissa.getDomDocument("http://schemas.xmlsoap.org/soap/envelope/","Envelope");
        var elm_Document = obj_DomDoc.documentElement;

        // Add the header
        var elm_Header = this.createNewElement("Header", "http://schemas.xmlsoap.org/soap/envelope/", obj_DomDoc);
        elm_Document.appendChild(elm_Header);
        
        // Add the body
        var elm_Body = this.createNewElement("Body", "http://schemas.xmlsoap.org/soap/envelope/", obj_DomDoc);
        elm_Document.appendChild(elm_Body);
      

Fourth, we will override the inherited "response" method so that we can implement specific handling of the document sent in response from the web service. In this implementation, the response method callsback to a further function that will take over the processing of the response:

        this.response = function(doc_ResponseXML, obj_Error){
        ...
          callback.call();
        }
      

Now that these four elements of the client are in place, further utlity methods can be added, and the class is ready to be instantiated within a HTML and JavaScript only web page:

        // Create the AJAX client
        obj_Client = new com.icodeon.CmiServiceClient(
          str_HttpAction,
          str_ServiceEndPoint,
          b_Async);
          
        // Invoke a named web service operation, and name a function to recieve the response
        obj_Client.invoke(str_OperationName, fn_CallBack);   
      

Chapter 8. Conclusion

Table of Contents

8.1. Project Outcomes

8.1. Project Outcomes

    *

      The document literal wrapped web service style has proved challenging to build using tools available for the Java platform. Icodeon have found that the equivalent tools available for the .NET platform have been easier to use by several orders of magnitude, with greatly improved times for code generation.
    *

      No single Java tool was found to be sufficient. Instead, combining the outputs from several "best of breed" tools, each dedicated to a specific task was found to be successful: Apache Axis for web services description files (WSDL), and Castor for the XSD schemas linked to the WSDL files.
    *

      The Apache JMeter load testing tool can be used very successfully for both functional testing and stress testing of document style web services. The response document from a web service call can be dynamically queried and the results added to a parameterized version of a document for a subsequent web service request.
    *

      The toolset for building sophisticated client side code in the JavaScript programming language is becoming mature. In this project many of the features of traditional object oriented code (classes, encapsulation, inheritance etc) along with Unit Test frameworks and Documentation Generation tools were employed to build AJAX clients for document based web services.

Icodeon Ltd

July 2007
This document was created using the &gt;e-novative&gt; DocBook Environment (eDE) 
</div>
Advertisements