6. Policy

The term “policy” can mean many things. When some people talk about policy, they are referring to governance rules that mandate steps and procedures that must be followed in order to develop, deploy, or modify an application. This is not what is meant by “policy” in SCA.

In SCA, a policy is a statement that controls or constrains some capability that is provided by the infrastructure.

Policy Examples

Examples of infrastructure capabilities that can be managed using SCA’s policy framework include the following:

• Authentication

• Confidentiality (encryption)

• Integrity (signing)

• One-way message reliability

• Transaction propagation

These are not just arbitrary examples. They are the set of policies that were in the minds of the designers of SCA’s policy mechanism, so they should work fairly well. For other things that might be described as policies, the more they differ from the preceding examples, the less likely they are to be well-suited to SCA’s policy framework. Service level agreements (SLAs), such as promises about the average latency that will be provided by a service, are near the edge of this distinction. We do not know of any reason why they should not be well-suited to SCA’s policy framework, but there is also very little experience with the framework.

When we say that policies are capabilities provided by the infrastructure, we are differentiating them from the capabilities that are part of the code of components. Application code should not deal with the numerous transport and protocol issues that surround the sending and receiving of messages. Developers should be able to just concentrate on the application logic.

Nonetheless, it is sometimes necessary for someone to provide details about how certain infrastructure capabilities will be provided. For example, what encryption standard will be used to achieve confidentiality? In SCA, the person who makes those decisions and configures the associated details is the policy administrator.

SCA Policy in Brief

Before going into more detail on features of the policy framework, it is useful to get an overview of the policy mechanism by looking at how encryption would be specified by the various roles involved in application development.

The developer specifies an @Confidentiality annotation next to the @Reference annotation that designates a reference. This turns into a reference declaration in the component type that includes the following:

<reference requires="sca:confidentiality" ...>

That @requires attribute holds a list of intents. This means that the developer requires that some mechanism must be used to ensure confidentiality on calls through this reference.

It is the job of the policy administrator to make sure that the “confidentiality” intent lines up with a policySet. The policy set definition contains policy assertions (usually in the form of WS-Policy), the intent that is guaranteed by that policy set, and the circumstances in which it applies. The circumstances are in the form of an XPath expression run on the binding where the policy will be used, although in many cases, the XPath expression can just be the name of the binding type (for example, @appliesTo="binding.ws").

Policy administrators install these policies by including them in definitions.xml files that are installed into the domain using the contribution mechanism, which will be described in Chapter 9, “The Domain.”

Intents

An intent specifies a capability without identifying how it will be provided. The definition of an intent is specified in a definition.xml file that may be installed into a domain using a contribution. However, runtimes usually have a number of intents preinstalled, such as the intents that are specified by the SCA standard itself.

Listing 6.1 provides the definition of SCA’s confidentiality intent.

Listing 6.1 Confidentiality Intent Definition

image

The intent can be specified in implementation files, interface files, component types, and composites. In component types and composites, the intents are specified as a space-separated list in an @requires attribute. The attribute can be put on any element. For binding and implementation elements, the attribute directly affects the element.

For any other element, the effect is indirect. The required intents are added to any binding or implementation element that is a descendent of the element that defines it (using the XML hierarchy). This enables you to specify confidentiality on every binding within a composite by specifying @requires="sca:confidentiality" on the composite element itself. In some cases, compositewide intents might be overridden by intents on lower-level elements.

PropagatesTransaction—An Example of a Required Intent

Transaction policy is an example of a situation where the component developer needs to be able to declare that it needs something from the infrastructure. Only the developer of a component knows if an atomicity guarantee is required for a component. If atomicity is required, the component must be developed in a way that guarantees any operation will either complete in its entirety, or the system will be put back into the same state it was in before the operation began.

One way to achieve this guarantee is for both client and service providers to create appropriate compensation logic, which undoes any completed steps if there is a failure before the entire logical transaction completes. Creating such compensation logic is a pain but is necessary if transactions can’t be used. This can happen if a component has references to services that can’t enlist in the same transaction as the component.

If transactions can be used, guaranteeing atomicity is much simpler. In order to include a service call in the transaction of the caller, SCA defines an intent called propagatesTransaction. If this intent is present on a reference, the reference must be wired to a service that can enlist in the transaction. This frees the developer from creating compensation logic. However, it also constrains the deployer, because the reference can only be wired to services that can join the transaction. The propagatesTransaction intent places this constraint.

In the example from the previous chapter, the credit component might mark its references to the scoring system and the audit system as needing to enlist in the transaction, so the scoring result is recorded in the audit log, no matter what. Figure 6.1 shows the references that require propagatesTransaction in white.

Figure 6.1 Credit components require some references to propagate transactions.

image

policySets

policySets are defined by policy administrators, and they state the details of how a particular intent should be accomplished under various circumstances. policySet definitions are found in definition.xml files, which can be installed in a domain via a contribution.

policySet definitions hold collections of policy assertions that accomplish some intent (or set of intents) under curtain circumstances. The policy assertions are typically WS-Policy expressions, although other policy languages are allowed. The intents that the policySet accomplishes are listed in the definition of the policy set in a @provides attribute. The circumstances where the policySet applies are represented as an XPath expression inside an @appliesTo attribute.

For example, the propagatesTransaction intent listed previously would be provided by the following policySet (see Listing 6.2).

Listing 6.2 A Composite Scoped Component Implementation

image

The contents of this policySet is a WS-Policy assertion that was defined in the WS-AtomicTransaction specification. The @provides attribute lists the sca:propagatesTransaction intent as the only intent that is provided by the policySet.

The @appliesTo attribute is actually a relative XPath expression, although in this case, as in many cases, it is simply a QName.

How @appliesTo Is Used

The processing rule for the @appliesTo XPath expression is that it runs against the parent element of every binding or implementation element in the document. If the relative XPath expression returns the binding or implementation element that you are checking, this policySet applies to this binding; if not, it doesn’t.

The result of this processing rule is that the most common thing that determines the applicability of a policySet, the binding type, is specified with an XPath expression that is just the QName of the binding, such as "sca:binding.ws". That XPath expression run on the parent element of some binding.ws element will return all bindings with a QName of sca:binding.ws. Because the binding being checked is in this set, the policySet applies.

If you only wanted a policySet to apply if the @uri attribute on the binding starts with the “https” scheme, you would write the appliesTo attribute as follows:

appliesTo="sca:binding.ws[starts-with(@uri, "https:")]"

You can also apply bindings only when they are used in specific contexts. For example, you could write a policySet that is used on services or references that use the CreditService WSDL interface, by using an appliesTo of the following:

sca:binding.ws[../interface.wsdl/@interface="bb:CreditService"]

The conditional part traverses to the parent element (the “..”), which will be either a service or a reference element. Either of these can specify their interface using interface.wsdl, so this finds the ones whose interface.wsdl has a @name attribute with the QName of "bb:CreditService".

Finding the Right Policy Set

The SCA Policy Framework specification has a detailed algorithm that describes how to propagate intents down the XML hierarchy and how to find a matching policy set. The algorithm works on a composite, and the result of the algorithm should be that all intents are discovered to be satisfied in some way—either by finding an appropriate policy set or because of the built-in capabilities of bindings or implementations.

Some of the basic steps in the algorithm are the following:

1. Copy intents from each component type (that is, from the implementation) into the components that use those component types.

2. Copy in the intents from the interfaces used by any service or reference.

3. Propagate intents down the XML tree.

4. For each binding or implementation in the composite, find the smallest set of policy sets that provide all the intents listed for that binding or implementation and where the @appliesTo matches.

If there are any bindings or implementations where no set of policySets can be found that achieve that set of intents, there is an error. If more than one set can be found that achieve it, the deployer has to choose.

Perspective: Declarative Policy Versus API

SCA’s separation of policy details from application logic is not new. Transaction monitors, database systems, message-oriented middleware (MOM), and other infrastructure software products have always made it possible to offload the implementation of these capabilities from the average developer. However, in the past, these systems have been made available through APIs. This means that calls to control these infrastructure capabilities have been intermixed with application logic. Often it is the case that the amount of code devoted to calling infrastructure APIs dwarfs application logic. For example, the following is an example of reliably sending a message to a JMS queue:

image

The concepts of the queue connection, queue session, and queue sender are all needed so that there are places to put the APIs for configuring the numerous capabilities of JMS.

This approach has the following disadvantages:

• The application logic is hard to follow by looking at the code. If application conditions change, it is hard to find the corresponding logic that needs to change.

• Organizations have to hire developers who know both the subtleties of the options available through the infrastructure APIs, as well as the potentially complex control flows and business rules that are critical to the business.

• If, after deployment, it is discovered that some different infrastructure option would be better-suited for some component or some communication path, the code has to be rewritten.

SCA does not assume that all the complexities associated with the myriad of capabilities provided by infrastructure software will suddenly disappear. Rather, it assumes that those complexities will continue to exist but they will be separated from application code. The component developer will specify as little as possible in order to guarantee the correct execution of the component. The infrastructure choices will then be specified declaratively, using either binding configuration or through policy.

The “little as possible” mentioned previously has to do with the fact that sometimes the writer of the business logic knows that certain capabilities from the container must be provided in order for the component to operate correctly.

Now consider what it looks like in SCA to send the previous JMS message persistently:

image

Some of the differences have nothing to do with policy. JMS is loosely typed, so it uses a general-purpose send() method. SCA requires a more specific operation signature for each asynchronous method.

The policy aspect of this is the top line, which requires the exactlyOnce intent. This is saying that the one-way method invocation should be delivered using whatever infrastructure configuration is necessary in order to get the message there exactly one time (no duplicates and no dropped messages). The details of how the system is going to achieve this, including the timeout values that should be used, the persistent mechanism to be used, and other such details, are left to the policy administrator.

Wire Validity

When policy sets are found for each binding, SCA has to make sure the resulting wires are valid from a policy perspective. SCA does not define how it accomplishes this for anything other than WS-Policy.

For WS-Policy, SCA uses the policy intersection algorithm that was defined in WS-Policy. The policy expressions on the bindings on each side of a wire have to line up (intersect) in order for the wire to be valid.

With our PropagatesTransaction example, the WS-Policy assertion is <wsat:ATAssertion>. In order for there to be a match, both sides of the wire need to have that assertion. If both sides don’t specify it, there is no match and the wire is invalid.

The fact that the WS-Policy matching algorithm would fail when the reference has the ATAssertion and the service does not is appropriate in this scenario. In essence, the reference is saying: “I must be wired to a service that knows how to undo its work if I ever roll back, because I don’t have any compensation handling logic.” If the service doesn’t declare that it can join the transaction, as far as SCA can tell, it will not be able to, and the requirement made by the reference is not met.

WS-Policy

The preceding scenario used a trivial WS-Policy expression. It had a single policy assertion, <wsat:ATAssertion>, which had to exist on both sides of the wire. In general, WS-Policy matching is a bit more complicated than this, because each side of the wire can include arbitrarily complex expressions that involve optional policy assertions or alternative assertions. WS-Policy does not define any specific policies; it just describes an expression language for combining them. The concepts defined by WS-Policy are the following:

Policy assertion—Policy assertions are the atoms from which larger policy expressions can be built. A policy assertion is simply an XML element. It can come from any namespace. Two policy assertions definitely match if they have the same element QName, the same attribute values, and the same subelements (if any). If they differ, the policy assertions may or may not match—it depends on the definition of the policy assertion (what a pain!).

ExactlyOne E1, E2, ...—This says that E1, E2, and so on are policy choices. Exactly one of those policy expressions must be used when using the service.

All E1, E2, ...—This says that expressions E1, E2, and so on are policy expressions that must be enforced together.

Optional policy assertions—A policy assertion may be marked with an attribute of wsp:optional="true". This means that the policy is available if the other side of the wire asks for it, but it isn’t required by this side. It is defined as a macro expansion of an ExactlyOne with two subexpressions—one with the assertion, and one without it.

Ignorable policy assertions—A policy assertion may be marked with an attribute of wsp:ignorable="true". This is used to declare characteristics of a service that don’t require cooperation from both sides—such as audit logging. One side of the wire is declaring that it is going to do something (for example, log messages). This way, if the other side of the wire requires that it should only be wired to services that do such logging, the assertion is there to cover it. If the other side doesn’t care, the wire will work anyway (and the logging will happen anyway).

Looking at the preceding concepts, you can see that they are basically the XOR and AND operators from predicate logic. It is notable that they do not have the inclusive OR operator or the NOT operator. The lack of a NOT operator means that it is not possible for a reference, for example, to say that it must not be wired to a service that does audit logging.

The lack of an inclusive OR means that if a service can do A, B, or A and B, instead of just being able to say:

</Or><A/><B/></Or>

it must say:

image

So, inclusive OR is possible; it is just ugly.

Because WS-Policy doesn’t define any policy assertions, it must leave those to other specifications. Some of the specifications that define standard policy assertions include WS-AT, WS-SecurityPolicy, or WS-ReliabilityPolicy.

There is one final important concept that WS-Policy defines, as follows:

Policy intersection—This is used for determining if two policies are “compatible.” The two policies are set side-by-side, and any place one side has an exactlyOne, the other side has to have a policy expression that matches one of the alternatives (possibly also within an exactlyOne). The intersection continues in a predictable manner given the concepts defined previously. One thing that is noteworthy about WS-Policy intersection is that it is symmetric. The service provider and the service client both specify policy expressions, and the two sides are treated the same. This may seem odd at first, because service providers seem like the more natural place to declare requirements on any client of that service. (After all, WSDL is only defined for the provider side, not for clients.) But, in fact, clients may sometimes have requirements of their own, which need to be communicated to whatever human or system is going to be finding a matching service.

Returning to our example, consider the one wire between the Credit and the Scoring components (see Figure 6.2).

Figure 6.2 Required intents on the two ends of a wire

image

Each of these intents will resolve to the same policySet—namely, the policySet mentioned previously:

image

The policy infrastructure requires the intersection of the policies on each end of the wire in order to determine the policy actually used. In this case, this becomes the intersection of a policySet with itself, so the intersection is the same as the original policySet.

Because the <wsat:ATAssertion/> policy assertion is present on the wire, the transaction will be propagated.

Policies for One-Way Messaging

SCA also defines a few intents related to the reliability of one-way messaging.

Let’s return to the application presented in Chapter 5, “Composition,” but this time after it ended up looking like what is shown in Figure 6.3.

Figure 6.3 Big Bank’s top-level composite

image

The outermost composite in this picture is shown in Listing 6.3.

Listing 6.3 Wiring to the Audit Component

image

We haven’t previously looked at the interface for the AuditComponent, so let’s do that now. AuditComponent will have a single @OneWay operation called auditEntry, which takes as a parameter a string representation of the auditable step that needs to be recorded (see Listing 6.4).

Listing 6.4 Interface for the AuditComponent

image

The operation is a one-way operation, because it does not want to slow down the normal flow of the business logic. However, it is also important that the audit entries not get lost! This is where policy can help. We should add the following requirements to this wire:

exactlyOnce—So that we don’t lose any audit entries, and none get entered multiple times

authentication—So that no one forges an audit entry

integrity—So that the entries are signed, preventing third-party modification of the audit entry

To add these requirements to LoanApplicationComposite, all that is necessary is to add an entry for each of these requirements into a @requires attribute of the reference (see Listing 6.5).

Listing 6.5 Wiring to the AuditComponent

image

Qualified Intents

There are times when a developer might want to specify more about how a generic capability is going to be provided than is possible with simple intents. Qualified intents are intents that provide this additional detail beyond some existing simple intent.

Consider the confidentiality intent that was introduced at the beginning of this chapter. Confidentiality is typically accomplished through encryption, although different mechanisms exist. Some handle encryption on a point-to-point basis. This is typically called transport-level encryption. Other techniques exist to encrypt a message in such a way that intermediaries can process and route the message without decrypting the body of the message. This is called message-level encryption.

If a developer specifies that she requires sca:Confidentiality on a reference, this does not constrain the kind of encryption technique that is used. However, some developers may know enough about the data and the way it is supposed to be protected to know that message-level encryption is required. In this case, it should be possible to require message-level encryption, but still to do it at the level of intents, rather than having to dive down into the details of a specific binding.

In SCA, an intent may be qualified by extending it with a “.” and the name of some valid qualifier for that intent. In the case of confidentiality, there are two qualifiers, which are written as sca:Confidentiality.Message and sca:Confidentiality.Transport.

Qualified intents are not independent intents from intents that they qualify. The most important relationship between them is the fact that anything that satisfies a qualified intent also implicitly satisfies the intent that it qualifies. This is especially useful between the various roles involved in application development. If a developer requires a specific intent, an assembler or deployer may further refine that requirement by requiring a qualified version of the intent.

Even within a single role, it is sometimes valuable to specify a more general intent that applies broadly, perhaps to everything within a composite, and then to refine that general intent down to a specific intent for some specific service. For example, the confidentiality intent might be specified on the composite, but some of its services might specify Confidentiality.Message.

Profile Intents

There are times when a collection of intents are so frequently used together that it makes sense to have a single intent that expands into other intents. A profile intent is such an intent. The definition of a profile intent includes a @requires attribute that lists the intents that it should expand into.

Among the set of standard SCA intents, the reliability intent of sca:ExactlyOnce is a profile intent that expands to sca:AtLeastOnce and sca:AtMostOnce. As is always the case with an @requires attribute, the semantics of the list is AND—all the intents must be satisfied.

Listing 6.6 presents the definition of the sca:ExactlyOnce profile intent.

Listing 6.6 ExactlyOnce Intent Definition

image

Standard Intents

A variety of the standardized intents have been referenced in the previous section. The following is an exhaustive list of the intents that have been standardized as of SCA 1.0. All these intents are in the SCA namespace.

Security Intents

The security intents include the following:

Authentication—The identity of the requestor of the service must be verified.

Confidentiality—Some mechanism (such as encryption) must be used to prevent a message from being read by anyone other than the intended recipient of the message.

Integrity—A message must be protected from being surreptitiously modified after it has been created (such as by attaching an electronic signature).

Each of the three preceding intents have the following two qualifiers:

Transport—The guarantee need only be guaranteed at the transport level (that is, for a single hop).

Message—The guarantee should be at the message level. It should provide an end-to-end guarantee.

Delivery Intents

The delivery intents include the following:

AtLeastOnce—At least one copy of the message must be delivered (duplicates allowed).

AtMostOnce—At most, one copy of the message must be delivered (no duplicates; dropped messages allowed).

ExactlyOnce—The message must be delivered once and only once.

Ordered—Messages from the same client must be delivered in the same order that they were sent.

Transaction Intents

Transaction intents include the following:

PropagatesTransaction—The intent requires that the other side of the wire must be able to be in the same transaction. If the intent is on a reference, it means that the service provider must be able to join the transaction. If it is on a service, it means that the client must be able to provide a transaction to join. This must be used with ManagedTransaction.global on the implementation.

SuspendTransaction—This intent goes on service and references and prevents any transaction that might be active from being shared across that wire.

ManagedTransaction—This intent goes on implementations and means that the components must be run within some kind of transactional environment, although it may not be global. Usually this means that it has to be able to use a database to do the work of a method as one atomic unit (without having to specify explicitly transaction boundaries), but doesn’t need to share its transaction with its clients or its downstream services.

ManagedTransaction.Global—This is a qualified intent that extends ManagedTransaction to say that the transaction must be one that can enlist upstream or downstream components.

ManagedTransaction.Local—This qualifier clarifies that a local transaction, rather than a global transaction, should be used. This provides better performance, but introduces the possibility that a system crash could cause the changes to one resource to be lost even while changes made from the same transaction to a different resource are committed.

NoManagedTransaction—No managed transaction should be used.

Miscellaneous Intents

Miscellaneous intents include the following:

SOAP—The SOAP messaging model should be used. Note that this does not constrain the transport that might be used to send the SOAP message. If it is unqualified, any version of the SOAP standard may be used.

SOAP.1_1—The SOAP v.1.1 standard message model must be used.

SOAP.1_2—The SOAP v.1.2 standard message model must be used.

JMS—This application uses the JMS API, so the binding must support this API.

NoListener—The binding must be able to handle any incoming traffic through the back channel of an outbound request. There is no listener for inbound traffic. For asynchronous responses, the binding may need to use polling.

Differences from Java EE Transactions

Java EE also provides transactional component behavior through declarative configuration. In Java EE, this capability is called container-managed transactions. In J2EE version 2.1 and earlier, the deployment descriptor would mark the session beans with a <transaction-type> element with a value of Container. There is then a separate section that describes which Java EE transaction mode each of these beans requires. A small example in SCA would be represented as the following:

image

The same component might be represented in a deployment descriptor, as follows:

image

This declares that the MyAppBean will require the existence of a transaction, which should be guaranteed by the container. In this case, the ManagedTransaction intent basically maps to the Required Java EE transaction attribute. The values for that attribute are: NotSupported, Supports, Required, RequiresNew, Mandatory, and Never.

One of the problems with the Java EE transaction attributes is that they confuse two concepts, as follows:

• Should the component be run in a transaction?

• Should the transaction be the one propagated from the client?

For example, the “Mandatory” Java EE attribute says that the component should run in a transaction, and the client must propagate a transaction for it to join.

In SCA, the preceding two questions are represented by separate intents. The ManagedTransaction intent says that the component should be run in a transaction. This is an implementation intent, so it is not seen by clients. However, clients can see a separate interaction intent called PropagatesTransaction. This intent says that the clients must send a transaction that the component can join. If the interaction intent had been SuspendsTransaction, the client could know that any transaction associated with the client would not be used by the component.

In general, it is not really part of the component contract if the component uses a transaction but does join in the transaction of the client. The client doesn’t need to know whether it uses transactions in that case.

Summary

This chapter has covered using SCA policy to declaratively configure such things as transactions, security, and reliability. One of the key goals of SCA policy is to abstract the complexity associated with specifying security, reliability, and other qualities of service from application code. SCA achieves this policy abstraction by providing policy intents that are used by developers to signal the need for an abstract quality of service, and then are turned into concrete policies by policy administrators.

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

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