Difference between revisions of "Integration and Interoperability Facilities Framework: Client Libraries Framework"

From Gcube Wiki
Jump to: navigation, search
(Overview)
Line 40: Line 40:
 
= Overview =
 
= Overview =
  
The main design pattern defined by the model is that of a [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Proxy_Lifetime|service proxy]]. This requires that <code>foo</code> endpoints be represented in the library by an interface <code>Foo</code> and an implementation <code>DefaultFoo</code>. The former defines methods that the latter implements against the API of the <code>foo</code> stubs. For example, if <code>FooPortType</code> is the type of <code>foo</code> stubs and <code>bar()</code> one of their <code>String</code>-valued methods:
+
We consider first the requirements that the model raises against client libraries. This illustrates the challenges faced by client libraries to achieve compliance with the model, hence the likelihood of variations in style and quality across their implementations. We then overview the support offered by the framework towards meeting those challenges in a consistent and cost-effective manner.
  
<source lang="java">
+
== Implementation Requirements ==
 +
 
 +
The design model for client libraries mandates the use of  [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Proxy_Lifetime|service proxies]]. The library represents <code>foo</code> with an interface <code>Foo</code> and its default implementation <code>DefaultFoo</code>. <code>Foo</code> defines methods that correspond to the remote operations of <code>foo</code> endpoints, and <code>DefaultFoo</code> implements the methods against the lower-level API of <code>foo</code> stubs.
 +
 
 +
For example, if <code>FooPortType</code> is the type of <code>foo</code> stubs and <code>bar()</code> one of their <code>String</code>-valued methods, the proxy pattern maps onto code of this form:
 +
 
 +
<source lang="java" highlight="1,3,7,9">
 
public interface Foo {
 
public interface Foo {
 
   
 
   
Line 67: Line 73:
 
</source>
 
</source>
  
While the pattern is straightforward, complexity arises from:
+
In itself, the pattern is straightforward. Some complexity may arises from the design requirements of particular <code>Foo</code> methods, including particular types inputs or outputs (e.g. e.g. [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Streams|streams]])), faults with diverse semantics (e.g. [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Faults|outages vs. contingencies]]), and particular invocation semantics (e.g. [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Asychronous_Methods|asynchronous]])). The framework offer limited support here, as the design directives provided by the model do not require it on average. Where they do, the model indicates dedicated gCube libraries that provide it (e.g. the streams library, the scope library, or the security library). The framework does include the classes and interfaces upon which its directives are based, however. We discuss these components and their placement within the framework [[#Additional Support|here]].
  
* the requirements of particular methods. The model specifies how <code>Foo</code> and <code>DefaultFoo</code> should be designed when methods take or return given types of values (e.g. [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Streams|streams]]), handle faults with given semantics (e.g. service [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Faults|contingencies]]), or execute under a given operational semantics (e.g. [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Asychronous_Methods|asynchronously]]).
+
Arguably, the strongest demand that the model makes on the client library concerns how <code>Foo</code> proxies bind to service endpoints. The requirement is for two binding modes:  
  
* the configuration and operation of proxies. The model requires proxies to work in either one of two modes:
+
* in [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Direct_Mode|direct mode]], the proxies obtain the address of given service endpoints from clients and execute all their methods against those endpoints;
  
*: in [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Direct_Mode|direct mode]], proxies obtain the address of given service endpoints from clients and execute all their methods against those endpoints. Clients model addresses as <code>W3EndpointReference</code>s or - depending on wether <code>foo</code> is a stateless or stateless service - as <code>(host, port)</code> pairs or <code>(host, port, key)</code> triples. Since stubs APIs for gCore services model addresses as <code>EndpointReferenceType</code>s, the library must implement address conversion and address validation;
+
* in [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Discovery_Mode|discovery mode]], the proxies identify service endpoints from queries to gCube discovery services;
  
*: in [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Discovery_Mode|discovery mode]], proxies identify service endpoints from queries to gCube discovery services and are responsible for using the results in a fault-tolerant and optimised manner. [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Instance_Queries|Queries]] must be high-level value-objects and the library must implement specific [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Endpoint_Management|binding and caching strategies]] for query results.
+
Implementing direct mode is fairly simple, as clients provide all the binding information. They model addresses as <code>W3CEndpointReference</code>s or - depending on wether <code>foo</code> is a stateless or stateful service - as <code>(host, port)</code> pairs or <code>(host, port, key)</code> triples. As stubs APIs for gCore services model addresses as <code>EndpointReferenceType</code>s, the library is required to implement address conversion and address validation. Though conceptually simple, the task is error-prone and sufficiently boilerplate to call for reuse through the framework.
  
The framework offer limited support for the implementation of <code>Foo</code>‘s API, as there are external libraries that provide it when and where it is required (e.g. the streams library, the scope library, or the security library). However, the framework includes the common faults and callback components defined by the [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model|model]]. We discuss these components and their placement within the framework [[#Additional Support|here]].
+
Implementing discovery mode is significantly more complicated, as the proxies are responsible for using query results in a fault-tolerant and optimised manner. The model requires that the library implements [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Endpoint_Management|binding and caching strategies]] which depend on correct handling of a variety of different fault types. [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries_Design_Model#Instance_Queries|Queries]] must be value objects that hide the lower-level idioms of query formulation and submission required by the gCube discovery services.
+
 
On the other hand, the framework can meet all proxy configuration and operation requirements on behalf of the library. We give a tour below of the support available in this area and expand on individual components in later sections.
+
Since the two modes are markedly different, combining them in a single proxy implementation presents its own challenges. In particular, it becomes difficult to implement <code>Foo</code>’s methods uniformly, regardless of the binding mode of proxy instances. Lack of homogeneity extends to proxy configuration and threatens the overall testability of the code. Solutions to these problems are likely to vary in style and quality across client libraries.
 +
 
 +
== Framework Support ==
 +
 
 +
We now give a tour of the support offered by the framework towards meeting the implementation challenges discussed above. We expand on the role and use of individual framework components in later sections.
 +
 
 +
The key contribution of the framework comes in the form of <code>ProxyDelegates</code>, i.e. components that know how to make calls in a given mode on behalf of proxies. The idea is that the library defines explicit <code>Call</code> objects and its proxies pass them to the delegates for execution.
  
 
= Calls and Delegates =
 
= Calls and Delegates =

Revision as of 12:32, 22 May 2012

gCube includes client libraries for many of its services and defines a general model for their design. The model requires that all libraries offer a common set of capabilities and adopt uniform patterns for the design of their APIs, regardless of service semantics and technology stacks. The model, however, does not indicate how capabilities and patterns should be implemented, nor does it mandate low-level API details.

The client library framework supports the implementation of client libraries which comply with the model. Through code sharing, the framework reduces development costs for client libraries and ensures the consistency and correctness of their implementations.

In this document, we assume familiarity with the design model and illustrate how the framework can be used to develop a model-compliant client library for a hypothetical foo service.

Distribution

The framework is layered across as a set of components, all of which are available in our Maven repositories as artifacts in the org.gcube.core group.

common-clients is the top layer of the framework and comprises classes and interfaces that do not depend on particular technology stacks. In this sense, common-clients is as general as the design model. Lower layers of the framework adapt common-clients to specific stacks. At the time of writing, gCore is the dominant technology stack for gCube services and their clients. common-gcore-clients is thus the only available specialisation of common-clients.

We assume accordingly that foo is a gCore service, i.e. a JAX-RPC Java service that can be deployed in one or more gCore containers on some gCube hosting nodes. We also assume that the client library for foo is developed as a Maven project, in line with system recommendations. To use the framework, the library declares a compile-time dependency on common-gcore-clients in its POM, as follows:

<dependency>
 <groupId>org.gcube.core</groupId>
 <artifactId>common-gcore-clients</artifactId>
 <version>...</version>
 <scope>compile</version>
</dependency>

This dependency brings common-gcore-clients and its transitive dependencies, including common-clients, on the compile-time classpath of the library. The version will vary over time and is 2.0.0 at the time of writing.

The library depends also on the stub library of foo, which we also assume available as a Maven artifact, e.g.:

<dependency>
 <groupId>org.gcube.samples</groupId>
 <artifactId>foo-stubs</artifactId>
 <version>...</version>
 <scope>compile</version>
</dependency>


Framework-dependencies.png

Overview

We consider first the requirements that the model raises against client libraries. This illustrates the challenges faced by client libraries to achieve compliance with the model, hence the likelihood of variations in style and quality across their implementations. We then overview the support offered by the framework towards meeting those challenges in a consistent and cost-effective manner.

Implementation Requirements

The design model for client libraries mandates the use of service proxies. The library represents foo with an interface Foo and its default implementation DefaultFoo. Foo defines methods that correspond to the remote operations of foo endpoints, and DefaultFoo implements the methods against the lower-level API of foo stubs.

For example, if FooPortType is the type of foo stubs and bar() one of their String-valued methods, the proxy pattern maps onto code of this form:

public interface Foo { 
  String bar() throws ...; 
}
 
public class DefaultFoo implements Foo { 
  public String bar() throws ... { 
   ...FooPortType endpoint...
 
   try {
 
     return endpoint.bar();
 
   }
   catch(...) { //fault handling
 
   }
 
}

In itself, the pattern is straightforward. Some complexity may arises from the design requirements of particular Foo methods, including particular types inputs or outputs (e.g. e.g. streams)), faults with diverse semantics (e.g. outages vs. contingencies), and particular invocation semantics (e.g. asynchronous)). The framework offer limited support here, as the design directives provided by the model do not require it on average. Where they do, the model indicates dedicated gCube libraries that provide it (e.g. the streams library, the scope library, or the security library). The framework does include the classes and interfaces upon which its directives are based, however. We discuss these components and their placement within the framework here.

Arguably, the strongest demand that the model makes on the client library concerns how Foo proxies bind to service endpoints. The requirement is for two binding modes:

  • in direct mode, the proxies obtain the address of given service endpoints from clients and execute all their methods against those endpoints;
  • in discovery mode, the proxies identify service endpoints from queries to gCube discovery services;

Implementing direct mode is fairly simple, as clients provide all the binding information. They model addresses as W3CEndpointReferences or - depending on wether foo is a stateless or stateful service - as (host, port) pairs or (host, port, key) triples. As stubs APIs for gCore services model addresses as EndpointReferenceTypes, the library is required to implement address conversion and address validation. Though conceptually simple, the task is error-prone and sufficiently boilerplate to call for reuse through the framework.

Implementing discovery mode is significantly more complicated, as the proxies are responsible for using query results in a fault-tolerant and optimised manner. The model requires that the library implements binding and caching strategies which depend on correct handling of a variety of different fault types. Queries must be value objects that hide the lower-level idioms of query formulation and submission required by the gCube discovery services.

Since the two modes are markedly different, combining them in a single proxy implementation presents its own challenges. In particular, it becomes difficult to implement Foo’s methods uniformly, regardless of the binding mode of proxy instances. Lack of homogeneity extends to proxy configuration and threatens the overall testability of the code. Solutions to these problems are likely to vary in style and quality across client libraries.

Framework Support

We now give a tour of the support offered by the framework towards meeting the implementation challenges discussed above. We expand on the role and use of individual framework components in later sections.

The key contribution of the framework comes in the form of ProxyDelegates, i.e. components that know how to make calls in a given mode on behalf of proxies. The idea is that the library defines explicit Call objects and its proxies pass them to the delegates for execution.

Calls and Delegates

Plugins

Queries

Additional Support