Policy
Fabric3 supports an extensible interceptor framework for implementing cross-cutting policies based on SCA. Policies are used to implement behaviors such as transactions, security, or SLA alerts. Policy interceptors are enabled in three ways:
- Declared explicitly using Java annotations or in a composite
- Externally "attached" using XPath pattern matching at deployment
- Dynamically using XPath pattern matching against existing components
Policies can be thought of as a type of Aspect Oriented Programming (AOP) for services. However, instead of applying to OO constructs such as classes and methods, policies can also be defined to apply to services, components, and remote communications. Further, policies can be applied across applications and dynamically after components have been deployed, even in a cluster.
Policies are an extremely useful feature for reducing application complexity. For example, security code can be implemented as a policy and reused across applications. This isolates complexity in a single place and avoids the error-prone task of repeating a task multiple times.
The samples provide an example of implementing a custom policy that intercepts operation calls for services and counts invocations.
Concepts
Policies are commonly used without needing to delve into the details of how they are enabled. For example, Fabric3 transaction support is built on the policy framework. However, to make use of transactions, one need only understand transaction semantics and how to apply the @ManagedTransaction
annotation:
@ManagedTransaction public class MyTransactionalComponent { // ... }
The specifics of SCA policy are beyond the scope of this reference. Briefly, though, intents are abstract requirements that can be declared by a component or on a reference or service. Intents are matched to policy sets, which provide concrete configuration for a behavior. For example, a "message authentication" intent may be mapped to a policy set that specifies WS-Security or HTTP Basic Auth. Intents are therefore a way to specify a requirement without tying a component to a specific underlying technology. For transports that do not use WS-Security or HTTP, the "message authentication" intent could be mapped to a different security technology, such as a security API used by a JMS provider.
Intents may be specified using Java annotations or via the @requires
attribute or as part of a composite XML.
Creating Custom Policies
Policies are defined in a definitions file, definitions.xml
, that is deployed as part of a contribution archive. The definitions file can contain intents, policy sets, binding types, and implementation types. Custom policies may be bundled directly with user code (e.g. if it is only applicable to a service or set of services) or as a separate contribution.
Defining and Applying Policies
Implementing a custom policy involves implementing and configuring an interceptor that will be called to process a message during an invocation. The following XML definitions file demonstrates how to define an interceptor policy:
<definitions xmlns="[http://docs.oasis-open.org/ns/opencsa/sca/200903|http://docs.oasis-open.org/ns/opencsa/sca/200903]" xmlns:f3="urn:fabric3.org"> <policySet name="testImplementationPolicy" f3:phase="INTERCEPTION" attachTo="//component"> <f3:interceptor class="org.fabric3.interceptor.TestInterceptor"/> </policySet> </definitions>
The above example uses external attachment to enable policy. The "attachTo" attrbitute instructs Fabric3 to apply the interceptor to all components in the domain (note you would probably not want to do this in a real-world application). Again, the details of these SCA mechanisms are beyond the scope of this reference. Briefly, though, the value of the attachTo attribute is an XPath expression that is applied to the domain infoset. This is an extremely powerful capability as policies can be dynamically attached to any component, binding, or wire in the domain by specifying an XPath expression. This attachment can happen at deployment or be applied to already deployed components, bindings, and wires.
Note that in addition to external attachment, Fabric3 also supports a "pull" policy model where policies (or intents) are specified in the component configuration or via annotations.
The interceptor class for the previous policy set is shown below:
package org.fabric3.interceptor; import org.fabric3.spi.invocation.Message; import org.fabric3.spi.wire.Interceptor; public class TestInterceptor implements Interceptor { private Interceptor next; public Message invoke(Message message) { // perform some processing. return next.invoke(message); } public void setNext(Interceptor interceptor) { next = interceptor; } public Interceptor getNext() { return next; } }
Note the interceptor class implements org.fabric3.spi.wire.Interceptor
, which is located in the Fabric3 SPI archive (fabric3-spi.jar).
Including Policies in a Contribution
Policies (e.g. the definitions.xml file and supporting classes such as an interceptor) can be packaged and deployed as part of a contribution. This is useful if the policy only applies to a particular set of services. The only required step to do this is to ensure the contribution manifest (sca-contribution.xml) contains an import.java entry for the org.fabric3.spi package:
<contribution xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200903" xmlns:f3="urn:fabric3.org"> <import.java package="org.fabric3.spi.*"/> </contribution>
Packaging Policies Separately
Policies can also be packaged and deployed as individual contributions. Again, the only specific requirement is that the contribution manifest import the org.fabric3.spi
package. One important difference, however, with this approach is that the interceptor class will be loaded in a different classloader than the source and targets of a wire. In most cases, this will not matter as the component classloaders will not be needed. In cases where the source and target classloaders must be accessed, the policy contribution must use the lower level Fabric3 interceptor builder SPI to obtain references to the appropriate classloaders.
Creating Custom Intent Annotations
Custom intent annotations can be created by using the @Intent
meta-annotation. When @Intent
is applied to an annotation, Fabric3 will detect the annotation and apply its corresponding intent. For example, the SCA org.oasisopen.sca.annotation.Authentication
intent is defined in this manner:
@Inherited @Target({TYPE, FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) @Intent(Authentication.AUTHENTICATION) public @interface Authentication { String AUTHENTICATION = SCA_PREFIX + "authentication"; String AUTHENTICATION_MESSAGE = AUTHENTICATION + ".message"; String AUTHENTICATION_TRANSPORT = AUTHENTICATION + ".transport"; /** * List of authentication qualifiers (such as "message" or "transport"). * * @return authentication qualifiers */ @Qualifier String[] value() default ""; }
When the Fabric3 runtime encounters @Authentication
on a class, it will apply the "SCA authentication" intent to it.