7. Wires

This chapter picks up more advanced topics associated with wiring components. In particular, it covers wiring to multiple providers of a service, autowire, and wire reinjection after a component has been deployed to a domain.

Wiring to Multiple Service Providers

In distributed systems, there is often a need for clients to make the same request to a number of services. A common scenario where this arises is when a request-for-quote is issued: A client will make the same request to multiple service providers, as illustrated in Figure 7.1.

Figure 7.1 Wiring to multiple service providers

image

In Figure 7.1, the client contains a collection of references that it iterates through and invokes with the same request. If BigBank wanted to expand its credit check procedures, it could use multiple credit score components that produced ratings based on different criteria. Let’s see how this is done.

A reference can be wired to multiple targets by specifying more than one component name in the @target attribute of the <reference> element. Component names are separated by a space. Listing 7.1 shows the same composite with the LoanComponent reference wired to multiple CreditComponents.

Listing 7.1 Wiring to Multiple Targets

image

A reference with multiple wires is injected into a component implementation instance as a collection. In Java, the component implementation could choose to have the reference injected as a java.util.List, as shown in Listing 7.2.

Listing 7.2 Multiple Wire Injection

image

When handling a request, the component iterates and invokes the various services individually, as shown in the excerpt in Listing 7.3.

Listing 7.3 Invoking Multiple Wires

image

When a reference is configured with multiple wires, it is said to have a multiplicity greater than one. Multiplicity defines the number of wires a reference may have. Recalling that references may be required or optional (in Java, setting the “required” attribute on the @Reference annotation to true or false), references may have the following multiplicities:

0..1—Denotes an optional reference. In Java, the reference is specified using @Reference(required = false).

1..1—Denotes a required reference. In Java, the reference is specified using @Reference(required = true) or simply @Reference.

0..n—Denotes an optional reference that may be configured with multiple wires. In Java, the reference type must be a java.util.Collection and is specified using @Reference(required = false).

1..n—Denotes a required reference that may be configured with multiple wires. In Java, the reference type must be a java.util.Collection and is specified using @Reference(required = true) or @Reference.

Those familiar with modeling languages may recognize that the 0..1, 1..1, 0..n, and 1..n notation used by SCA to express multiplicity derives from Unified Modeling Language (UML).

Perspective: Why SCA Did Not Use UML

With its concepts of services, components, and wires, SCA lends itself naturally to modeling an application and representing it visually. Unified Modeling Language (UML) is the industry-recognized standard for modeling applications. Given this, why did SCA not adopt UML as its starting point?

Although UML is a powerful modeling tool, the SCA authors didn’t want its complexity. A key goal of SCA is to create a simplified programming model. At the same time, SCA’s modeling requirements were modest in comparison to UML’s scope. It was felt that requiring people to learn UML would have resulted in the need to master more concepts than strictly required.

The <wire> Element

Having to specify target names using a space-delimited list in the @target attribute of the <reference> element can be difficult to read by humans. Going back to the multiplicity example we used previously, it may not be immediately apparent that the LoanComponent.creditService reference is wired to multiple services (see Listing 7.4).

Listing 7.4 Wiring Multiple Targets for the LoanComponent

image

To make configuration more readable (and for those who prefer to separate wiring from component definitions), SCDL also supports a <wire> element, which may be used to define multiple wires for a reference (see Listing 7.5).

Listing 7.5 Using the <wire> Element

image

The <wire> element has source and target attributes. The source attribute identifies the reference the wire configures and is specified using the component and reference names separated by a /. The target attribute identifies the target service the reference is wired to, which is done using the name of the component providing the service and the service name separated by a /. The preceding example omitted the service name because both CreditComponents have only one service (recalling from Chapter 2, “Assembling and Deploying a Composite,” that if a component implements more than one service, the service name would have to be specified using the “component name/service name” SCDL syntax). Similarly, if the source component had only one reference, it does not need to be specified. If LoanComponent only had the "creditService" reference, the preceding SCDL could have specified the source as source="LoanComponent".

Multiplicity and Callbacks

In the previous example, the interaction pattern used when wiring to multiple providers was blocking request-response—that is, the LoanComponent iterated through the collection of wires and waited for a response after each CreditService invocation. In situations where there are many providers or where a response may take some time, a callback may be more appropriate.

Using non-blocking operations and callbacks with references having multiple wires is not really different from references having a single wire. Recalling that the CreditService is defined using a Java interface, annotating the checkCredit operation with @OneWay will make it non-blocking. As the LoanComponent iterates through the collection of credit service wires, it will be able to invoke each without blocking, resulting in the client issuing multiple invocations without waiting for others to complete. This can greatly improve the overall performance of an application because a number of tasks can be processed at the same time.

To return a result from a non-blocking operation, a service provider uses a callback, which we discussed in Chapter 3, “Service-Based Development Using Java.” To enable callbacks, the LoanComponent implements the CreditScoreCallback interface, as shown in Listing 7.6.

Listing 7.6 The CreditScoreCallback Interface

image

When a credit CreditComponent is invoked (also written in Java), it is injected with a callback proxy, which it uses to return the credit score response. As each CreditComponent finishes processing the request, it invokes the onResult operation on the callback proxy. This results in the LoanComponent receiving multiple callbacks. In this case, it is likely that the LoanComponent will want to correlate the credit score with the service provider that made the callback. This can be done by adding additional information to the CreditScore data, such as a provider ID.

What’s Behind a Wire?

How are wires manifested in an SCA runtime? Although SCA runtimes may implement wires differently, the principles remain the same. A wire is a proxy that implements the service contract required by a reference and is responsible for flowing an invocation to a target, potentially a remote web service or service offered by another SCA component (see Figure 7.2).

Figure 7.2 A wire is a proxy that dispatches an invocation to a target service.

image

A wire may be responsible for dispatching an invocation over a remote binding, such as web services or RMI. In this case, it would be responsible for serializing the invocation parameters, possibly in a SOAP message, and invoking a transport-specific API. In the case of synchronous invocations (that is, ones expecting a direct response), the wire would also deserialize the return value to the client or throw an exception if one occurred.

Wires may perform additional tasks related to an invocation, such as flowing transactional or security context. One common way of implementing this is through an interceptor. Interceptors are linked units of code that perform a specific action and pass on processing to the next interceptor in a chain. In Java, servlet filters are a common type of interceptor. Common tasks include passing (or “propagating”) security credentials or transaction context.

When an invocation is made on the proxy, the wire is responsible for dispatching it down the appropriate interceptor chain. After the interceptor chain has finished processing, the invocation is then dispatched through a communications layer to the target service (see Figure 7.3).

Figure 7.3 A wire interceptor chain

image

In Java-based runtimes, wires can be implemented using JDK proxies or through more exotic (and potentially more performant) means, such as bytecode generation. Fortunately, these details remain hidden from components, which interact with wires as if they were normal Java objects. From application code, dispatching over a wire appears as a standard method invocation.

Automated Wiring: Autowire

XML-based configuration can become verbose and fragile, particularly as the number of components and wires in an application increases. To help reduce excessive XML and make wiring easier, SCA allows references to be automatically wired, or “autowired,” by the runtime. When a reference is autowired, the runtime selects a suitable target service, as opposed to having it explicitly specified in the target attribute of a <reference> or <wire> element. When autowiring a reference, the runtime will attempt to select a matching service from all services provided by components in the same composite. The runtime will perform the selection by comparing the required service contract of the reference with the service contract of the target service to see if they are compatible (for more information on compatibility, see the following sidebar). If more than one suitable target is found, the runtime will select one in an implementation-specific manner. This may be as simple as selecting one at random, or more complex, such as preferring a collocated service for performance reasons.

Service Compatibility

When wiring references to services, SCA requires their service contracts to be compatible. Be careful not to confuse this with equality. In other words, the service contract required by a reference does not have to be the same as the target service. For example, the required contract for a client component written in Java may be determined by the Java interface used by the reference. When autowiring, a target service could be selected that is provided by a BPEL-based component, whose interface would be defined by WSDL. In this case, the runtime would check compatibility between the Java interface and WSDL definition.

What constitutes compatibility? SCA defines a series of rules for determining compatibility, as follows:

• The operations defined by the target service contract must be the same or be a superset of those defined by the source.

• Operation matching is determined by the following criteria. The operation name, parameter types, and return types must be the same. The order of operation parameters and return values (for languages that support multiple return values) must be the same. Finally, the set of faults and exceptions declared by the source and target contracts must be the same.

• Callback service contracts, if present, must match.

• The source and target contracts must either both be local or remotable.

• Other specified attributes must also be the same. For example, if a source service contract is conversational, the target contract must be as well.

For cases where both source and target contracts are the same (that is, Java-to-Java or WSDL-to-WSDL), matching is straightforward. Where things get interesting is when the contracts are not the same. For example, it is possible in SCA to wire a reference to a service that uses different Java interfaces to define their service contracts. As long as they are compatible, the runtime is responsible for establishing the connection. This may involve some form of mediation as a request is flowed from the source to the target service.

Fortunately, the complexity associated with matching source and target service contracts is hidden from applications. However, it is useful to understand how matching is performed and to not assume that it rests on both sides of a wire being the same.

The composite shown in Listing 7.7 demonstrates the use of autowire.

Listing 7.7 A Composite Using Autowire

image

By default, autowire is disabled. Unless explicitly enabled, the runtime will not attempt to wire unconfigured references. The SCDL in Listing 7.7 enables autowire for the entire composite by setting the “autowire” attribute on the composite definition to true. As a result, component references not explicitly wired to a target will be wired by the runtime. Because LoanComponent#creditService is not configured (that is, there is no corresponding <reference> entry in the SCDL), the runtime will automatically wire it to a suitable matching target—in this case, the CreditService provided by the CreditComponent.

SCA allows autowire to be enabled for a composite, a component, or a reference. If we had wanted to autowire just the LoanComponent references, we would have set the autowire attribute to true on its <component> element (see Listing 7.8).

Listing 7.8 Using the Autowire Attribute on a Component

image

Similarly, we could have restricted autowire to the individual reference (see Listing 7.9).

Listing 7.9 Using the Autowire Attribute on a Reference

image

Autowire can also be disabled for specific components or references. For example, if autowire is enabled for a composite, it may be turned off for specific components and references by setting their autowire attribute to false.

Autowire can also be used to wire references with a multiplicity greater than one. In these situations, the runtime will inject wires for all matching services in the composite. So, if the LoanComponent was a multiplicity, as shown in Listing 7.10, and autowire was enabled as in the previous SCDL examples, the component implementation would be injected with all matching CreditServices.

Listing 7.10 Autowire and Multiplicity References

image

Perspective: When to Use Autowire

Autowire is a somewhat controversial feature. We prefer autowire because it reduces the amount of “manual” assembly required for applications. Moreover, it makes the resulting composite configuration less susceptible to breaking during refactoring. If a component name changes, or a service is moved to a different component, autowired references will automatically be adjusted by the runtime. Explicitly targeted references will need to be manually updated.

Some (rightfully) point out that autowire makes wiring less apparent when looking at the composite XML. Although graphical tooling can help visualize how references will be autowired, it can’t help with avoiding unintended consequences, such as the runtime selecting the “wrong” service. This can happen if two services implement the same interface contract and the runtime is unable to select among them. In these cases, the best option is explicit wiring because it will guarantee that the correct target service is chosen.

Autowire and Composition

Autowire has specific rules for composition—namely, if not specified for a composite, the autowire setting is inherited from the composition hierarchy. To understand how this works, consider the case where the LoanApplicationComposite contains a component whose implementation is provided by the CreditScoreComposite. If the CreditScoreComposite does not specify an autowire value and the LoanApplicationComposite does, autowire will be inherited from the latter, as shown in Figure 7.4.

Figure 7.4 Inheriting autowire settings

image

In contrast, when autowire is explicitly set in the CreditScoreComposite, this value takes precedence over settings in the LoanApplicationComposite (see Figure 7.5).

Figure 7.5 Explicitly setting autowire in the CreditScoreComposite

image

Now consider a slightly different case where the CreditScoreComposite inherits its autowire setting (that is, it does not specify autowire), but the CreditComponent, which uses the composite as its implementation, explicitly declares an autowire setting. In this case, the inherited value will be determined from the CreditComponent setting (see Figure 7.6).

Figure 7.6 Explicitly setting autowire in the CreditComponent

image

If autowire values are not set, inheritance will be calculated by examining the composition hierarchy for an autowire declaration until the top-level domain composite is reached. If no setting is found, autowire will be false, because it is the default.

Wire Reinjection

SCA is often misinterpreted as being based on a static wiring model—that is, once a component is deployed, the services to which its references are wired cannot be changed. In fact, SCA allows for wires to be changed dynamically at runtime in certain circumstances. How rewiring is done (for example, through a management tool, command-line utility, or other means) is runtime-specific. SCA defines the following rules for how reinjection occurs:

• The component must be composite or conversation scoped. Rewiring stateless components would not make much sense and could have potentially damaging results as the target of a reference could change in the middle of a request being processed. For conversation-scoped components, a wire can only be changed if a conversation is not active. (If the change occurred during a conversation, the runtime would apply the change after the conversation has ended.)

• When a reference is rewired, the runtime injects a new service proxy or collection containing service proxies in the case of a multiplicity reference. Reinjection will be done for field- and setter-based references. Note that if a component uses constructor injection, it will not be injected with a new service proxy (or proxies).

Summary

This chapter has covered some of the more advanced wiring capabilities provided by SCA. The next chapter turns to a discussion of deployment and runtime management, and specifically in-depth coverage of SCA domains.

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

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