Service Programming Model

   

The Service Programming model is based on the illustration in Figure 12.1, which shows the object system and the relationships as implemented by GT3. The core concepts we can infer from this model include grid service base and its implementation, grid service callback mechanisms, operation providers, factory and its callback concepts, and the service data. The next few sections explain the details on these concepts.

Figure 12.1. The Service Programming Model.

graphics/12fig01.gif

Grid Service Behavior Implementation

The OGSI-defined grid service behaviors are defined in the GridService interface, and is implemented by the default GT3-provided GridServiceBase interface. This GridServiceBase interface forms the basis of all grid services created by GT3. This base interface provides operations for OGSI-defined grid service behaviors, service instancespecific properties management, service data query and modifications, service data set management, and service facilities to add/delete operation providers.

Figure 12.2 lists all the operations available with a GT3-created grid service. All these interface operations are not exposed to the client; only GridService interface operations are exposed through WSDL.

Figure 12.2. This illustration depicts the GT3 Grid Service exposed operations.

graphics/12fig02.gif

This framework utilizes the rest of the interfaces to control grid service behavior. This separation of responsibilities is controlled through a security authorization mechanism. In addition to these standard interfaces, the grid service may implement custom-exposed interfaces defined through WSDL. Another mechanism by which we can expose the service interfaces is through operation providers, and registering them with the service implementation. The next section provides the details on this topic.

As we have discussed in Figure 12.1, GT3 provides two default implementations of the Grid-ServiceBase interface. These are:

  • GridServiceImpl. This implementation forms the base class for services that are transient. These services are created through the OGSI factory mechanism.

  • PersistentServiceImpl. This forms the base class for all persistent services, which are created through configuration entries and always available in the container. These services are not created through the OGSI factory mechanism.

These base classes provide a number of functionalities, including service data management, operation provider management, and service instance lifecycle management. We are deferring the more detailed discussion on persistent and transient services to later sections of this book.

Based upon this discussion, there are two design patterns available for our service creation. These are:

  1. Service extending GridServiceImpl, or PersistentServiceImpl implementing our service exposed interface(s).

    Figure 12.3. The grid service implementation pattern utilizing the service interface implementation.

    graphics/12fig03.gif

  2. Service extending GridServiceImpl or PersistentServiceImpl while using operation providers to implement our service interfaces (Figure 12.4).

    Figure 12.4. The Grid Service implementation pattern utilizing the operation provider.

    graphics/12fig04.gif

Operation Providers

The above discussion addressed the base implementation classes (i.e., PersistentServiceImpl and GridServiceImpl) provided by GT3. These are useful for service developers to hide a number of complexities associated with the service creation. However, some have a number of extensibility problems with this approach, which are:

  • Due to the unavailability of multiple inheritances in Java, service developers utilize the default interface hierarchy, as provided by the framework.

  • Some of the behaviors implemented by the aforementioned classes are specific to the GT3 container, and hence porting the service implementation may not be possible.

  • Dynamic configurations of service behaviors are not possible.

Related to resolving these problems, GT3 has introduced a dynamic delegation model for service operations. This is a flexible and dynamically customizable model, where these operation providers are integrated into the service configuration at deployment time. In addition to the above static deployment model, there are provisions available with GridServiceBase to dynamically add the operation providers during execution.

This flexibility allows one to program the business logic into these providers, and the grid service implementation will then delegate the service calls (as described in the WSDL) to these providers. Given this, we can change them, add new functionalities, and add new interfaces with new operations to expose more functional capabilities (i.e., provided these operations are listed in WSDL).

To assist grid service developers to handle some common design patterns, the default GT3 comes with three different types of operation providers: factory provider, ServiceGroup provider, and NotificationSource provider (respectively). Since these providers enable some of the important functionalities for any service's Utility value, we are providing a detailed discussion on these special types of providers later in this chapter.

Let us now examine some specific code segments behind an operation provider.

  1. The following will create an "OperatingSystem" grid service that implements service behaviors, and then deploys this functionality to the GT3 container.

Listing 12.1. The operating system service implementation (most of the implementation is omitted for code clarity).
 public class OperatingSystemImpl extends PersistentGridServiceImpl{     public OperatingSystemImpl () {         super("Operating system Service");     }     public void postPersistentCreate(GridContext context)         throws GridServiceException {     } ......................................... } 
Listing 12.2. The operating system service deployment in a configuration file.
  1.  <service name="ogsa/cmm/OperatingSystemService" provider="Handler"                       style="wrapped" use="literal">  2.  <parameter name="allowedMethods" value="*"/>  3.  <parameter name="className"                value="org.ogsa.core.cmm.OperatingSystemPortType"/>  4.  <parameter name="baseClassName" value=" org.ogsa.core.cmm                 .OperatingSystemImpl "/>  5.  <parameter name="persistent" value="true"/>  6.  <parameter name="schemaPath" value="schema/core/cmm/                 operatingsystem_service.wsdl"/>  7.  <parameter name="handlerClass"                 value="org.globus.ogsa.handlers.RPCURIProvider"/>  8.  <parameter name="securityConfig" value="org/globus/ogsa/impl/                 core/management/security-config.xml"/>  9.  </service> 

Listings 12.1 and 12.2 show how we are able to create a service and configure that service for the container. Here we are deploying the service into the default Apache AXIS Web service container using the Web Service Deployment Descriptor (WSDD). Once this service is deployed onto the container, the client should be able to start invoking methods on the service using the base OGSI GridService portType.

  1. Later we decide that the above grid service needs to support OGSI Notification source behaviors so that interested clients can subscribe for service state change notifications.

If we find that the default NotificationSourceProvider function that is offered by GT3 is sufficient (see the discussion later on regarding this default provider), then we can establish that behavior just by changing the deployment configuration (next) from what was previously shown in Listing 12.2.

Listing 12.3. The operating system service deployment with a NotificationSourceProvider.
  1.  <service name="ogsa/cmm/OperatingSystemService" provider="Handler"  2.  style="wrapped" use="literal">  3.  <parameter name="allowedMethods" value="*"/>  4.  <parameter name="className"  5.  value="org.ogsa.core.cmm.OperatingSystemPortType"/>  6.  <parameter name="baseClassName" value=" org.ogsa.core.cmm                  .OperatingSystemImpl "/>  7.  <parameter name="persistent" value="true"/>  8.  <parameter name="schemaPath"  9.  value="schema/core/cmm/operatingsystem_service.wsdl"/>  10.  <parameter name="handlerClass"                  value="org.globus.ogsa.handlers.RPCURIProvider"/>  11.  <parameter name="securityConfig"  12.  value="org/globus/ogsa/impl/core/management/                  security-config.xml"/>  13.  <parameter name="operationProviders"  14.  value="org.globus.ogsa.impl.ogsi                  .NotificationSourceProvider"/>  15.  </service> 

Note these changes in the configuration information in Listing 12.3 on line 13. In addition to the normal grid service behaviors, the client can subscribe for notifications on the operating system service. This provides an amazing degree of flexibility for grid service developers.

Another way to add an operation provider is by using the "addOperationProvider" operation on the GridServiceBaseImpl class. This is performed in the service postCreate() or postPersistentCreate() calls, as shown in Listing 12.4.

Listing 12.4. The dynamically adding operation provider.
  1.  public void postCreate(GridContext context){  2.  addOperationProvider(new NotificationSourceProvider ());  3.  } 
  1. After a while, the operating service provider now decides to use a new notification source provider with more complex functionalities. For example, the service developer decided to provide a JMS message queueing facility rather than the simple point-to-point mechanism for notification. For this functionality, the service developer must create a new NotificationSource provider and register that with the container. Listing 12.5 shows a sample skeleton of the JMSNotificationSourceProvider.

Listing 12.5. The JMSNotificationSourceProvider implementation (the low-level details on the implementation are avoided for the sake of simplicity and clarity).
 public class JMSNotificationSourceProvider implements              OperationProvider, .......{     private static final QName[] operations = new QName[] {new         QName("http://www.gridforum.org/namespaces/2003/03/                 OGSI","subscribe")}     public NotificationSourceProvider(){}     public void subscribe(ExtensibilityType subscriptionExpression,                    LocatorType sink,                    ExtendedDateTimeType expirationTime,                    LocatorTypeHolder subscriptionInstanceLocator,                    TerminationTimeTypeHolder currentTerminationTime){         //Our implementation goes here.     } .......................... } 

In order for the service to use this new JMS Notification provider described in Listing 12.5, we need to change the class name in Line 13 of Listing 12.3 with the new operation provider class name. The framework will take care of the rest of the processing.

Internal Details of Operation Provider Processing

One may be asking at this stage exactly how operation provider processing is happening inside the GT3 framework. Let us explore some basic information, as this will help us construct more robust services.

  1. NotificationSource provider exposes the QNames of the operations it is exposing to the clients. These must match the WSDL-described operations name and their namespace. In Listing 12.5, we can see that the class contains a static QName list of the service operations (i.e., "subscribe" operation in the "OGSI" namespace) it is exposing. This operation matches the WSDL NotificationSource portType's "subscribe" operation. We can always add wildcard characters for QName parameters, namespace name, and operation name. For example, in order to expose all public operations in a provider, create a QName with new QName("*"."*"). This is a flexible approach to expose all public operations in the provider. Note that for the standard interfaces defined by OGSI, it is required to provide the namespace qualifier that matches the OGSI operation namespace.

  2. All notification providers expose the "getOperations()" method for retrieving the exposed operations (as described above).

  3. For correct operation name lookup and invocation, all the exposed operations must follow the same signature, as described by the corresponding WSDL operation.

  4. The above sequence diagram (Figure 12.5) illustrates how the framework handles this operation provider, and then the corresponding invocation method.

    Figure 12.5. A sequence diagram for handling the operation providers.

    graphics/12fig05.gif


Factory Callback Mechanism

The OGSI and GT3 provides standard mechanisms for the construction of grid services using a factory pattern. The factory extracts the grid service construction mechanisms as an abstraction. GT3 provided the "factory callback" mechanism, and enables the facilities for adding custom factories for additional services. These custom factories provide capabilities to create services in a remote hosting environment, and can then align with the native host/container-specific service creation patterns, for example, work with EJB Home to create an EJB service.

GT3 provides a default factory implementation through a factory provider, which delegates the service creations to the "default factory callback." This default factory callback is capable of creating services inside the same class loader where the factory is.

In Listing 12.6, we can see how to configure the deployment descriptor to add a factory provider and the GT3-provided default factory callback implementation.

Listing 12.6. The factory provider and the default factory callback.
  1.  <service name=" ogsa/cmm/OperatingSystemServicefactoryService"    provider="Handler" style="wrapped">  2.  ..................................................................  3.  <parameter name="operationProviders"    value="org.globus.ogsa.impl.ogsi.factoryProvider"/>  4.  <parameter name="factoryCallback"    value="org.globus.ogsa.impl.ogsi.DynamicfactoryCallbackImpl"/>  5.  ..................................................................  6.  </service> 

The previous XML configuration fragment describes the factory operation provider and the callback implementation for the service.

Let us now implement a custom factory callback facility that is capable of connecting to an EJB Home to create an EJB service. Listing 12.7 explains how we can implement such a custom factory using the factory callback implementation.

Listing 12.7. The custom factory callback implementation.
  1.  public class EJBfactoryCallbackImpl implements factoryCallback {  2.  private GridServiceBase base;  3.   4.  public void initialize(GridServiceBase base) throws    GridServiceException{  5.  this.base = base;  6.  }  7.  synchronized public GridServiceBase createServiceObject(  8.  ExtensibilityType extensibility) throws GridServiceException {  9.  ................................  10.  Context initial = new InitialContext(env);  11.  //look up the home interface using jndi  12.  Object homeObj = initial.lookup(ejbLookupString);  13.  EJBHome home =  14.  (EJBHome) PortableRemoteObject.narrow(homeObj,                 EJBHome.class);  15.  // Create EJB object  16.  .................................  17.  // Create Grid service object and assign with the EJB Object  18.  }  19.  <<other private methods>>  20.  } 
Listing 12.8. The register and the new callback implementation, as shown in the configuration.
  1.  <service name=" ogsa/cmm/OperatingSystemServicefactoryService"    provider="Handler" style="wrapped">  2.  ..................................................................  3.  <parameter name="operationProviders"    value="org.globus.ogsa.impl.ogsi.factoryProvider"/>  4.  <parameter name="factoryCallback" value="EJBfactoryCallbackImpl "/>  5.  ..................................................................  6.  </service> 

Listings 12.7 and 12.8 describe how one can register the new factory callback implementation with a service.

One can now deploy the above service and all the calls to create the service (i.e., createService call on factory) and attain delegation to the EJBfactoryCallbackImpl's "createServiceObject" method. We can see more information on the internals of this factory pattern in the following discussion.

Internal Details of Factory Callback Processing

The services can be configured to use an OGSI-provided factory mechanism by setting a factory operation provider for that service. On a service container startup, it reads the configuration and loads the necessary configurations to the Service container repository, and then activates the factory service based on the configuration property (i.e., 'activateOnStartup').

Following this activation, when a client attempts to create a service instance, the RPCURIHandler follows the pattern as depicted in the Figure 12.6.

Figure 12.6. The factory callback processing sequence.

graphics/12fig06.gif


The next section discusses the GT3-provided grid service lifecycle model, where we will examine a grid service lifecycle, service state persistence mechanisms, and recovery capabilities.

Grid Service Lifecycle Callbacks and Lifecycle Management

Many of the distributed component models support efficient and scalable memory management through autonomic component activation and deactivation facilities. The GT3 container model is providing some basic scalability management functionalities. The following points describe these functionalities.

  • Services getting activated only on the first utilization by the container, even if the services are statically deployed and persistent in nature.

  • Container provides scalability mechanisms by deactivating unutilized services. Some of the most commonly deactivated services include notification subscriptions and service group entries. Normally speaking, these service instances are created in large numbers . GT3 utilizes algorithms for this deactivation process. These are referred to as:

    • TTL Time to Live

    • LRU Least Recently Used

    We are able to configure the TTL property in the configuration file. One notable thing about this deactivation is that the container still holds some metadata that is required to activate these services.

  • Services are deactivated to a persistent storage in its entirety. GT3 provides a facility called "ServiceLoader," which we can use to achieve this kind of deactivation. This loader is also responsible for dynamically deploying and activating a service on its first activation.

  • A provision for "lazy creation" enables a factory to return a unique handle for the service instance without any explicit deployment or activation of that service instance. The service deployment and activation happens later on, when the client tries to resolve the handle to a reference. For this, a lazy creation callback is involved.

All these scalability management functions are part of the container and transparent to the user of the container. Now we can take a closer inspection on these capabilities, and the programmatic/configuration requirements of a service to achieve these behaviors.

These services transition from activate to deactivate, and vice versa, and are enabled by the GridServiceCallback interface, as described in Listing 12.9.

Listing 12.9. The service lifecycle interface.
 public interface GridServiceCallback  {   public void preCreate(GridContext context) throws GridServiceException;   public void postCreate(GridContext context) throws GridServiceException;   public void activate(GridContext context) throws GridServiceException;   public void deactivate(GridContext context) throws GridServiceException;   public void preDestroy(GridContext context) throws GridServiceException; } 

All grid services implemented from GridServiceBase, in turn , implement this interface (as shown in Figure 12.2). The operation providers and factory callbacks should implement this interface to maintain fine-grain control on the underlying resource that they provide. Another very important interface associated with this service behavior is the ServiceLifeCycleMonitor, as shown in Listing 12.10. This is an interceptor to a grid service lifecycle state transition.

Listing 12.10. The service lifecycle interceptor.
 public interface ServiceLifecycleMonitor {   public void create(GridContext context) throws GridServiceException;   public void preCall(GridContext context) throws GridServiceException;   public void postCall(GridContext context) throws GridServiceException;   public void destroy(GridContext context) throws GridServiceException; } 

For example, if there is a lifecycle monitor configured with a grid service, utilizing the "life-cycleMonitor" property, then the factory provider calls this interface operation "create" following the service instance creation. This factory provider only calls this operation after the service instance creation, and following the preCreate, postCreate, and activation calls on the created service.

The diagram in Figure 12.7 details the lifecycle interfaces and their interaction patterns. Let us now discuss the lifecycle management options available with a grid service in more detail.

Figure 12.7. The service lifecycle interfaces and integration process.

graphics/12fig07.gif

Service Activation

Service activation is a key operation in any grid service. The following discussion provides treatment to the operations involved in this process.

Activate Utilizing the Lazy Creation Mechanism

As shown in Figure 12.7, the factory provider utilizes a method called lazyCreate(), which will create a GSH, and then returns that handle to the client without actually creating the real service instance.

An example of such a lazy creation is shown in Figure 12.7 where the ServiceGroupRegistration calls the factoryProvider "lazyCreate" operation to create the service entries. The lazy creation can be enabled by setting the "entryInstanceCreation" property to "true" in the configuration file. Later, when the client tries to utilize that service, the handle resolver provider calls the service's parent; this is because the specific service instance node is not in the repository. In this case, the handle provider calls the ServiceGroupRegistration service's "lazyCreate" method with the handle of the instance it wants to invoke. This service, in turn, creates the child service instance for that handle by calling createService in the factory.

Activation on Service Startup

Normally speaking, a service is activated on its first usage. This activation results in an activate call to the grid service lifecycle interface.

Service Deactivation

The default policy adopted by the framework is that activated services never get deactivated. The framework provides a default deactivation mechanism based upon the time to live (TTL) policy. This is easy to configure, as shown in Listing 12.11.

Listing 12.11. The default service deactivator configuration.
 <service name=" ogsa/cmm/OperatingSystemServicefactoryService" provider="Handler" style="wrapped"> <parameter name="className" value="=" org.ogsa.core.cmm.impl.OperatingSystemImpl "/> <parameter name="persistent" value="true"/> ................................................. <parameter name="lifecycleMonitorClass" value="org.globus.ogsa.repository.DefaultServiceDeactivator"/> <!-- idleTTL before deactivation in milliseconds--> <parameter name="instanceDeactivation" value="10000"/> </service> 

This deactivator is configured with a factory, as shown in Listing 12.11. This helps to monitor all the service instances created by a factory. The functionality of this deactivator is to integrate into the described ServiceLifeCycleMonitor interface calls, and update the service usage timestamp. This current usage time is utilized by a Timer task, already executing, based on a Java Timer event. This is to validate the time to live parameter of a service, and calls the deactivate method on the service if the TTL time expires . This may not, however, be sufficient for more complex scenarios. There are provisions available to integrate deactivators with a service factory, or instance, using the "lifecycleMonitorClass" configuration option.

Service State Data Persistence Mechanisms

This discussion addresses the state of the data, the persistence of the data, and the operations that affect this process.

Service Loader

Most of the services require state data to be persistent, such that it can be recovered from the subsequent startups , as required. For this reason, the framework provides a service persistence mechanism through a ServiceLoader interface, as shown in Listing12.12. This allows a service to store and load the service related state into a persistent storage area.

Listing 12.12. The serviceLoader interface.
 public interface ServiceLoader {     public boolean load(String id) throws ServiceLoaderException;     public void store(String id) throws ServiceLoaderException;     public void remove(String id) throws ServiceLoaderException; } 

The framework provides default SimpleFileLoader implementation that can be used with any service. This is a primitive mechanism, and can be extended with backend database storage for maintaining all the configuration data and state data.

In addition to the above framework mechanism, GT3 provides a default mechanism of storing the configuration information for a transient service in the deployment configuration file, utilizing the "ServiceDeployment" framework component. In these situations, we can observe that the service instance specific configuration information is written to the WSDD configuration file.

GT3-Provided Service Types

Let us examine the types of services available with the GT3 framework, its configuration properties, and its creation semantics. There are two types of services: transient services and persistent services.

Transient Services

These kinds of services are created by the OGSI factory and deployed at runtime to the deployment descriptor. These services are extensions of the GridServiceImpl base class, supplying a factory provider and a callback implementation class in the configuration.

Persistent Services

A persistent service is deployed when it is added to the deployment descriptor through some type of out-of- band mechanism. These services usually extend the PersistentGridServiceImpl GT3 class. These services usually do not have a factory or provider.

Grid Service Lifecycle Model

In addition to the above service lifecycle, there are two kinds of lifecycle models associated with the state data recovery on service restart: persistent and transient.

Persistent

For the lifecycle to be recoverable, all persistent services need to have a configuration property in their configuration file with 'lifecycle="persistent"'.

 <parameter name="instanceLifecycle" value="persistent"/> 

The framework creates deployment information for these service instances and will be stored in the deployment configuration file. This deployment information is removed from the configuration file upon service destruction and deactivation using a ServiceLoader, as shown above.

Transient

This is the default behavior and no information is persistent in the deployment descriptor.

To conclude, Table 12.1 shows the GT3-provided service lifecycle properties, their options, and their default values.

Table 12.1. The Default State Management Support in GT3

Service Property

Options

Service Type

Persistent (default), Transient

Lifecycle Model

Persistent, Transient (default)

Activation

Startup, Lazy (default)

Deactivation

None (default), TTL/LRU

GT3-Supported Programming Model for Service Data Management

The service data forms the globally exposed state of a service. We have already discussed the details and importance of service data during our discussion on the OGSI. Let us now see how GT3 is implementing this core OGSI feature.

In GT3, there is a concept called service data set (ServiceDataSet class), which holds all the service data for a service. Figure 12.8 shows this relationship model. Every grid service instance has a service data set that holds a collection of service data wrapper objects. This service data set is responsible for sending notification of any service data changes, with the changed service data wrapper element, to the interested listeners. A service data wrapper (ServiceData class) is holding the service data values, or uses a callback mechanism often referred to as service data "pull," to get the value from other registered data providers. These callbacks must then implement the ServiceDataValueCallback interface, and must be registered with the service data wrapper class.

Figure 12.8. The service data classes and relationships.

graphics/12fig08.gif

There are three ways by which we can create and add service data to a service data set:

  1. Predefined service data elements in GWSDL . Normally speaking, on the service startup, all the service data is loaded into the service data set. There are APIs in service data set (create/add/remove) to dynamically add service data, provided that they are conforming to their descriptions in WSDL (service data descriptions).

We will explore this process in detail in the following discussion.

Service data elements are defined in the WSDL. Every standard OGSI-defined interface already contains some SDE definitions. Listing 12.13 shows the OGSI GridService portType GWSDL with SD elements defined.

Listing 12.13. OGSI-defined GridService portType with service data elements and static values.
 <!-- Grid Service Port Type --> <gwsdl:portType name="GridService">   <operation name="findServiceData"> .................................................. </operation> .................................................. <sd:serviceData name="interface"                   type="xsd:QName"                   minOccurs="1"                   maxOccurs="unbounded"                   mutability="constant"                   modifiable="false"                   nillable="false"/> <sd:serviceData name="gridServiceHandle "                   type="ogsi:HandleType "                   minOccurs="0"                   maxOccurs="unbounded"                   mutability="extendable"                   modifiable="false"                   nillable="false"/> ....................................................   <sd:staticServiceDataValues>     <ogsi:findServiceDataExtensibility         inputElement="ogsi:queryByServiceDataNames"/>     <ogsi:setServiceDataExtensibility         inputElement="ogsi:setByServiceDataNames"/>     <ogsi:setServiceDataExtensibility         inputElement="ogsi:deleteByServiceDataNames"/>   </sd:staticServiceDataValues> </gwsdl:portType> 

Upon service startup, these already defined service data elements are created and added to the service data set during the GridServiceBase's postCreate() call. See the sample algorithm code in Listing 12.14, which is taken from the GridServiceBaseImpl class to illustrate this process.

Listing 12.14. Service data wrapper objects creation from GWSDL SDE definition elements.
  1.  private void addServiceData() throws GridServiceException {  2.  // All Service Data elements are defined in WSDL GSR  3.  Get the GSR (WSDL) for the current service  4.  Get all the 'GWSDL' portTypes in the GSR (not the WSDL portTypes)  5.  For each GWSDL portType (portTypes qualified with GWSDL namespace)  6.  // Create SD wrapper classes using SDE definition in GWSDL  7.  Get the service data elements defined in that portType  8.  For each service data element  9.  Get the Service data name and its namespace  10.  Create a service Data Wrapper class with the above attributes  11.  Add wrapper to the Service Data Set  12.   13.  // Now get the default static values defined in GWSDL  14.  Get the Static Service Data values in the GWSDL portType  15.  For each Static Service Data  16.  Get each static service data value  17.  Add the all service data values to the Service Data Wrapper             class provided their name and namespace matches  18.  } 

The above postCreate() mechanism in the GridServiceBaseImpl class sets up the service data set with the appropriate service data wrappers, and their associated predefined (static) values.

Later, if there is a need to update some service data value that already has a wrapper in the service data set, Listing 12.15 explains how to do this.

Listing 12.15. Creating and adding service data values.
 1. Get the Service Data Wrapper class from the service data set using the    QName of the service data element as defined in WSDL       for example:       To get the predefined (already created from GWSDL) "Handle" Service       Data element, use the code below.       ServiceData handleServiceDataElement =       this.serviceDataSet.create("gridServiceHandle"); 2. Now create the value for that service data element. This element must    be of the ServiceDataElement type. These types are defined in the XSD    and the framework must have already created corresponding Java types    (using tools WSDL2JAVA). Here "gridserviceHandle" is of the type "HandleType" and hence can create and add the value to the ServiceDataElement wrapper class:       HandleType handle = new HandleType(handle)       handleServiceDataElement.setValue(handle); 3. Update the service data set with Service Data Wrapper and the new value       for example:       this.serviceDataSet.add(handleServiceDataElement); 
  1. Dynamic service data elements and values. So far, we have been discussing the service data elements, which are statically defined in the WSDL. There may be cases when a grid service needs to create the service data elements and add them dynamically to the service data set.

Creating Dynamic Service Data Elements

The code in Listing 12.16 shows how to construct a dynamic service data element for a service. Normally speaking, this is done on a postCreate call to make the service data available to the clients upon service startup.

Listing 12.16. Creating and adding dynamic service data elements.
 // Create Service Data Element serviceData = this.getServiceDataSet().create("MyServiceData"); // Set the value of the SDE to a MyServiceDataType instance MyServiceDataType sd = new MyServiceDataType (); serviceData.setValue(sd); // Set initial values of MyServiceData ad.setName("test"); // Add SDE to Service Data Set this.getServiceDataSet().add(serviceData); 

As shown in Listing 12.16, these are the steps we must follow to create an SDE and add it to the service data set:

  • Create a new SDE by calling the create method of the service instance's service data set with a unique name or QName. This SDE is initially empty and has no value. In our example, the name of the SDE will be MyServiceData with an empty namespace.

  • Set a value for the SDE. The value of the SDE of type MyServiceDataType.

  • Set the initial values of MyServiceDataType.

  • Add the SDE to the service data set.

The client must be aware of this SDE and SDE type to work with the above example. It can find out the available SDEs through service introspection using the "findServiceData" operation on the grid service with "serviceDataName." This operation should list all the available SDEs in the service.

Service Data from Service Annotation

We can now turn our attention to the third mechanism of creating service data using Java service documentation and associated tools, or doclets . This process includes a service annotation, running tools to create the service data callback classes and corresponding service data element WSDL, and finally registering these callbacks with the service data wrapper upon service startup. Let us go through each step:

  • Annotate Java service implementation with service data element information

Listing 12.17. A service Java file decorated with Service data element definition.
 /** * The current value of the lifecycle. * @ogsa:service-data *     name = "currentLifecycleValue" *     minOccurs = "1" *     maxOccurs = "1" *     mutability = "mutable" */ public string getValue() throws RemoteException {     return currentLifecycleValue; } 

Listing 12.17 illustrates two concepts: how to define the service data element using the service annotation and how we can define the callback for service data values.

The above callback method gets registered with the service data wrapper class upon service instance startup. This method is invoked each time someone tries to access the service data element "currentLifecycleValue."

  • Run tools to create the corresponding service data wrapper class and service data element definition.

Once we are done with the above annotation on the service, then we can run the "javadoc" command with a GT3-provided "ServiceDataDoclet" callback class on this service file. This extracts all the service data and creates a ServiceDataAnnotation class that corresponds to the above definition, and stores it in a disk file with an extension of "-sdAnnotation."

Once we have created the above file, we can export the service data element definitions to the WSDL using the "GenerateSDD" tool.

  • Register the callbacks with the service implementation.

The registration of the ServiceDataAnnotation classes as callbacks are done during service initialization using the calls found in Listing 12.18.

Listing 12.18. Service data annotation callback setup process.
 // lifecycle callbacks public void postCreate(GridContext context) throws GridServiceException {     ServiceDataAnnotation.setupServiceData(this, serviceData); } 

This results in the ServiceDataAnnotation class loading the already created "-sdAnnotation" file, setting up the callback with the service data wrappers, and adding the service data element with the service data set.

Important Information on Service Data Element Types and Their Values

Each service data element has a type associated with it, which can be used to define an XML schema type of the SD value. This is shown below in the SDE definition.

 <sd:serviceData name="gridServiceHandle " type="ogsi:HandleType "                   minOccurs="0"                   maxOccurs="unbounded"                   mutability="extendable"                   modifiable="false"                   nillable="false"/> 

The above example shows a "type" attribute associated with the service data element.

Normally speaking, upon running the tools (WSDL2Java) a corresponding Java type gets generated from the above XSD type. We can use this type directly in our program to create new SDE values. The example below shows the type generated by running WSDL2Java.

 package org.gridforum.ogsi; public class HandleType                  implements Java.io.Serializable,                  org.apache.axis.encoding.SimpleType {     private org.apache.axis.types.URI value;     public HandleType() {    }     // Simple Types must have a String constructor     public HandleType(org.apache.axis.types.URI value) {         this.value = value;     }     public org.apache.axis.types.URI getValue() {         return value;     }     public void setValue(org.apache.axis.types.URI value) {         this.value = value;     } // type mapping and serialization / de serialization information } 

These generated classes contain necessary type-mapping and serialization/deserialization information to convert the types from XML messages to native language (Java) types, and vice versa.


Service Data Query Support in GT3

Query on service data is the most complex and powerful capability provided by the OGSI specification. Based on OGSI, every grid service exposes a "findServiceData" operation which allows the client to query the service for the service data. The default query supported by the framework is based on the service data name ("queryByServiceDataName"). However, we know that the specification provides an extensible query capability by allowing service to provide different query mechanisms. Some of the notable queries we can think of are Query by XPath, XQuery, and SQL. GT3 supports a plug-and-play query framework to support these requirements to an agreeable level. It provides facilities for extensible query engine support.

A service provides support to add a query execution engine of our choice. This query engine provides capabilities for adding any number of query expression evaluators. Some of the expression evaluators supported in GT3 are:

  1. ServiceDataNameEvaluator

  2. ServiceDataNameSetEvaluator

  3. ServiceDataNameDeleteEvaluator

  4. ServiceDataXPathEvaluator

We can see the Query Framework object model in Figure12.9.

Figure 12.9. Service query framework.

graphics/12fig09.gif

Let us go through these available evaluators found in GT3. We will explore the details on the query execution feature in the next section.

ServiceDataNameEvaluator. This is a simple evaluator that enables the client to call the service to get service data values, using the service data element names . The client invokes a "findServiceData" operation to start the query execution process.

ServiceDataNameSetEvaluator. This evaluator enables the client to call the service to set service data values for service data elements identified by their QName. The addition of elements to a service data value depends on the other constraints, including minOccurs, maxOccurs and mutability. The client invokes a "setServiceData" operation to start this query execution process.

ServiceDataNameDeleteEvaluator. This evaluator enables the client to call the service to delete service data elements identified by their QName. The client invokes a "setServiceData" operation to start the query evaluation process.

ServiceDataXPathEvaluator. This is a complex evaluator to enable the client to call the service to evaluate an XPath expression on service data elements. The client invokes a "findServiceData" operation to start the query evaluation process. We will explore the details below.

Another notable feature is the ability to register the engine and evaluators at different levels. Normally speaking, these objects are registered at the service level, but we can also register them at the global factory level. This enables the service to use an engine of choice at runtime.

Service Data Retrieval and Transformation Support

The Service Data framework provides a number of mechanisms to enable query support. This includes mechanisms to transform service data values to:

  • XML document where we can apply the XPath

  • XML string

  • Apache AXIS MessageElement Object arrays

Internal Query Processing Information

Every service instance has a query engine and each query engine has a number of query evaluators. A service-specific query engine may contain a parent engine to delegate expressions that it cannot handle. The global query engines are normally initialized at the factory level (global query engines). The local engines are set at the service instance level. The normal processing sequence of a query engine is in Figure 12.10.

Figure 12.10. Normal processing sequence of a query engine in GT3.

graphics/12fig10.gif

Custom Query Engines and Evaluators

We can write our own query engine by inheriting from QueryEngine interface. This query engine can be registered as part of the service instance, or can become a global engine. As shown in the configuration in Listing 12.19, we can set the default evaluators available for the container.

Listing 12.19. Query engine and evaluator configurations.
 <parameter name="queryEngine" value="com.ph.test.myQueryEngineImpl"/> <parameter name="queryEvaluators"   value="org.globus.ogsa.impl.core.service.ServiceDataNameEvaluator          org.globus.ogsa.impl.core.service.ServiceDataXPathEvaluator          org.globus.ogsa.impl.core.service.ServiceDataNameDeleteEvaluator          org.globus.ogsa.impl.core.service.ServiceDataNameSetEvaluator"/> 

As shown in Figure 12.10, on the "findServiceData" call, our engine will be called to execute the query with the current service data set of the service instance and the query expression. One thing to notice is that the real query execution functionality is occurring at the evaluators registered with the engine. It is possible to register a global query engine with our local query engine, such that we can delegate the calls to that engine for the expressions we have not designed. This is a flexible and hierarchical approach. For example, it helps us write global query engines with more general query handling mechanisms (QueryServiceDataByName or XPath), while our service can implement more specific evaluators for the service.

Another interesting aspect of OGSI and GT3 is the capability to discover the queries supported by a service using the "findServiceData" operation with the input expression "findServiceDataExtensibility."

XPath Query Support in GT3

Let us examine how the XPath query is supported by the GT3 framework.

We can perform an XPath query by issuing a "findServiceData" operation on the service instance and passing a ServiceDataXPathQueryExpressionType object as the parameter.

The structure of this expression type is shown in Listing 12.20.

Listing 12.20. XPath expression type.
 <complexType name="ServiceDataXPathQueryExpressionType">     <sequence>         <element name="name" type="QName"/>         <element name="XPath" type="string"/>         <element name="namespaces" type="string" minOccurs="0"                        maxOccurs="unbounded"/>     </sequence> </complexType> 

Since XPath queries are going to be the flexible approach for service data query, we need a detailed discussion on its capabilities and usage model. These elements in Listing 12.20 are:

  • The name element denotes the name of the service data element to which we are applying the query. This can be a wildcard, which specifies applying the XPath on the service data elements that match. This may result in an array of result elements for the XPath query.

  • The XPath parameter represents the real XPath expression for the service data element identified by the name parameter.

  • Finally, a set of namespace arrays that require special attention. This namespace array is used to convey the namespace of the nodes of the service data element in the XPath query. If namespace mappings are not provided, the default behavior is to use the current context node, the SDE root element, to resolve the namespaces. However, this may not be sufficient when searching for child nodes, which contain namespace attributes that are not present in the root node. One must be careful to provide all possible namespaces of interest that are likely to be encountered when traversing the SDE. There are some XPath APIs, including local-name() and namspace-uri(), to overcome this problem but may not be sufficient for all cases.

Let us now review some sample XPath queries. For example, the code in Listing 12.21 explains how we can do this in GT3. This code shows how to write an XPath to test whether the service supports XPath queries.

Listing 12.21. A sample XPath expression.
 xmlns:gsdl=http://www.gridforum.org/namespaces/2002/10/gridServices //gsdl:queryExpressionType[.='gsdl:queryByServiceDataXPath' 

We will see in the next chapter some runtime sample codes that detail the service data usage and the application of query on the service data.

Service Data Change Notification

The most important part of a distributed system is the capability to send asynchronous messages to the interested parties on some topic of interest. The OGSI specification provides a standard mechanism to send asynchronous messages on state changes. This provision is handled through a set of OGSI-provided interfaces, NotificationSource, NotificationSubscription, and NotificationSink, respectively. We have already discussed the behaviors exhibited by these interfaces in the last section. Now we will focus our discussion on the GT3 implementation aspects of these interfaces.

There are two important aspects to this notification: make service available for notification and enable service data to send notification messages on its state change.

Let us now discuss these concepts in greater detail.

Make Service Available for Notification

A service can expose to its clients, for Notification purposes, by implementing the NotificationSource interface. We have already discussed the operation provider concepts, which allow a service to expose some of the core operations through an external mechanism, rather than implementing this by itself. GT3 provides a default NotificationSource provider, which enables us to do that. See Listing 12.3, which shows how we can expose our operating system service with the NotificationSource interface. The client can now subscribe for notifications on service data changes. The default implementation supports subscription of the type "subscribeByServiceDataNames" subscription. The other possible subscriptions may include "subscribeByXPath."

Figure 12.11. The GT3-provided service notification process.

graphics/12fig11.gif

Another notable feature is that this framework is a topic-based messaging framework. By default, when the client subscribes for notification on a service data change, a topic will be created for the service data name. This topic will be evaluated before sending a change notification to the client.

We can now extend the above topic-based concept to other areas of notifications, such as enabling a service to send a notification on some topic of interest other than the service data name. For example, our operating system service wants to send a notification on a call to its method "reboot." We can do that easily by letting the service instance register a new topic called "Reboot" with the notification provider. This is, normally speaking, on our service preCreate() method. The service instance can then send a notification message during the reboot operation. This is illustrated in Listing 12.22.

Listing 12.22. Service sending notifications.
 public class OperatingSystemImpl extends GridServiceImpl {   NotificationProvider provider = null;   public void postCreate(GridContext context) throws GridServiceException {     try {       super.postCreate(context);       this.provider = (NotificationProvider) getProperty(ServiceProperties                       .NOTIFICATION_SOURCE);       // create a Topic and add that to the provider       this.provider.addTopic("RebootNotificationMessage",new                        QName("http://com.ph.sample/cmm/OperatingSystem",                        "RebootNotifDataType"));     } catch (Exception e) {       throw new GridServiceException(e);     }   }   public void reboot() throws RemoteException {     // do reboot and inform our listeners      try {        this.provider.notify(" RebootNotificationMessage", new RebootNotifDataType ("The                      system rebooted"));     } catch (Exception e) {     }   }   ............................ } 
Enable Service Data to Send Notification on State Change

The service data set is associated with a service instance, and it provides mechanisms to register listeners to receive notifications on service data changes. Regarding a service data change, the service data wrapper sends a change notification message to the service data set, and it will then evaluate the change and send the message to the interested parties.

As shown in the above sequence diagram, the listener can evaluate the change with the topic created during subscription. If the change is valid for the constraints expressed in the topic, then a separate thread is created to deliver the change message.

The process we have just described and shown is a simple notification mechanism on service data change. We can envision and program this to complex situations, including service stage changes, dynamic notification topics, and so forth.

Another value-added enhancement may be the support for a broker, and how it can reside between the grid service clients and the service instance, as shown in Figure 12.12. This enables a more flexible model with different transport bindings and notification mechanisms (e.g., pub-sub/topic, and so forth).

Figure 12.12. The notification broker.

graphics/12fig12.gif

Client Programming Model

We discussed in the architecture chapter that the OGSI, and in turn the GT3, do not dictate any specific programming models for the client. The client is based on the JAX-RPC programming model. By default, it provides Apache AXIS client-side framework, tools, and runtime. GT3 provides a set of helper classes to deal with the concepts surrounding GSHs, its resolution to GSR, and GSR introspection without client involvement. GT3, in fact, provides a number of tools to deal with these extensions and add grid service behaviors to the normal JAX-RPC stubs. We will discuss the details on these tools later in this chapter.

Figure 12.13 shows the extension to the JAX-RPC programming model through the service locator construct.

Figure 12.13. Grid service client helper class and usage.

graphics/12fig13.gif

The basic client-side invocation pattern is:

  • A client accesses the GSH to a service from a registry, or some other out-of-band mechanisms.

  • The client passes this handle to the service grid locator, that in turn talks to the handle resolution service to dereference the GSH to a service GSR. The resolution process constructs and associates the instance address with the necessary stub. The proxy, so generated, exposes the JAX-RPC style interface to the service.

  • The client calls service methods on that proxy using the normal JAX-RPC invocation model.

Let us now spend some time understanding aspects of the client-side programming models provided by JAX-RPC. We can always write GT3 clients that can support the entire JAX-RPC programming model invocations.

Based on JAX-RPC specifications, there are three different models for invoking a service endpoint from a client, as shown in Figure 12.14.

Figure 12.14. JAX-RPC client-side programming models.

graphics/12fig14.gif

These client-side programming models are independent of any specific service implementation or endpoint model, however, provide well-defined interfaces for the users. We will explore the details of each in the following discussion.

Stub-Based Model (Static Stubs)

These static stub classes are created either from the WSDL or from a service endpoint interface. A generated stub class is required to implement both Javax.xml.rpc.stub and the service endpoint interface. This stub interface provides APIs to configure stubs by setting properties such as endpoint address, typemapping, session, user name, password, and so on.

Dynamic Proxies

In contrast to the static stubs discussed above, the client at runtime creates these dynamic proxy stubs using the Javax.xml.rpc.Service interface. The client has a priori knowledge of the WSDL and the service it is going to invoke. It uses the Servicefactory classes to create the service and get the proxy.

DII (Dynamic Invocation Interface)

This software pattern eliminates the need for clients to know in advance a service's exact name, operations, and parameters. A DII client can discover this information at runtime using a service broker that can look up the service description (WSDL).

GT3 Tools

Transformation Tools

We know from our earlier discussion that grid services are stateful Web services and are hosted in a Web service hosting environment. For example, GT3 is deployed to the Apache AXIS Web service container environment. This necessitates the need to convert some of the grid-specific extensions to Web service operateable entities. Most notable of this transformation is the transformation of multiple interface inheritance in OGSI grid service to a single interface Web service model. This requires tooling to do the transformation from OGSI-defined GWSDL to WSDL 1.1.

GWSDL2WSDL. This is an interesting tool that requires a detailed discussion. In the previous section, we have discussed the GWSDL namespace and how OGSI supported the open portType extensions and portType inheritance model using the GWSDL portType. OGSI allows multiple portType inheritance and open content model for portTypes. This flexibility is not available with WSDL 1.1 and hence developers can use the new GWSDL namespace and GWSDL portType provided by OGSI specification to create this multiple inheritance and portType extensibility model. Once WSDL 1.2 becomes standard, frameworks and tools will support this feature. However, in order to support the current WSDL1.1 tools and framework, OGSI GWSDL portTypes are converted to the WSDL 1.1 based single portType model. This tool is used to transform GWSDL portType definitions to the corresponding WSDL 1.1 definition.

Figure 12.15 illustrates how a GWSDL with the "OperatingSystem" portType has an inheritance hierarchy is converted to a WSDL 1.1 "OperatingSystem" portType model single interface using the GWSDL2JAVA tool.

Figure 12.15. Transformation of OGSI GWSDL to WSDL 1.1 using the GWSDL2WSDL tool.

graphics/12fig15.gif

As developers and senior technical leaders , we should be aware of the important aspects about this conversion:

  1. A new WSDL port type is constructed containing the same operations as the GWSDL port type, as well as all operations from the extended port types.

  2. All operations messages are qualified with the namespace definitions as defined originally and hence no operation overloading is allowed.

  3. The name of the new WSDL portType is the same as the name of the old GWSDL portType.

  4. The newly generated WSDL keeps the old GWSDL portType as a WSDL extensibility element, but does not contain any operationsonly service data.

Service and Client-Side artifacts

In order to avoid client-side and server-side developers from dealing with the complexity marshalling and demarshalling of parameter types, creation of artifacts to deal with the Web service messaging (SOAP) and support to the programming models (JAX-RPC) tools are required. These tools are used to create stubs/proxies, language artifacts, type-mapping, deployment information, and helper classes. Below are some of the commonly used GT3 tools.

  • GenerateBinding. A WSDL file can be divided into an abstract portType representation, and a valid binding representation. Normally speaking, clients do not need to be concerned with the valid binding generation. GT3 provides this tool to generate the necessary binding information for a given portType.

The default values used by this tool are "http" for transport binding protocol, "document" for SOAP message style, and "literal" for message encoding. This flexibility helps the clients concentrate on the design of the portType definitions and then use this tool to generate the necessary bindings.

Sample usage:

 GenerateBinding <filename>.wsdl, where the input file contains the portType declaration. 

This will generate <filename>_service.wsdl and <filename>_binding.wsdl, respectively.

  • GSDL2Java. This is a special case implementation of the WSDL2Java tool provided by AXIS, which is used by the clients to generate the stubs, bindings, service interfaces, server-side service implementation skeletons, and client locator classes. We can use this tool against a WSDL file, which contains the <wsdl:service> element declaration to achieve a top-down design model.

Sample usage:

 GSDL2Java service.wsdl 
  • ServiceDataDoclet. This is running javadoc on the Java service implementation code with GT3-provided "doclet" callback class "org.globus.ogsa. utils .ServiceDataDoclet." This results in the creation of ServiceDataAnnotation class for each service data element defined in the service file. Finally, all these generated ServiceDataAnnotation classes will be dumped into a file with <className>"-sdAnnotation."

Usage:

 Javadoc <org.globus.ogsa.utils.ServiceDataDoclet> <service implementation Java file> 
  • GenerateSDD. This utility tool reads the above generated service class file with SD annotations and collects all the service data elements in the file stored as ServiceDataAnnotation list and then adds those service data elements to the specified WSDL.

Sample usage:

 GenerateSDD <wsdl definition file> <class name with service data annotations> 

GT3 Configuration

GT3 uses AXIS WSDD to configure

  • the services and its parameters

  • JAX-RPC and AXIS handlers and the parameters needed for the handlers

  • Request and response flow with the handlers

  • Global parameters for AXIS engine and the GT3 container

Listing 12.23 shows such a deployment configuration file with one service in it. We will use this listing for our discussion of the server-side deployment configuration.

Listing 12.23. A sample server-config.wsdd.
 <deployment ....> <globalConfiguration>     <parameter name="adminPassword" value="admin"/>     <parameter name="sendMultiRefs" value="true"/>            .............................................     <requestFlow>         <handler type="URLMapper"/>         <handler type="PersistentServiceHandler"/>            .............................................     <requestFlow>     <responseFlow>         <handler type="RoutingResponseHandler"/>             ............................................     </responseFlow> </globalConfiguration> <transport name="http">     <requestFlow>         <handler type="URLMapper"/>     <requestFlow>     <responseFlow>             ............................................     </responseFlow> </transport> <service name="ogsa/cmm/OperatingSystemService" provider="Handler"                style="wrapped" use="literal">     <! service factory name- ->     <parameter name="name" value="Operating system Provider factory"/>     <! service instance specific information - ->     <parameter name="instance-name" value=" Operating system Service"/>     <parameter name="instance-schemaPath" value=                        "schema/core/cmm/operatingsystem_service.wsdl"/>     <parameter name="instance-className" value=                        "org.ogsa.core.cmm.OperatingSystemPortType"/>     <parameter name="instance-baseClassName" value=                        "org.ogsa.core.cmm.impl.OperatingSystemImpl "/>     <parameter name="instance-operationProviders" value=                        "org.ogsa.core.cmm.impl.OperatingSystemProvider"/>     <! service factory specific information - ->     <parameter name="schemaPath" value="schema/ogsi/                        ogsi_notification_factory_service.wsdl"/>     <parameter name="baseClassName"             value="org.globus.ogsa.impl.ogsi.PersistentGridServiceImpl"/>     <parameter name="handlerClass"            value="org.globus.ogsa.handlers.RPCURIProvider"/>     <parameter name="className"            value="org.gridforum.ogsi.Notificationfactory"/>     <parameter name="allowedMethods" value="*"/>     <parameter name="factoryCallback"            value="org.globus.ogsa.impl.ogsi.DynamicfactoryCallbackImpl"/>     <parameter name="operationProviders"            value="org.globus.ogsa.impl.ogsi.factoryProvider            org.globus.ogsa.impl.ogsi.NotificationSourceProvider"/>     <!This service is a Grid service - ->     <parameter name="persistent" value="true"/>     <parameter name="allowedMethods" value="*"/> </service> </deployment> 

We will examine the service-specific configuration requirements. Upon careful examination of Listing 12.23, we can see that the service has a unique service name (remotely accessible name of the service) using a "wrapped" message style and the provider is a handler. This enables the AXIS machine to dispatch the calls to the GT3-provided container and instantiate the service inside the GT3 container.

Let us now see the specific parameters utilized for the grid service. Note that we may find an Axis Web service along with grid services in a container. We can easily identify grid services if they have a parameter "persistent" with a value of "true." We must be aware of this important distinction. Table 12.2 lists all the parameters of interest for grid services. To help us understand better, note that the parameters starting with instance are specific to the service instance while others may be for a factory or other providers.

Table 12.2. Deployment and Configuration Parameters

Parameter

Description

className

This class specifies a class or an interface that has public methods corresponding to all operations in the WSDL. This interface/class must expose all the operations exposed by the service and providers.

persistent

If this attribute value is true, it represents a GRID service. Otherwise, a normal Web service using Apache Axis.

baseClassName

The name of the class that implements the service. If this is the same as the className parameter, this is optional.

operationproviders

The list of classes to be loaded along with the service. Note that order indicates the initialization order.

handlerClass

This handler specifies the dispatcher to use at the server side. AXIS engine dispatches the call to this dispatcher (pivot handler).

schemaPath

The WSDL of the service identified by className.

factoryCallback

The factory callback class used to create a service.

instance-name

This identifies the name of the service instance to be created.

instance-schemaPath

This indicates the service instance WSDL.

instance-className

This indicates the service instance class name.

instance-baseClassName

This indicates the service instance base class name.

instance-Operationproviders

Lists the service instancespecific operation providers.

In addition to the above configuration parameters, there are other service-specific and global configurations, such as static type-mapping, query engine/evaluator, lifecycle, and container-specific configurations, which act upon host name, port name, and mapping.

GT3-Provided Default Implementation Classes

GT3 provides a number of built-in classes to provide some default functionality, which helps the service and client developer to hide the complexity of a grid service programming model. We have already covered most of these classes in the earlier sections. In Table 12.3, we list some of these classes for quick reference.

Table 12.3. GT3-Provided Default Implementation Classes

Class Name

Description

Used By (Service/Client) Developer

GridServiceBaseImpl , PersistentGridServiceImpl

Provides the basic grid service behaviors and lifecycle management functionalities. Grid service can be implemented by deriving from these classes.

Service

factoryProvider , DynamicfactoryCallbackImpl , DynamicfactoryLifecycleCallbackImpl

This factory provider is an operation provider and these classes together provide a service instance creation framework.

Service

HandleResolverProvider

Default handle resolution service operation provider. By default it supports http and https protocols.

Service

NotificationSourceProvider , NotificationSubscriptionImpl

These classes together provide the support for a service notification framework. This framework enables a topic whereby a service can support different topics. By default all service supports notification on "service data name."

Service

<portTypeName>ServiceGridLocator

A client-side helper class that provides the JAX-RPC programming model but hides the details of grid execution workflow including handle resolution, GSR validation, and stub address management through GSR location address. In addition, supports the normal Web service calls.

Client

We will see the utilization of these default classes in the next chapter.

Significance of Message Handlers in GT3

Message handlers provide additional message-handling facilities to the Web/grid service endpoints (both client and server) as extensions to the basic service implementation logic. As we have seen in the architecture section, these handlers can manage encryption and decryption, logging and auditing, and so on. By now we are already familiar with this powerful feature and its use with GT3.

There are two types of handlers supported in AXIS, JAX-RPC handlers and the default AXIS handlers. The JAX-RPC handlers are based on the JAX-RPC specification whereas the AXIS handlers are native to AXIS framework. On runtime, AXIS wraps these JAX-RPC handlers using an AXIS standard handler (JAXRPCHandler). Being the core implementation part of the system, we can spend some time analyzing these handlers and their usage patterns.

JAX-RPC Handlers

The current JAX-RPC runtime system defines only SOAP message handlers. The JAX-RPC runtime system can flexibly define other handlers and is free from any processing model of the messages.

The JAX-RPC Handlers API defines three basic methods, along with two lifecycle methods, as shown in Listing 12.24.

Listing 12.24. JAX-RPC handler methods.
 package Javax.xml.rpc.handler; public class Handler{         handleRequest(MessageContext context);         handleResponse(MessageContext context);         handleFaults(MessageContext context);         init(HandlerInfo info);         destroy();         // return the headers processed by this handler         QName[] getHeaders(); } 

The handler invocation pattern is shown in Figure 12.16. A handler should be implemented as a stateless instance. By providing the initialization interface (Handler.init [HandlerInfo info]), the runtime system can pass the required context information to the handler. This will help a handler obtain access to container-specific value-added features, including authentication mechanisms, transaction processing, logging framework, and so on.

Figure 12.16. Service and handler invocation model.

graphics/12fig16.gif

A handler chain represents an ordered list of handlers. This grouping helps to define policies that we want to associate with the handler invocation model. Examples of such policies include order of invocation, style of invocation (for example, one-way call invokes only handleRequest(); no handleResponse()), and so on.

This association can be configured to the handler through the Handler.init() method passing a HandlerInfo object. The handler chain continues processing the handlers only if the current processing handler returns "true." Listing 12.25 shows a sample implementation of a handler that can access a SOAP message header.

Listing 12.25. Sample handler implementation.
 Public class WSSecurityHandler extends GenericHandler{     Public Boolean handleRequest(MessageContext ctx){         try{             SOAPMessageContext mc = (SOAPMessageContext)ctx;             SOAPMessage msg = mc.getMessage();             SOAPPart sp = msg.getSOAPPart();             SOAPEnvelop se = sp.getEnvelop();             SOAPHeader header= se.getHeader();             // Now we can process the header             Document doc = WSSecurityRequestEngine.getEngine().                                 processSecurityHeader(se, ctx);             if (security validation is fine)                     return true;                     //continue processing             else{                     //Return false results in chaining to stop                     return false;             }         }catch(Exception ex){         }     }     ....................... } 

As shown in Listing 12.25, this is a simple handler implementation to validate WS-Security headers. The GenericHandler is a JAX-RPC-provided default "Handler" implementation class. It is recommended to use JAX-RPC handlers wherever possible for interoperability purposes. These are J2EE/J2SE standard based and, hence, can be ported to any environment.

AXIS Handlers

Before JSR 101, the AXIS decided to establish a standard for SOAP message processing, resulting in the generation of widely accepted AXIS handlers. This is different from JAX-RPC handlers. The most commonly used interface method is the "invoke." See Listing 12.26.

Listing 12.26. Handler methods.
 package org.apache.axis ; public class Handler{     public void invoke(MessageContext msgContext) throws AxisFault ;     public void onFault(MessageContext msgContext);     public boolean canHandleBlock(QName qname);     public void init(); ....................... } 

As shown in the code in Listing 12.26, the JAX-RPC handlers API defines three basic methods, along with two lifecycle methods.

The code in Listing 12.27 explains a sample implementation of a handler that can access a SOAP message header.

Listing 12.27. Sample AXIS handler implementation.
 public class HandleResolverHandler extends BasicHandler {     public void invoke(MessageContext messageContext) throws AxisFault {         try {             String target = (String) messageContext.getTargetService();         ....................................         }catch (Exception e) {             throw AxisFault.makeFault(e);         }     }     public void onFault(){     // to do a fault processing     } } 

We have already noticed that one of the main differences between the AXIS handler and the JAX-RPC handler is the return value. In JAX-RPC a value of true or false indicates the continuation of the execution process. A value of "false" will stop the handler chain execution process. In the case of AXIS handler an exception must be thrown to stop the processing. All the handlers that previously processed this specific message get a chance to do some fault processing using an "onFault" operation. This feature is not mandated in the JAX-RPC handlers.

GT3 Security Implementation and Programming Model

In the previous architecture section, we did mention the available security mechanism in GT3, which are transport-level GSI security and message-level security. We also mentioned that the transport level security would be depreciated in the coming Globus toolkit releases in favor of message-level security. The GT3 message-level security is based on WS-Security and associated standards (WS-SecureConversation, WS-Trust, and so forth). Our discussion would concentrate around the message-level security part of the GT3. This discussion includes the details on security architecture, the programming aspects, and deployment/configuration requirements.

The message-level security is based on WS-Security, XML Encryption, and XML Signature standards. We did cover these topics earlier in the book when we talked about Web services and global XML architecture. The GT3 framework provides two different message-level authentication mechanisms:

  • GSI Secure Conversation. In this protocol, at first, a secure context is established between the client and the service. On the subsequent calls to the service this context is used to sign/verify/encrypt/decrypt the messages. Figure 12.17 illustrates a high-level view of this secure conversation.

    Figure 12.17. GSI secure conversation.

    graphics/12fig17.gif

  • GSI XML Signature. This process is simple, where a message is signed with a given set of credentials.

GT3 Security Handlers

In GT3 the security is handled by using a number of AXIS and JAX-RPC handlers. Figure 12.17 shows the message flow and the available handlers (some of them). The figure depicts a secure conversation establishment between the client and server. This needs an exchange of tokens to establish a context. Once the context is established, that context token is used to secure the message. GT3 provides services to establish the token, and revalidate the token in case of expiry. We will discuss internal details on the secure context establishment process later in this section.

Now, let us do a quick overview on some of the most commonly used handlers to secure the message exchange in GT3 using Table 12.4.

Table 12.4. Common Server-Side Handlers

GT3 Security Handlers

Request/Response

Description

AuthenticationServiceHandler

Request

Responsible for mapping incoming GSI Secure Conversation requests to authentication service.

WSSecurityHandler

Request/Response

Responsible for processing WS-Security Header elements in a SOAP message. These messages are delegated to a WS-SecurityEngine for XML signature handling and XML encryption/decryption.

SecurityPolicyHandler

Request

This handler in turn loads and executes two other handlers, AuthHandler and RunAsHandler.

AuthHandler is responsible for checking whether the invocation of a method on a service is allowed or not. If the service is available, it needs to be activated to get the method-level security info.

RunAsHandler is used to set the subject for invocation of the method. This enables service of a method to specify the identity required to run. These identities are Caller, System, or Service level.

AuthorizationHandler

Request

The above handlers set the subject for method invocation. This handler is trying to authorize the subject based on the service authorization needs. Service specifies this through configuration (we will discuss details later). This can be none, self, or gridmap.

X509SignHandler

Response

If signature is requested for the response message, this handler is used. This handler uses a JAAS lookup to get the credential of the call originator. This originator may be different from the thread-level subject. Used with GSI XML signature.

GSSHandler

Response

In a GSI secure conversation, the return message is protected either by XML signature or XML encryption.

The handlers described in Table 12.4 are the most commonly used handlers. One thing we must pay close attention to is the processing sequence of these handlers. So we must be careful of their order in the configuration file. Another important aspect we must be aware of is the type of handler. There are two types of handlers available, JAX-RPC and AXIS. The difference is mainly on the signature and parameters processing. Listing 12.28 describes the handler flow.

Listing 12.28. Security handler configuration in the service side.
 <globalConfiguration> <parameter name="authenticationService" value="gsi/AuthenticationService"/> <requestFlow> ........................ <handler type= "Java:org.globus.ogsa.impl.security.authentication.service                                           .AuthenticationServiceHandler"/> <handler type="Java:org.globus.ogsa.handlers.RoutingSecRequestHandler"/> <handler type="Java:org.globus.ogsa.utils.JAXRPCHandler">          <parameter name="className"          value="org.globus.ogsa.impl.security.authentication.wssec          .WSSecurityHandler"/> </handler> <handler type="Java:org.globus.ogsa.impl.security.authentication                                               .SecurityPolicyHandler"/> <handler type="Java:org.globus.ogsa.impl.security.authorization                                               .AuthorizationHandler"/> <handler type="Java:org.globus.ogsa.impl.security.authentication                                               .CredentialRefreshHandler"/>   ................................. </requestFlow> <responseFlow> ................................... <handler type="Java:org.globus.ogsa.utils.JAXRPCHandler">   <parameter name="className"     value="org.globus.ogsa.impl.security.authentication.X509SignHandler"/> </handler> <handler type="Java:org.globus.ogsa.utils.JAXRPCHandler">   <parameter name="className"     value="org.globus.ogsa.impl.security.authentication.GSSHandler"/> </handler> </responseFlow> </globalConfiguration> <service name="gsi/AuthenticationService" provider="Handler"                                              style="wrapped" use="literal"> <parameter name="baseClassName" value="org.globus.ogsa.impl.security.authentication.service                                               .AuthenticationServiceImpl"/> .................................. </service> 
Secure Authentication Service

This grid service (AuthenticationService) is used to establish a security context. This service is configured in the configuration section, as shown in Listing 12.28.

If the authentication service handler is present in the request handler chain, and the service call is targeted toward a secure authentication (i.e., the target endpoint ends with "/authService"), then the calls will be directed to this "AuthenticationService" for creating or refreshing the secure context.

This "AuthenticationServiceImpl" class is responsible for secure context establishment and implements "continueTokenExchange" and "initTokenExchange" methods. We will see the internal implementation of this call later in the section.

Configuring Service-Specific Secure Authentication and Authorization Information

A service can specify its authentication and authorization requirements through an external configuration file. A service informs the framework about this configuration file through its configuration property in the WSDD file. This is shown in Listing 12.29. The sample specifies a "self" authorization process and provides an XML file for security configuration.

Listing 12.29. Configuring a service with security information.
 <service name="ogsa/cmm/OperatingSystemService" provider="Handler" style="wrapped">     ...................................   <parameter name="securityConfig" value=" ogsa/cmm/impl/security-       config.xml"/>   <parameter name="authorization" value="self"/>     ................................... </service> 

We can now explore the details of the security configurations, as specified through the security-config.xml file. A sample configuration discussing a secure service is in Listing 12.30.

Listing 12.30. GT3 security mapping file.
 <securityConfig xmlns="http://www.globus.org">  <method name="cmm:reboot"          xmlns:cmm="http://ogsa.org/cmm/operatingsystem">    <run-as>      <caller-identity/>    </run-as>    <auth-method>     <gsi/>    </auth-method>  </method>  <!-- defaults -->  <auth-method>   <none/>  </auth-method> </securityConfig> 

The authorization information in the configuration file enables a service to specify method-level authorization and code execution policy (run-as identity). For example, this enables the operating system service to specify the reboot operation to run under the caller's identity and use a GSI authorization mechanism. This provides fine-grained control over method execution. In addition, it enables a service to specify the authentication mode of choice from the list of "none," "gsi," and "self."

We can now move on to the commonly defined client-side security handlers listed in Table 12.5.

Table 12.5. Client-Side Security Handlers

GT3 Security Handlers

Request/Response

Description

X509SignHandler

Request

If signature is requested for the request message, this handler is used. This is used with GSI XML signature.

SecContextHandler

Request

This is the core security handler responsible for establishing the security context. Once the context is established, it will be associated with the stub.

GSSHandler

Request

In a GSI secure conversation, the request message is protected either by XML signature or XML encryption using the context established using SecContextHandler.

WSSecurityClientHandler

Response

Responsible for handling the response from the service either with GSI XML signature or secure conversation messages.

The handlers, as shown in Table 12.5, are the most commonly used for the client-side security enabling mechanisms. The processing sequence of these handlers is very important. So, we must be careful of their order in the configuration file. Listing 12.31 shows the usage.

Listing 12.31. Client-side security configuration.
 <globalConfiguration> <requestFlow>              ........................................   <handler type="Java:org.globus.ogsa.utils.JAXRPCHandler">     <parameter name="className"               value="org.globus.ogsa.impl.security.authentication                      .X509SignHandler"/>   </handler>   <handler type="Java:org.globus.ogsa.utils.JAXRPCHandler">     <parameter name="className"               value="org.globus.ogsa.impl.security.authentication                      .SecContextHandler"/>     <parameter name="authService" value="auto"/>   </handler>   <handler type="Java:org.globus.ogsa.utils.JAXRPCHandler">     <parameter name="className"               value="org.globus.ogsa.impl.security.authentication                      .GSSHandler"/>   </handler> </requestFlow> <responseFlow>         ....................................     <handler type="Java:org.globus.ogsa.utils.JAXRPCHandler">       <parameter name="className"                 value="org.globus.ogsa.impl.security.authentication.wssec                        .WSSecurityClientHandler"/>     </handler>   </responseFlow> </globalConfiguration> 
Client-Side Programming Model to Enable Security

The following GSI properties can be set on the client stub to control the authentication/authorization process:

  • The credential to use

    It is used to pass a specific set of credentials for authentication. User can specify a credential of type " org.ietf.jgss.GSSCredential" instance. By default, if not specified, the default user proxy credential is used.

  • Authorization mechanism to use

    It is used to set authorization type to perform. There are different authorizations available: SelfAuthorization, HostAuthorization, BasicSubjectAuthorization and so on. By default, if not specified, host authorization is performed.

  • GSI delegation mode

    This delegation mode can be one of:

    • GSIConstants.GSI_MODE_NO_DELEG performs no delegation (default)

    • GSIConstants.GSI_MODE_LIMITED_DELEG performs limited delegation

    • GSIConstants.GSI_MODE_FULL_DELEG performs full delegation

    • These constants are used for GSI Secure Conversation or transport security only. It is used to set GSI delegation mode.

Here is a sample client-side code snippet that illustrates the usage pattern of these properties:

 OGSIServiceGridLocator factoryService = new OGSIServiceGridLocator();   factory factory = factoryService.getfactoryPort(new HandleType(handle));   // enable GSI Secure Conversation message level security   ((Stub)factory)._setProperty(Constants.GSI_SEC_CONV, Constants.SIGNATURE);   // enable limited delegation   ((Stub)factory)._setProperty(GSIConstants.GSI_MODE,GSIConstants.GSI_MODE     _LIMITED_DELEG);   // set client authorization to none   ((Stub)factory)._setProperty(Constants.AUTHORIZATION, NoAuthorization.getInstance()); 

The handlers described in Table 12.5 will kick off the security enabling process. It provides message level security through message signatures and encryption without user involvement.

Internal Security Design Workflow Details

Secure Context Establishment

GT3 provides a plug-and-play mechanism for secure context establishment process. It defines Secure Authentication service, which implements SecureContextEstablishmentPortType. This service is a stateless Grid service, which in turn talks to other security managers to get hold of a secure token for that service.

In the client-side, a secure context is established for each instance of the stub the client is using to communicate with the service. These stubs in turn use "SecContextHandler" handler (defined in client-config.wsdd) to create and hold the context information. Since the context is established for the user associated with the current thread, it is illegal to use the same stubs from other thread. This context is negotiated each time there is a change in the GSI mode. The allowable modes include:

  • Full delegation

  • Limited delegation

  • No delegation

Figure 12.18. Client-side secure conversation establishment.

graphics/12fig18.gif

This secure context is established by communicating to the Secure Authentication Service deployed in the server. For this the client has to get the current JAAS subject and credential. For GSI, the client contacts the GSS Manager to get the context by calling manager.createCredentialCall() using the subject and context. Then the client calls the authentication service's "initTokenExchange" or "continueTokenExchange" method depending on whether the call is first time or not.

This operation is a normal SOAP call through an SSL. The authentication service returns a context ID and a binary token. The client runs this authentication process till they get the context. Once the client gets the context, it is attached with the current stub.

WS-Security Handling

The above secure negotiation creates a secure token and the WS-Security handler uses this token to sign the message and encrypt the message. This token is passed along with the SOAP message header. Work is going on to define a standard security profile for this GSI token exchange through WS-Security header.

Service Message Exchange Logging

Based upon our experiences, we find that the most valuable information we need to collect during message interaction between a client and a service is the real messages exchanged between them. This is valuable during development of services. The current default GT3 configuration enables SOAP message exchange over HTTP. This message helps us understand the target service, the operations to be invoked, and the message exchanged. We can do a number of debugging and compatibility tests with these messages. For example, if we can collect a SOAP message from a client to a service, we can do a WS-I basic profile validation on the collected message to check for compatibility with other implementers. In fact, WS-I basic profile tools are working in a similar fashion, as we can see in the TCPMon case below.

There are three mechanisms we can do to enable this message capture:

  1. Using GT3-provided MessageHandler in both client and service side. This is the simplest process by putting a global handler on the client and server side. Listing 12.32 below shows how we can perform this in our server-config.wsdd and client-config.wsdd files.

Here is how we account for that in server-config.wsdd:

Listing 12.32. MessagingLoggingHandler configuration.
 <requestFlow>     <handler type="Java:org.globus.ogsa.handlers.MessageLoggingHandler"/>     ....................... </requestFlow> <responseFlow>     <handler type="Java:org.globus.ogsa.handlers.MessageLoggingHandler"/>     ....................... </responseFlow> 

Note that the message displayed by this handler is dependent on their position in the configuration handler list. This is because some handler may modify the message. Since handlers are using Apache common logging mechanisms for message logging, we must configure that to display the messages correctly.

Listing 12.33 shows a sample message displayed by the above setting.

Listing 12.33. A sample SOAP message.
 [7/22/03 13:40:50:649 EDT]  f9f2b20 MessageLoggin I org.globus.ogsa.handlers.MessageLoggingHandler  SOAPEnvelope: <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>   <createService xmlns="http://www.gridforum.org/namespaces/2003/03/OGSI">    <terminationTime ns1:before="infinity" ns1:after="infinity" xmlns:ns1="http://www.gridforum.org/namespaces/2003/03/OGSI"/>    <creationParameters xsi:nil="true"/>   </createService>  </soapenv:Body> </soapenv:Envelope> 
  1. Using the TCPMon utility with some configuration changes. Using the TCPMon utility, we can relay all the messages through a TCP port. Start the tcpmon using

 Java org.apache.axis.utils.tcpmon [listenPort targetHost targetPort] 

This will help us display most of the messages flowing through the wire. For complex situations we may need to do more work on the server configuration to get all the messages. This is because the server is modifying the dynamic soap address for the GSR using the information available with it. This is normal in the case of handle resolution. In such situations the client's call using the above soap address may not be directed to the tcpmon's port unless we change some configurations in the server. This includes changing "httpPort" and "schemaRoot" properties. In some cases (distributed machines) we may need to change the server host name too. Listing 12.34 indicates how to change the default port number to be displayed in the GSR soap address (and instance of elements).

Listing 12.34. A sample configuration for setting internal service port numbers.
 <globalConfiguration>     <!-- to change the port number -->     <parameter name="httpPort" value="8080"/>     <!-- to change the WSDL retrieval schema root  -->     <parameter name="schemaRoot" value="http://localhost:8080/ogsa/"/>     ............................................... </globalConfiguration> 
  1. Using the Apache AXIS logging feature. This enables us to configure the select Apache AXIS classes for message logging. The properties (ogsilogging.properties) needed to set are shown in Listing 12.35.

Listing 12.35. Configuring message log for Apache AXIS messages.
 org.apache.client.Call=console,debug   org.apache.axis.transport.http.HTTPSender=console,debug 

Other Important Elements in GT3

Before we conclude our discussion on the programming model aspects of GT3 grid service, there are some concepts needing special discussion. These concepts include:

  • Message encoding

  • Type-mapping and serialization support

Let's examine these concepts in more detail.

Message Style and Encoding

We know that there are two SOAP message styles, document and rpc, which define how a SOAP message body is formatted. In addition, there are two modes to encode an operation's parameters, literal and encoded.

SOAP Message Encoding
  • Message encoding. This encoding is based on SOAP graph encoding, as described in the SOAP specification.

  • Literal encoding. This SOAP encoding is using XML schema for message description.

SOAP Message Style

RPC. This is based on "rpc"-style message formatting where operation name and signatures are defined correctly in XML and embedded in the SOAP body. The process is complicated, as each body element must map to the parameter of the operation.

Document. In this case, the operation name maps to a document, and the SOAP body contains that document with the operation name as the first element. In most of the implementations (AXIS and .NET) this encoding is actually resulting in "wrapped"-style messaging. In AXIS, we can see this configuration for each service in the server-config.wsdd and the generated SOAP stubs.

To conclude, readers must note that the WS-I organization is encouraging the use of document/literal messaging and deprecating the other encoding formats. As we can see, GT3 also encourages the use of the document/literal approach. The bottom line is, unless we have strong reasons in favor of other kinds of encoding, we should use the document/literal approach.

Type-Mapping and Serialization

The type mapping and serialization/deserialization process involves

  • Creating native language types from XML schema elements. The AXIS-provided WSDL2Java can convert the XSD types defined in the WSDLs to its corresponding Java types.

  • Mapping the XML messages fragments to native language type created in the previous process and vice versa. This mapping process needs the conversion guidelines to be defined by the client-side stub or the service container. The client-side process is simple based on the fact that these types are created and registered with the stubs. The framework can handle this conversion on runtime during SOAP message parsing. There we may need to register this type-mapping information with the configuration files. Listing 12.36 shows how we can register type-mapping information statically in a container. There are dynamic AXIS APIs available to set the type-mapping on runtime.

Listing 12.36. A sample type-mapping configuration.
 <typeMapping       xmlns:ns="http://www.gridforum.org/namespaces/2003/03/OGSI"       qname="ns:handleSet"       type="Java:org.gridforum.ogsi.LocatorType"       serializer="org.apache.axis.encoding.ser.BeanSerializerfactory"       deserializer="org.apache.axis.encoding.ser.BeanDeserializerfactory"       encodingStyle="" /> 

GT3 handles XML types using generic XML Infoset type for the cases where these type-mappings are not available.

  • Deserializing the native language types to XML fragments.



Grid Computing (IBM Press On Demand Series)
Windows Vista(TM) Plain & Simple (Bpg-Plain & Simple)
ISBN: 131456601
EAN: 2147483647
Year: 2002
Pages: 118

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