BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles "Can I call you back about that?" Building Asynchronous Services using Service Component Architecture

"Can I call you back about that?" Building Asynchronous Services using Service Component Architecture

This item in japanese

This article (by Mike Edwards of IBM) discusses the need for asynchronous services when you build an application using a service-oriented architecture. Building asynchronous services can get complicated, but is made straightforward using Service Component Architecture (SCA). The steps involved in using SCA to create an asynchronous service and asynchronous service client are described in this article.

For a general introduction to SCA, see [1].

Business Processes and the need for Asynchronous Services

While we all think it's great to get things done immediately, right here and now, in real life it's often the case that things take a while to happen, involving multiple steps in a sequence.

Think of ordering some books online. Up front, as well as finding the books you want, you may want to check availability, expected dispatch dates and alternative delivery methods. Once the order is placed and paid for, things don't happen immediately:

  • First, the book seller sends a confirmation of the order, along with the expected dispatch date. You are also given an order ID of some kind that allows you to check the status of the order.
  • At some later time, the book seller sends you a confirmation of dispatch of your order, along with a tracking number you can use to check the status of the delivery with the shipping company.
  • Finally, if you're lucky, the books arrive!

Many kinds of business processes have these features. When we aim to build applications that support these business processes, it becomes clear that the typical "call-and-return" style of service programming doesn't work very well.

Call-and-return, also known as synchronous processing, means that the client to a service calls the service and then stops, waiting for the service to complete its task before the client code continues. If the client code has nothing else to do, this simply places a burden on the system running the client, since it has to keep the code hanging around in memory with nothing going on. Not a problem if the client code is the only thing happening on the system – but it quickly leads to trouble when the system is dealing with thousands of separate client requests a minute.

Another case is where the client code could be doing other things while it waits for the called service to complete. Client code setting up a vacation may need to call services to set up flights, hotels, rental cars. It is much faster to process each of these requests in parallel, rather than calling them one at a time and waiting for each one to complete before going on to the next one.

In these cases, what is called for is asynchronous processing. You can think of this as if the client fires off a request message to a service and then goes on to some new work. The called service does its work and, when it is done, it fires a response message back to the client. Or possibly it fires more than one response message to the client as we see in the book ordering process, where one message confirms the order, another message indicates dispatch of the order.

Facilities for Asynchronous Services

Given that there is a need for asynchronous services, what facilities exist to help us build such services – and for building clients to asynchronous services?

Facilities for writing clients to synchronous services are well provided by most programming models and frameworks. The same can be said for writing synchronous services. The synchronous call-and-return model is the standard form for writing code in most programming languages. As a result, writing a synchronous service or a synchronous service client is usually not much more than a simple extension of the regular programming model. In Java, for example, this usually means that a service is implemented as a method in a class, while the service client is the invocation of a method on a class.

Facilities for Asynchronous Services

Given that there is a need for asynchronous services, what facilities exist to help us build such services – and for building clients to asynchronous services?

Facilities for writing clients to synchronous services are well provided by most programming models and frameworks. The same can be said for writing synchronous services. The synchronous call-and-return model is the standard form for writing code in most programming languages. As a result, writing a synchronous service or a synchronous service client is usually not much more than a simple extension of the regular programming model. In Java, for example, this usually means that a service is implemented as a method in a class, while the service client is the invocation of a method on a class.

Facilities for writing asynchronous services and for writing clients to asynchronous services are less well developed. Java is typical of many programming languages in that first-class support for asynchronous programming has only be added relatively recently – with the emergence of the concurrency classes in Java 5.

There are also facilities for asynchronous processing made available through some protocol-related programming models [2]. As an example, JAX-WS [5] provides a means of writing an asynchronous client to a synchronous service, associated with using Web services as the communication method between the client and the service.

In this, if the client code for a synchronous service invocation is like this:

OrderResponse placeOrder( OrderRequest oRequest );

Example 1: Simple synchronous placeOrder operation

the equivalent asynchronous service invocation is like this:

Future placeOrderAsync( OrderRequest oRequest,                            
AsyncHandler responseHandler );

Example 2: Asynchronous form of the placeOrder request

and the asynchronous response to the invocation is sent back to the client via an asynchronous response handler declared as follows:

class OrderResponseHandler implements AsyncHandler {

public void handleResponse(
Response theResponse ){

OrderResponse orderResponse = theResponse.get();
}

}

Example 3: Handler method called when placeOrder completes

JAX_WS does not provide support for writing asynchronous services. It also only supports the cases where there is exactly one response message for a particular request.

A Java framework which supports asynchronous style clients and asynchronous services is the JMS API [6]. The JMS API is useable when the communication method between client and service is a messaging system which supports JMS. JMS is also usable in the Java EE framework by means of Message Driven Beans [7]. The JMS approach supports a range of asynchronous services, but JMS does tie the client and service code to the use of some messaging infrastructure.

Finally, there is a whole language which has been developed with asynchronous business processes in mind. This is the Business Process Execution Language (BPEL for short) [8]. BPEL is particularly good at coordinating groups of services, both synchronous and asynchronous, but it is probably less good at writing services or clients which involve a significant amount of data manipulation.

SCA and Asynchronous Services

SCA provides first class support for creating asynchronous services and also for creating clients to asynchronous services. SCA supports asynchronous services with a simple interaction pattern (one response message for each request message) and also supports more complex interaction patterns (zero, one or multiple response messages for each request message) – remember that our "simple" book ordering process generated two response messages for each book order request.

SCA is able to support asynchronous services written in any of a wide range of programming languages, including Java and BPEL for example. In addition, SCA is capable of using a variety of alternative communication methods between the client and the provider – it may use a messaging infrastructure (as used by JMS) but it may alternatively use a Web services infrastructure. SCA does not tie the client or the service provider code to any particular communication infrastructure.

SCA models the communication between a service client and the service provider using the concepts of

  • a service, which is a set of one or more operations contained in an interface which is implemented by the service provider
  • a reference, which is used by the client to invoke the operations of the service provider. The reference is typically a proxy object which implements the same interface as the service provider implements

In the case of synchronous services, each operation in the interface has a call-and-return style, where a message is sent to the service provider as the parameter of the operation and the response is the return value of the operation:

OrderResponse placeOrder( OrderRequest oRequest );

Example 4: Simple synchronous placeOrder operation

For asynchronous services, SCA has the concept of the callback. A callback implies that communication is two-way and asynchronous. The client calls the service provider using operations on a – responses involve the service provider calling back to the client using a separate service interfacecallback interface. While it is the service provider that implements the service interface, it is the client that must implement the callback interface.

The asynchronous callback equivalent of the synchronous operation shown in Example 4 consists of a request operation like this:

void placeOrder( OrderRequest oRequest );

Example 5: Asynchronous placeOrder service request

…combined with an operation in the callback interface that looks like this:

void placeOrderResponse( OrderReponse oResponse );

Example 6: Callback operation for the placeOrder response

An important aspect of the SCA approach to asynchronous services is that the client can be called back by the service provider via the callback interface at any time after the initial invocation of the service by the client. The service provider may use any of the operations defined in the callback interface and in response to a single request from the client, the provider may call one operation or multiple operations or zero operations in the callback interface. The simplest case has the provider call exactly one operation in the callback interface for each service operation called by the client.

A Simple Example

Let's take a look at a simple example of an asynchronous service used by a client – in this example, both the client and the service are written using plain Java classes. This is a simple version of a service for placing an order for some widgets, where the service is asynchronous and the client is informed of progress via a callback interface.

First, there are the interfaces for the service and for the callback:

public interface OrderService {
public void placeOrder( OrderRequest oRequest );
}

Example 7: The OrderService interface

public interface OrderCallback {
public void placeOrderResponse( OrderResponse oResponse );
}

Example 8: The OrderCallback interface

Next, there is the code for the implementation of the OrderService (ie the service provider):

import org.osoa.sca.annotations.*;  

public class OrderServiceImpl implements OrderService {

// A field for the callback reference object
private OrderCallback callbackReference;

// The place order operation itself
public void placeOrder( OrderRequest oRequest ) {
// …do the work to process the order…
  // …which may take some time…

  // when ready to respond…

  OrderResponse theResponse = new OrderResponse();

 callbackReference.placeOrderResponse( theResponse );
}

// A setter method for the callback reference

@Callback
public void setCallbackReference( OrderCallback theCallback ) {
callbackReference = theCallback;
  }
}

Example 9: The OrderService implementation

and finally, there is the code for the client to the OrderService, which provides an implementation of the OrderCallback interface:

import org.osoa.sca.annotations.*;

public class OrderServiceClient implements OrderCallback {

// A field to hold the reference to the order service

 private OrderService orderService;

 public void doSomeOrdering() {

 OrderRequest oRequest = new OrderRequest();

//… fill in the details of the order …

 orderService.placeOrder( oRequest );

// …the client code can continue to do processing
 }

 public void placeOrderResponse( OrderResponse oResponse ) {

 // …handle the response as needed
}

// A setter method for the order service reference

@Reference
public void setOrderService( OrderService theService ) {
orderService = theService;
}
}

Example 10: The client to the OrderService

The Service Implementation

First, let's examine the code for the asynchronous service, shown in Example 9.

There is a setter method called setCallBackReference, which is annotated with @Callback. The @Callback annotation instructs the SCA runtime to inject a reference object representing the callback interface to the client – this is done at the time that the service receives the request to the placeOrder operation, so that the service implementation has at hand a way of calling back to the client.

The service implementation does not have to concern itself with the identity or the location of the client – this is sorted out by the SCA runtime and all this information is contained within the supplied callbackReference object.

The service operation, placeOrder, simply receives the OrderRequest message and goes about its work to process the order. This can take whatever amount of time necessary – whenever the placeOrder operation is ready to respond to the client, be it in seconds or be it in days, then it simply uses the callbackReference which provides the placeOrderResponse operation for the call back to the client.

If necessary, the OrderService implementation can store away a copy of the callbackReference and fetch it back when needed. This might be the right approach, for example, if a human task like packaging up the order is part of the process and where the OrderService needs to wait for some input from the person doing that task before it can proceed.

The Service Client

The code for the client to the OrderService is shown in Example 10.

This has two main parts – the doSomeOrdering method which calls the placeOrder service, and the placeOrderResponse method which handles the callback from the placeOrder request. There is also a setter method for the orderService reference. Note that this method is marked with the @Reference annotation which instructs the SCA runtime to provide a reference object to the OrderService before the client starts its work. The reference object provides the business interface to the OrderService, while all the information about the location of the service and the communication method used to talk with it is held within the reference object.

The client prepares its OrderRequest in the doSomeOrdering method. When it is ready, it calls the placeOrder operation of the OrderService by invoking that operation on the orderService reference object. This transmits the request to the OrderService. The client code returns from the placeOrder service immediately, without waiting for any response from the OrderService. The client code can move on to do other processing, or it can simply return, having completed its initial work.

When the OrderService sends its response to the original placeOrder request, the OrderResponse message is routed to the placeOrderResponse method of the client.

The placeOrderResponse method can then do any work needed to handle the response – for example, it may record it in a database.

The placeOrderResponse method may be called a long time after the original placeOrder request was made. It may be the case that the OrderReponse message contains information that relates back to the original OrderRequest message (eg an Order number), in which case the response probably contains all the information necessary for the client to tie the response to the original request.

However, it may be the case that the OrderResponse message cannot be so easily tied to the original request. In this case, the client code has the capability of attaching a callbackID to the original request, which is then returned with the response message. The callbackID is a simple serializable object, such as a string, that is used to uniquely label the original request. This ID can then be used to link back to some state data which identifies the original request to the client.

The Connection Between the Client and the Service

One question you're probably asking is - "how is the connection between the client and the service actually defined?" The code for the client and for the service looks like it uses some piece of magic, with the references for the service and for the callback arriving through injection from the SCA runtime.

SCA defines an assembly model [3], which defines how groups of components are configured and connected to one another to build a solution. All the information needed to define the location of the service, the location of the client and how they are connected together comes from the composite which assembles them into a solution. The SCA runtime reads the information from the composite and uses this to provide the reference and callback proxies that are used by the client and service code at runtime.

SCA Assembly diagram for simple Client and Service

Figure 1: SCA Assembly diagram for simple Client and Service

SCA can represent composites using diagrams – a picture is worth a thousand words! The diagram shown in Figure 1 might be the one used to describe the assembly of the OrderClient and the OrderService. For the OrderClient, there is a reference with the name "OrderService". On the OrderService there is a service with the name "OrderService". The reference is connected to the service by a wire, which says that the client reference is connected to the service. The fact that the connection involves a callback is defined by the interface used to describe the service – the interface is a "dual interface" with both the OrderService interface and the OrderCallback interface present.

"Under the covers", SCA represents the composite as XML metadata, which can also contain other information such as the communication method used to link the reference to the service. The XML matching the diagram here might look something like the following:

xml version="1.0" encoding="UTF-8"?>

<composite name="orderComposite"

      xmlns:sca="http://www.osoa.org/xmlns/sca/1.0">

 

  <component name="OrderClient">

      <implementation.java class="OrderServiceClient"/>

      <reference name="OrderService"

                 target="OrderService/OrderService">

            <binding.ws/>

      reference>

  component>

 

  <component name="OrderService">

      <implementation.java class="OrderServiceImpl"/>

      <service name="OrderService">

            <interface.java interface="OrderService"

                  callbackinterface="OrderCallback"/>

            <binding.ws/>

      service>

  component> 

composite>

Example 11: Composite linking OrderClient and OrderService

In this case, the OrderClient has its OrderService reference wired to the OrderService service of the OrderService component, through the value of the @target attribute on the reference element. The presence of a element on both the OrderClient reference and on the OrderService service indicates that Web services are the method used to communicate between them.

Summary

Asynchronous services, where responses are returned a long time after an initial request is made, are simply a fact of life – not everything happens immediately!

Providing Asynchronous service implementations and also providing clients to asynchronous services is made simpler with Service Component Architecture. SCA provides a request followed by callback response model for asynchronous services, allied with the use of callbackID to tie the original request to the callback response.

SCA enables this to be done for a variety of underlying communication methods between the client and the service, eliminating the need for the code to be dependent on complex middleware APIs.

If you'd like to try out SCA for real and see for yourself how it simplifies the programming of service-oriented applications, there are commercial products available from a variety of companies, which you can see described on the following page:

http://www.osoa.org/display/Main/Implementation+Examples+and+Tools 

There are also open source implementations of SCA which you can use:

Apache Tuscany: http://cwiki.apache.org/TUSCANY/
Fabric3: http://fabric3.codehaus.org/

References

[1] "Setting out for SCA" http://www.infoq.com/articles/setting-out-for-sca
[2] "Develop asynchronous Web services with Axis2" http://www.ibm.com/developerworks/webservices/library/ws-axis2
[3] SCA Assembly Model Specification http://www.osoa.org/download/attachments/35/SCA_AssemblyModel_V100.pdf
[4] SCA Java Common Annotations and APIs Specification http://www.osoa.org/download/attachments/35/SCA_JavaAnnotationsAndAPIs_V100.pdf
[5] JAX-WS Specification http://jcp.org/aboutJava/communityprocess/mrel/jsr224/index2.html
[6] JMS Specification http://java.sun.com/products/jms/docs.html
[7] Message Driven Beans in EJB 3.0 Specification http://jcp.org/aboutJava/communityprocess/final/jsr244/index.html
[8] Business Process Execution Language (BPEL) http://docs.oasis-open.org/wsbpel/2.0/wsbpel-v2.0.pdf

About the Author

Mike Edwards is a Strategist in emerging technologies at the IBM Hursley labs near Winchester, England. He is currently working on SOA and in particular on the Service Component Architecture specifications. He is Co-Chair of the OASIS SCA Assembly technical committee and he is a committer on the Apache Tuscany project.

Rate this Article

Adoption
Style

BT