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.
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.
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 CreditComponent
s.
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.
When handling a request, the component iterates and invokes the various services individually, as shown in the excerpt in Listing 7.3.
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).
<wire>
ElementHaving 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).
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).
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 CreditComponent
s 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"
.
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.
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.
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.
The composite shown in Listing 7.7 demonstrates the use of autowire.
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).
Similarly, we could have restricted autowire to the individual reference (see Listing 7.9).
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
.
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.
In contrast, when autowire is explicitly set in the CreditScoreComposite
, this value takes precedence over settings in the LoanApplicationComposite
(see Figure 7.5).
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).
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.
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).
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.