Chapter 5. Implementing components using the Java language

This chapter covers

  • SCA Java annotations
  • Java interfaces for services and references
  • Services, references, and properties in Java implementations
  • Component scope
  • Callbacks and conversations
  • Passing SCA service references
  • Error handling

Each component in a Tuscany SCA composite application is implemented using an implementation type. The SCA specifications define a number of implementation types, for example:

  • implementation.java
  • implementation.spring
  • implementation.bpel

The Tuscany project has also added a few more, for example, implementation.script.

In this chapter we’ll focus on implementation.java. The other implementation types mentioned will be discussed in chapter 6.

The SCA Java Component Implementation specification defines implementation.java (http://www.osoa.org/download/attachments/35/SCA_JavaComponentImplementation_V100.pdf). This implementation type allows application developers to use new or existing Java classes and interfaces to implement SCA components. These components can then be wired with other components, either locally or remotely, to form a composite application.

In this chapter we’ll explain how to declare a Java component in an SCA composite file, how to write a Java class and interface with SCA annotations, and how to map a Java class and interface into an SCA component type. We’ll describe the Java language–specific details of the interaction patterns we discussed generally in chapter 4, and we’ll finish this chapter with some discussion of error handling.

You’re probably somewhat familiar with the Java implementation type because we’ve already used it in earlier chapters in this book. Here we’ll be bringing together all the Java language–specific information in one place, so you’ll see some things in this chapter that we’ve already said. Because there’s a lot of information, you may want to skip some of the more advanced sections the first time through. For example, the callback and conversational patterns have been covered at a high level in chapter 4. Until you need to know the details, you can safely skip over them.

The samples we’ll use in this chapter are primarily based on the payment-related components from the full travel-booking application. The payment components deal with payment processing once the user has decided to buy the trips held in the shopping cart. You haven’t come across these components yet because they’re not included in the introductory version of the travel-booking application in chapter 1. You’ll get to know them in some detail in this chapter and the next. Let’s start by reviewing the basics of how you’ll define a Java language–based component implementation.

5.1. Defining a Java component implementation

Defining a Java component implementation is simple. We’ll use the Payment component from the TuscanySCATours application as an example. In its simplest form the Payment component is defined using the implementation.java element, which references the Java class com.tuscanyscatours.payment.impl.PaymentImpl. The following snippet, from the composite file in the payment-java sample contribution, shows how the component is defined and made available in the Payment composite:

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
targetNamespace="http://tuscanyscatours.com"
name="payment">

<component name="Payment">
<implementation.java
class="com.tuscanyscatours.payment.impl.PaymentImpl" />
</component>

</composite>

Figure 5.1 shows how the Payment component Java implementation fits, in relation to SCA services, references, and properties.

Figure 5.1. The Java implementation (PaymentImpl) provides the business logic for a component, providing services and using references and properties.

In the TuscanySCATours application, the Payment component collaborates with other components. Figure 5.2 shows a composite application that consists of the Payment component connected to other components, each implemented using implementation.java.

Figure 5.2. The Payment Java component connected to the other components that it depends on

The CustomerRegistry component looks up customer payment information based on the customer’s ID, the CreditCardPayment component handles the payment itself, and the EmailGateway component notifies the customer of the payment status.

We’ll use this example throughout this chapter and the next to demonstrate how the various implementation types work. The TuscanySCATours application demonstrates the Java language version of the Payment component, along with the CustomerRegistry and EmailGateway components, in the payment-java contribution. The CreditCardPayment component is described in the creditcard-payment-jaxb contribution. Not all of the features of the Java implementation type have matching sample code, but this chapter includes code snippets based on the sample Payment component.

The following code snippet shows the configuration of the Payment component as it appears in a composite file:

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
targetNamespace="http://tuscanyscatours.com"
name="payment">

<component name="Payment">
<implementation.java
class="com.tuscanyscatours.payment.impl.PaymentImpl" />
<service name="Payment">
<binding.ws uri="http://localhost:8081/Payment" />
<binding.sca />
</service>
<reference name="customerRegistry" target="CustomerRegistry"/>
<reference name="creditCardPayment" target="CreditCardPayment">
<binding.ws uri="http://localhost:8082/CreditCardPayment" />
</reference>
<reference name="emailGateway" target="EmailGateway"/>
<property name="transactionFee">0.02</property>
</component>
...

</composite>

Let’s move on and use the payment example to look at how Java annotations can be added to a Java class to define SCA services, references, and properties for the Payment component. You can use different styles to do this, and we’ll look at these styles in the following sections.

5.2. Using SCA annotations in Java implementations

A component type describes the shape of a component in terms of the services it provides and the references and properties that it uses. SCA defines several Java annotations that allow the component implementation developer to describe the component type easily. The annotations are defined in the SCA Java Common Annotations and APIs specification (http://www.osoa.org/download/attachments/35/SCA_JavaAnnotationsAndAPIs_V100.pdf).

In our example, the PaymentImpl class implements the Payment interface and provides the business logic for the Payment component. The Payment interface is generated from Payment.wsdl, using code in the pom.xml or build.xml files, and can be found in the payment-java/target/jaxws-source directory. Listing 5.1 shows the source code for PaymentImpl. The annotations @Service, @Reference, and @Property are used to identify services, references, and properties. The fields associated with @Reference and @Property annotations will be initialized automatically by the Tuscany runtime.

Listing 5.1. Implementation class for the Payment component
@Service(Payment.class)
public class PaymentImpl implements Payment {
@Reference
protected CustomerRegistry customerRegistry;
@Reference
protected CreditCardPayment creditCardPayment;

@Reference
protected EmailGateway emailGateway;

@Property
protected float transactionFee = 0.01f;

public String makePaymentMember(String customerId, float amount) {
Customer customer = customerRegistry.getCustomer(customerId);
String status = creditCardPayment.authorize(customer.getCreditCard(),
amount + transactionFee);
emailGateway.sendEmail("[email protected]",
customer.getEmail(),
"Status for your payment",
customer + " >>> Status = " + status);
return status;
}
}

Figure 5.3 gives a pictorial representation of how the PaymentImpl class uses SCA Java annotations to define SCA services, references, and properties. In this figure, @Service is used to describe Payment as an available service. The @Reference annotation is used to describe the dependency of the Payment component on the CreditCardPayment service. The @Property annotation is used to describe how property values in the business logic can be set from the composite file. The rest of the PaymentImpl class contains business logic to implement the business process.

Figure 5.3. Mapping the Java implementation class to an SCA component type using SCA annotations

You now have the basic idea of how a Java class can be used to implement an SCA component. One thing you may have noticed is that services and references in the Java implementation are described using Java interfaces. Let’s look in more detail at how Java interfaces are used.

5.3. Services and references with Java interfaces

Each SCA component has a formal contract, called the component type, that defines its service, references, and properties. Tuscany can derive the component type directly from the component’s Java implementation rather than having to specify it manually.

In this section we’ll look at the role of Java interfaces in defining the component type, and we’ll start by looking at the difference between local and remote services.

5.3.1. Identifying local and remote services

A composite application can consist of components that run in the same JVM or that communicate remotely across the network. You may remember from chapter 4 that SCA allows component service interfaces to be defined as local or remote. A Java interface is local by default and describes a component that must be running within the same JVM as the reference that calls it. The following snippet shows a local Java interface:

public interface CustomerRegistry {
Customer getCustomer(String id);
}

Remotable interfaces can also be used for components running in the same JVM, but they’re flexible enough to be distributed over the network too. The data exchange semantics for remotable interfaces is pass-by-value. You can annotate an interface with @Remotable to denote that it’s a remotable interface. The following snippet shows a remotable Java interface:

@Remotable
public interface EmailGateway {
public boolean sendEmail(String sender,
String recipient,
String subject,
String body);
}

In addition to Java interfaces that are annotated with @Remotable, the Java language itself defines certain patterns for interfaces that are considered remotable, for example, the javax.rmi.Remote interface in RMI.

Any object that’s a remote RMI object must directly or indirectly implement this interface. Another example is a Java interface that’s annotated with the JAX-WS annotation @javax.jws.WebService to indicate that the interface defines a Web Services interface that’s remotable.

 

RMI

The Java Remote Method Invocation API (or RMI) is an API that allows Java applications to invoke operations on objects in another JVM that have been exposed as RMI services. RMI is provided as a core part of the Java language.

 

 

JAX-WS

JAX-WS stands for Java API for XML Web Services. It defines rules and Java annotations for mapping Web Services to Java classes and vice versa. You can read the specification at http://jcp.org/en/jsr/detail?id=224. You don’t need to be an expert in JAX-WS or associated technologies such as JAXB and the various WS-* standards in order to use web services with Tuscany. However, gaining some background knowledge from the many online sources will help you solve any problems that arise as you build and deploy applications. In the TuscanySCATours sample we’ll mainly use JAX-WS for generating Java interfaces from WSDL files. For example, if you look at the contributions/ payment-java/pom.xml file, you can see the jaxws-maven-plugin configured to generate Java classes based on the Payment.wsdl and CreditCardPayment.wsdl files. If you prefer to use Ant, the build.xml file uses a wsimport target that’s been built for the sample and can be found in the antdefs.xml file in the sample’s root directory. JAX-WS is built directly into the Java 6 JDK but not the Java 5 JDK. If you’re using Java 5, then the TuscanySCATours sample ships with the JAX-WS JARs in the lib/jaxws directory, and the various build scripts will look there if required.

 

In SCA, we automatically treat the Java interfaces that follow such protocol-specific remote patterns as remotable interfaces. For example, if you already have a JAX-WS—style interface, such as that shown in the following listing, then the Tuscany runtime will treat it as a remotable SCA service interface.

Listing 5.2. A remotable interface generated from a WSDL by JAX-WS: payment.Payment
@WebService(name = "Payment",
targetNamespace = "http://www.tuscanyscatours.com/Payment/")
@XmlSeeAlso({ObjectFactory.class})
public interface Payment {
@WebMethod(action = "http://www.tuscanyscatours.com/Payment/makePayment")
@WebResult(name = "Status", targetNamespace = "")
@RequestWrapper(localName = "makePaymentMember",
targetNamespace = "http://www.tuscanyscatours.com/Payment/",
className = "payment.MakePaymentMemberType")
@ResponseWrapper(localName = "makePaymentMemberResponse",
targetNamespace = "http://www.tuscanyscatours.com/Payment/",
className = "payment.MakePaymentMemberResponseType")
public String makePaymentMember(
@WebParam(name = "CustomerId", targetNamespace = "")
String customerId,
@WebParam(name = "Amount", targetNamespace = "")
float amount);
}

This looks a little complicated but will be familiar to those used to Java interfaces generated according to JAX-WS rules. The point here is that you don’t have to use SCA annotations to make a Java interface remotable.

It’s also important to understand that remotable interfaces don’t mean that the components have to be deployed remotely. They can run just as well within a local composite assembly. We often start by testing with a local assembly of components that are then deployed to a distributed environment later on.

5.3.2. Implicit and explicit definition of component interfaces

When we show examples of component definitions from composite files, we usually show the form that assumes that a component’s service and reference interfaces are derived implicitly from the implementation. For example, let’s assume our Payment component is being provided over Web Services and, in turn, accesses other services over Web Services.

<component name="Payment">
<implementation.java
class="com.tuscanyscatours.payment.impl.PaymentImpl" />
<service name="Payment">
<binding.ws/>
</service>

<reference name="creditCardPayment">
<binding.ws uri="http://localhost:8082/CreditCardPayment"/>
</reference>
...
</component>

The Java class PaymentImpl implements the Java interface Payment, and that, in turn, describes the component service interface. The Java type of the reference field creditCardPayment describes the component reference interface in this example. The default behavior is equivalent to the following composite definition:

<component name="Payment">
<implementation.java
class="com.tuscanyscatours.payment.impl.PaymentImpl" />
<service name="Payment">
<interface.java interface="com.tuscanyscatours.payment.Payment" />
<binding.ws/>
</service>
<reference name="creditCardPayment">
<interface.java interface=
"com.tuscanyscatours.payment.creditcard.CreditCardPayment" />
<binding.ws />
</reference>
</component>

It’s sometimes the case that the technology used to describe the component implementation’s interfaces is different from that used to describe the SCA component’s service and reference interfaces. Imagine that a WSDL description has been generated by hand, or by a tool such as Eclipse, to describe the Payment component service interface. This WSDL can be referenced explicitly from the composite file as follows:

<service name="Payment">
<interface.wsdl interface="http://.../Payment/#wsdl.interface(Payment)"/>
<binding.ws/>
</service>

 

Mapping between Java and WSDL interfaces

WSDL provides an XML-centric description of a service interface and the operations it contains. The data that passes into and out of the service operations is represented using XML types. Operations in a Java interface can be mapped to operations in a WSDL interface according to the rules described in the JAX-WS specification (http://jcp.org/en/jsr/detail?id=224). This, in turn, implies the use of the JAXB (http://jcp.org/en/jsr/detail?id=222) rules for mapping Java objects that appear in operation signatures to XML schema. At its simplest, primitive Java types map to XML schema simple types while complex Java types, JavaBeans or collections, map to XLM schema complex types. There are complexities in the mapping, however, depending on what style of WSDL you choose. Many good online resources discuss the topic of Java-to-WSDL mapping. Also, chapter 9 describes in more detail the runtime transformations Tuscany can perform between WSDL-defined XML types and Java.

 

Internally the Tuscany runtime holds a custom model of a service’s interface that’s neither WSDL nor a Java interface. It’s able to convert both Java interfaces and WSDL descriptions to this internal format and check that the interfaces are compatible. When converting between Java interfaces and WSDL service interfaces, the Tuscany runtime applies the default Java interface and WSDL mapping rules described by JAX-WS.

5.3.3. Interface compatibility and mapping

For a component service and implementation interface to be compatible, the component service interface must describe a compatible subset of the operations that the component implementation interface provides. This means that an implementation may provide some operations that the SCA service doesn’t make available for use by other components. For example, if our Payment component implementation interface exposes operations for handling payments from both members and nonmembers, we may want to configure our component’s service interface so that only the operation that accepts payments from members is exposed. Continuing with our WSDL example, let’s assume we’ve built a WSDL description with only a paymentMember operation. This WSDL can be referenced from a interface.wsdl element added to the Payment service element of the Payment component description, as we did in the previous section. The Payment service will then accept only calls to the paymentMember operation.

The same approach holds for references. References within the Java implementation will be described using Java interfaces. But the SCA reference could be described using WSDL. For example, a WSDL file may have been created by the credit card payment processing company in order to describe the remote services they provide. They may have generated the WSDL by hand or used a WSDL generation tool. The WSDL can be used to configure the component reference that will connect to the CreditCardPayment service.

<reference name="creditCardPayment">
<interface.wsdl interface=
"http://.../CreditCardPayment/#wsdl.interface(CreditCardPayment)"/>
<binding.ws />
</reference>

Again, the Java interface and the WSDL interface are both mapped to Tuscany’s interface representation, and the runtime checks that the component reference interface describes a compatible superset of the operations described by the implementation reference interface. To put it another way, the remote service may provide some operations that the implementation never calls. If the interfaces don’t match, the Tuscany runtime will raise an error when the application is started.

5.3.4. Transforming messages between interfaces

A Java implementation may use any number of technologies to describe the data that flows in and out of the interface operations that the implementation supports. For example, you could build a Java component implementation that uses JAXB, SDO, or POJOs to describe interface operation parameters and return types.

 

Data formats for parameters and return types

There are many different ways to represent structured data in the Java language. You could use a plain old Java object (POJO). Alternatively, you could use a technology designed to represent the data and map it to XML. Java Architecture for XML Binding (JAXB) and Service Data Objects (SDO) are examples of this kind of technology.

 

Messages passing between components often need to be converted from one form to another in order to be passed out to or received from the network by an SCA binding.

Fortunately, Tuscany defines a databinding framework that takes care of this data format transformation. In this way, the Java implementation deals entirely with familiar data objects and is insulated from any data formats that are required by the Tuscany bindings. The databinding framework is described in chapter 9. Here we’ll focus on how data is handled within the Java implementation type.

5.3.5. Pass-by-reference and pass-by-value

By default, data is exchanged between remotable service interfaces using pass-by-value semantics. Pass-by-value means that input data is copied between reference and service, and any changes that a service makes to the input data are not automatically seen by the referencing component. Changes will be seen by the referencing component only if the service explicitly returns the changed data as output data. To make this work, the Tuscany infrastructure has to ensure that the data is copied between reference and service.

In some cases, the called component implementation knows it won’t change the request data, and the client is free to change the response data. To allow the runtime to optimize data exchanges, you can annotate the implementation class or methods with @AllowsPassByReference. This indicates to the runtime that the data can be safely passed by reference over the remotable interface, for example:

@AllowsPassByReference
public class PaymentImpl {

public boolean makePayment(Customer customer, float charge) {
...
}
}

The runtime is able to use this information to optimize communications when two connected components are running in the same JVM. When communicating remotely from one JVM to another, communications will always be pass-by-value.

Now that you know how Java interfaces are exploited generally, let’s look in detail at how the services that an SCA component provides are defined.

5.4. Java component services

The Java implementation class for an SCA component can provide one or more SCA services. In this section, we’ll describe how SCA services can be declared in the implementation class, how to define an interface for the service, and how to derive the name of the service from its interface.

5.4.1. The @Service annotation

The @Service annotation can be used with a Java class to explicitly define the SCA services provided by a component. The following snippet defines an SCA service whose interface is CustomerRegistry:

@Service(CustomerRegistry.class)
public class CustomerRegistryImpl implements CustomerRegistry {
...
}

If there’s more than one service, the interface’s parameter for @Service can be used to list all the services by including each interface as an element in an array.

@Service(interfaces={CustomerRegistry.class, AnotherService.class})
public class CustomerRegistryImpl implements CustomerRegistry,
AnotherService {
...
}

The @Service annotation isn’t the only way to identify services for a Java component. Let’s look at the alternatives.

5.4.2. Alternatives to the @Service annotation

When the @Service annotation isn’t present, the list of interfaces implemented by the Java class will be introspected by Tuscany to find out if there are any remotable interfaces. Each remotable interface will become an SCA service described by that interface. You’ve seen previously that the Payment interface is a remotable interface. Here Payment is recognized as a service because it’s implemented by the PaymentImpl class and is remotable:

public class PaymentImpl implements Payment, ... {
...
}

If no remotable interfaces are found, or the class doesn’t implement any interfaces, then a single SCA service is assumed whose interface, and consequently the service name, is the implementation class. In the following snippet, the SCA service name is CustomerRegistryImpl (not CustomerRegistry) and all of the methods in the CustomerRegistryImpl class are available as operations on the service interface. This is because, although CustomerRegistryImpl does implement the CustomerRegistry interface, the interface isn’t remotable.

public class CustomerRegistryImpl implements CustomerRegistry {
...
}

 

Service naming gotcha

This often catches the new user. You’re happily creating components that implement remotable Java interfaces. You naturally, and correctly, use the name of the interface as the name of the component’s service. Then you implement a component using a nonremotable interface, and all of a sudden the runtime reports errors. In this case, the service name is the name of the implementation class and not the interface.

 

For Java components without an @Service annotation, the name of the service is formed from the local (last) part of the interface name. For example, if the interface is com.tuscanyscatours.payment.Payment, then the service name is Payment.

Table 5.1 gives a summary of the various ways to declare SCA services in Java implementations.

Table 5.1. Ways to declare SCA services in Java implementations

Implementation class

Service name

@Service(Payment.class)
public class PaymentImpl
implements Payment {
...
}
Payment
public class PaymentImpl
implements Payment {
...
}
Payment (Payment interface is remotable.)
@Service(Payment.class)
public class PaymentImpl {
...
}
Payment
@Service(CustomerRegistry.class)
public class CustomerRegistryImpl
implements CustomerRegistry {
...
}
CustomerRegistry
public class CustomerRegistryImpl
implements CustomerRegistry {
...
}
CustomerRegistryImpl (CustomerRegistry interface is local.)

Now that you’ve seen the various ways that the Java implementation can be mapped to SCA component services, let’s look at how SCA component references are handled.

5.5. Java component references

A Java component often depends on other services. We’ll need to declare SCA references in order to exploit these other services. An SCA reference has the following attributes:

  • name—The name that uniquely identifies the reference in the component
  • multiplicity—Controls how many target services can be associated with the reference
  • interface—The interface of the reference
  • target—One or more services that the reference will be wired to

Reference targets are configured in the composite file rather than from within the component implementation. We discussed this already in chapter 2, and the advantage is that the target can be changed without having to change the component implementation code. You identify references in the Java implementation, and Tuscany ensures that each reference proxy is connected to the correct service.

The interface of a reference has to be compatible with the service to which it’s connected. We already discussed interfaces in section 5.3. With the Java implementation, the reference interface type can be introspected from the implementation. To do this, the Tuscany runtime looks for those fields annotated with @Reference. Let’s start by looking at how this annotation works.

5.5.1. The @Reference annotation and reference injection

A referenced service needs to be found before it can be called from within a referencing component’s implementation. The Tuscany runtime injects the proxy for each reference service when it detects the @Reference annotation. The @Reference annotation also allows the Tuscany runtime to determine the reference interface type and the type of proxy required. The @Reference annotations can be used in three different styles. You can mix the following styles in your component implementation, and in all three cases the reference interface here is CreditCardPayment.

  1. A public or protected field— The service proxy will be injected directly into the field.
    @Reference
    protected CreditCardPayment creditCardPayment;
  2. A public or protected setter method— The service proxy will be injected by calling the setter method.
    @Reference
    public void setCreditCardPayment(CreditCardPayment creditCardPayment) {
    ...
    }
  3. A parameter on the class constructor— The service proxy will be injected as a parameter when the object is instantiated using the constructor.
    @Constructor({"creditCardPayment", ...})
    public PaymentImpl(@Reference CreditCardPayment creditCardPayment, ...) {
    ...
    }

Style 1 allows the injection of the service proxy without other logic. Both style 2 and style 3 allow code to perform additional checking when the service proxy is injected.

In style 2, the service proxy can be reinjected via the setter method after a component implementation has been created, whereas styles 1 and 3 make the SCA reference immutable.

The Tuscany runtime doesn’t currently take advantage of the ability to reinject references because the domain implementation is static and references aren’t changed once a component instance is created. The intention is to provide more dynamic domain support in the future.

5.5.2. Reference naming

Each reference has a unique name in the component. The name can be explicitly specified by the name attribute of the @Reference annotation, for example:

@Reference(name="creditCardPayment")
protected CreditCardPayment ccPayment;

This explicitly declares an SCA reference named creditCardPayment. If the name attribute isn’t present, then Tuscany derives the reference name from the name of the field or method where @Reference is declared.

@Reference
protected CreditCardPayment creditCardPayment;

The reference name is again creditCardPayment and is derived from the name of the creditCardPayment field. If a setter method is marked as a reference but without an explicit reference name, the reference name is derived from the available information.

@Reference
public void setCreditCardPayment(CreditCardPayment creditCard);

Again, the reference name is creditCardPayment. This is the property name for the setter method as defined by the JavaBeans specification. The set prefix is trimmed from the method name, and the first letter is converted to lowercase unless the second letter is uppercase too. Here C becomes c.

5.5.3. Reference multiplicity

Each SCA reference can potentially be wired to any number of target services. This is controlled by the multiplicity attribute on the reference element in the composite file:

  • Exactly one service provider (multiplicity=1..1)
  • An optional service provider (multiplicity=0..1)
  • At least one service provider (multiplicity=1..n)
  • Any number of service providers (multiplicity=0..n)

When using implementation.java, the lower bound of the SCA reference multiplicity is determined by the required attribute of the @Reference annotation, for example:

@Reference(required=true)

If required is true, then the lower bound of the multiplicity is 1; else it’s 0. If the required attribute isn’t specified, the required value defaults to true.

The upper bound is controlled by the Java type. If the Java type is a subclass of java.util.Collection or an array, the upper bound is n; otherwise the upper bound is 1.

Table 5.2 summarizes the range of reference multiplicities that can be described.

Table 5.2. The mapping between @Reference and reference multiplicity

@Reference declaration

SCA reference

Multiplicity

@Reference(required=false)
protected OtherService
optionalService;
Name: optionalService Interface: OtherService 0..1
@Reference(required=true)
protected OtherService
requiredService; or @Reference
protected OtherService
requiredService;
Name: requiredService Interface: OtherService 1..1
@Reference(required=false)
protected Collection<OtherService>
optionalServices; or @Reference(required=false)
protected OtherService[]
optionalServices;
Name: optionalServices Interface: OtherService 0..n
@Reference(required=true)
protected Collection<OtherService>
requiredServices; or @Reference(required=true)
protected <OtherService>
requiredServices;
Name: requiredServices Interface: OtherService 1..n

You now know how to declare references in the Java implementation class. Let’s move on and look at properties.

5.6. Java component properties

A flexible business service needs to adapt to business changes. For example, a catalog can list items using different currencies, or a discount rate can be changed based on prevailing business conditions. SCA allows business components to present configurable attributes as properties. An SCA property has the following attributes:

  • name—The name of a property that uniquely identifies the property in the component.
  • type—The type for the property.
  • many—Controls whether multiple values can be supplied for the property.
  • mustSupply—Indicates whether a value has to be supplied.
  • value—The value of the property that’s typically configured in the composite file. It can be changed in the composite file without modifying the Java class.

The property name, type, many, and mustSupply values are usually defined in the component implementation, whereas the property value is supplied in the composite file, for example:

<component name="Payment">
<implementation.java
class="com.tuscanyscatours.payment.impl.PaymentImpl" />
...
<property name="transactionFee">0.02</property>
</component>

This means that property values can be set at the time the composite files are created, and there’s no need to change a component’s implementation in order to set property values.

In the following subsections we’ll show how properties are identified, named, and typed and also look at how property value multiplicity is handled. We’ll start by looking at identifying properties in Java component implementations using the @Property annotation.

5.6.1. The @Property annotation and property injection

As with references, SCA and Tuscany provide various methods for declaring a property in the Java implementation class:

  1. A public or protected field— The Tuscany runtime will inject the value of the property that’s specified in the composite into the component implementation.
    @Property
    protected float transactionFee;
  2. A public or protected setter method— The Tuscany runtime will inject the property value by calling the setter method.
    @Property
    public void setTransactionFee(float transactionFee) {
    ...
    }
  3. A parameter on the class constructor— The service proxy will be injected as a parameter when the object is instantiated using the constructor.
    @Constructor({"transactionFee", ...})
    public PaymentImpl(@Property float transactionFee, ...) {
    ...
    }

As with references, you can mix these styles within a component implementation. Style 1 allows properties to be injected without the implementation developer needing to write any extra code. Styles 2 and 3 allow the implementation developer to add additional code to the injection process.

Styles 1 and 3 inject immutable properties, because they’re injected once when the component instance is created. The point at which a component instance is created depends on the scope of the component, for example, a STATELESS scoped component instance is created each time a message arrives. Component scope is discussed later in section 5.7. Style 2, in theory, allows property values to be reinjected while the component instance is running, although the Tuscany runtime currently doesn’t exploit this feature.

5.6.2. Property naming

Each property has a unique name in the component. The name can be explicitly specified by the name attribute of the @Property annotation, for example:

@Property(name="transactionFee")
protected float txFee;

This declares an SCA property named transactionFee. If the name attribute isn’t present, then the name is derived from the field or method where @Property is declared.

@Property
protected float transactionFee;

Again the property name is transactionFee, which is the name of the transactionFee field. If a setter method is marked as a property but without an explicit property name, the property name is derived from the available information.

@Property
public void setTransactionFee (float transactionFee);

The property name is again transactionFee based on the property name for the setter method as defined by the JavaBeans spec. The set prefix is trimmed from the method name, and the first letter is converted to lowercase. Here T becomes t.

5.6.3. Property types

The Java type of a property has to be able to map into an XML type. The precise details of this mapping depend on which mapping is in force for a particular component property. By default Tuscany assumes a JAXB mapping, and you can find the details of this in the JAXB specification (http://jcp.org/en/jsr/detail?id=222). In general, simple types, such as int, float, or String, can be directly mapped into simple XML types. Our payment example has already shown a float type being specified in a Java field and being set in the composite file.

Complex XML types will map to a JavaBean. We described in chapter 2 how complex XML structures can be used to set properties in the composite file, so we won’t repeat the details here. It’s worth noting, though, that in a Java implementation you can represent complex typed properties using any of the Java object technologies for which Tuscany supports an XML-to-Java transformation, such as JAXB or SDO. Chapter 9 discusses the databinding layer and how these transformations work.

5.6.4. Property value multiplicity

A property can be single valued or many valued. A many-valued property is represented in the Java component implementation using a collection class. A property can also be required or optional. This is controlled by the mustSupply attribute in the composite file. In a Java implementation, the required attribute of the @Property annotation specifies whether mustSupply is set or not. Table 5.3 shows the various forms of the @Property annotation and the corresponding property multiplicity.

Table 5.3. The mapping between @Property and SCA component property

@Property declaration

mustSupply

SCA component property

Many valued

@Property(required=false)
protected String
optionalProperty; or @Property
protected String
optionalProperty;
false Name: optionalProperty Type: String false
@Property(required=true)
protected String
requiredProperty;
true Name: requiredProperty Type: String false
@Property(required=false)
protected Collection<String>
optionalProperties; or @Property(required=false)
protected String[]
optionalProperties;
false Name: optionalProperties Type: String true
@Property(required=true)
protected Collection<String>
requiredProperties; or @Property(required=true)
protected String[]
requiredProperties;
true Name: requiredProperties Type: String true

To configure a many-valued property in the composite file, you’ll need to set the many attribute to true and provide multiple whitespace-separated values inside the property element, for example:

<property name="validCurrencies" many="true" type="xsd:string">
"USD" "GBP" "EUR"
</property>

Note that the set of whitespace characters used for separating property values includes spaces, tabs, line feeds, and carriage returns but doesn’t include commas.

A property with a complex XML type can be many valued as well. It’s a matter of including multiple complex values in the property element.

So far in the implementation.java section, you’ve learned how to declare SCA services, references, and properties using SCA annotations. Now we’ll look at how to control the creation of the component instances that use these constructs.

5.7. Java component instance creation and scope

The business logic for a Java component may have to manage its state. State management is directly related to how component instances are created and controlled. SCA defines a number of different component scopes that control when new instances of components are created and when they’re removed.

In the following subsections we’ll show how to control the scope of a component instance and how to execute code when a component instance is created and destroyed. Let’s start by looking at how to specify a component’s scope using the @Scope annotation.

5.7.1. Stateless, composite, and conversational scopes

To help Java language developers manage implementation states, SCA introduces the implementation scope concept. In the Java language you can annotate the implementation class with @Scope to tell the SCA runtime how to manage instances of a component’s Java implementation class. Note that other application frameworks also define @Scope annotations, for example, the Spring framework. It you’re using Tuscany in combination with such technologies, you’ll need to look for potential conflicts and qualify the Tuscany Scope annotation with its full namespace, org.osoa.sca.annotations.Scope, as required. Table 5.4 shows the different scopes that can be used in the Tuscany runtime.

Table 5.4. The implementation scopes supported by the Tuscany runtime

Scope

Description

STATELESS Create a new component instance on each call.
COMPOSITE Create a single component instance for all calls.
CONVERSATION Create a component instance for each conversation.

Using the STATELESS scope, a different implementation instance will be created to service each request. Implementation instances will be newly created automatically by the Tuscany runtime each time the component is called. We can imagine a future enhancement of the Tuscany runtime where instances are drawn from a pool of instances for performance reasons, but Tuscany doesn’t do this at the moment. A stateless Java component is usually good for scalability because there’s no overhead required for state maintenance.

For example, the CreditCardPayment component receives all of the data it needs to complete its processing as input parameters and doesn’t need to maintain any state from one call to the next.

@Scope("STATELESS")
public class CreditCardPaymentImpl implements CreditCardPayment {
...
}

The CreditCardPaymentImpl class will be guaranteed by the SCA runtime to be thread safe because concurrent requests from different threads won’t be dispatched to the same object instance. To put it another way, a new component instance is created for each thread.

The CustomerRegistry component has a different requirement. It needs to access a database to work with customer data. To ensure data consistency and leverage caching, it’s desirable that a single implementation instance be used to serve all the requests in the containing composite.

@Scope("COMPOSITE")
public class CustomerRegistryImpl implements CustomerRegistry {
...
}

Note that the implementation of a COMPOSITE-scoped component must be designed to be thread safe because all requests to the component will go through the same component instance.

If you need a COMPOSITE-scoped implementation instance to be created when its containing composite is started, that is, when the Tuscany node that’s running it is started, you can annotate the class with @EagerInit. For example, connecting to a database and loading some data into memory might be slow. You can immediately perform database connection processing when the component instance is created and before any requests arrive.

The third scope is CONVERSATION. Conversational interactions were described in chapter 4 and represent a series of correlated interactions between a client and a target service. The same implementation instance of a CONVERSATION-scoped component will be used throughout the lifetime of a conversation.

You may be wondering if there’s a way you can do some processing whenever a Java component instance is created or destroyed. Let’s look at that next.

5.7.2. Interacting with component instance creation and destruction

The SCA runtime allows special processing to happen when a scope begins and ends. To act on these lifecycle events, the application code can denote two special methods with @Init and @Destroy.

@Service(CustomerRegistry.class)
@Scope("COMPOSITE")
@EagerInit
public class CustomerRegistryImpl implements CustomerRegistry {
@Init
public void init() {
...
}

@Destroy
public void destroy() {
...
}
}

In this code snippet, the init() operation will be called after the CustomerRegistryImpl class is instantiated. We could have code here to connect to the customer database and prefetch some information into a cache. In the destroy() operation, we can then dispose of the cache so that memory is freed up.

We’ve now looked at the basics of constructing a Java component implementation. Now we’ll look at how the implementation code interacts with the services that references are wired to. Chapter 4 describes a number of interaction patterns, and we’ll discuss how both callbacks and conversations are controlled using the APIs available to a Java implementation. Let’s start by looking at callbacks.

5.8. Making callbacks

In chapter 4 you saw how the callback interaction pattern can be used by a component to make callbacks to a client that has invoked a service of the component. If you don’t need to know the details of how to use callbacks with Java components at the moment, you can safely skip forward to section 5.9.

For the Java implementation type, a service makes a callback using a callback proxy. Callback proxies can be used by service implementations to make service calls in a similar way to the reference proxies we already described. The important difference is that a callback proxy’s destination is selected at runtime based on a previous call to the service, whereas a reference proxy’s destination is selected once at node start time based on reference wiring and binding configuration.

To further illustrate how callbacks can be used, we’ll add a callback to our Payment example. The CreditCardPayment service might need to perform additional security checks to authorize certain payments. For example, for large transactions, the credit card company might need to request an additional security password from the customer before approving the transaction. We can use an SCA callback for this password request.

In the following sections we’ll use this Payment callback example to explore various aspects of the callback programming model that are specific to Java implementations. We’ll show how callbacks are declared using bidirectional interfaces, and we’ll review how bidirectional services obtain callback proxies by injection. We’ll look at how bidirectional services can obtain callback proxies using API calls instead of by injection, and we’ll demonstrate how bidirectional services can use callable references to provide more flexibility in making the callback. We’ll also show how clients can identify and correlate individual callbacks using callback IDs. Finally, we’ll look at how clients can delegate callback handling to another service.

5.8.1. The credit card security callback scenario

We’ll use the credit card security password example to illustrate the various steps involved in creating implementation instances, injecting callback proxies, and making callbacks. You can find this example in the payment-java-callback sample contribution. Figure 5.4 shows two payment components calling the same credit card payment service on behalf of different customers.

Figure 5.4. Two different payment components are wired to the same bidirectional service of a credit card payment component.

The interaction diagram in figure 5.5 uses the components shown in figure 5.4 and shows Jim making a payment of $1500, whereas John is making a payment of $500.

Figure 5.5. Two different client components are processing payments for Jim and John using the same CreditCardPayment component. For each payment authorization request, a different instance of the CreditCardPayment component is created. Each instance of CreditCardPayment gets the appropriate callback proxy injected and can use this to make callbacks to the correct client component.

In figure 5.5, the first interaction is a call to the credit card payment service to authorize Jim’s payment of $1500 , causing Tuscany to create a new instance of CreditCardPayment and inject this instance with a callback proxy for Jim . Next, a call for John’s payment of $500 is made , causing another instance of CreditCardPayment to be created and injected with a callback proxy for John . Jim’s payment is larger and needs an additional security check, so the CreditCardPayment implementation calls back to Jim requesting a password . John’s payment is below the limit for additional security, so the CreditCardPayment implementation authorizes this payment without making a callback . Jim’s callback responds with the correct password , and the CreditCardPayment implementation authorizes Jim’s payment and returns .

We’ll use this scenario to look at how bidirectional services and callback clients are defined and programmed with the Java implementation type.

5.8.2. Creating a bidirectional interface with the @Callback annotation

As we described in chapter 4, the first step in implementing a callback with a Java interface is to define a bidirectional service interface with a callback interface. The following code snippet gives a quick reminder of how to do this:

@Remotable
public interface CreditCardSecurity {
String checkSecurity(String securityPrompt);
}


@Remotable
@Callback(CreditCardSecurity.class)
public interface CreditCardPayment {
String authorize(CreditCardDetailsType ccDetails, float amount);
}

In this code we’ve defined an interface, CreditCardSecurity, and used the @Call-back annotation to add this as the callback interface for the CreditCardPayment interface. Now we’ll look at how the Java component implementation makes a callback.

5.8.3. The service programming model for callbacks

To make a callback, the service implementation needs a callback proxy. As with reference proxies, Tuscany creates and injects callback proxies automatically based on annotations within the service implementation class. The implementation class uses the @Callback annotation to specify how injection should be performed, using either of the following styles:

  • A public or protected field— The callback proxy will be injected into the field. Here’s an example of declaring a field for callback injection:
    @Callback
    protected CreditCardSecurity ccSecurity;
  • A public or protected setter method— The callback proxy will be injected by calling the setter method. The following code shows a setter method declared for call-back injection:
    @Callback
    public void setCardSecurity(CreditCardSecurity ccSecurity) {
    ....
    }

The following listing shows an example Java implementation for the credit card payment service that uses @Callback to annotate a field in the Java implementation.

Listing 5.3. The implementation for the bidirectional credit card payment service

For implementations with a single callback service, Tuscany injects callback proxies into all fields and setter methods, marked with @Callback, that match the type of the callback interface for the bidirectional service. The names of fields or setter methods marked with @Callback aren’t important. In CreditCardPaymentCallbackImpl in listing 5.3, the callback interface is CreditCardSecurity and there’s one matching callback field, ccSecurity. For every call to the authorize() method, Tuscany creates a new instance of CreditCardPayment and injects the ccSecurity field with a callback proxy for the client making the call. CreditCardPaymentCallbackImpl can use the proxy to make one or more callbacks to the client, or it may not call back to the client at all.

If the implementation has no callback fields or setter methods that match its callback interface, no callback proxies are injected. This can happen if the implementation has no need to make callbacks, even though the bidirectional interface permits it.

If a service implementation class offers more than one bidirectional service, a call to one of these services will cause callback proxies to be injected only into the callback fields and setter methods that match the type of the callback interface for the current service call. It’s also possible for a service implementation class to offer both a bidirectional service and a nonbidirectional service, in which case callback proxies are injected only when the bidirectional service is called. In these situations, when different proxies can be injected by different calls to the service, it’s important to not call a callback proxy without first checking that the proxy has been injected, that is, that it’s not null. In our payment sample the proxy is not checked for null because the CreditCardPaymentCallbackImpl class offers a single bidirectional service, so the callback proxy will always be injected.

Next we’ll look at how a callback client for a bidirectional service is implemented in the Java language.

5.8.4. The client programming model for callbacks

Listing 5.4 shows an example Java implementation for the Payment component. It’s a callback client for the CreditCardPayment component shown in listing 5.5. The code in listing 5.4 is based on listing 5.1 with changes to accommodate callbacks from the bidirectional service.

Listing 5.4. The client implementation for the bidirectional credit card payment service

The code in listing 5.4 has two additions to the code in listing 5.1 because CreditCardPayment now has a bidirectional service: PaymentCallbackImpl implements an additional interface, CreditCardSecurity, and it contains an implementation of the method checkSecurity() defined by that interface. These additions enable PaymentCallbackImpl to handle callback invocations made by a bidirectional service of type CreditCardPayment. The majority of the code from listing 5.1 has not been repeated for clarity.

You’ve seen how to use the basic programming model for bidirectional interfaces and callbacks in Java implementations. Now it’s time to look at some other features that you can use in Java implementations to customize various aspects of a callback interaction.

5.8.5. Getting the callback proxy from the request context

In listing 5.3, the callback proxy was injected into the service implementation instance. This works if a new service implementation instance is created for each request to the service, because each instance will contain injected callback proxies targeted at the service whose request created the instance. To put this another way, there will be a one-to-one relationship between service requests, implementation instances, and injected proxies.

Other situations don’t have this simple one-to-one relationship. For example, a composite-scoped implementation has a single instance that’s used to process all service requests for the component. Callback proxies are injected into the component instance when it’s first created and are not reinjected, and so these proxies will be correct only for the first client that calls the component.

For composite-scoped components, multiple service requests can even run concurrently on different threads within the same component instance. If these requests are from different clients of a bidirectional service, the callback proxies for the requests will contain different destination addresses corresponding to the clients that made the service calls. If callback proxies for all incoming requests were injected into the same instance of a composite-scoped component, the results would be unpredictable because new incoming requests would overwrite previously injected callback proxies that might still be needed to make callbacks. The interaction diagram in figure 5.6 illustrates this problem.

Figure 5.6. Two different client components are processing payments for Jim and John using the same CreditCardPayment component. For each payment authorization request, the same instance of the CreditCardPayment component is used. The injected callback proxies overwrite each other in the CreditCardPayment instance, and the callback is made to the wrong client component.

In figure 5.6 there’s only one instance of CreditCardPayment, so step would overwrite the callback proxy for Jim that was injected in step . When request is made to check Jim’s security password, it would be directed to John instead of Jim.

To avoid this problem, Tuscany doesn’t overwrite previously injected callback proxies when new requests are dispatched to an existing implementation instance. But this still causes problems for multiclient scenarios. If injected proxies retain their original values instead of being overwritten, the callback to Jim would go to the correct destination, but callbacks intended for John would go to Jim instead.

The solution is to write the implementation so that it doesn’t use proxy injection in these cases. Instead, the implementation obtains a callback proxy from the current request context using the RequestContext Java SCA API, as follows:

@Context
protected RequestContext rqContext;

public String authorize(CreditCardDetailsType card, float amount) {
....
CreditCardSecurity ccSecurity = rqContext.getCallback();
String pwd = ccSecurity.checkSecurity("Enter password");
....
}

The @Context annotation can be placed on a field or setter method of type RequestContext. In this example we’ve placed it on the rqContext field. Tuscany will inject this field with a request context object which has access to the current service request. The implementation uses the getCallback() method on the request context object to get the callback proxy for the current request.

You may be thinking that we’ve just moved the problem because we’re now injecting a request context object when the component instance is first created. But the request context code is thread safe and ensures that all callbacks go to the correct client, no matter how many clients are making concurrent calls to the same service.

Next we’ll look at how a bidirectional service can delegate or defer callbacks using CallableReference objects.

5.8.6. Using callable references to provide callback flexibility

So far we’ve looked at callbacks made by a bidirectional service implementation before the original service request returns. It’s also possible for the service implementation to delegate the callback responsibility to some other service or for the callback to be made asynchronously sometime after the original service request has returned. To support these cases, Tuscany supports SCA CallableReference objects in Java implementations.

A CallableReference is an object that represents a callback. It provides methods to retrieve various kinds of information about the callback. CallableReference objects can also be passed on service method calls or stored persistently for later retrieval. A CallableReference is serializable, in the Java language sense, and so any mechanisms you use to store serialized Java objects can store a CallableReference.

To illustrate these scenarios, we’ll use a different form of the CreditCardPayment service, called CreditCardPaymentConfirm. This has a bidirectional interface with a different callback interface from the previous example. The following listing shows that instead of using the callback interface to prompt for a security password, we’ll use the callback interface to confirm successful payment and provide a payment reference.

Listing 5.5. Using a CallableReference object to delegate the callback
@Remotable
public interface CreditCardConfirm {
void confirmPayment(String paymentRef);
}

@Remotable
@Callback(CreditCardConfirm.class)
public interface CreditCardPaymentConfirm {
String authorize(CreditCardDetailsType ccDetails, float amount);
}

public class CreditCardPaymentConfirmImpl implements
CreditCardPaymentConfirm {

@Callback
protected CallableReference<CreditCardConfirm> callbackRef;

@Reference
protected CardCompany cardCompany;

public String authorize(CreditCardDetailsType card,
float amount) {
cardCompany.makePayment(card, amount, callbackRef);
return "InProgress";
}
}

In listing 5.5, CreditCardPaymentConfirmImpl has a reference, cardCompany, for a service of type CardCompany that will make the real payment. The @Callback annotation is placed on a field of type CallableReference<CreditCardConfirm>, which causes Tuscany to inject a CallableReference object instead of a callback proxy. CreditCardPaymentConfirmImpl calls the CardCompany service to make the payment and passes the CallableReference object on this service call. The CardCompany service makes the payment, generates a payment reference, and makes a callback confirmation to the original client using the CallableReference object that it was passed. The code to make the callback would look like the following:

callbackRef.getService().confirmPayment(paymentRef);

This code uses the getService() method of the CallableReference object to obtain a callback proxy that’s used to call the client’s confirmPayment() method.

You’ve seen how a bidirectional service can use CallableReference objects to provide more flexibility for how and when callbacks are made. Now we’ll look at how a client can identify and correlate a specific callback using its callback ID.

5.8.7. Using a callback ID to identify a specific callback

In some cases the client may need to correlate a specific service request with a callback made by the service as a result of that request. SCA provides this capability by generating a unique callback ID for every call to a bidirectional service and making this call-back ID available to callbacks that a service makes on behalf of the original call.

By default, the callback ID for a call to a bidirectional service is generated by Tuscany and can be obtained by the client method making the call, the service implementation method called by the client, and the client method that receives the callback from the service. Alternatively, the client method can choose to set its own unique value for the callback ID. This value would usually be something that’s meaningful in business terms, such as an order number or payment reference. If the client implementation wants to customize the callback ID, it can do this by obtaining a ServiceReference object for the bidirectional service and calling the setCallbackID() method on this ServiceReference object. See section 5.8.8 for more information about ServiceReference objects.

The next listing is a modification of the PaymentCallbackIDImpl class showing how the client implementation can set and retrieve a callback ID.

Listing 5.6. Client implementation that sets and retrieves the callback ID

Listing 5.6 shows how the callback ID can be used in a callback to correlate the call-back with the original service request and retrieve relevant information for processing the callback. Some of the detail has been omitted in order to focus on the code that deals with the callback ID.

First we’ll need a ServiceReference object in order to set the callback ID. The easiest way to obtain a ServiceReference object is to declare the injected callback as being of type ServiceReference .

To customize the callback ID that will be used for the authorize() call and its associated callback, checkSecurity(), we’ll call the setCallbackID() method on the ServiceReference object, passing the dummy audit ID for this payment.

The checkSecurity() callback method retrieves the callback ID by calling getServiceReference() on the RequestContext object and calling getCallbackID() on the returned ServiceReference object . The callback ID is the audit ID for this payment, and we’ll use this to retrieve the audit record holding the saved payment details.

We’ve looked at various ways that the service and client can customize the callback interaction. In the next section, you’ll see how the client can redirect callbacks so that they go to another service.

5.8.8. Redirecting the callback to another service

A client implementation that calls a bidirectional service can choose not to handle callbacks itself, but redirect callbacks to another service instead. To do this, the client implementation obtains a ServiceReference object for the bidirectional service and calls the setCallback() method on this ServiceReference object, passing another ServiceReference object identifying a service that will handle callbacks from the bidirectional service. The following code is a modification of the code in listing 5.6, showing an example of this.

Listing 5.7. Delegating callbacks to another service

In listing 5.7, PaymentCallbackRedirectImpl doesn’t implement the CreditCardSecurity interface or the checkSecurity method. Instead, it declares a ServiceReference object, cpRef, for the credit card payment service and another ServiceReference object, csRef, for a service that will handle CreditCardSecurity callbacks instead of PaymentCallbackRedirectImpl. Before calling the credit card payment service, PaymentCallbackRedirectImpl calls the setCallback() method on cpRef so that future calls through this reference will have callbacks redirected to the service identified by csRef. When the credit card payment service makes callbacks to get security passwords, it calls the service identified by csRef directly, and PaymentCallbackRedirectImpl won’t be involved in any way.

In this section you’ve seen how to declare bidirectional interfaces and implement services and clients that use callbacks, and you’ve looked at the SCA APIs that Java implementations can use to customize callback interactions. Next we’ll look at how Java implementations handle conversations.

5.9. Holding conversations

You saw in chapter 4 that SCA and Tuscany support the conversational service interaction pattern. In SCA, holding a conversation with a service means that all of the messages sent to a service from a specific reference arrive at the same service instance. This allows the service to maintain context as it processes a sequence of messages. Components implemented with Java are provided with a number of features for creating and controlling conversations. If you don’t need to know the details of how to create and control conversations with Java components, you can safely skip forward to section 5.10.

5.9.1. Defining and controlling conversations in Java implementations

Table 5.5 shows the implementation.java-specific features that allow you to define and control a conversational service.

Table 5.5. The SCA features used to control conversational behavior

Feature

Description

@Conversational Marks a service or service interface as being conversational.
@EndsConversation Marks a service operation as being the last operation in the conversation.
@ConversationID Identifies a field to be injected with the conversation ID of the current conversation.
@Scope("CONVERSATION") Sets the scope of a service implementation to be CONVERSATION. A new component instance will be created at the start of each new conversation, and the instance will remain until the conversation ends. All messages that are part of the conversation, as determined by the conversation ID, will be directed to the same service instance.
CallableReference.isConversational() Returns true if the reference points to a conversational service.
CallableReference.getConversation() Retrieves the conversation object, which implements the Conversation interface for getting the conversation ID and ending the conversation manually.
Conversation.getConversationID() Returns the conversation ID for the conversation represented by this conversation object.
Conversation.end() Ends the conversation.
ServiceReference.getConversationID() Retrieves the conversation ID for the current conversation for this service reference.
ServiceReference.setConversationID() Sets the conversation ID for the conversation about to start on this service reference.
@ConversationAttributes(
maxAge="10 minutes",
maxIdleTime="5 minutes",
singlePrincipal=false)
Associates conversation configuration with the service implementation.

You’ll notice that some of these features rely on Java annotations, and some of them are supported using a Java API. Let’s look at the annotations first.

5.9.2. Starting, using, and stopping conversations using annotations

Chapter 4 gave an overview of the conversational interaction pattern, and here we’ll look at the Java language–specific features. In keeping with chapter 4, we’ll move away from the payment example briefly and review the CartStore component from the shoppingcart contribution. As a reminder, the following listing shows how a service interface is marked as being conversational.

Listing 5.8. The conversational CartStore interface

The @Conversational annotation in listing 5.8 identifies this interface as being conversational. The first call to any of its operations starts a new conversation. Subsequent calls to this interface’s operations continue the conversation until the operation marked with the @EndsConversation annotation is called. At this point the conversation ends.

The next listing shows the service implementation itself. Note that the scope of the component is set to CONVERSATION so a new instance is created for each conversation.

Listing 5.9. The conversational CartStore component implementation

The conversation starts when the first message arrives. A component instance is created and the conversation ID is injected into the field marked with the @ConversationID annotation, cartId . The operations annotated with @Init are also called. This is an opportunity to arrange for conversational state to be stored.

Now that the conversational component has been created, any of its operations that are called through the reference that sent the first message will arrive at the same component instance. The conversational state is therefore easily available.

When the operation marked with @EndsConversation in the interface, reset in this case , is called, the conversation ends. Any operations marked with the @Destroy annotation will be called. This gives you an opportunity to free up any resources associated with the conversation. Finally, the component instance is removed.

You don’t have to use @Init and @Destroy with conversational services, but it’s a convenient way to add code that runs automatically at the start and end of a conversation.

The lifetime of a conversation can also be controlled by conversational attributes . The maxAge attribute determines how long the conversation can live for before it’s stopped automatically. The maxIdleTime attribute determines how long the conversation can live between incoming messages before it’s stopped automatically. The singlePrincipal attribute should allow you to indicate that a conversation can be used only by the client that started it, but this attribute isn’t supported by Tuscany at the moment.

Some extra features of conversations can be controlled via the SCA Java API.

5.9.3. Controlling conversations using the SCA Java API

As an alternative to relying on injection to access the conversation ID, you can set and retrieve it using the ServiceReference Java API. For example, here’s code that retrieves a reference to the CartStore component and starts a new conversation with it:

ServiceReference<CartStore> cartStore =
componentContext.getServiceReference(CartStore.class,
"cartStore");
cartStore.setConversationID(UUID.randomUUID().toString());
cartStore.addTrip(bookedTrip);

Once you have a service reference to a conversational service, you can continue to call operations on it or even pass it to other services so that they can call operations as part of the conversation. The conversation continues until it’s ended by, in this case, calling the reset operation.

A conversation object can be retrieved from a service reference. This object implements the Conversation interface defined by the SCA Java API, which provides another way of retrieving the conversation ID and also provides an end operation that allows a conversation to be terminated prematurely.

We’ve finished looking at how to use the conversation pattern with components implemented in the Java language, so let’s move on and look at how service references can be obtained and passed from one component to another.

5.10. Passing SCA service references

It’s often desirable to get a reference to a service instead of using the proxy injected automatically into a component reference. This is particularly important when collaborating components need to exchange service references. We’ll first look at a scenario where this can be useful and then look at how to retrieve, pass, and use a service reference.

5.10.1. A service reference–passing scenario

Let’s introduce a variant of the Payment component’s interaction with the EmailGateway and CreditCardPayment components. You can find these in the payment-java-reference-pass sample contribution. Figure 5.7 shows the various interactions. Let’s assume that the authorize operation of the CreditCardPayment is time consuming and that we don’t want to prevent the Payment component from continuing with its processing. We do, though, want to ensure that whenever the credit card transaction is done, we send an email notification.

Figure 5.7. By using service references passed between SCA components, the CreditCardPayment component can be asked to call the EmailGateway component.

The Payment component must get a service reference that refers to the EmailGateway service and pass this to the CreditCardPayment component when the authorize operation is called, and then the CreditCardPayment component uses this reference to call the EmailGateway component when processing is complete.

Let’s find out how the Payment component can retrieve a service reference to the EmailGateway component.

5.10.2. Retrieving service references

The first way to retrieve a service reference is to declare an SCA reference with the Java type ServiceReference. For example, in the following snippet, a ServiceReference for the EmailGateway component service will be injected into the emailGateway field by the runtime. Please note that the parameterized type for the ServiceReference interface is the business interface EmailGateway.

@Reference
protected ServiceReference<EmailGateway> emailGateway;

The second way to retrieve a service reference is through the ComponentContext SCA Java API. To do this, the component context must be injected into the Payment component.

@Context
Protected ComponentContext context;
...
ServiceReference<EmailGateway> emailGateway =
context.getServiceReference(EmailGateway.class, "emailGateway");

As you can see, the component context is then used to retrieve the service reference by name. Now we’ll need to pass the service reference to the CreditCardPayment component.

5.10.3. Passing a service reference to another component

The service reference for emailGateway can be passed to the CreditCardPayment component explicitly as a parameter on the authorize method in the following way:

void authorize(CreditCardDetailsType cc,
float amount,
ServiceReference<EmailGateway> emailGateway,
String emailAddress);

The code in the Payment component to call the authorize method passing a service reference would be as follows:

@Reference
protected ServiceReference<EmailGateway> emailGateway;

@Reference
protected CreditCardPayment creditCardPayment;

public String makePaymentMember(String customerId, float amount) {
creditCardPayment.authorize(ccDetails,
amount,
emailGateway,
emailAddress);
...
}

This gives the CreditCardPayment component the information it needs to call the EmailGateway component when the authorization is complete. Let’s see how the CreditCardPayment component uses the service reference it receives.

5.10.4. Making a call via a service reference

The Payment component has passed in a ServiceReference for the EmailGateway through the parameters of the authorize method. Once credit card processing is complete, the CreditCardPayment component calls the emailGateway to send out the notification email as follows:

emailGateway.getService().sendEmail(...);

The EmailGateway service now sends out an email to notify the customer of the result of the transaction, and the Payment component doesn’t have to wait for this to happen.

We’ve talked a lot about how to build a component implementation in Java when everything is working as expected. We haven’t yet covered what to do when things go wrong.

5.11. Handling errors

When an SCA component interacts with other services, error conditions may occur. In general, there are two types of exceptions: business exceptions and SCA runtime exceptions. We’ll cover both how to define and handle business exceptions and how to handle SCA runtime exceptions, so let’s start by looking at business exceptions.

5.11.1. Business exceptions

Business exceptions are defined as checked exceptions on the Java interface or faults on the WSDL port type and are thrown by the component implementation. Business exceptions are part of the contract for an SCA service or reference and are used to represent and carry data for an error condition that occurs in the business logic. For example, you can define a CustomerNotFoundException in the Java language to indicate that a customer ID can’t be found in the customer registry or an authorizationFailure fault in WSDL to indicate that a credit card transaction can’t be authorized because the card number is invalid.

Let’s look at two examples to explain how business exceptions can be declared on the business interface and how to handle them in the component implementation class.

The first example uses a local Java interface. Component implementations written in the Java language use the same error-handling techniques that an ordinary Java program uses. For example, the customer registry component locates customer details based on a customer identifier. If an invalid customer identity is passed in, then the customer registry component must be able to report that the requested customer can’t be found. To indicate this error state, we’ll create a CustomerNotFoundException type, which contains the customerId in question.

public class CustomerNotFoundException extends Exception {
private String customerId;
...
}

We can then extend the service interface to indicate that this service may return this exception under certain conditions. Because we’re working with implementation.java, and the service interface is described in the Java language, this is a matter of adding the exception to appropriate operations in the Java CustomerRegistry interface.

public interface CustomerRegistry {
Customer getCustomer(String id) throws CustomerNotFoundException;
}

The CustomerRegistryImpl class throws the CustomerNotFoundException if the lookup of the customer registry doesn’t return a record for a given customerId.

A client of this service should test for this error condition. Again, because we’re working in the Java language, we can use the normal Java language techniques to do this. Note that in this case we also check for any unchecked exceptions that might be received.

public String makePaymentMember(String customerId, float amount) {
Customer customer = null;

try {
customer = customerRegistry.getCustomer(customerId);
} catch (CustomerNotFoundException ex) {
return "Payment failed due to " + ex.getMessage();
} catch (Throwable t) {
return "Payment failed due to system error " + t.getMessage();
}
}

When dealing with business exceptions, the remotable interface is more complicated than the local interface. For remote service interactions, the business exceptions are sent back to the client side within protocol messages. Different bindings may have different ways to pass exception information. Let’s extend the CreditCardPayment component to illustrate remote error handling following the JAX-WS rules for the Web Services binding.

We’ll first define the faults that the CreditCardPayment service interface returns by updating CreditCardPayment.wsdl. Listing 5.10 shows the related definitions.

Listing 5.10. Adding fault types to the CreditCardPayment WSDL interface description

In the WSDL we define a global element authorizeFault . A WSDL message is added with a part that points to the authorizeFault element . Finally, a WSDL fault is added under both the portType/operation and the binding/operation .

The JAX-WS wsimport tool is used to generate Java interfaces from WSDL. Chapter 9 discusses how to use this tool in more detail. When wsimport is run against this WSDL, three related artifacts are generated:

  • A JAXB class payment.creditcard.AuthorizeFault for the fault element (this is no different than a regular parameter or return type).
  • A Java exception payment.creditcard.AuthorizeFault_Exception that wraps the fault. See listing 5.11.
  • A Java interface payment.creditcard.CreditCardPayment with the authorize() method that throws the payment.creditcard.AuthorizeFault_ Exception.
Listing 5.11. The generated payment.creditcard.AuthorizeFault_Exception
package payment.creditcard;

import javax.xml.ws.WebFault;



@WebFault(name = "authorizeFault",
targetNamespace = "http://www.tuscanyscatours.com/CreditCardPayment/")
public class AuthorizeFault_Exception extends Exception
{
private AuthorizeFault faultInfo;
public AuthorizeFault_Exception(String message,
AuthorizeFault faultInfo) {
super(message);
this.faultInfo = faultInfo;
}

public AuthorizeFault_Exception(String message,
AuthorizeFault faultInfo,
Throwable cause) {
super(message, cause);
this.faultInfo = faultInfo;
}

public AuthorizeFault getFaultInfo() {
return faultInfo;
}

}

The @WebFault annotation shown above the class definition in listing 5.11 is a JAX-WS annotation added to indicate that the Java exception represents a WSDL fault.

The authorize() method of the CreditCardPayment interface now has a throws clause of AuthorizeFault_Exception to indicate that in some cases this method can throw this exception:

public String authorize(@WebParam(name ="CreditCard", targetNamespace ="")
CreditCardDetailsType creditCard,
@WebParam(name = "Amount", targetNamespace = "")
float amount)
throws AuthorizeFault_Exception;

The component implementation class, CreditCardPaymentImpl, now has the code to create and throw the AuthorizeFault_Exception, as follows:

public String authorize(CreditCardDetailsType creditCard,
float amount)
throws AuthorizeFault_Exception {

if (creditCard != null) {
...
} else {
ObjectFactory factory = new ObjectFactory();
AuthorizeFault fault = factory.createAuthorizeFault();
fault.setErrorCode("001 - Invalid card");
AuthorizeFault_Exception ex =
new AuthorizeFault_Exception("Invalid card",
fault);
throw ex;
}

return "OK";
}

On the client side, where the credit card payment is called, the PaymentImpl component implementation captures the exception and extracts the error code:

try {
status = creditCardPayment.authorize(ccDetails,
amount);
} catch (AuthorizeFault_Exception e) {
status = e.getFaultInfo().getErrorCode();
}

As you can see, even though the exception is now described in the WSDL interface and could pass across a remote binding, such as the Web Services binding, the Java implementation still deals with the Java exception types that it’s familiar with. Regardless of which binding you use, the Tuscany runtime will ensure that the behavior remains consistent when errors arise.

5.11.2. SCA runtime exceptions

Unlike business exceptions, some error conditions aren’t expected by the business logic and instead are caused by the Tuscany runtime, for example:

  • There are configuration errors in the application’s composite files.
  • A target service isn’t running or it can’t be reached because of networking problems.
  • Quality of service constraints are violated; for example, the user can’t be authenticated or can’t be authorized.

In most cases, SCA runtime exceptions can’t be handled properly by the application code. Instead they’re handled using strategies such as these:

  • Don’t catch the runtime exceptions and let them pop up the calling stack.
  • Catch it and throw a business exception to indicate the abnormal system status.
  • Catch it and resend the message or initiate a compensating action.

Of these, the best practice is to catch the exception and either throw it as a business exception described explicitly on your service interface or initiate a compensating action if you’ve designed your components to work in this way. Allowing system exceptions to work their way back through the stack of calling component references and services can be problematic. There’s a good chance that system details from deep within the call stack will be exposed in unexpected places, with the obvious implications for security.

You’ve now had an extensive tour of the various SCA features you can exploit to configure components implemented using Java classes. Let’s wrap up now before moving on to the look at other implementation types in the next chapter.

5.12. Summary

In the initial chapters of the book, you learned how SCA offers a technology-agnostic component model. Its support of various component implementation types enables different technologies to work with one another in an SCA composite application.

This chapter offered a technical perspective of the current Java component implementation type. We covered quite a lot of ground looking at the details of how you define services, references, and properties in a Java implementation. We also looked at how the callback and conversation interaction patterns described in chapter 4 are supported in Java implementations and how to handle errors. The subject of how to apply policy intents to Java component implementations using Java annotations will be covered in chapter 10, along with the wider discussion of policy support in Tuscany.

The main lesson of this chapter is that it’s easy to do simple things using Tuscany and vanilla Java component implementations. Exploiting the more complex SCA features is also easy using SCA annotations. The Java language has been a particular focus of the SCA specifications and has the most features defined. Some, but not all, of these features are available in other component implementation types. Let’s move on now and look at some of those other implementation types.

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

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