Service bindings

To access a service, you must specify both how to interact with the external logic and where it resides. You can think of the two kinds of information in an abbreviated way: "how" and "where."

This topic includes the following sections:

For background information, see Resource bindings.

Specifying "how" and "where" in the call statement

If you are accessing a service that was written in EGL, you can specify "how" and "where" in the call statement. The usage is particularly simple and is available whether the service is being deployed with a Rich UI application (as a dedicated service) or outside of a Rich UI application (as an EGL REST-RPC service):
  • The following example accesses either kind of service, depending on the EGL deployment-descriptor entry named myEntry:
    myBindingVar IHttp?{@Resource {uri="binding:myEntry"}};
    
    call 
       MyServiceType.myFunction("abc")        // "how"
       using myBindingVar                     // "where"
       returning to myCallBackFunction
       onException myExceptionHandler;    

    The example uses the IHttp? Interface type for flexibility. Later, if you want to switch to a different kind of access target, you switch to a different deployment descriptor. You will not need to change the invocation, and you will not need to regenerate the example.

  • In the next example, the Service type identifies "how," and the lack of a call-statement using clause indicates that you are accessing a dedicated service:
    call 
       MyServiceType.myFunction("abc")        // "how"
                                              // "where" (the service is deployed
                                              //          with the Rich UI application)
      returning to myCallBackFunction
       onException myExceptionHandler;   

    This second case is even simpler, but any future use of an EGL REST-RPC service requires that you change the invocation and regenerate the example code.

Specifying "how" and "where" in a proxy function

If you are accessing a REST or EGL REST-RPC service, you can encapsulate the "how" and "where" information in a single location, by adding a proxy function to a library or handler. The call statement invokes the proxy function, which acts as an intermediary between that statement and the backend code. You also specify a third kind of information in the proxy function, for use by the EGL generator that is storing invocation code in place of the annotations.

At development time, the proxy function is empty. It lists the invocation parameters and, if appropriate, a return type. Here is the outline of such a function:
function myProxyFunction(p1 string, p2 string) RETURNS(int)
   {}
end

You do not write any logic for the proxy function. Instead, you tell an EGL generator what is required. In particular, you specify an annotation that is specific to the kind of backend code that will be invoked.

Here are examples:
  • If you accessing a dedicated or EGL REST-RPC service, declare an EglService annotation:
    function myProxyFunction(p1 string, p2 string) RETURNS(int) // "how" 
    {
       @EglService{serviceName="MyServiceType"}                 // "what more" does
                                                                //  the generator need?
    }
    end
  • If you are accessing a third-party REST service, declare a Restannotation:
    function myProxyFunction02(p1 string, p2 string) RETURNS(int) // "how" 
    {  
       @Rest                                                      // "what more" does
                                                                  // the generator need?
       {
          method = HttpMethod._GET, 
          uriTemplate = "/org/search/?string01={p1}&string02={p2}"     
       }   
    } 
    end
To add "where" information to the proxy function, specify the Resource annotation at the same level as the other annotation. Here are the previous examples with the additional detail:
function myProxyFunction(p1 string, p2 string) RETURNS(int)   // "how" 
{
   @EglService{serviceName="MyServiceType"},                  // "what more" 
   @Resource{uri = "binding:myEntry"}                         // "where"  
}
end

function myProxyFunction02(p1 string, p2 string) RETURNS(int) // "how" 
{  
   @Rest                                                      // "what more"
   {
      method = HttpMethod._GET, 
      uriTemplate = "/org/search/?string01={p1}&string02={p2}"     
   },   
   @Resource{uri = "binding:myEntry02"}                       // "where"  
} 
end

The "where" information that you specify in the proxy function is optional and, if present, is a default. The detail is ignored if you specify the "where" detail in the code that invokes the proxy function.

Last, if the proxy function has a name different from the name of the service operation, declare an ExternalName annotation:
function myProxyFunction02(p1 string, p2 string) RETURNS(int) // "how" 
{  
   @Rest                                                      // "what more"
   {
      method = HttpMethod._GET, 
      uriTemplate = "/org/search/?string01={p1}&string02={p2}"     
   },   
   @ExternalName{value = "my-Operation"},                     // "what more"  
   @Resource{uri = "binding:myEntry02"}                       // "where"  
} 
end

The primary reason to declare an ExternalName annotation is that the name of a third-party service operation is not valid as the name of an EGL proxy function.

Deciding on the placement of "how" and "where"

You say, "I can specify the 'how' and 'where' information in two places. What is the best practice?"

The proxy function is particularly useful when you are accessing a service that is in a more-or-less permanent location. In this case, you have the minor complexity of creating a proxy function, but you invoke the backend logic simply:
call 
   myProxyFunction("abc")                   // "how" and "where"
   returning to myCallBackFunction
   onException myExceptionHandler;

As shown, you have no binding variable and have hidden the "how" and "where" information in the proxy function.

If you are coding a service written in EGL, you might avoid coding a proxy function at all. In this case, you rely on the Service type to tell "how." Two variations apply:
  • As shown earlier, the EGL deployer handles the "where" automatically if you are accessing a dedicated service. You can write a call statement without specifying either a binding variable or a call-statement using clause:
    call 
       MyServiceType.myFunction("abc")        // "how"
                                              // "where" (the service is deployed
                                              //          with the Rich UI application)
    
       returning to myCallBackFunction
       onException myExceptionHandler;
  • If you are developing an EGL REST-RPC service and want to switch easily between accessing the code being developed and, later, the code that is deployed, you can handle the situation by updating the deployment-descriptor entry or by switching deployment descriptors. The use of multiple deployment descriptors is robust, but requires that you assign the same-named entry in each of them.

Specifying "where" in a service binding

A resource binding that is specifically for service access is known as a service binding. The main detail in the EGL deployment descriptor is in one of three categories:
  • If the service is deployed on an application server, you specify a Universal Resource Identifier (URI) that begins with the http: or https: prefix. Here is an example:
    • http://myserver:8080/myproject/restservices/myservice

    Although you can run the deployed service during an EGL debugging session, the EGL debugger will not step into the service.

  • If the Rich UI application references a Service type in a way that indicates use of an EGL REST-RPC service, you can specify a workspace URI. A workspace URI points to a workspace location, as shown here:
    • workspace://mySourceProject/servicepackage.MyServiceType

    A workspace URI is useful only at development time, when an internal Test Server enables you to debug the code. The Test Server is described here: EGL test server.

    In this case, your task in the EGL Deployment Descriptor editor is twofold: you update not only the Resource Binding tab, but the Service Deployment tab as well. That secondary requirement ensures that the deployment descriptor file includes the detail necessary to deploy the service. However, before you fulfill the EGL deployment step, you'll need to ensure that the URI in the resource binding is pointing to the deployed EGL REST-RPC service.

  • If a Rich UI application references a Service type in a way that indicates use of a dedicated service, the internal Test Server enables you to debug the service logic. The service binding, if any, has no detail.

At this writing, you can bind to a REST, EGL REST-RPC, or dedicated service. The distinctions among the service types are explained here, with "binary-exchange service" in place of the more narrowly defined "dedicated service": Service-oriented architecture (SOA) for EGL developers (http://www.eclipse.org/edt/papers/topics/egl_soa_overview.html).

For details on defining a REST or EGL REST-RPC service binding in the EGL deployment descriptor, see Adding a REST binding to the EGL deployment descriptor.

If you are defining a workspace binding, you must also deploy the Service type. For details, see Adding web-service deployment details to the EGL deployment descriptor.

Retrieving a service binding and changing it in your code

Here is an example of retrieving and changing a service binding and then using it to access a third-party REST service:
myBindingVariable IHttp? = Resources.getResource("binding:myEntry");             
myBindingVariable.request.encoding = encoding.json;   
myBindingVariable.request.headers = new dictionary{edt.proxy.invocation.timeout = 6};
                
call myProxyFunction() using myBindingVariable  
     returning to myCallBackFunction 
     onException myExceptionHandler;    	
end	
The code acts as follows:
  1. Declares a binding variable and creates an object that is based on the specified deployment-descriptor entry.
  2. Adds detail to the object. In this example, the detail has two purposes: to ensure that data is transferred to and from the service in JSON format; and to establish a timeout value of 6 seconds.
  3. Calls a proxy function, referencing the binding variable. The encoding detail takes precedence over the equivalent value, if any, in the proxy function. The headers detail can be set only in the code that invokes the proxy function.

The example is accessing an instance of an HttpRest object.

Creating a service binding in your code

You can create a service binding in your code, in which case the EGL deployment descriptor is not involved. For example, you might substitute the first statement in the following code for the first three statements in the preceding section:
myBindingVariable IHttp? = new HttpRest{
   restType = eglx.rest.ServiceType.TrueRest, 
   request.uri = "http://www.example.com/myproject/restservices/weather_service",
   request.encoding = Encoding.json,
   request.headers = new dictionary{edt.proxy.invocation.timeout = 6}};

call myProxyFunction() using myBindingVariable  
   returning to myCallBackFunction 
   onException myExceptionHandler;    	
end	

For details on the HttpRest object, see eglx.http package.