Chapter 4. Service interaction patterns

This chapter covers

  • Remote interaction
  • Local interaction
  • Request response interaction
  • One-way interaction
  • Conversational interaction
  • Callback interaction

Components may interact with one another in different ways. It’s similar to the way we interact with other people. For example, when Mary books a holiday using a travel agent, she may decide to visit them and talk to them directly, she may have to wait for them to call her back with some information, and she will certainly have an ongoing conversation about the holiday she’s booking.

SCA defines common service interaction patterns and makes it simple to create component services and references that exploit them. In this chapter we’ll start by introducing the different interaction patterns that SCA and Tuscany support. Then we’ll look at each pattern in more detail and explore the motivation, configuration, and applicability of each pattern.

We’ll demonstrate each SCA interaction pattern by using a component from the travel-booking application. We’re going to use the Hotel, Calendar, CurrencyConverter, and ShoppingCart components here. These components can be found in the following sample contributions, respectively:

  • contribution/hotel
  • contributions/calendar
  • contributions/currency
  • contributions/shoppingcart

The components don’t work in isolation, so for each interaction pattern we’ve written a simple client component. For example, the InteractionLocalClient component demonstrates local interactions by sending a local message to the Calendar component. All the samples for this chapter are run with a single launcher from the launchers/interaction directory. The name of the client and target component for the interaction pattern being discussed is given so you can find the sample code easily. To run the example you’ll need the following contributions as well as the contributions listed previously:

  • contributions/common
  • contributions/interaction-service-remote
  • contributions/interaction-client

In this chapter we’ll concentrate on the generic patterns, although we’ll use the Java language in the samples to demonstrate these patterns. The details of how to configure and control these interaction patterns for Java implementations and interfaces are given in the next chapter (chapter 5). In Tuscany currently, support for the more complex interaction patterns is only provided by the implementation.java type.

By the end of this chapter, you’ll have good understanding of the service interaction patterns described by SCA and will know how to use Tuscany to enable these patterns. This knowledge will help you choose suitable patterns for your application. Let’s get started by first looking at the range of SCA interaction patterns.

4.1. Understanding the range of SCA service interaction patterns

SCA and Tuscany have been designed to allow you to exploit a wide variety of service interaction patterns between SCA components. The first two patterns describe the locality of the interacting component.

SCA components can have local or remote service interfaces. Services with local interfaces are able to communicate able only with other components that are deployed locally and that are running within a single JVM. On the other hand, services with remote interfaces can be deployed remotely and are able to communicate with other components, either in the same JVM or in different JVMs. Local and remote service interfaces exhibit different semantics. Local interfaces are pass-by-reference while remote interfaces are pass-by-value.

  • Remote— The interacting components are designed to run in two different Tuscany nodes, in the same or separate Java virtual machines (JVMs), and could be running on different physical computers. Data passed between remote components is passed by value.
  • Local— The interacting components are designed to run only in the same node and JVM. The Tuscany runtime uses in-memory communication in this case. Data passed between local components is passed by reference.

Regardless of whether components are local or remote, the following set of interaction patterns describes the style of each message exchange:

  • Request response— The calling component sends a message to the called component. This is referred to as the forward direction. The calling component expects an immediate response for each message that’s sent.
  • One way— The calling component sends a message and doesn’t wait for a response.
  • Conversational— The calling component sends a sequence of related messages that are associated with each other by means of a common context maintained between calling and called components. When no conversation is in effect, each message is stateless and isn’t related to the messages that come before or after it.
  • Callback— A forward message from calling to called component instigates a call from the called component back to the calling component at some point in the future. This is referred to as the callback direction. When no callback is configured, messages travel in the forward direction only.

Some simple examples will help explain these patterns. First, imagine a simple SCA component service on the internet that provides a description of a holiday resort on request. The service has a single operation that accepts the request, looks up the resort details in a database, and returns them directly.

  • Is the target service local? No, it’s accessible across the internet (remote).
  • Will the service return a direct response? Yes, the resort details are returned directly (request response).
  • Is the request related to other requests? No, each request for information is separate (stateless).
  • Will the service call back? No, no further action is expected once details are returned (forward only).

Now consider a more complex SCA component service that’s able to find a resort that has available hotel rooms. This operation takes a little time, and so the service is designed to call back to the calling component once the information is available. The single operation in this service interface doesn’t provide a result directly.

  • Is the target service local? No, it’s accessible across the internet (remote).
  • Will the service return a direct response? No (one way).
  • Is the request related to other requests? No, each request for information is separate (stateless).
  • Will the service call back? Yes, once availability has been established, the calling component will be notified (callback).

Using this question-and-answer style is a useful approach to identifying when each pattern is applicable. Table 4.1 shows a set of questions that help demonstrate how the various patterns that SCA supports can be applied. The section numbers are included so that you can easily find the pattern in question. Because you don’t need to use all of these patterns all of the time, you don’t necessarily need to read all of the details in this chapter the first time through. You can use this table to help you pick and choose which sections are of interest.

Table 4.1. Questions that help you identify which service interaction patterns are appropriate and the sections where the patterns are documented

Question

Answer

Pattern

Section

Is the called component running in a different SCA runtime (Tuscany node) when compared to the calling component making the request? Do you expect to get a direct response to the request? Yes No Yes No Remote Local Request response One way 4.2 4.3 4.4 4.5
Is the reference component expecting to be called back in the future as a result of the request? Yes No Callback Forward only 4.6 4.4
Is the request related to a previous request to the same service? Yes No Conversational Stateless 4.7 4.4

You can combine these patterns. For example, you could use one-way messages on the forward leg of a callback.

In the following sections we’ll look at the details of each pattern. We’ll first look at the remote and local interaction patterns, given that the other patterns apply equally to either of these.

4.2. Remote interaction

In this section we’ll show how the remote interaction pattern allows SCA components to communicate with other SCA components or non-SCA software using pass-by-value semantics. The remote interaction pattern is vital to assembling coarse-grained, loosely coupled, and distributed components.

Coarse-grained components are typically responsible for carrying out large pieces of processing such as the TuscanySCATours Hotel component that interacts with business partners to find available hotel rooms. Such coarse granularity leads to loosely coupled components where message exchanges tend to comprise self-contained and independent messages. This gives the components great flexibility in how, where, and when to complete the tasks they’ve been asked to perform.

Figure 4.1 shows a client component wired to the Hotel component in order to search for available hotel rooms. The two components are running in separate JVMs, which can be running on separate computers in different geographical locations.

Figure 4.1. Remote interactions between components in separate Java virtual machines. The components communicate using the web service binding.

Coarse-grained components usually run remotely from the calling component. Often this is because they’re developed and operated independently by different parts of an organization or even different organizations. In the TuscanySCATours travel application, the Hotel component is implemented by a department that specializes in working with hoteliers. It’s made available over the web services binding for any part of the organization to call.

Configuring a component in SCA to allow its service to be accessed remotely is a simple matter of marking the service interface appropriately.

4.2.1. Configuring remote interaction

For service interfaces described using Java interfaces, SCA defines a Java annotation @Remotable. This annotation indicates that component services that implement the Java interface can be remote and that any data passed to the service will be passed by value. The following code, which can be found in contributions/common, shows how the Search interface is configured to be remotable:

@Remotable
public interface Search {
TripItem[] searchSynch(TripLeg tripLeg);
}

The Hotel component, which implements this Search interface, can run anywhere on the network. Bindings such as web service or JMS bindings can then be configured to allow clients to communicate with the Hotel component.

SCA bindings implement protocols for exchanging information between component services across a network. For example, the web service binding uses SOAP over HTTP to transfer messages in XML-formatted SOAP envelopes. Tuscany provides several bindings, as described in chapter 7.

The following code shows the InteractionRemoteClient component reference hotelSearchRemote configured with a web service binding pointing to the Hotel component Search service:

<component name="InteractionRemoteClient">
<implementation.java class=
"scatours.client.impl.InteractionRemoteClient" />
<reference name="hotelSearchRemote">
<binding.ws uri="http://localhost:8081/Hotel/Search"/>
</reference>
</component>

Note that the reference name hotelSearchRemote is just a name. The inclusion of the word Remote here is to remind us that this sample is about remote interaction. This is true for the other samples in this chapter where the name of the interaction pattern is included in the reference name.

A separate composite describes the Hotel component as follows:

 <component name="Hotel">
<implementation.java class="com.tuscanyscatours.hotel.impl.HotelImpl"/>
<service name="Search">
<binding.ws uri="http://localhost:8081/Hotel/Search"/>
</service>
</component>

If you choose to use interface.wsdl to describe a service interface in the composite file, as discussed previously in section 2.2.2, then the service you describe is assumed remotable.

4.2.2. Exploiting remote interaction

The developer of a client component doesn’t need to worry about whether the Hotel component is running in the same JVM or not. The Tuscany infrastructure handles the details of marshalling the message to the network and unmarshalling the response.

The term marshalling used here means the conversion of a message in the form in which it appears in memory into the form in which it appears in a communication protocol, for example, converting from a JavaBean to XML. Unmarshalling means the opposite, where the message is converted back from its communications protocol form to the form it takes in memory.

Here’s the implementation of the InteractionRemoteClient component. The getTestTripLeg() method is omitted because it just creates a TripLeg structure.

public class InteractionRemoteClient implements Runnable, SearchCallback{

@Reference
protected Search hotelSearchRemote;

public void run() {
TripLeg tripLeg = getTestTripLeg();
TripItem[] tripItems = hotelSearchRemote.searchSynch(tripLeg);
}
}

The InteractionRemoteClient component talks to the Hotel component using the hotelSearchRemote reference proxy. This proxy is injected into the InteractionRemoteClient implementation by the Tuscany runtime based on the definition of the component in the composite file. In this case, this means that the proxy will use SOAP/HTTP web services to call the Hotel component using the address http://local-host:8081/Hotel/Search.

The implementation of the Hotel component has no notion that the client that’s sending the request is remote. At the service end, the Tuscany infrastructure handles the details of unmarshalling the message from the network and marshalling the response.

public class HotelImpl implements Search{
public TripItem[] searchSynch(TripLeg tripLeg) {
List<TripItem> items = new ArrayList<TripItem>();

for(HotelInfo hotel : hotels){
if (hotel.getLocation().equals(tripLeg.getToLocation())){
TripItem item = new TripItem(hotel);
items.add(item);
}
}

return items.toArray(new TripItem[items.size()]);
}
}

The implementation is just a normal Java class implementing the Search Java interface. No additional work is required of the implementation developer.

As we described in chapter 2, messages passed to remotable services are passed by value. Pass-by-value semantics are enforced even if the remotable component service happens to be running in the same process as the calling component. Hence the behavior of the composite is predictable regardless of where the components run.

If a Java service interface is not marked with @Remotable, then the service is configured by the Tuscany runtime to take part in only local interactions.

4.3. Local interaction

The local interaction pattern is useful for connecting components that are located within the same JVM. Unnecessary network traffic can be avoided because communication passes directly from one component to the next without the need to marshal messages to and from the network.

Figure 4.2 shows a client component wired to a Calendar component, which in turn provides operations that manipulate date values. In this particular example, the InteractionLocalClient component and Calendar component are wired within a single JVM and are local to one another.

Figure 4.2. Local interactions between components within a single Java virtual machine

The local interaction pattern is often used to connect fine-grained components to create a coarse-grained component that in turn exposes a remote interface. For example, you might combine the Calendar component with a TripBooking component to provide a service that can answer date-oriented queries such as “find all the flights that leave on Monday.” This kind of fine-grained composition can, of course, be done outside SCA. But you may choose to use SCA to present a consistent programming model regardless of whether you’re using remote or local interaction.

4.3.1. Configuring local interaction

Local interaction is the default mode of operation in SCA when using Java component implementations, and so local Java service interfaces require no SCA-specific configuration.

In the following example, the local Calendar service interface is defined by a simple Java interface:

public interface Calendar {
String getEndDate(String startDate, int duration);
}

Each component service defined by a Java interface without the SCA @Remotable annotation is by default assumed to use the local interaction pattern. If you choose to use WSDL interfaces to describe your component’s service interface, then they can’t be used to define local component interactions.

4.3.2. Exploiting local interaction

Tuscany supports the SCA concept of a default SCA binding, binding.sca, which will work for both local and remote services. The default binding is assumed to be active for any wires where no binding is explicitly specified. This makes wiring two local components straightforward. The following shows how the InteractionLocalClient component is wired to the Calendar component:

<component name="InteractionLocalClient">
<implementation.java class=
"scatours.client.impl.InteractionLocalClient" />
<reference name="calendarLocal" target="Calendar"/>
</component>

<component name="Calendar">
<implementation.java
class="com.tuscanyscatours.calendar.impl.CalendarImpl"/>
</component>

Note that no bindings are specified on the reference of the InteractionLocalClient component. Also note that no service is explicitly specified here for the Calendar component. The Tuscany runtime uses binding.sca configuration when no binding is provided, and this is enough to connect the two components.

The following code shows a cut-down version of the InteractionLocalClient component’s implementation. The getTestTripLeg() method is omitted because it just creates a TripLeg structure:

public class InteractionLocalClient implements Runnable {

@Reference
protected Calendar calendarLocal;

public void run() {
TripLeg tripLeg = getTestTripLeg();
String toDate = calendarLocal.getEndDate(tripLeg.getFromDate(), 10);
tripLeg.setToDate(toDate);
}
}

Looking at the implementation of the client component, you see the calendarLocal field. A proxy to the Calendar component Calendar service is injected into this field by the Tuscany runtime. The run() method uses the calendarLocal proxy to communicate with the Calendar component.

Remember from chapter 2 that if your service interface is local, it can be wired only to references of components running in the same Tuscany node using binding.sca. This may be useful for fine-grained composition where pass-by-reference is the norm. But you can’t then change your configuration and attach bindings, such as binding.ws, to the service and pass messages to it from components running in other Tuscany nodes. For this your service interface must be remotable.

Within an SCA domain, the Tuscany implementation of binding.sca will ensure that messages reach a remotable service regardless of whether it’s running in the same node or running in a remote node. This brings a lot of flexibility to your application. In this way, components can be deployed in different configurations within the SCA domain without having to change the binding configuration within the composite.

Regardless of whether a component service is remote or local to the component that’s trying to call it, the two interacting components need to exchange information. Let’s now move on to look at the first interaction pattern that builds on top of local and remote communication: request response.

4.4. Request response interaction

Once two services are wired together, either locally or remotely, they’re ready to begin to communicate with one another. When a component uses a reference to call another component’s service, the next step in processing often depends on the response from the other component. In this situation, the requesting component is willing to, and must, wait for the response to come back before it proceeds.

For example, figure 4.3 shows a test client calling the CurrencyConverter component using the request response interaction pattern. The InteractionRequestResponseClient component calls the CurrencyConverter component and waits for the result to be retuned before continuing with its processing.

Figure 4.3. A request response message flowing between the InteractionRequestResponseClient and CurrencyConverter components. The flow of execution is depicted using the dashed line. Program execution at the client waits until the request completes and the response is returned.

The normal SCA diagram style has been extended here with a dashed line that shows the flow of execution through the pair of components. The InteractionRequestResponseClient component implementation makes a request though the currencyConverterRequestResponse reference to the CurrencyConverter service. It then waits until the CurrencyConverterImpl component implementation has finished processing the request and has returned a response before continuing.

4.4.1. Configuring request response interaction

Request response is the default interaction pattern in SCA. No extra annotation is required to enable this mode of operation. Looking again at the interface for the CurrencyConverter component, you can see that this is just a normal Java interface:

public interface CurrencyConverter {

double getExchangeRate(String fromCurrencyCode,
String toCurrencyCode);
}

The getExchangeRate method is expecting to take two String parameters, fromCurrencyCode and toCurrencyCode, do its processing, and return the resulting exchange rate as a double value. A caller of this method will call getExchangeRate with the appropriate inputs and will then wait until the exchange rate is returned before continuing.

4.4.2. Exploiting request response interaction

The request response interaction pattern is familiar to most programmers. When writing a program in, say, the Java language or C++, a call to a method or function usually follows the request response style. The calling method pushes the call details onto the stack, the called method performs its intended function, and it’s not until the called method completes that the stack pops and the calling method continues processing.

The pattern in SCA is much the same; no extra configuration is required to use it either in the component implementations or in the composite files. The CurrencyConverter component’s service implementation is plain Java code, as follows:

public class CurrencyConverterImpl implements CurrencyConverter {

public double getExchangeRate(String fromCurrencyCode,
String toCurrencyCode){
return rates[currencyIndex.get(fromCurrencyCode).intValue()]
[currencyIndex.get(toCurrencyCode).intValue()];
}
}

The InteractionRequestResponseClientImpl that calls this service is also simple and requires only the SCA @Reference configuration to identify the reference proxy:

@Service(Runnable.class)
public class InteractionRequestResponseClient implements Runnable {

@Reference
protected CurrencyConverter currencyConverterRequestResponse;

public void run() {
double exchangeRate =
currencyConverterRequestResponse.getExchangeRate("GBP", "USD");
}
}

The request response interaction pattern is not appropriate in many cases. For example, imagine you want to extend the TuscanySCATours application to allow a request to be sent for a set of paper brochures to be delivered to the customer. This doesn’t lead immediately to the brochures’ being delivered electronically by return. The brochure-ordering service must arrange for appropriate brochures to be collated and delivered to the customer’s address. An immediate response is not required by the calling component, and a one-way pattern could be more appropriate.

4.5. One-way interaction

We’ve looked at the default request response interaction pattern where the caller waits for a response to a request. When the focus is on coarse-grained and loosely coupled services, the request response pattern doesn’t necessarily fit well. A request response interaction can grow into a network of interactions if the called services also make request response interaction–style calls to other services. This extended graph of service calls, waiting for other services to complete their processing, is often at odds with the objective of using loosely coupled services.

SCA defines the one-way interaction pattern as an alternative to request response. Anyone who has used threads in the Java language or C++ applications will recognize that it’s more attractive to arrange for processing to proceed in parallel. In this way, the calling processing can continue at the same time as the called processing.

Figure 4.4 shows a test-client component sending a request to the TuscanySCATours Hotel component to perform a search.

Figure 4.4. The InteractionOneWayCallbackClient component sends a search request to the Hotel component using a oneway interaction pattern. The flow of execution is depicted using the dashed line. This diagram doesn’t show how the results are retrieved from the Hotel component.

The search processing may take a while to complete. Instead of waiting, the client component can move onto other things, such as searching for flights and cars. We’ll talk about how to retrieve the response from the Hotel component when we cover the callback interaction pattern in the next section.

With the one-way pattern, we lose the temporal coupling between components in the call graph. A delay in one part of the graph won’t automatically be multiplied because other components in the graph wait for the delayed service to complete.

Closely related to the problem of temporal coupling is the need to successfully manage the ever-changing load in an active application. When request response interaction is in force, it’s hard to delay or move the processing at a specific component in order to smooth out processing fluctuations. With services operating independently, the details of where and when processing happens vary and are easier to control.

The flexibility gained by using the one-way pattern leads to less-fragile applications. For example, let’s assume that the request response pattern is used when making a request to a Hotel component to search for available hotels. If a power failure stops the computer running the Hotel component, then the client component that’s waiting for it to complete its processing will fail. Extra code is needed to detect such failures and retry the request.

If, on the other hand, the Hotel component is asked to process the request using the one-way pattern, the client component can continue processing. It’ll be notified when the hotel search results are available. If in the meantime the Hotel component stops and is restarted, it can be coded to continue processing the original request. The client component won’t be affected. In such a case, the one-way interaction pattern is more appropriate.

4.5.1. Configuring one-way interaction

Individual methods on a Java service interface can be marked as one way using the @OneWay annotation. No extra configuration is required in a composite file to use this pattern. The following code shows the Search interface extended to include both request response (searchSynch) and one-way (searchAsynch) operations for initiating a search.

public interface Search {
TripItem[] searchSynch(TripLeg tripLeg);

@OneWay
void searchAsynch(TripLeg tripLeg);
}

The @OneWay annotation can be applied only to operations that don’t return data. For example, in the Java language, this means operations that have a void return type and have no declared exceptions.

4.5.2. Exploiting one-way interaction

Anyone familiar with using message-oriented middleware will be familiar with the oneway interaction pattern. The calling component uses an SCA reference proxy to send a message to another component and doesn’t wait for a response.

The Hotel component implements the searchAsynch operation using the same logic as the searchSynch operation because both operations return the same results.

public class HotelImpl implements Search{

public void searchAsynch(TripLeg tripLeg) {
TripItem[] items = searchSynch(tripLeg);
...
}
}

Note that we don’t show here what happens with the results. The InteractionOneWayCallbackClientImpl component implementation is also simple:

public class InteractionOneWayCallbackClientImpl implements Runnable {

@Reference
protected Search hotelSearchOneWayCallback;

public void run() {
TripLeg tripLeg = getTestTripLeg();
hotelSearchOneWayCallback.searchAsynch(tripLeg);
...
}
}

With the one-way version of the searchAsynch operation, you can see that no return data is expected by the client. You may be wondering how the results of the search are returned. One option is to configure the component that implements the Search interface so that it can call back to the client component when the search is complete. That’s why the client here is called the InteractionOneWayCallbackClient. We’ll use the same example in the next section to show how callbacks work.

4.6. Callback interaction

SCA defines a model for calling back from target to client components that’s configured by using callback interfaces. The combination of a forward interface with its callback interface is referred to as bidirectional interfaces in the SCA specifications. The full details of the Java language–specific callback programming model are described in chapter 5, but because Tuscany currently supports callbacks in only the Java component implementation, we’ll use some Java code here to aid our description of the general pattern.

Using the callback interaction pattern, a target component can send a response back to the client component asynchronously. The client doesn’t wait for the result; it gets notified when it’s available. For example, a one-way operation doesn’t allow a service to return data to the client component directly. As soon as the request is sent, the client component continues processing. At some point in the future, the target service will process the message. If a result is to be provided back to the client component, then a callback style of message exchange is required.

Even in the case of a request response–style operation, processing may have been kicked off at the target component that requires a call to be made back to the client component at some point in the future. It’s often the case that the immediate response is used to indicate that processing has been successfully started, whereas the real results are delivered later using a callback.

Consider again the Hotel component from the TuscanySCATours application that we used when talking about the one-way pattern. In the TuscanySCATours application the TravelCatalog component, see contributions/travelcatalog, uses the Hotel component, see contributions/hotel, to search for available hotel rooms for a customer’s trip. It may take the Hotel component some time to complete its search, and preferably the TravelCatalog should be notified when the request is processed instead of waiting for a response. In the meantime, the TravelCatalog component starts searching for flights and cars. In figure 4.5 we’ve pulled the Hotel component out of the TuscanySCATours application, and we’ll show an InteractionOneWayCallback-Client component that we’ll use here to demonstrate the callback interaction pattern alongside the one-way interaction pattern. We’ve wired this client component directly to the Hotel component. It’s important to note that there’s only one wire between the two components. The callback is handled by the Tuscany runtime without the need for a separate callback wire.

Figure 4.5. A bidirectional service combines both forward and callback interfaces. The flow of execution is depicted using the dashed line. The InteractionOneWayCallbackClient component makes a request through the forward interface to the Hotel component and continues processing immediately. The Hotel component processes the request and sends the results back via the callback interface.

A component service that implements the callback interaction pattern has two interfaces: the forward interface and the callback interface. The forward interface is implemented by the target component. The callback interface is implemented by the client component to which the future callback will be directed.

Bidirectional service interfaces can be used for both local and remote interactions. In this case, the forward and callback interfaces must be either both local or both remotable.

4.6.1. Configuring callback interaction

To indicate that a component service has a bidirectional interface, the service interface must be annotated with the name of the interface through which the service will perform the callback. The following code shows part of the Java Search service interface, see contributions/common, that the Hotel component implements:

@Remotable
@Callback(SearchCallback.class)
public interface Search {
@OneWay
void searchAsynch(TripLeg tripLeg);
}

The @Callback(SearchCallback.class) annotation indicates that the services that implement this interface are bidirectional and will use the SearchCallback interface to perform their callbacks. The callback interface is a Java interface, as follows:

@Remotable
public interface SearchCallback {
void searchResults(TripItem[] items);
}

There’s nothing special about this interface, but the client component must implement it so that the component service can call back to it.

With this @Callback annotation, the Tuscany runtime has enough information to determine that a normal wire between a reference and a service describes a bidirectional interface without further configuration. The following component descriptions show how the client component and Hotel component could be wired to support a bidirectional interaction if the components were defined with default bindings:

<component name="InteractionOneWayCallbackClient">
<implementation.java class=
"scatours.client.impl.InteractionOneWayCallbackClient" />
<reference name="hotelSearchOneWayCallback" target="Hotel"/>
...
</component>

<component name="Hotel">
<implementation.java class="com.tuscanyscatours.hotel.impl.HotelImpl"/>
</component>

The hotelSearchOneWayCallback reference targets the Hotel component in exactly the same way as the forward-only case. When this composite file is loaded into the Tuscany runtime, the following steps are taken to configure the wire between the InteractionOneWayCallbackClient component and the Hotel component:

  1. The hotel component service endpoint is created using the default SCA binding (binding.sca).
  2. A callback service endpoint is added to the InteractionOneWayCallbackClient component and given the name SearchCallback. This too will use the default SCA binding and is the service that receives callback messages from the target service.

This composite configuration relies on the defaults that SCA assumes to be in force. When it comes time to deploy components in your environment, you may want to control the callback message path in more detail. SCA allows you to configure the interface for the callback service as well as the binding that will be used for callbacks.

Figure 4.6 shows the forward and callback interfaces and bindings and the role they play in bidirectional interaction.

Figure 4.6. The forward and callback interfaces and bindings in bidirectional interfaces. The flow of execution is depicted by the dashed line.

Bidirectional interface configuration is achieved by adding a callbackInterface attribute to the reference and service interface elements. The interface can be described using any of the supported interface styles, currently Java interface and WSDL. The following code shows how a bidirectional interface can be specified in the composite file using the Java interface description style:

<interface.java interface="com.tuscanyscatours.common.Search"
callbackInterface="com.tuscanyscatours.common.SearchCallback"/>

Bidirectional binding configuration is achieved by adding a <callback/> element to reference and service elements in the composite file. The following code snippet shows how to control the forward and callback bindings for a service:

<service name="Search">
<binding.ws />
<callback>
<binding.jms/>
</callback>
</service>

The forward and callback bindings don’t have to match. This can be useful if callback messages need to be separated from forward messages.

At the reference, the configuration takes a similar form. The following code shows how to configure the reference in order to control the forward and callback bindings. Again, the forward and callback bindings can be different:

<reference name="hotelSearchOneWayCallback">
<binding.ws />
<callback>
<binding.jms/>
</callback>
</reference>

In our example the Hotel component is remote and is running in a separate node to the InterfaceOneWayCallbackClient. The web services binding is used for both forward and callback bindings. The component definition for the Hotel component is as follows:

<component name="Hotel">
<implementation.java class="scatours.hotel.HotelImpl"/>
<service name="Search">
<interface.java interface="com.tuscanyscatours.common.Search"
callbackInterface="com.tuscanyscatours.common.SearchCallback"/>
<binding.ws uri="http://localhost:8081/Hotel/Search"/>
<callback>
<binding.ws uri="http://localhost:8080/Client/SearchCallback"/>
</callback>
</service>
</component>

The <interface.java callbackInterface=""/> attribute explicitly describes what the callback interface is expected to be. In this case, the interface is com.tuscanyscatours.common.SearchCallback.

The <callback/> element contains the binding configuration associated with the callback for the Search service. Here both forward and callback messages are delivered using the web services binding. The callback binding description at the service effectively describes a reference to the callback service.

The reference that exploits the bidirectional interface on the Hotel component is configured as follows:

<component name="InteractionOneWayCallbackClient">
<implementation.java class=
"scatours.client.impl.InteractionOneWayCallbackClient" />
<reference name="hotelSearchRemote">
<interface.java interface="com.tuscanyscatours.common.Search"
callbackInterface="com.tuscanyscatours.common.SearchCallback"/>
<binding.ws uri="http://localhost:8081/Hotel/Search"/>
<callback>
<binding.ws uri="http://localhost:8080/Client/SearchCallback"/>
</callback>
</reference>
</component>

Again, both the callback interface and binding are explicitly described. At the reference the callback binding effectively describes a service. At runtime this information is used to create a callback service on the reference component. In Tuscany the name of this service will be the name of the reference that defines it, in this case hotel-SearchRemote. But there’s no need to explicitly refer to the callback service because the runtime ensures that callbacks arrive at the right component.

4.6.2. Exploiting callback interaction

To send a callback message, the service implementation has to obtain a proxy that points back to the calling component. When using a Java implementation, you can retrieve the proxy to the callback service by adding the SCA @Callback annotation to the field into which you want Tuscany to inject the callback proxy.

public class HotelImpl implements Search {

@Callback
protected SearchCallback searchCallback;

public void searchAsynch(TripLeg tripLeg) {
TripItem[] items = searchSynch(tripLeg);
searchCallback.searchResults(items);
}
}

The InteractionOneWayCallbackClient that calls the Hotel component must implement the callback interface, in this case SearchCallback, so that the Hotel component can call back to it.

public class InteractionOneWayCallbackImpl implements Runnable,
SearchCallback{
@Reference
protected Search hotelSearchOneWayCallback;

public void run() {
TripLeg tripLeg = getTestTripLeg();
TripItem[] tripItems = hotelSearchOneWayCallback.searchAsynch(tripLeg);
...
}

public void searchResults(TripItem[] items){
...
}
}

In this example, following the call to searchAsynch, the Hotel component calls back to the searchResults operation on the IteractionOneWayCallbackClient component. It’s then the client component’s responsibility to process the results.

You now have a high-level understanding of how to build bidirectional interfaces and describe component wiring in order to configure callbacks for operations in your application.

At the moment, Tuscany SCA supports the callback mechanism only in the Java implementation type. We’ll describe the details in chapter 5. You can, of course, simulate the callback interaction pattern when it’s not directly supported by Tuscany. To do this you’ll need a service on the target component to which the forward reference can be wired. The client component must also provide a service to which a callback reference from the target component can be explicitly wired. Generation and maintenance of an ID to tie the callback call to a forward call is the responsibility of the application.

Now let’s move on and look at how to correlate a series of messages into a single conversation.

4.7. Conversational interaction

The conversational interaction pattern describes how operations in a service interface are associated with one another. As with callbacks, Tuscany supports the conversational interaction pattern only in Java component implementations. The details of the Java programming model for conversations, including its many related Java annotations, are discussed in chapter 5. Here we’ll omit many of the detailed annotations and take a high-level look to complete our list of interaction patterns.

 

Note

The future of conversational interfaces in the SCA specifications is under discussion. Although there are similar concepts in some other middle-ware technologies, there’s currently no accepted industry standard for conversational semantics. Because of the lack of industry standardization in this area, conversations aren’t included in the SCA 1.1 specifications being defined by OASIS and will be reconsidered for a future version of SCA. They’re supported in the Tuscany SCA 1.x codebase but not in Tuscany 2.x.

 

Using the conversational interaction pattern, a component can process a sequence of messages as part of the same conversation and rely on the Tuscany runtime to start and end the conversation as defined by the service interface. For example, in the TuscanySCATours application, the ShoppingCart component, which can be found in contributions/shoppingcart, adds items to the CartStore component in order to build up the trip package as the customer selects trip items. The CartStore component, which can also be found in contributions/shoppingcart, manages these trip items as part of a single conversation.

To demonstrate conversations we’ve pulled the CartStore component out of the TuscanySCATours application and call it with the InteractionConversationClient. Figure 4.7 shows how the InteractionConversationClient sends multiple messages to the CartStore component in the context of a single conversation.

Figure 4.7. Multiple conversational calls to the CartStore component can be correlated so that each message is processed by the same component instance. The flow of execution is depicted using the dashed lines.

The CartStore component stores the contents of the customer’s shopping cart for the trips the customer is trying to book. A conversation with the CartStore component begins when the first item is added.

As the client adds and removes trips, the Tuscany runtime ensures that messages sent to the customer’s CartStore are all processed in the context of the same conversation. The messages belonging to a conversation may be seconds or days apart. Therefore, the conversation context should be identifiable over the period of time that the conversation is in effect.

The conversation continues, and the client can retrieve the list of trips currently in the store at any time. At some point the customer decides to purchase the items in the shopping cart, the CartStore is emptied, and the conversation ends.

Now let’s look at how to configure conversational interactions in SCA applications.

4.7.1. Configuring conversational interaction

A Java service can take part in a conversation if the service interface is marked with the @Conversational Java annotation. The following listing shows the CartStore service interface.

Listing 4.1. Configuring conversational interaction

The @Conversational annotation is shown just above the interface definition. This means that the first call to any of the interface’s operations will start a conversation. The conversation will continue as the client calls subsequent operations. The conversation ends when the operation annotated with @EndsConversation is called. No extra configuration is required in the composite files in order to use the conversational interaction pattern.

4.7.2. Exploiting conversational interaction

The Tuscany runtime tracks related requests that are part of the same conversation by associating a conversation ID with each request. The conversation ID is used to automatically locate the component instance associated with each ongoing conversation. Because each component instance is associated with only one conversation, the component implementation can use this knowledge to store and retrieve conversation state as it sees fit.

Using a Java language example, a component that’s intended to participate in a conversation must include the @Scope annotation that indicates CONVERSATION scope. CONVERSATION scope ensures that each component instance is associated with a separate conversation ID. Because this annotation is Java language specific, it’s described in more detail in chapter 5. Conversations won’t work if you don’t configure your service with CONVERSATION scope because multiple threads for different conversations could be passing through the same component instance, and hence the state of the component instance couldn’t be tied to a single conversation.

The following code shows how the CartStore component implementation uses CONVERSATION scope.

Listing 4.2. The CartStore component implementation with CONVERSATION scope

When a message arrives at a CONVERSATION-scoped component, the Tuscany runtime automatically uses the conversation ID contained in the incoming message to locate the correct instance of the target component. It then passes the incoming message to this instance. This process is repeated as each message arrives for the conversational component until the conversation ends and the component instance is removed.

The writer of the component implementation needn’t be concerned with how the conversation is identified. The conversation ID is available in the Java component implementation if required, using the @ConversationID annotation. See chapter 5 for details.

The Tuscany runtime doesn’t persist any conversational state or conversation IDs automatically. If the runtime is stopped or crashes, then conversational state will be lost. It’s the responsibility of the application developer to persist conversational state as it accumulates, if required.

In a component implemented using the Java language, you can use your favorite Java persistence technology to store a conversational component instance’s state data. For example, you could use JDBC to store all component member data each time it changes. In that way, if the runtime is stopped, you can arrange for the state to be read back from the database when the application is restarted.

You now know how to treat a sequence of messages as a single conversation. It’s important to point out that neither the conversational nor the callback interaction patterns preclude you from passing conversational and callback-style information along with application data in order to implement these patterns manually if you prefer. The Tuscany runtime supports these patterns but doesn’t mandate their use.

4.8. Summary

Tuscany and SCA are all about allowing you to describe services and wire them together into composite applications. In this chapter you’ve seen that SCA and Tuscany provide a great deal of flexibility in how such applications can be constructed.

You can control the location of components involved in the composite application. They can be local, in the same process, or remote. SCA components can interact with one another either synchronously or asynchronously, depending on your business requirements.

When the asynchronous pattern is appropriate, you have the choice of using oneway interfaces and callbacks to manage the way that messages are exchanged.

Finally, conversations allow a component to relate one incoming message to another so that state can accumulate as a business process runs and messages are exchanged between components.

The interaction patterns described in this chapter provide a common base for all SCA composite applications, and you’ll notice that they’re used in various examples throughout the book. The Java implementation type, implementation.java, has the best-developed support for the various interaction patterns we’ve discussed here. We’ll cover the details of how to control each interaction pattern in components implemented using the Java language in the next chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.248.239