Difference between revisions of "Common-gcore-stubs"

From Gcube Wiki
Jump to: navigation, search
(Wrapped or Bare ?)
(Collections)
 
(46 intermediate revisions by 2 users not shown)
Line 8: Line 8:
 
</source>
 
</source>
  
== Quick Tour==
+
== Overview ==
  
At the time of writing, most gCube services are [http://jcp.org/aboutJava/communityprocess/final/jsr101/ JSR101] (JAX-RPC) services implemented and running on the gCore stack inside a gCube Hosting Node. <code>common-gcore-stubs</code> allows us to invoke such services without dependencies on that stack, hence from within arbitrary client environments. It does so by interacting on our behalf with the [http://jcp.org/aboutJava/communityprocess/final/jsr224 JSR224] (JAX-WS) runtime, which is part of the Java platform since version 1.6 as the standard for SOAP-based Web Services and Web Service clients. With <code>common-gcore-stubs</code>, we use a modern standard to call services that align with a legacy standard.
+
At the time of writing, most gCube services are [http://jcp.org/aboutJava/communityprocess/final/jsr101/ JSR101] (JAX-RPC) services implemented on the gCore stack and running inside a gCube Hosting Node.  
 +
 
 +
<code>common-gcore-stubs</code> allows us to invoke such services without dependencies on that stack from arbitrary client environments. It does so by interacting on our behalf with the [http://jcp.org/aboutJava/communityprocess/final/jsr224 JSR224] (JAX-WS) runtime, which is part of the Java platform since version 1.6. Effectively, with <code>common-gcore-stubs</code> we use a modern standard to call legacy services.
  
 
We provide the library with:
 
We provide the library with:
  
* information about the target service, such as its gCube coordinates (service class, service name) and its WSDL coordinates (namespace, porttype name);
+
* a ''service descriptor'', i.e. information about the service such as gCube coordinates (service class, service name) and WSDL coordinates (service name, porttype name);
* the address of a target endpoint of the services;
+
* a ''Service Endpoint Interface'' (SEI), i.e. a local Java interface that models the remote API of the service and provides additional information about its endpoints through [http://jcp.org/aboutJava/communityprocess/final/jsr181/ JSR-181] annotations.  
* the Service Endpoint Interface (SEI) of the service, i.e. the local Java interface that models the remote API of the service and provides additional information about its endpoint through [http://jcp.org/aboutJava/communityprocess/final/jsr181/ JSR-181] annotations.  
+
* the address of target endpoints/instances of the service;
  
The library gives us back a dynamically generated proxy implementation of the SEI, which is first synthesised by the JAX-WS runtime and then appropriately configured by the library to issue gCube calls to the target endpoint (i.e. propagate the call scope, target service coordinates, client identity, etc.).
+
The library gives us back dynamically generated implementations of the SEI. The implementations are first synthesised by the underlying JAX-WS runtime and then appropriately configured by the library to issue gCube calls to the target endpoint (i.e. propagate the call scope, target service coordinates, client identity, etc.).
 +
 
 +
SEI implementations can then serve as local stubs for service endpoints/instances. Typically, we use them to implement higher-level [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries|client libraries]].
 +
 
 +
The SEI and the other required information may be distributed as a stand-alone component, like for JAX-RPC stubs. Alternatively, they may be integral part of our client libraries. The fact that these 'stubs' have a minimal footprint, do not need to be generated through tooling, and are client-only artefacts makes the embedding option natural and in fact appealing.
 +
 
 +
== Quick Tour==
  
The generated proxy can serve as a local stub for Acme endpoints. Typically, we use this stub in the context of higher-level proxying facilities, such as gCube [[Integration_and_Interoperability_Facilities_Framework:_Client_Libraries|Client Libraries]]. The SEI and the other required information may be distributed as a stand-alone component, like for JAX-RPC stubs. Alternatively, they may be integral part of the higher-level Client Library which uses them with <code>common-gcore-stubs</code>. The minimal footprint of these 'stubs', the fact that they do not need to be manually generated (though they can), and the fact that they serve a client-only role (as opposed to JAX-RPC stubs, which we use also service-side) makes the embedding option natural and appealing.
+
We run through a simple example to illustrate the process and relevant APIs.
  
In the following, we run through a simple example to illustrate the process and relevant APIs.
+
===Acme===
  
===A Sample Service===
+
For the sake of simplicity, let us illustrate how to use <code>common-gcore-stubs</code> to call a fictional gCore '''Acme''' service.
  
For the sake of simplicity, let us illustrate how to use <code>common-gcore-stubs</code> to call a fictional gCore '''Acme''' service. Let us assume that the remote API of <code>Acme</code> is defined by the following WSDL:
+
Let us assume that the remote API of <code>Acme</code> is defined by the following WSDL:
  
 
<source lang="xml">
 
<source lang="xml">
Line 43: Line 51:
 
</types>
 
</types>
 
 
<message name="fooInputMessage">
+
<message name="foo">
 
<part name="request" element="tns:foo"/>
 
<part name="request" element="tns:foo"/>
 
</message>
 
</message>
<message name="fooOutputMessage">
+
<message name="fooResponse">
 
<part name="response" element="tns:fooResponse"/>
 
<part name="response" element="tns:fooResponse"/>
 
</message>
 
</message>
Line 53: Line 61:
 
 
 
<operation name="foo">
 
<operation name="foo">
<input message="tns:fooInputMessage"/>
+
<input message="tns:foo"/>
<output message="tns:fooOutputMessage"/>
+
<output message="tns:fooResponse"/>
 
</operation>
 
</operation>
 
 
 
</portType>
 
</portType>
  
       <binding name="binding:AcmePortTypeSOAPBinding" type="tns:AcmePortType" xlmns:binding=""http://acme.org/bindings"">
+
       <binding name="AcmePortTypeSOAPBinding" type="tns:AcmePortType">
 
           <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
 
           <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
 
           <operation name="foo">
 
           <operation name="foo">
             <soap:operation soapAction="http://acme.org/StatelessPortType/fooRequest"/>
+
             <soap:operation soapAction="http://acme.org/AcmePortType/fooRequest"/>
 
             <input>
 
             <input>
 
                 <soap:body use="literal"/>
 
                 <soap:body use="literal"/>
Line 73: Line 81:
  
 
       <service name="service:AcmeService" xlmns:service="http://acme.org/service">
 
       <service name="service:AcmeService" xlmns:service="http://acme.org/service">
         <port name="AcmePortTypePort" binding="binding:StatelessPortTypeSOAPBinding">
+
         <port name="AcmePortTypePort" binding="tns:AcmePortTypeSOAPBinding">
 
             <soap:address location="...some address..."/>
 
             <soap:address location="...some address..."/>
 
         </port>
 
         </port>
Line 84: Line 92:
 
Like most gCore services, Acme:
 
Like most gCore services, Acme:
  
* defines a single porttype, <code>AcmePortType</code>. The porttype includes a single operation, <code>foo</code> that takes and returns a string;
+
* defines a single porttype, <code>AcmePortType</code>. In this case the porttype includes a single operation, <code>foo</code>, which takes and returns a string;
* <code>foo</code> can be invoked at a given Acme endpoint through SOAP over HTTP in a [http://www.w3.org/TR/wsdl#_how-s document/literal] style. In particular, <code>foo</code> request and response messages contain a single part and this part is an element declared in the <code><types></code> section and named in such a way that the request can be easily dispatched to the service implementation.
+
* <code>foo</code> can be invoked through SOAP over HTTP in a [http://www.w3.org/TR/wsdl#_how-s document/literal] style (essentially, messages are made directly of elements defined in the <code><types></code> section)
  
'''Note''': since the WSDL describes an Acme endpoint, it includes also its address. However, this address is largely irrelevant to our purposes because we use the WSDL to call different endpoint, whose addresses we discover only at call time.  
+
'''Note''': since the WSDL describes an Acme endpoint, it includes also its address. However, this address is largely irrelevant to our purposes because we use the WSDL to call different endpoints which we locate dynamically through our Information Services.  
  
'''Note:'''  gCore services are normally developed WSDL-first and use tooling to derive a WSDL complete with binding information from a partial WSDL that includes only logical definitions (types, port-types, operations). By design, the tooling spreads the derived WSDL across a number of files that follow a chain of imports. It also defines ad-hoc namespaces for the information it derives (e.g. <code>http://acme.org/service</code> for the <code><service></code> definitions, or <code>http://acme.org/bindings</code> for <code><binding></code> definitions). Here, we present the WSDL as a whole but keep using different namespaces for port-types, bindings, and service definitions so as to facilitate mapping the example to the WSDLs of real gCube services.  
+
'''Note:'''  gCore services are normally developed WSDL-first and use tooling to derive a WSDL complete with binding information (cf. <code><binding></code>, <code><service></code>) from a partial WSDL that includes only logical definitions (cf. <code><types></code>, <code><message></code>s, <code><portType></code>). By design, the tooling spreads the complete WSDL across a number of files that follow a chain of imports. It also defines ad-hoc namespaces for the information it derives (e.g. <code>http://acme.org/service</code> for the <code><service></code> definitions). Here, we present the WSDL as a whole but keep using a different namespace for the <code><service></code> definition, so as to facilitate mapping the example to the WSDLs of real gCube services.  
  
 
The WSDL provides the following coordinates about Acme, which we capture in a class of constants to use later with <code>common-gcore-stubs</code>:
 
The WSDL provides the following coordinates about Acme, which we capture in a class of constants to use later with <code>common-gcore-stubs</code>:
Line 99: Line 107:
 
public class AcmeConstants {
 
public class AcmeConstants {
  
   public static final String serviceNS = "http://acme.org/service";
+
   public static final QName name = new QName("http://acme.org/service","AcmeService");
  public static final String serviceLocalName = "AcmeService";
+
  public static final QName serviceName = new QName(serviceNS,serviceLocalName);
+
+
 
   public static final String porttypeNS = "http://acme.org";
 
   public static final String porttypeNS = "http://acme.org";
   static final String porttypeLocalName = "AcmePortType";
+
   public static final String porttypeLN = "AcmePortType";
 
}
 
}
  
 
</source>
 
</source>
  
===The Service Endpoint Interface ===
+
===Service Endpoint Interface ===
  
In JAX-WS terminology, the SEI is a local Java interface that mirrors the remote API of the Acme service. Its declaration includes annotations that provide the JAX-WS runtime with (some of the) information required to generate an implementation of the interface which can correctly call an Acme endpoint.
+
In JAX-WS terminology, the SEI is a local Java interface that mirrors the remote API of service endpoints. The interface includes annotations that provide the JAX-WS runtime with (some of the) information required to generate an implementation on the fly.
  
 
One way to obtain a SEI is to derive it from the WSDL with tooling, such as the [http://docs.oracle.com/javase/6/docs/technotes/tools/share/wsimport.html wsimport] utility which ships with the JDK. For this example, however, we produce the SEI manually, which gives us more control and makes for cleaner code.
 
One way to obtain a SEI is to derive it from the WSDL with tooling, such as the [http://docs.oracle.com/javase/6/docs/technotes/tools/share/wsimport.html wsimport] utility which ships with the JDK. For this example, however, we produce the SEI manually, which gives us more control and makes for cleaner code.
Line 119: Line 124:
 
...
 
...
  
@WebService(name=porttypeLocalName,targetNamespace=porttypeNS)
+
@WebService(name=porttypeLN,targetNamespace=porttypeNS)
 
public interface AcmeStub {
 
public interface AcmeStub {
  
Line 129: Line 134:
 
</source>
 
</source>
  
We name the SEI to reflect that it acts as a stub of the Acme service. As required by JAX-WS, we annotate the SEI with <code>@WebService</code>, providing the coordinates of the porttype that includes the operations to be proxied through the SEI. For this, we use the constants of the <code>AcmeConstants</code> class defined [[#A Sample Service|above]], which we statically import for improved legibility of the code.
+
We name the SEI so as to reflect that it acts as a local API for remote service endpoints. We annotate it with <code>@WebService</code>, providing the coordinates of the service porttype that defines the remote operations. For this, we use the constants of the <code>AcmeConstants</code> class defined [[#Acme|above]], which we statically import for improved legibility of the code. We then declare the method <code>foo</code> and annotate it with <code>@SOAPBinding</code> to indicate that its input and output should be put/found in request/response elements directly under the SOAP body.  
  
We then declare the method <code>foo</code> with the expected signature. We also annotate the method with <code>@SOAPBinding</code>, to indicate that the input and output strings should be put/found in elements directly under the SOAP body, rather than inside some 'wrapper' element of sorts. In other words, specifying the "bare" parameter style tells the JAX-WS runtime that the WSDL declaration for <code>foo</code> does ''not'' follow the so-called [http://www.ibm.com/developerworks/webservices/library/ws-usagewsdl wrapped pattern], which is otherwise assumed by default.
+
We discuss how to write SEIs in more detail [[#Writing SEIs|later on]]. For now, let us concentrate on how we use it with <code>common-gcore-stubs</code>.
  
'''Note''': For operations that take and return a single primitive type, such as <code>foo</code> the bare pattern is the natural choice  (provided that we name the elements in such a way that the service runtime can easily dispatch requests).
+
===Service Descriptor ===
  
===The Service Descriptor ===
+
We now provide <code>common-gcore-stubs</code> with a descriptor for Acme, i.e. the set of information which are required to call its endpoints. We provide the descriptor as an instance of the <code>GCoreService</code> class, which we build in a fluent style with the help of a <code>GCoreServiceBuilder</code>. A convenient place to do this is directly in the <code>AcmeConstants</code> class introduced [[#Acme|above]]. The following example illustrates:
 
+
With the SEI under our belt, we're left with providing <code>common-gcore-stubs</code> with a descriptor for the Acme service, i.e. the remaining pieces of information which are needed to call it.We provide the descriptor as an instance of the <code>GCoreService</code> class, which we build in a fluent style with the help of a <code>GCoreServiceBuilder</code>. A convenient place to do this is directly the <code>AcmeConstants</code> class first introduced [[#A Sample Service|above]].
+
 
+
The following example illustrates:
+
  
 
<source lang="java5">
 
<source lang="java5">
Line 149: Line 150:
 
  ...
 
  ...
 
   
 
   
  public static String service_class="....";
+
  public static String gcubeClass=....
  public static String service_name="...";
+
  public static String gcubeName=...;
  
  public static final GCoreService<AcmeStub> acme = service().withName(serviceName)
+
  public static final GCoreService<AcmeStub> acme = service().withName(name)
                                                             .coordinates(service_class,service_name)
+
                                                             .coordinates(gcubeClass,gcubeName)
 
                                                             .andInterface(AcmeStub.class);  
 
                                                             .andInterface(AcmeStub.class);  
  
Line 161: Line 162:
 
</source>
 
</source>
  
Since we will not need more than one instance of the descriptor, we create it once and for all as a constant named after the service. We use the static method <code>GCoreServiceBuilder#service()</code> to kick off the process and the follow the type system to provide the remaining information, using the constants already available within the class, pus the gCube coordinates of the service. The static 'star' import is just a convenience to improve further the legibility of the code.
+
Since we will not need more than one instance of the descriptor, we create it once and for all as a constant named after the service. We invoke the static method <code>GCoreServiceBuilder#service()</code> to begin with and then follow the methods of the fluent API to provide the remaining information, using the constants already available within the class, plus the gCube coordinates of the service. The static 'star' import is just a convenience to improve further the legibility of the code.
  
=== Stubbed Calls ===
+
=== Calling Endpoints ===
  
When it's finally time to call an Acme endpoint, we use the SEI and the <code>GCoreService</code> descriptor to obtain a an implementation of the SEI configured for the target endpoint:
+
When it's finally time to call a service endpoint, we use the SEI and the <code>GCoreService</code> descriptor to obtain a SEI implementation configured for the endpoint:
  
 
<source lang="java5">
 
<source lang="java5">
Line 174: Line 175:
 
...
 
...
  
String address =  
+
URI address = ...
  
 
AcmeStub stub = stubFor(acme).at(address);
 
AcmeStub stub = stubFor(acme).at(address);
Line 182: Line 183:
 
</source>  
 
</source>  
  
We build our <code>AcmeStub</code> using the static method <code>stubFor(GCoreService)</code> of the <code>StubFactory</code> class, which as usual we statically import for convenience.
+
We build our stub using the static method <code>stubFor(GCoreService)</code> of the <code>StubFactory</code> class, which we statically import for convenience.
We provide the factory with the <code>acme</code> descriptor that we've defined [[#The Service Descriptor|earlier]] in the <code>AcmeConstants</code?> class, which we also statically import.
+
We provide the factory with the <code>acme</code> descriptor defined [[#Service Descriptor|earlier]], which we also statically import.
We also provide the address of the target endpoint, here modelled as a string in the assumption that the service is stateless. We shall see [[#Calling Stateful Services|later]] how to model reference instances of stateful services.
+
We also provide the address of the endpoint, here modelled as a <code>URI</code>.  
  
 
Finally, we use the stub returned by the <code>StubFactory</code> to call the endpoint through the SEI.
 
Finally, we use the stub returned by the <code>StubFactory</code> to call the endpoint through the SEI.
  
'''Note''': as we're calling a gCube service, we need to do so in a given scope or the stub will refuse to go ahead. The classic way to associate the call with a scope is to set it on the current thread:
+
'''Note''': as we're calling a gCube service, we need to do so in a given scope or the stub will refuse to go ahead. One way to associate the call with a scope is to set it on the current thread:
  
 
<code>ScopeProvider.instance.set('..some scope...");</code>
 
<code>ScopeProvider.instance.set('..some scope...");</code>
 +
 +
=== Calling Instances ===
 +
 +
So far, we've implicitly assumed that Acme is stateless. However, many gCore services are stateful, their endpoints encapsulate data which they use and change upon processing client requests. Such endpoints can be modelled as collections of service instances, where all instances have the API of the service but are bound to different state. A service instance is typically created by an endpoint of a companion factory service which returns references to the instances. Such references are modelled on the wire as the ''endpoint references'' defined by [http://www.w3.org/TR/2005/CR-ws-addr-core-20050817/ WS-Addressing]. In Java, the class [http://docs.oracle.com/javase/6/docs/api/javax/xml/ws/EndpointReference.html EndpointReference] models such references.
 +
 +
We will see [[#Factory_Operations|later]] how to model instance references in the SEIs of factory services. For now, let us assume to have one, either  because we've manually built it during testing (cf. Java's own [http://docs.oracle.com/javase/6/docs/api/javax/xml/ws/wsaddressing/W3CEndpointReferenceBuilder.html W3CEndpointReferenceBuilder]), or because we've obtained it from a factory service, or else because we've dynamically discovered by querying the Information Services. We use the reference to call a service instance as follows:
 +
 +
<source lang="java5">
 +
 +
import ......AcmeConstants.*;
 +
import static org.gcube.common.clients.stubs.jaxws.StubFactory.*;
 +
 +
...
 +
 +
EndpointReference reference = ...
 +
 +
AcmeStub stub = stubFor(acme).at(reference);
 +
 +
String response = stub.foo("...");
 +
 +
</source>
 +
 +
We thus interact with instances as we interact with endpoints, the only difference being that we pass the reference to the <code>at()</code> clause of the build expression.
 +
 +
'''Note''': we can use <code>EndpointReference</code>s even when we target endpoints of stateless services, instead of <code>URI</code>s. In other words, we can use the API of <code>common-gcore-stubs</code> uniformly, if the code so requires.
  
 
== Writing SEIs ==
 
== Writing SEIs ==
  
The example [[#Quick Tour|above]] considers the simplest case of remote operation, where inputs and outputs are single atomic values. The range of remote operations we can encounter across gCore services is in fact quite wide, and different are also the patterns used by services to describe those inputs and outputs in WSDLs. While the ultimate reference to annotating SEIs is provided by the JAX-WS [http://jcp.org/aboutJava/communityprocess/final/jsr224 specifications] and the [http://jcp.org/aboutJava/communityprocess/mrel/jsr222 [http://jcp.org/aboutJava/communityprocess/mrel/jsr222 specifications] (JAXB), we consider some of the common cases below, for convenience.
+
The example [[#Quick Tour|above]] considers the simplest case of remote operation, where inputs and outputs are single atomic values. The range of remote operations we can encounter across gCore services is in fact quite wide. While the ultimate reference to writing and annotating SEIs is provided by the JAX-WS [http://jcp.org/aboutJava/communityprocess/final/jsr224 specifications] and the [http://jcp.org/aboutJava/communityprocess/mrel/jsr222 specifications] (JAXB), we consider some of the common cases below for convenience.
  
 
In all the cases, we ask the same question:  
 
In all the cases, we ask the same question:  
Line 200: Line 226:
 
:: ''how do we define and annotate the methods of the SEI so that the implementation generated by the JAX-WS runtime: a) sends requests which are understood by the service, and b) understands the responses produced by the service?''  
 
:: ''how do we define and annotate the methods of the SEI so that the implementation generated by the JAX-WS runtime: a) sends requests which are understood by the service, and b) understands the responses produced by the service?''  
  
As it turns out, for each case, the answer depends on how the service describe operations in its WSDL. In what follows we assume that, like for the Acme service of our earlier [[#A Sample Service|example]]:
+
In what follows we assume that, like for the Acme service of our earlier [[#Acme|example]]:
  
 
* the WSDL follows a document/literal style for its SOAP/HTTP binding, with requests and responses defined by a single element in accordance to [http://www.ws-i.org/profiles/basicprofile-1.1-2004-08-24.html WS-I profile];
 
* the WSDL follows a document/literal style for its SOAP/HTTP binding, with requests and responses defined by a single element in accordance to [http://www.ws-i.org/profiles/basicprofile-1.1-2004-08-24.html WS-I profile];
* the request element is named after the corresponding operation and the response element is named like the request element with the addition of the "Response" suffix. This convention follows best practices, as it allows SOAP engines to unambiguously dispatch requests.
+
* the request element is named after the corresponding operation. This convention follows best practices, as it allows SOAP engines to unambiguously dispatch requests. The response element is more freely named, though the convention is to suffix "Response" to the name of the operation.
  
 
The definitions of <code><message></code>, <code><portType></code>, <code><binding></code> and <code><service></code> elements become then boilerplate for our discussion, and we can omit it to concentrate solely on the definitions of request and response elements. In fact, we can simplify the presentation further if we agree to omit namespace prefixes altogether. Then we can illustrate each case by instantiating the following template:
 
The definitions of <code><message></code>, <code><portType></code>, <code><binding></code> and <code><service></code> elements become then boilerplate for our discussion, and we can omit it to concentrate solely on the definitions of request and response elements. In fact, we can simplify the presentation further if we agree to omit namespace prefixes altogether. Then we can illustrate each case by instantiating the following template:
Line 219: Line 245:
 
</source>
 
</source>
  
=== Wrapped or Bare ? ===
+
=== Wrapped or Bare? ===
  
An important way in which operations differ within and across services is in the general 'pattern' adopted in the WSDL to define their request and response elements. We've [[#The Service Endpoint Interface|already]] discussed briefly the difference between the <code>BARE</code> and <code>WRAPPED</code> patterns:
+
One way in which operations differ within and across services is in the 'pattern' with which request and response elements are defined in the WSDL. As gCore services are document/literal services, the [http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl well-known] patterns are <code>BARE</code> and <code>WRAPPED</code>. Given that the WSDLs of gCore services have the characteristics listed [[#Acme|above]], the difference between the two patterns is no more than the following:
  
* in the <code>BARE</code> pattern, request and response elements are the actual payloads of the operation, i.e. the inputs and outputs.  
+
* in the <code>BARE</code> pattern, request and responses have simple content.  
* In the <code>WRAPPED</code> pattern, request and response elements serve solely as operation-specific containers for the actual payloads.
+
* In the <code>WRAPPED</code> pattern, they have complex context.
  
In the example [[#A Simple Service|above]], Acme used the <code>BARE</code> pattern for its payloads made of single atomic values:
+
Clearly, the difference emerges only when operations take or return simple types, otherwise the pattern is necessarily <code>WRAPPED</code>.
 +
 
 +
In the example [[#Acme|above]], the service used the <code>BARE</code> pattern:
  
 
<source lang="xml">
 
<source lang="xml">
Line 235: Line 263:
 
</source>
 
</source>
  
In the SEI, <code>op</code> was then modelled as follows:
+
In the SEI <code>op</code> was then modelled as follows:
  
 
<source lang="java5">
 
<source lang="java5">
Line 244: Line 272:
 
</source>
 
</source>
  
The JAX-WS runtime generates a request element called after the operation, <code>op</code>, and includes in it the input string as simple content. Similarly, it expects a response element called <code>opResponse</code> with simple content.
+
In response to the <code>@SOAPBinding</code> annotation, the JAX-WS runtime generates a request element called after the operation, <code>op</code>, and includes in it the input string as simple content. Similarly, it expects a response element with simple content.
 
+
  
Had Acme used the <code>WRAPPED</code> pattern, its WSDL would have included something like this:  
+
Had the WSDL used the <code>WRAPPED</code> pattern, the request and response elements would have looked like this:  
  
 
<source lang="xml">
 
<source lang="xml">
Line 265: Line 292:
 
</source>
 
</source>
  
A service may choose this pattern to make its clients more resilient to future changes in the remote API. If <code>op</code> evolves to take further inputs, perhaps optional ones, clients that used to send a single string may still interact with the service.
+
The pattern is rather verbose in this case, but services may choose it to make clients more resilient to future changes to the API. If <code>op</code> evolves to take further inputs, perhaps optional ones, clients that used to send a single string may still interact with the service. Similarly, if <code>op</code> evolves to return multiple values, clients that expected a single string may still process the response.
  
In this case, we have two ways of modelling <code>op</code> in the SEI. In the first, we first define bean classes that bind to request and response messages. At their simplest, they may look as follows:
+
Where the WSDL follows the <code>WRAPPED</code> pattern, we have two ways of modelling <code>op</code> in the SEI. In the first, we first define JAXB-annotated bean classes that bind to request and response messages. At their simplest, these may look as follows:
  
 
<source lang="java5">
 
<source lang="java5">
Line 284: Line 311:
 
</source>
 
</source>
  
'''Note''': there is not strong incentive to make these beans more complex than they are, e.g. add accessors and mutators over private fields. The act solely ad data carriers.
+
'''Note''': since they act solely ad data transfer objects (DTOs), there is little incentive to make these beans more complex than they are, e.g. add accessors and mutators over private fields.
  
 
We then model <code>op</code> in the SEI as follows:
 
We then model <code>op</code> in the SEI as follows:
Line 295: Line 322:
 
</source>
 
</source>
  
Here, the JAX-WS runtime prepares an <code>op</code> request element but it delegates to the JAXB runtime the task of creating its children. The JAXB runtime creates the <code>param</code> child accordingly. Similarly for the response element.
+
Here, the JAX-WS runtime prepares an <code>op</code> request element and looks into the <code>Op</code> bean for instructions on how to create its children. The JAXB annotations indicate that request element should have a <code>param</code> child. Similarly for the response element.
  
 
+
Alternatively, we can signal to the JAX-WS runtime that the WSDL uses the <code>WRAPPED</code> pattern and let it generate the bean classes for us. We do so by specifying a <code>WRAPPED</code> parameter style in the <code>@SOAPBinding</code> annotation:
Alternatively, we can signal to the JAX-WS runtime that the parameter style is <code>WRAPPED</code> (the default) and add annotations that instruct JAX-WS how to generate the wrapping request and response elements for us:
+
  
 
<source lang="java5">
 
<source lang="java5">
  
 +
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
 
@WebResult(name="return")
 
@WebResult(name="return")
 
String op(@WebParameter(name="param") String s);
 
String op(@WebParameter(name="param") String s);
Line 307: Line 334:
 
</source>
 
</source>
  
<code>@WebResult</code> tells the JAX-WS runtime the name of the element in <code>opResponse</code> that contains the string we declare to return. Similarly, <code>WebParameter</code> indicates to it the child element of the <code>op</code> request that contains the string that we declare to take.
+
Here, <code>@WebParameter</code> names the child of the request element which should contains the string that we declare to take. Similarly, <code>@WebResult</code> names the child of the response element which contains the string that we declare to return. JAX-WS generates the beans for request and elements, populates the request element with the string we declare to take, and extracts from the response element the string that we declare to return.
 +
 
 +
Note that we cannot use a <code>WRAPPED</code> parameter style only for the input parameters, or only for the return value. If we tell JAX_WS to wrap the input parameters in a request element, then it will unwrap the response element, and vice-versa. We are thus forced to use the <code>BARE</code> style when the operation:
 +
 
 +
* follows one pattern for the input and the other for the output, or vice-versa;
 +
* has a response element with multiple children.
 +
 
 +
'''Note''': In the latter case, we ''could'' still use the <code>WRAPPED</code> style and <code>Holder</code> input parameters (cf. Sect. 2.3.2 and Sect. 2.3.3 of the JAX-WS [http://jcp.org/aboutJava/communityprocess/pfd/jsr224 specifications]), but we find that doing so is more trouble than worth.
 +
 
 +
In summary:
  
When we manually generate the SEI, this latter approach reduces the number of bean classes that we need to produce. It is accordingly the approach we prefer.
+
* we ''can'' use a <code>WRAPPED</code> parameter style if the request element has a complex type;
 +
* we ''must'' use a <code>BARE</code> parameters style if the request element or the response element have a simple type, or if the response element has more than one child element.
  
'''Note:''' in some cases, we may find WSDLs that mix <code>BARE</code> and <code>WRAPPER</code> styles for the same operation, e.g. take a wrapper request element but return an unwrapped response element. In this case, the corresponding method of the SEI will need to use a <code>BARE</code> parameter style and defined a bean class for the wrapper request element.
+
Since we will most often have to use a <code>BARE</code> parameter style in the SEI, we can move the <code>@SOAPBinding</code> annotation on the class declaration itself, and override this default on individual methods when we can and wish to use the <code>WRAPPED</code>style.
  
=== The <code>void</code> Case ===
+
In the following, we avoid showing how to write SEIs for all the possible pattern usage cases: pure <code>BARE</code>, pure <code>WRAPPED</code>, and mixed cases. Rather, we show solutions based on <code>BARE</code> and <code>WRAPPED</code> parameter style for inputs alone, with the understanding that the former will be required in the mixed case.
  
Let us deal first with the case in which the service expects no input:
+
=== The Empty Case ===
  
 +
Let us now consider an operation that expects no input:
  
 
<source lang="xml">
 
<source lang="xml">
Line 326: Line 364:
 
</source>
 
</source>
  
 +
This is of course a corner case of the <code>WRAPPED</code> pattern. If we [[#Wrapped or Bare?|can use]] the corresponding parameter style in the SEI, we can model <code>op</code> as simply as follows:
  
Continuing with the example [[#Quick Tour|above]], imagine Acme exposes an (unlikely!) operation <code>nothing</code> that takes and returns nothing.
+
<source lang="java5">
  
The simplest way in which Acme could model this in the WSDL (retaining its document/literal style) could be as follows (we include only the relevant bits to the WSDL introduced [[#A Sample Service|earlier]]):
+
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
 +
@WebResult(name="...")
 +
T op();
  
<source lang="xml">
+
</source>
  
....
+
If instead we're forced to use a <code>BARE</code> parameter style, then we need to use an empty bean class as the input. For convenience, an <code>Empty</code> bean class is included <code>JAXWSUtils</code> class of <code>common-gcore-clients</code>:
  
<xsd:element name="empty">
+
<source lang="java5">
<xsd:complexType />
+
</xsd:element>
+
  
<message name="emptyMessage">
+
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
<part name="part" element="tns:empty"/>
+
void op(Empty empty);
</message>
+
 
 +
</source>
 +
 
 +
The SEI method may then be invoked as follows:
 +
 
 +
<source lang="java5">
 +
import static ....JAXWSUtils.*;
  
 
...
 
...
  
<operation name="nothing">
+
stub.op(empty);
  <input message="tns:emptyMessage"/>
+
  <output message="tns:emptyMessage"/>
+
</operation>
+
  
 
</source>
 
</source>
  
In this case, our SEI could be simply extended as follows:
+
where <code>empty</code> is a constant of type <code>Empty</code> provided by <code>JAXWSUtils</code>.
 +
 
 +
=== Record Structure ===
 +
 
 +
Let us move on to an operation that expects the following request element:
 +
 
 +
<source lang="xml">
 +
 
 +
<element name="op" >
 +
  <complexType>
 +
    <sequence>
 +
        <element name="one" type="string"/>
 +
        <element name="two" type="integer"/>
 +
    </sequence>
 +
  </complexType>
 +
</element>
 +
 
 +
</source>
 +
 
 +
If we [[#Wrapped or Bare?|can use]] the <code>WRAPPED</code> parameter style in the SEI, we can model <code>op</code> as simply as follows:
 +
 
 +
<source lang="java5">
 +
 
 +
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
 +
@WebResult(name="...")
 +
T op(@WebParam(name="one") String one, @WebParam(name="two") int two);
 +
 
 +
</source>
 +
 
 +
If instead we're forced to use a <code>BARE</code> parameter style, then we need to use a bean class for the input.
  
 
<source lang="java5">
 
<source lang="java5">
  
 
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
 
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
void nothing(Empty empty);
+
T op(Op input);
  
 
</source>
 
</source>
  
where <code>Empty</code> is an empty bean class included for convenience in the <code>JAXWSUtils</code> class of <code>common-gcore-clients</code>.
+
where <code>Op</code> is the following bean class:
  
'''Note''': in the likely case that most of the SEI methods require the <code>@SOAPBinding</code> annotation, we can specify it directly on the SEI itself.
+
<source lang="java5">
  
The SEI method may then be invoked as follows:
+
@XmlRootElement
 +
public class Op {
  
<code>stub.nothing(empty);</code>
+
  @XMLElement
 +
  public String one;
  
where <code>empty</code> is a constant of type <code>Empty</code> provided by <code>JAXWSUtils</code>.
+
  @XMLElement
 +
  public int two;
  
=== Complex Parameters ===
+
}
  
Assume Acme exposes an operation <code>complex</code> that takes and returns complex objects. Omitting the declaration of operation and messages, the WSDL will contain these new element declarations:
+
</source>
 +
 
 +
The example of course generalise to nested structure. We are firmly on JAXB ground and can use any of its annotations to deal with complex XML structures.
 +
 
 +
=== Collections ===
 +
 
 +
Operations often expects collections of values,e.g:
  
 
<source lang="xml">
 
<source lang="xml">
  
....
+
<element name="op" >
 +
  <complexType>
 +
      <sequence>
 +
          <element name="element"  maxOccurs="unbounded" type="string">
 +
      </sequence>
 +
  </complexType>
 +
</element>
  
<xsd:element name="complex">
+
</source>
  <xsd:complexType>
+
    <xsd:sequence>
+
        <xsd:element name="in1" type="xsd:string"/>
+
        <xsd:element name="in2" type="xsd:integer"/>
+
    </xsd:sequence>
+
  </xsd:complexType>
+
</xsd:element>
+
  
<xsd:element name="complexResponse">
+
Modelling such operations in the SEI is just a special case of working with [[#Record Structure|record structure]]. If we [[#Wrapped or Bare?|can use]] the <code>WRAPPED</code> parameter style in the SEI, we can model <code>op</code> as simply as follows:
  <xsd:complexType>
+
 
<xsd:sequence>
+
<source lang="java5">
          <xsd:element name="out" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
+
 
</xsd:sequence>
+
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
  </xsd:complexType>
+
@WebResult(name="...")
</xsd:element>
+
T op(@WebParam(name="element") List<String> elements);
  
...
 
 
</source>
 
</source>
  
In this case, our SEI could declare the following method:
+
where we have chosen a <code>List</code> type for the richness of the interface, though we could have chosen an array or a <code>Set</code> if the intended usage or knowledge of data semantics had made this choice preferable.
 +
 
 +
If instead we're forced to use a <code>BARE</code> parameter style, then we resort to the usual bean class for the input:
  
 
<source lang="java5">
 
<source lang="java5">
  
 
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
 
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
ComplexOutput complex(ComplexInput input);
+
... op(Op input);
  
 
</source>
 
</source>
  
where <code>ComplexInput</code> and <code>ComplexOutput</code> may be as simple as the following field-only,  JAXB-annotated beans:
+
where <code>OpInput</code> is the bean class defined as:
  
 
<source lang="java5">
 
<source lang="java5">
  
@XmlRootElement
+
public class Op {
public class ComplexInput {
+
  
  @XMLElement
+
  @XmlElement(name="element")
  public String in1;
+
  public List<String> elements;
 +
}
  
  @XMLElement
+
</source>
  public int in2;
+
  
 +
where we slightly customise the binding for a clearer API.
 +
 +
=== Variable Structures ===
 +
 +
Some operations take or return heterogenous data with heterogeneous forms. In some cases the variability is controlled, in that the data can take one of a predefined number of shapes. In other cases, variability is open-ended, i.e. the data can be any well-formed XML structure. We discuss some of the most common approach below.
 +
 +
==== Subtyping ====
 +
 +
In the following example, the operation expects one of two subtypes of a base type:
 +
 +
<source lang="xml">
 +
 +
<element name="op"  type="base" />
 +
 +
<complexType name = "base" abstract="true">
 +
    <sequence>
 +
          <element name="a" type="string"/>
 +
    </sequence>
 +
</complexType>
 +
 +
<complexType name="sub1">
 +
  <complexContent>
 +
    <extension base="base">
 +
<sequence>
 +
  <element name="b" type="string"/>
 +
        <sequence>
 +
    </extension>
 +
  </complexContent>
 +
</complexType>
 +
 +
<complexType name="sub2">
 +
  <complexContent>
 +
    <extension base="base">
 +
<sequence>
 +
  <element name="c" type="integer"/>
 +
        <sequence>
 +
    </extension>
 +
  </complexContent>
 +
</complexType>
 +
 +
</source>
 +
 +
Here, the request element can have the content model defined by either <code>sub1</code> or <code>sub2</code>, both of which inherit a property from <code>base</code>.
 +
 +
Since the pattern followed in the WSDL is of course <code>BARE</code>, we model <code>op</code> in the SEI as follows:
 +
 +
<source lang="java5">
 +
 +
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
 +
... op(Op base);
 +
 +
</source>
 +
 +
where <code>Op</code> is the abstract class defined as:
 +
 +
<source lang="java5">
 +
 +
@XmlSeeAlso({Subone.class,Subtwo.class})
 +
public abstract class Op {
 +
 +
  @XmlElement
 +
  public String a;
 
}
 
}
  
 +
</source>
 +
 +
and <code>Sub1</code> and <code>Sub2</code> are subclasses of <code>Op</code>
 +
 +
<source lang="java5">
 +
 +
public class Sub1 extends Op {
 +
 +
  @XmlElement
 +
  public String b;
 +
 +
}
 +
 +
public class Sub2 extends Op {
 +
 +
  @XmlElement
 +
  public int c;
 +
 +
}
 +
 +
</source>
 +
 +
Note the JAXB <code>@XmlSeeAlso</code> annotation on <code>Op</code>, which points the JAX-WS runtime to <code>Sub1</code> and <code>Sub2</code> whenever it needs to serialise an instance of those two types.
 +
 +
We can now invoke <code>op()</code> as follows:
 +
 +
<source lang="java5">
 +
 +
Sub1 s1 = ...
 +
Sub2 s2 = ...
 +
 +
....stub.poly(s1)....
 +
....stub.poly(s2)...
 +
 +
</source>
 +
 +
Let us now consider the case in which <code>op</code> follows the <code>WRAPPED</code> pattern in the WSDL, e.g.:
 +
 +
<source lang="xml">
 +
 +
<element name="op">
 +
<complexType>
 +
    <sequence>
 +
      <element name ="param"  type="base" />
 +
    </sequence>
 +
</complexType>
 +
</element>
 +
 +
....type definitions for Base, Sub1, and Sub2
 +
 +
</source>
 +
 +
We distinguish between the usual cases. If we [[#Wrapped or Bare?|can use]] the <code>WRAPPED</code> parameter style, we can model <code>op</code> in the SEI as follows:
 +
 +
<source lang="java5">
 +
 +
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
 +
@WebResult(name="...")
 +
... op(@WenParam(name="param") Base base);
 +
 +
</source>
 +
 +
reusing the definitions given above for <code>Base</code>, <code>Sub1</code>, and <code>Sub2</code>.
 +
 +
If we are instead forced to the the <code>BARE</code> parameter style, we can model <code>op</code> in the SEI as follows:
 +
 +
<source lang="java5">
 +
 +
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
 +
... op(Op base);
 +
 +
</source>
 +
 +
where <code>Op</code> is defined as:
 +
 +
<source lang="java5">
 +
 +
public class Op {
 +
    @XmlElement
 +
    public Base param;
 +
}
 +
 +
</source>
 +
 +
==== Choices ====
 +
 +
In the previous case, the two subtypes inherited a common property from the base type. Often, however, the request element can take two entirely unrelated form. While it is possible (and indeed advantageous) to use an empty base type to model this case as the previous, it is more common to express the variation as an explicit <code>xsd:choice</code>, as in the following example:
 +
 +
 +
<source lang="xml">
 +
 +
<element name="op">
 +
  <complexType>
 +
    <choice>
 +
          <element name="one" type="choice1"/>
 +
          <element name="two" type="choice2"/>
 +
    </choice>
 +
  </complexType>
 +
</element>
 +
 +
<complexType name="choice1">
 +
    <sequence>
 +
      <element name="a" type="string"/>
 +
    <sequence>
 +
</complexType>
 +
 +
<complexType name="choice2">
 +
    <sequence>
 +
      <element name="b" type="string"/>
 +
    <sequence>
 +
</complexType>
 +
 +
</source>
 +
 +
Notice that this approach follows the <code>WRAPPED</code> pattern.
 +
 +
We can follow either one of two approaches to model <code>op</code> in the SEI.
 +
 +
The first approach is based on overloading. Assuming we can only use the <code>BARE</code> parameter style:
 +
 +
<source lang="java5">
 +
 +
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
 +
... op(Op1 one);
 +
 +
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
 +
... op(Op2 two);
 +
 +
</source>
 +
 +
where <code>Op1</code> and <code>Op2</code> are defined as follows:
 +
 +
<source lang="java5">
 +
 +
public class Op1 {
 +
  @XmlElement
 +
  public Choice1 one;
 +
}
 +
 +
public class Op2 {
 +
  @XmlElement
 +
  public Choice2 two;
 +
}
 +
 +
</source>
 +
 +
and where <code>Choice1</code> and <code>Choice2</code> are defined as:
 +
 +
<source lang="java5">
 +
 +
public class Choice1 {
 +
  @XmlElement
 +
  public String a;
 +
}
 +
 +
public class Choice2 {
 +
  @XmlElement
 +
  public int b;
 +
}
 +
 +
</source>
 +
 +
Overloading is more appealing if we  [[#Wrapped or Bare?|can use]] the <code>WRAPPED</code> parameter style, as we can then avoid a level of wrapping and model <code>op</code> in the SEI as follows:
 +
 +
<source lang="java5">
 +
 +
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
 +
@WebResult(name="...")
 +
... op(@WebParam(name="one") Choice1 one);
 +
 +
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
 +
@WebResult(name="...")
 +
... op(@WebParam(name="two") Choice 2 two);
 +
 +
</source>
 +
 +
The other, more general approach is to retain a degree of polymorphism in the SEI:
 +
 +
<source lang="java5">
 +
 +
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
 +
... op(Op one);
 +
 +
</source>
 +
 +
where:
 +
 +
<source lang="java5">
 +
 +
@XmlSeeAlso({Choice1.class,Choice2.class})
 +
public abstract class Op {}
 +
 +
public class Choice1 extends Op {...as above...}
 +
public class Choice2 extends Op {...as above...}
 +
 +
</source>
 +
 +
'''Note''': we must use an abstract class here, we cannot replace it with an interface.
 +
 +
==== The <code>any</code> Case ====
 +
 +
Let us now consider the case of an operation that does not constrain the data to assume predefined forms. The standard WSDL idiom in gCore service for this relies on  the <code>xsd:any</code> element, which allows any well-formed XML element structures.
 +
 +
For example:
 +
 +
<source lang="xml">
 +
 +
<element name="op">
 +
  <complexType>
 +
<sequence>
 +
  <any ..../>
 +
</sequence>
 +
</complexType>
 +
</element>
 +
 +
</source>
 +
 +
Even if the pattern is implicitly <code>WRAPPED</code> here, we are forced to use the <code>BARE</code> parameter style in the SEI:
 +
 +
<source lang="java5">
 +
 +
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
 +
... op(Op any);
 +
 +
</source>
 +
 +
where <code>AnyElement</code> is the following bean class:
 +
 +
<source lang="java5">
 +
 
 +
public class Op {
 +
  @XmlAnyElement
 +
  public org.w3c.dom.Element any;
 +
}
 +
 +
</source>
 +
 +
where the annotation <code>@XmlAnyElement</code> instructs the runtime to serialise the <code>any</code> as generic XML.
 +
 +
==== The enumeration Case ====
 +
 +
Let's now consider the case of an Enumeration which is a constrain on the values a type  should assume.
 +
 +
For example:
 +
 +
<source lang="xml">
 +
 +
<xsd:simpleType name="type1">
 +
<xsd:restriction base="xsd:string">
 +
              <xsd:enumeration  value="value1"/>
 +
      <xsd:enumeration  value="value2"/>
 +
      <xsd:enumeration  value="value3"/>
 +
        </xsd:restriction>
 +
</xsd:simpleType>
 +
 +
</source>
 +
 +
In the example above the type ''type1'' is a restriction of the ''string'' type having only 3 possible values :  ''value1, value2 and value3''. This is simply translated in JAXB using the <code>@XmlEnum</code> annotation as follows:
 +
 +
<source lang="java5">
 +
 +
@XmlEnum(String.class)
 +
public enum type1 { value1, value2, value3 }
 +
 +
</source>
 +
 +
=== Faults ===
 +
 +
SEI clients may observe a wide range of failures, including: failures that occur in the client runtime, before remote calls to endpoints are issued; failures that occur in the attempt to communicate with endpoints; failures that occur in the runtime of endpoints. We distinguish between the following types of failures:
 +
 +
* ''errors'' are violations of the contract defined by the service which can imputed to faulty code or faulty configuration. Illegal SEI definitions are examples of client-side errors, while bugs in the implementation of the service are examples of service-side errors.
 +
 +
* ''contingencies'' are predictable violations of contract pre-conditions. There may be no bugs in either client or service code, but the endpoint is put in a state that prevents it to carry out the client’s request. Data that cannot be found or cannot be created are examples of contingencies.
 +
 +
* ''outages'' are I/O failures of the external environment and include network failures, database failures and disk failures.
 +
 +
We take the common view that errors ought to emerge as soon as possible, hence should be modelled as unchecked exceptions. Client-side errors emerge from the JAX-WS runtime as <code>WebServiceException</code>, which are <code>RuntimeException</code>s. Server-side errors emerge as <code>SoapFaultException</code>, also unchecked as a subclass of <code>WebServiceException</code>.
 +
 +
We also take the common view that outages cannot be recovered from, at most contained high up in the client stack. For this reason, we also suggest to model them with unchecked exceptions. Again, the JAX-WS runtime returns them as <code>SoapFaultException</code>s.
 +
 +
What we're left with is contingencies, which we suggest to model as checked exceptions under the expectations that they ''may'' be recoverable and clients should prepare for them.
 +
 +
Services model contingencies explicitly in WSDLs, e.g. :
 +
 +
<source lang="xml">
 +
 +
<element name="OpFault">
 +
    <complexType>
 +
          .....
 +
</complexType>
 +
</element>
 
...
 
...
 +
<message name="OpFault">
 +
<part name="fault" element="OpFault"/>
 +
</message>
 +
...
 +
<operation name="op">
 +
  <input message="..."/>
 +
  <output message="..."/>
 +
  <fault name="fault" message="OpFault"/>
 +
</operation>
  
@XmlRootElement
+
</source>
public class ComplexOutput {
+
  
  @XMLElement
+
'''Note''': Contingencies often extend shared fault types: <code>GCUBEUnrecoverableFault</code>, <code>GCUBERetrySameFault</code>, <code>GCUBERetryEquivalentFault</code>, all subclasses of <code>GCUBEFault</code>. Each class captures a generic aspect of the fault semantics that equally generic clients ''may'' react to. Since the approach relies on shared types, however, it is rather coupling. Accordingly, we will not attempt to preserve that general semantics in our SEIs.
  public List<String> out;
+
 
 +
To model <code>OpFault</code> in the SEI, we first define a corresponding exception <code>OpException</code>:
 +
 
 +
<source lang="java5">
 +
 
 +
@WebFault(name="OpFault")
 +
public class OpException extends Exception {
 +
 
 +
    ......
  
 
}
 
}
 
</source>
 
</source>
  
 +
where we use the annotation <code>@WebFault</code> to point the JAX-WS runtime to the corresponding element of the fault message returned by the service.
  
Alternatively, the SEI could declare the following method:
+
We then declare to throw it from the method that models <code>op</code>:
 +
 
 +
<source lang="java5">
 +
 
 +
.. op(...) throws OpException;
 +
 
 +
</source>
 +
 
 +
In some cases, <code>OpFault</code> carries descriptive information, e.g.:
 +
 
 +
<source lang="xml">
 +
 
 +
<element name="OpFault">
 +
    <complexType>
 +
          <sequence>
 +
              <element name="detail" type="string">
 +
          </sequence>
 +
</complexType>
 +
</element>
 +
 
 +
</source>
 +
 
 +
We can model this information as a bean class:
 +
 
 +
<source lang="java5">
 +
 
 +
public class OpExceptionInfo {
 +
@XmlElement
 +
public String details;
 +
}
 +
 
 +
</source>
 +
 
 +
and then expect instances of the bean class in one or both of the following constructors of <code>OpException</code>:
 +
 
 +
<source lang="java5">
 +
 
 +
@WebFault(name="OpFault")
 +
public class OpException extends Exception {
 +
 
 +
    private final OpExceptionInfo info;
 +
 
 +
    public class OpException(String msg, OpExceptionInfo info) {
 +
        super(msg);
 +
        this.info=info;
 +
    }
 +
 
 +
public class OpException(String msg, OpExceptionInfo info, Throwable cause) {
 +
        super(msg,throwable);
 +
        this.info=info;
 +
    }
 +
 
 +
  ......
 +
 
 +
  public OpExceptionInfo getFaultInfo() {
 +
      return info;
 +
  }
 +
}
 +
</source>
 +
 
 +
At fault time, the JAX-WS runtime will instantiate <code>OpExceptionInfo</code> from the fault element, and then it will pass it to the constructor/s of <code>OpException</code>.
 +
 
 +
=== Factory Operations ===
 +
 
 +
We've briefly discussed [[#Calling Instances|above]] the case of stateful services and their companion factory services. Let us know see how to model operations of factory services that create instances and return references to them.
 +
 
 +
Assume <code>create</code> is one such operation. Typically, the WSDL will define its response element as follows:
 +
 
 +
<source lang="xml">
 +
 
 +
<definitions ... xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" .... >
 +
    ...
 +
  <types>
 +
   
 +
      <import namespace="http://schemas.xmlsoap.org/ws/2004/03/addressing" .... />
 +
        ...
 +
 
 +
        <element name="createResponse type="wsa:EndpointReferenceType">
 +
        ...
 +
    </types>
 +
...
 +
 
 +
</source>
 +
 
 +
Here we're using a <code>BARE</code> pattern. We then model <code>create</code> in the SEI of the factory using Java's [http://docs.oracle.com/javase/6/docs/api/javax/xml/ws/wsaddressing/W3CEndpointReference.html W3CEndpointReference]:
 +
 
 +
<source lang="java5">
 +
 
 +
@SOAPBinding(parameterStyle=ParameterStyle.BARE) 
 +
W3CEndpointReference create(....) ... ;
 +
 
 +
</source>
 +
 
 +
Assume now a <code>WRAPPED</code> pattern was followed in the WSDL:
 +
 
 +
<source lang="xml">
 +
 
 +
<element name="createResponse>
 +
  <complexType>
 +
      <sequence>
 +
          <element name="return"  type="wsa:EndpointReferenceType"/>
 +
      </sequence>
 +
    </complexType>
 +
</element>
 +
 
 +
</source>
 +
 
 +
We then have the usual two options. If we can use the <code>WRAPPED</code> parameter style, then we can model <code>create</code> in the SEI as follows:
  
 
<source lang="java5">
 
<source lang="java5">
  
 
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
 
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
@WebResult(name="out")  
+
@WebResult(name="return")
List<String> complex(@WebParam(name="in1") String in1, @WebParam(name="in2") int in2);
+
W3CEndpointReference create(@WebParam(name="...")..., @WebParam(name="...")...,...);
  
 
</source>
 
</source>
  
i.e. use the <code>WRAPPED</code> mapping style, with the onus
+
If instead we're forced to use a code>BARE</code> parameter style, then we can model then can model <code>create</code> in the SEI as follows:
 +
 
 +
<source lang="java5">
 +
 
 +
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
 +
CreateResponse create(...);
 +
 
 +
</source>
 +
 
 +
where <code>CreateResponse</code> is the bean class:
 +
 
 +
<source lang="java5">
 +
 
 +
public class CreateResponse {
 +
@XmlElement
 +
public W3CEndpointReference return;
 +
}
 +
 
 +
</source>

Latest revision as of 14:49, 23 September 2013

common-gcore-stubs is a client-library that interacts with the JAX-WS runtime of the Java platform to generate dynamic JAX-WS proxies of remote gCore services. Architecturally, it operates at the lowest layer of the Featherweight Stack for gCube clients.

common-gcore-stubs is available through our Maven repositories with the following coordinates:

<artifactId>common-gcore-stubs</artifactId>
<groupId>org.gcube.core</groupId>

Overview

At the time of writing, most gCube services are JSR101 (JAX-RPC) services implemented on the gCore stack and running inside a gCube Hosting Node.

common-gcore-stubs allows us to invoke such services without dependencies on that stack from arbitrary client environments. It does so by interacting on our behalf with the JSR224 (JAX-WS) runtime, which is part of the Java platform since version 1.6. Effectively, with common-gcore-stubs we use a modern standard to call legacy services.

We provide the library with:

  • a service descriptor, i.e. information about the service such as gCube coordinates (service class, service name) and WSDL coordinates (service name, porttype name);
  • a Service Endpoint Interface (SEI), i.e. a local Java interface that models the remote API of the service and provides additional information about its endpoints through JSR-181 annotations.
  • the address of target endpoints/instances of the service;

The library gives us back dynamically generated implementations of the SEI. The implementations are first synthesised by the underlying JAX-WS runtime and then appropriately configured by the library to issue gCube calls to the target endpoint (i.e. propagate the call scope, target service coordinates, client identity, etc.).

SEI implementations can then serve as local stubs for service endpoints/instances. Typically, we use them to implement higher-level client libraries.

The SEI and the other required information may be distributed as a stand-alone component, like for JAX-RPC stubs. Alternatively, they may be integral part of our client libraries. The fact that these 'stubs' have a minimal footprint, do not need to be generated through tooling, and are client-only artefacts makes the embedding option natural and in fact appealing.

Quick Tour

We run through a simple example to illustrate the process and relevant APIs.

Acme

For the sake of simplicity, let us illustrate how to use common-gcore-stubs to call a fictional gCore Acme service.

Let us assume that the remote API of Acme is defined by the following WSDL:

<definitions name="Acme"
    targetNamespace="http://acme.org" xmlns:tns="http://acme.org" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 
 	<types>
	<xsd:schema targetNamespace="http://acme.org">
 
               <xsd:element name="foo" type="xsd:string" />
	       <xsd:element name="fooResponse" type="xsd:string" />
 
	</xsd:schema>
	</types>
 
	<message name="foo">
		<part name="request" element="tns:foo"/>
	</message>
	<message name="fooResponse">
		<part name="response" element="tns:fooResponse"/>
	</message>
 
	<portType name="AcmePortType">
 
		<operation name="foo">
			<input message="tns:foo"/>
			<output message="tns:fooResponse"/>
		</operation>
 
	</portType>
 
      <binding name="AcmePortTypeSOAPBinding" type="tns:AcmePortType">
           <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
           <operation name="foo">
             <soap:operation soapAction="http://acme.org/AcmePortType/fooRequest"/>
             <input>
                 <soap:body use="literal"/>
            </input>
           <output>
              <soap:body use="literal"/>
           </output>
         </operation>
       </binding>
 
       <service name="service:AcmeService" xlmns:service="http://acme.org/service">
         <port name="AcmePortTypePort" binding="tns:AcmePortTypeSOAPBinding">
             <soap:address location="...some address..."/>
         </port>
       </service>
 
</definitions>

Like most gCore services, Acme:

  • defines a single porttype, AcmePortType. In this case the porttype includes a single operation, foo, which takes and returns a string;
  • foo can be invoked through SOAP over HTTP in a document/literal style (essentially, messages are made directly of elements defined in the <types> section)

Note: since the WSDL describes an Acme endpoint, it includes also its address. However, this address is largely irrelevant to our purposes because we use the WSDL to call different endpoints which we locate dynamically through our Information Services.

Note: gCore services are normally developed WSDL-first and use tooling to derive a WSDL complete with binding information (cf. <binding>, <service>) from a partial WSDL that includes only logical definitions (cf. <types>, <message>s, <portType>). By design, the tooling spreads the complete WSDL across a number of files that follow a chain of imports. It also defines ad-hoc namespaces for the information it derives (e.g. http://acme.org/service for the <service> definitions). Here, we present the WSDL as a whole but keep using a different namespace for the <service> definition, so as to facilitate mapping the example to the WSDLs of real gCube services.

The WSDL provides the following coordinates about Acme, which we capture in a class of constants to use later with common-gcore-stubs:

import ....
 
public class AcmeConstants {
 
  public static final QName name = new QName("http://acme.org/service","AcmeService");
  public static final String porttypeNS = "http://acme.org";
  public static final String porttypeLN = "AcmePortType";
}

Service Endpoint Interface

In JAX-WS terminology, the SEI is a local Java interface that mirrors the remote API of service endpoints. The interface includes annotations that provide the JAX-WS runtime with (some of the) information required to generate an implementation on the fly.

One way to obtain a SEI is to derive it from the WSDL with tooling, such as the wsimport utility which ships with the JDK. For this example, however, we produce the SEI manually, which gives us more control and makes for cleaner code.

import .............AcmeConstants.*; 
...
 
@WebService(name=porttypeLN,targetNamespace=porttypeNS)
public interface AcmeStub {
 
  @SOAPBinding(parameterStyle=ParameterStyle.BARE)
  String foo(String s);
 
}

We name the SEI so as to reflect that it acts as a local API for remote service endpoints. We annotate it with @WebService, providing the coordinates of the service porttype that defines the remote operations. For this, we use the constants of the AcmeConstants class defined above, which we statically import for improved legibility of the code. We then declare the method foo and annotate it with @SOAPBinding to indicate that its input and output should be put/found in request/response elements directly under the SOAP body.

We discuss how to write SEIs in more detail later on. For now, let us concentrate on how we use it with common-gcore-stubs.

Service Descriptor

We now provide common-gcore-stubs with a descriptor for Acme, i.e. the set of information which are required to call its endpoints. We provide the descriptor as an instance of the GCoreService class, which we build in a fluent style with the help of a GCoreServiceBuilder. A convenient place to do this is directly in the AcmeConstants class introduced above. The following example illustrates:

import static org.gcube.common.clients.stubs.jaxws.GCoreServiceBuilder.*;
...
public class AcmeConstants {
 
 ...
 
 public static String gcubeClass=....
 public static String gcubeName=...;
 
 public static final GCoreService<AcmeStub> acme = service().withName(name)
                                                            .coordinates(gcubeClass,gcubeName)
                                                            .andInterface(AcmeStub.class); 
 
 
}

Since we will not need more than one instance of the descriptor, we create it once and for all as a constant named after the service. We invoke the static method GCoreServiceBuilder#service() to begin with and then follow the methods of the fluent API to provide the remaining information, using the constants already available within the class, plus the gCube coordinates of the service. The static 'star' import is just a convenience to improve further the legibility of the code.

Calling Endpoints

When it's finally time to call a service endpoint, we use the SEI and the GCoreService descriptor to obtain a SEI implementation configured for the endpoint:

import ......AcmeConstants.*;
import static org.gcube.common.clients.stubs.jaxws.StubFactory.*;
 
...
 
URI address = ...
 
AcmeStub stub = stubFor(acme).at(address);
 
String response = stub.foo("...");

We build our stub using the static method stubFor(GCoreService) of the StubFactory class, which we statically import for convenience. We provide the factory with the acme descriptor defined earlier, which we also statically import. We also provide the address of the endpoint, here modelled as a URI.

Finally, we use the stub returned by the StubFactory to call the endpoint through the SEI.

Note: as we're calling a gCube service, we need to do so in a given scope or the stub will refuse to go ahead. One way to associate the call with a scope is to set it on the current thread:

ScopeProvider.instance.set('..some scope...");

Calling Instances

So far, we've implicitly assumed that Acme is stateless. However, many gCore services are stateful, their endpoints encapsulate data which they use and change upon processing client requests. Such endpoints can be modelled as collections of service instances, where all instances have the API of the service but are bound to different state. A service instance is typically created by an endpoint of a companion factory service which returns references to the instances. Such references are modelled on the wire as the endpoint references defined by WS-Addressing. In Java, the class EndpointReference models such references.

We will see later how to model instance references in the SEIs of factory services. For now, let us assume to have one, either because we've manually built it during testing (cf. Java's own W3CEndpointReferenceBuilder), or because we've obtained it from a factory service, or else because we've dynamically discovered by querying the Information Services. We use the reference to call a service instance as follows:

import ......AcmeConstants.*;
import static org.gcube.common.clients.stubs.jaxws.StubFactory.*;
 
...
 
EndpointReference reference = ...
 
AcmeStub stub = stubFor(acme).at(reference);
 
String response = stub.foo("...");

We thus interact with instances as we interact with endpoints, the only difference being that we pass the reference to the at() clause of the build expression.

Note: we can use EndpointReferences even when we target endpoints of stateless services, instead of URIs. In other words, we can use the API of common-gcore-stubs uniformly, if the code so requires.

Writing SEIs

The example above considers the simplest case of remote operation, where inputs and outputs are single atomic values. The range of remote operations we can encounter across gCore services is in fact quite wide. While the ultimate reference to writing and annotating SEIs is provided by the JAX-WS specifications and the specifications (JAXB), we consider some of the common cases below for convenience.

In all the cases, we ask the same question:

how do we define and annotate the methods of the SEI so that the implementation generated by the JAX-WS runtime: a) sends requests which are understood by the service, and b) understands the responses produced by the service?

In what follows we assume that, like for the Acme service of our earlier example:

  • the WSDL follows a document/literal style for its SOAP/HTTP binding, with requests and responses defined by a single element in accordance to WS-I profile;
  • the request element is named after the corresponding operation. This convention follows best practices, as it allows SOAP engines to unambiguously dispatch requests. The response element is more freely named, though the convention is to suffix "Response" to the name of the operation.

The definitions of <message>, <portType>, <binding> and <service> elements become then boilerplate for our discussion, and we can omit it to concentrate solely on the definitions of request and response elements. In fact, we can simplify the presentation further if we agree to omit namespace prefixes altogether. Then we can illustrate each case by instantiating the following template:

<element name="op">
   ....
</lement>
 
<element name="opResponse">
   ....
</element>

Wrapped or Bare?

One way in which operations differ within and across services is in the 'pattern' with which request and response elements are defined in the WSDL. As gCore services are document/literal services, the well-known patterns are BARE and WRAPPED. Given that the WSDLs of gCore services have the characteristics listed above, the difference between the two patterns is no more than the following:

  • in the BARE pattern, request and responses have simple content.
  • In the WRAPPED pattern, they have complex context.

Clearly, the difference emerges only when operations take or return simple types, otherwise the pattern is necessarily WRAPPED.

In the example above, the service used the BARE pattern:

<element name="op" type="string" />
<element name="opResponse" type="string" />

In the SEI op was then modelled as follows:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
String op(String s);

In response to the @SOAPBinding annotation, the JAX-WS runtime generates a request element called after the operation, op, and includes in it the input string as simple content. Similarly, it expects a response element with simple content.

Had the WSDL used the WRAPPED pattern, the request and response elements would have looked like this:

<element name="op" >
  <complexType>
         <element name="param" type="string">  
  </complexType>
</element>
 
<element name="opResponse">
  <complexType>
         <element name="return" type="string">  
  </complexType>
</element>

The pattern is rather verbose in this case, but services may choose it to make clients more resilient to future changes to the API. If op evolves to take further inputs, perhaps optional ones, clients that used to send a single string may still interact with the service. Similarly, if op evolves to return multiple values, clients that expected a single string may still process the response.

Where the WSDL follows the WRAPPED pattern, we have two ways of modelling op in the SEI. In the first, we first define JAXB-annotated bean classes that bind to request and response messages. At their simplest, these may look as follows:

public class Op {
 
  @XmlElement
   public param; 
}
 
public class OpResponse {
 
  @XmlElement
   public return; 
 
}

Note: since they act solely ad data transfer objects (DTOs), there is little incentive to make these beans more complex than they are, e.g. add accessors and mutators over private fields.

We then model op in the SEI as follows:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
OpResponse op(Op s);

Here, the JAX-WS runtime prepares an op request element and looks into the Op bean for instructions on how to create its children. The JAXB annotations indicate that request element should have a param child. Similarly for the response element.

Alternatively, we can signal to the JAX-WS runtime that the WSDL uses the WRAPPED pattern and let it generate the bean classes for us. We do so by specifying a WRAPPED parameter style in the @SOAPBinding annotation:

@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
@WebResult(name="return")
String op(@WebParameter(name="param") String s);

Here, @WebParameter names the child of the request element which should contains the string that we declare to take. Similarly, @WebResult names the child of the response element which contains the string that we declare to return. JAX-WS generates the beans for request and elements, populates the request element with the string we declare to take, and extracts from the response element the string that we declare to return.

Note that we cannot use a WRAPPED parameter style only for the input parameters, or only for the return value. If we tell JAX_WS to wrap the input parameters in a request element, then it will unwrap the response element, and vice-versa. We are thus forced to use the BARE style when the operation:

  • follows one pattern for the input and the other for the output, or vice-versa;
  • has a response element with multiple children.

Note: In the latter case, we could still use the WRAPPED style and Holder input parameters (cf. Sect. 2.3.2 and Sect. 2.3.3 of the JAX-WS specifications), but we find that doing so is more trouble than worth.

In summary:

  • we can use a WRAPPED parameter style if the request element has a complex type;
  • we must use a BARE parameters style if the request element or the response element have a simple type, or if the response element has more than one child element.

Since we will most often have to use a BARE parameter style in the SEI, we can move the @SOAPBinding annotation on the class declaration itself, and override this default on individual methods when we can and wish to use the WRAPPEDstyle.

In the following, we avoid showing how to write SEIs for all the possible pattern usage cases: pure BARE, pure WRAPPED, and mixed cases. Rather, we show solutions based on BARE and WRAPPED parameter style for inputs alone, with the understanding that the former will be required in the mixed case.

The Empty Case

Let us now consider an operation that expects no input:

<element name="op" >
  <complexType />
</element>

This is of course a corner case of the WRAPPED pattern. If we can use the corresponding parameter style in the SEI, we can model op as simply as follows:

@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
@WebResult(name="...")
T op();

If instead we're forced to use a BARE parameter style, then we need to use an empty bean class as the input. For convenience, an Empty bean class is included JAXWSUtils class of common-gcore-clients:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
void op(Empty empty);

The SEI method may then be invoked as follows:

import static ....JAXWSUtils.*;
 
...
 
stub.op(empty);

where empty is a constant of type Empty provided by JAXWSUtils.

Record Structure

Let us move on to an operation that expects the following request element:

<element name="op" >
  <complexType>
     <sequence>
         <element name="one" type="string"/>
         <element name="two" type="integer"/>
     </sequence>
  </complexType>
</element>

If we can use the WRAPPED parameter style in the SEI, we can model op as simply as follows:

@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
@WebResult(name="...")
T op(@WebParam(name="one") String one, @WebParam(name="two") int two);

If instead we're forced to use a BARE parameter style, then we need to use a bean class for the input.

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
T op(Op input);

where Op is the following bean class:

@XmlRootElement
public class Op {
 
   @XMLElement
   public String one;
 
   @XMLElement
   public int two;
 
}

The example of course generalise to nested structure. We are firmly on JAXB ground and can use any of its annotations to deal with complex XML structures.

Collections

Operations often expects collections of values,e.g:

<element name="op" >
  <complexType>
      <sequence>
          <element name="element"  maxOccurs="unbounded" type="string">
      </sequence>
  </complexType>
</element>

Modelling such operations in the SEI is just a special case of working with record structure. If we can use the WRAPPED parameter style in the SEI, we can model op as simply as follows:

@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
@WebResult(name="...")
T op(@WebParam(name="element") List<String> elements);

where we have chosen a List type for the richness of the interface, though we could have chosen an array or a Set if the intended usage or knowledge of data semantics had made this choice preferable.

If instead we're forced to use a BARE parameter style, then we resort to the usual bean class for the input:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
... op(Op input);

where OpInput is the bean class defined as:

public class Op {
 
  @XmlElement(name="element")
  public List<String> elements;
}

where we slightly customise the binding for a clearer API.

Variable Structures

Some operations take or return heterogenous data with heterogeneous forms. In some cases the variability is controlled, in that the data can take one of a predefined number of shapes. In other cases, variability is open-ended, i.e. the data can be any well-formed XML structure. We discuss some of the most common approach below.

Subtyping

In the following example, the operation expects one of two subtypes of a base type:

<element name="op"  type="base" />
 
<complexType name = "base" abstract="true">
     <sequence>
          <element name="a" type="string"/>
     </sequence>
</complexType>
 
<complexType name="sub1">
  <complexContent> 
     <extension base="base"> 
	<sequence> 
	  <element name="b" type="string"/> 
        <sequence> 
     </extension> 
   </complexContent>
 </complexType>
 
<complexType name="sub2">
  <complexContent> 
     <extension base="base"> 
	<sequence> 
	  <element name="c" type="integer"/> 
        <sequence> 
     </extension> 
   </complexContent>
 </complexType>

Here, the request element can have the content model defined by either sub1 or sub2, both of which inherit a property from base.

Since the pattern followed in the WSDL is of course BARE, we model op in the SEI as follows:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
... op(Op base);

where Op is the abstract class defined as:

@XmlSeeAlso({Subone.class,Subtwo.class})
public abstract class Op {
 
  @XmlElement
  public String a;
}

and Sub1 and Sub2 are subclasses of Op

public class Sub1 extends Op {
 
  @XmlElement
  public String b;
 
}
 
public class Sub2 extends Op {
 
  @XmlElement
  public int c;
 
}

Note the JAXB @XmlSeeAlso annotation on Op, which points the JAX-WS runtime to Sub1 and Sub2 whenever it needs to serialise an instance of those two types.

We can now invoke op() as follows:

Sub1 s1 = ...
Sub2 s2 = ...
 
....stub.poly(s1)....
....stub.poly(s2)...

Let us now consider the case in which op follows the WRAPPED pattern in the WSDL, e.g.:

<element name="op">
 <complexType>
    <sequence>
      <element name ="param"  type="base" />
    </sequence>
 </complexType>
</element>
 
....type definitions for Base, Sub1, and Sub2

We distinguish between the usual cases. If we can use the WRAPPED parameter style, we can model op in the SEI as follows:

@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
@WebResult(name="...")
... op(@WenParam(name="param") Base base);

reusing the definitions given above for Base, Sub1, and Sub2.

If we are instead forced to the the BARE parameter style, we can model op in the SEI as follows:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
... op(Op base);

where Op is defined as:

public class Op {
    @XmlElement
    public Base param;
}

Choices

In the previous case, the two subtypes inherited a common property from the base type. Often, however, the request element can take two entirely unrelated form. While it is possible (and indeed advantageous) to use an empty base type to model this case as the previous, it is more common to express the variation as an explicit xsd:choice, as in the following example:


<element name="op">
  <complexType>
     <choice>
          <element name="one" type="choice1"/>
          <element name="two" type="choice2"/>
     </choice>
  </complexType>
</element>
 
<complexType name="choice1">
    <sequence> 
       <element name="a" type="string"/> 
     <sequence> 
 </complexType>
 
<complexType name="choice2">
    <sequence> 
       <element name="b" type="string"/> 
     <sequence> 
 </complexType>

Notice that this approach follows the WRAPPED pattern.

We can follow either one of two approaches to model op in the SEI.

The first approach is based on overloading. Assuming we can only use the BARE parameter style:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
... op(Op1 one);
 
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
... op(Op2 two);

where Op1 and Op2 are defined as follows:

public class Op1 {
  @XmlElement
  public Choice1 one;
}
 
public class Op2 {
  @XmlElement
  public Choice2 two;
}

and where Choice1 and Choice2 are defined as:

public class Choice1 {
  @XmlElement
  public String a;
}
 
public class Choice2 {
  @XmlElement
  public int b;
}

Overloading is more appealing if we can use the WRAPPED parameter style, as we can then avoid a level of wrapping and model op in the SEI as follows:

@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
@WebResult(name="...")
... op(@WebParam(name="one") Choice1 one);
 
@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
@WebResult(name="...")
... op(@WebParam(name="two") Choice 2 two);

The other, more general approach is to retain a degree of polymorphism in the SEI:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
... op(Op one);

where:

@XmlSeeAlso({Choice1.class,Choice2.class})
public abstract class Op {}
 
public class Choice1 extends Op {...as above...}
public class Choice2 extends Op {...as above...}

Note: we must use an abstract class here, we cannot replace it with an interface.

The any Case

Let us now consider the case of an operation that does not constrain the data to assume predefined forms. The standard WSDL idiom in gCore service for this relies on the xsd:any element, which allows any well-formed XML element structures.

For example:

<element name="op">
  <complexType>
	<sequence>
	   <any ..../>
	</sequence>
 </complexType>
</element>

Even if the pattern is implicitly WRAPPED here, we are forced to use the BARE parameter style in the SEI:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
... op(Op any);

where AnyElement is the following bean class:

 
public class Op {
  @XmlAnyElement
  public org.w3c.dom.Element any;
}

where the annotation @XmlAnyElement instructs the runtime to serialise the any as generic XML.

The enumeration Case

Let's now consider the case of an Enumeration which is a constrain on the values a type should assume.

For example:

<xsd:simpleType name="type1">
	<xsd:restriction base="xsd:string">
               <xsd:enumeration  value="value1"/>
	       <xsd:enumeration  value="value2"/>
 	       <xsd:enumeration  value="value3"/>
        </xsd:restriction>
</xsd:simpleType>

In the example above the type type1 is a restriction of the string type having only 3 possible values : value1, value2 and value3. This is simply translated in JAXB using the @XmlEnum annotation as follows:

@XmlEnum(String.class)
public enum type1 { value1, value2, value3 }

Faults

SEI clients may observe a wide range of failures, including: failures that occur in the client runtime, before remote calls to endpoints are issued; failures that occur in the attempt to communicate with endpoints; failures that occur in the runtime of endpoints. We distinguish between the following types of failures:

  • errors are violations of the contract defined by the service which can imputed to faulty code or faulty configuration. Illegal SEI definitions are examples of client-side errors, while bugs in the implementation of the service are examples of service-side errors.
  • contingencies are predictable violations of contract pre-conditions. There may be no bugs in either client or service code, but the endpoint is put in a state that prevents it to carry out the client’s request. Data that cannot be found or cannot be created are examples of contingencies.
  • outages are I/O failures of the external environment and include network failures, database failures and disk failures.

We take the common view that errors ought to emerge as soon as possible, hence should be modelled as unchecked exceptions. Client-side errors emerge from the JAX-WS runtime as WebServiceException, which are RuntimeExceptions. Server-side errors emerge as SoapFaultException, also unchecked as a subclass of WebServiceException.

We also take the common view that outages cannot be recovered from, at most contained high up in the client stack. For this reason, we also suggest to model them with unchecked exceptions. Again, the JAX-WS runtime returns them as SoapFaultExceptions.

What we're left with is contingencies, which we suggest to model as checked exceptions under the expectations that they may be recoverable and clients should prepare for them.

Services model contingencies explicitly in WSDLs, e.g. :

<element name="OpFault">
    <complexType>
          .....
 </complexType>
</element>
...
<message name="OpFault"> 
	<part name="fault" element="OpFault"/>
</message>
 ...
<operation name="op">
  <input message="..."/>
  <output message="..."/>
  <fault name="fault" message="OpFault"/>
</operation>

Note: Contingencies often extend shared fault types: GCUBEUnrecoverableFault, GCUBERetrySameFault, GCUBERetryEquivalentFault, all subclasses of GCUBEFault. Each class captures a generic aspect of the fault semantics that equally generic clients may react to. Since the approach relies on shared types, however, it is rather coupling. Accordingly, we will not attempt to preserve that general semantics in our SEIs.

To model OpFault in the SEI, we first define a corresponding exception OpException:

@WebFault(name="OpFault")
public class OpException extends Exception {
 
     ......
 
}

where we use the annotation @WebFault to point the JAX-WS runtime to the corresponding element of the fault message returned by the service.

We then declare to throw it from the method that models op:

.. op(...) throws OpException;

In some cases, OpFault carries descriptive information, e.g.:

<element name="OpFault">
    <complexType>
          <sequence>
              <element name="detail" type="string">
          </sequence>
 </complexType>
</element>

We can model this information as a bean class:

public class OpExceptionInfo {
 @XmlElement
 public String details;
}

and then expect instances of the bean class in one or both of the following constructors of OpException:

@WebFault(name="OpFault")
public class OpException extends Exception {
 
    private final OpExceptionInfo info;
 
    public class OpException(String msg, OpExceptionInfo info) {
         super(msg);
         this.info=info;
    }
 
 public class OpException(String msg, OpExceptionInfo info, Throwable cause) {
         super(msg,throwable);
         this.info=info;
    }
 
   ......
 
   public OpExceptionInfo getFaultInfo() {
      return info;
   }
}

At fault time, the JAX-WS runtime will instantiate OpExceptionInfo from the fault element, and then it will pass it to the constructor/s of OpException.

Factory Operations

We've briefly discussed above the case of stateful services and their companion factory services. Let us know see how to model operations of factory services that create instances and return references to them.

Assume create is one such operation. Typically, the WSDL will define its response element as follows:

<definitions ... xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" .... >
    ...
   <types>
 
       <import namespace="http://schemas.xmlsoap.org/ws/2004/03/addressing" .... />
        ...
 
        <element name="createResponse type="wsa:EndpointReferenceType">
        ...
    </types>
...

Here we're using a BARE pattern. We then model create in the SEI of the factory using Java's W3CEndpointReference:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)  
W3CEndpointReference create(....) ... ;

Assume now a WRAPPED pattern was followed in the WSDL:

 <element name="createResponse>
   <complexType>
      <sequence>
          <element name="return"  type="wsa:EndpointReferenceType"/>
      </sequence>
    </complexType>
</element>

We then have the usual two options. If we can use the WRAPPED parameter style, then we can model create in the SEI as follows:

@SOAPBinding(parameterStyle=ParameterStyle.WRAPPED)
@WebResult(name="return")  
W3CEndpointReference create(@WebParam(name="...")..., @WebParam(name="...")...,...);

If instead we're forced to use a code>BARE</code> parameter style, then we can model then can model create in the SEI as follows:

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
CreateResponse create(...);

where CreateResponse is the bean class:

public class CreateResponse {
 @XmlElement
 public W3CEndpointReference return;
}