There are two steps involved in developing your domain specific assertions, these are:
- Implementing the Assertion and AssertionBuilder interfaces, and registering the AssertionBuilder with the AssertionBuilderRegistry
- Providing runtime support for the Assertion, either in form of an interceptor or inside a conduit or a destination, and registering that support if necessary.
Th steps are outlined in some more detail below:
Implementing the Assertion Interface
You can chose to implement the Assertion interface from scratch, or decide to use one of the existing Assertion implementations in the cxf-api module, extending them as required:
PrimitiveAssertion
This class represents an assertion without any attributes or child elements (in particular without a nested Policy element). The AnonymousResponses or NonAnonymousResponses assertions in the addressing metadata namespace http://www.w3.org/2007/01/addressing/metadata are example of this type of assertions. The implementation of the equal and normalize methods in the class are trivial, and there should be no need to extend this class.
NestedPrimitiveAssertion
This class represents an assertion without any attributes, but with one mandatory nested Policy child element. The Addressing assertions in the addressing metadata namespace is an example of this type of assertion. The implementation of the equal and normalize methods are generic, and there should be no need to extend this class.
JaxbAssertion
This class represents an assertion described by an xml schema type that has been mapped to a Java class. The RM assertion as well as the assertions used in the HTTP module are extensions of this class. Although the equal and normalize methods are not abstract, you probably want to overwrite these methods.
Implementing and Registering the AssertionBuilder Interface
Assertion Builder class for custom assertion should implement AssertionBuilder<T> interface. The interface type can be Element, XMLStreamReader or OMElement.
Interface contains two methods: build() and getKnownElements().
Implementation of build() method should construct Assertion from the incoming type. It can be PrimitiveAssertion (without attributes or child elements), NestedPrimitiveAssertion (without attributes but with nested policy element) and JaxbAssertion (assertion described by any XML schema).
getKnownElements() method must return QNames of assertion elements from which assertion can be built.
Implementing the build method of the AssertionBuilder interface is straightforward (in the case of JaxbAssertions you can extend the JaxbAssertionBuilder class, which provides an appropriate JAXB context and some other useful methods).
The implementation of buildCompatible may need some more consideration if your assertion represents an element with attributes and/or child elements.
Registration of your AssertionBuilder with the AssertionBuilderRegistry is easy enough: simply add a bean for your AssertionBuilder to the cxf-* file of your module, or to the application's custom cfg file.
Implementing a Policy-Aware Interceptor
This is the easiest way of providing runtime support for an Assertion. Steps 1. and 2. listed in Interaction with the Framework can usually be coded as follows:
package mycompany.com.interceptors; import org.apache.cxf.ws.policy.AssertionInfoMap; class MyPolicyAwareInterceptor { static final QName assertionType = new QName("http://mycompany.com}", "MyType"}); public void handleMessage(Message message) { // get AssertionInfoMap org.apache.cxf.ws.policy.AssertionInfoMap aim = message.get(org.apache.cxf.ws.policy.AssertionInfoMap.class); Collection<AssertionInfo ais> = aim.get(assertionType ); // extract Assertion information for (AssertionInfo ai : ais) { org.apache.neethi.Assertion a = ai.getAssertion(); MyAssertionType ma = (MyAssertionType)a; // digest .... } // process message ... // express support for (AssertionInfo ai : ais) { ai.setAsserted(...); } } }
Sometimes, it may be more convenient to spead the above functionality accross several interceptors, possibly according to chain (in, in fault, out, outfault). In any case, you need to also provide a PolicyInterceptorProvider, and declare a corresponding bean. Either implement one from scratch or use the PolicyInterceptorProviderImpl in the api package and customise it as follows (assuming that one and the same interceptor is used for all paths). The main task of policy interceptor provider is to say which interceptors must be activated for specified policy assertion:
<bean name="MyPolicyAwareInterceptor" class="mycompany.com.interceptors.MyPolicyAwareInterceptor"/> <bean class="org.apache.cxf.ws.policy.PolicyInterceptorProviderImpl"> <constructor-arg> <!-- the list of assertion types supported by this PolicyInterceptorProvider --> <list> <bean class="javax.xml.namespace.QName"> <constructor-arg value="http://mycompany.com}"/> <constructor-arg value="MyType"/> </bean> </list> </constructor-arg> <property name="inInterceptors"> <list> <ref bean="MyPolicyAwareInterceptor"/> </list> </property> <property name="inFaultInterceptors"> <list> <ref bean="MyPolicyAwareInterceptor"/> </list> </property> <property name="outInterceptors"> <list> <ref bean="MyPolicyAwareInterceptor"/> </list> </property> <property name="outFaultInterceptors"> <list> <ref bean="MyPolicyAwareInterceptor"/> </list> </property> </bean>
All beans of type PolicyInterceptorProvider are automatically registered with the framework's PolicyInterceptorProviderRegistry.
It is also possible to implement policy interceptor provider programmatically from scratch. It's constructor gives assertions QNames as argument of super constructor and adds corresponded interceptors using getters:
public class MyInterceptorProvider extends AbstractPolicyInterceptorProvider { private static final long serialVersionUID = -5248428637449096540L; private static final MyInInterceptor IN_INTERCEPTOR = new MyInInterceptor(); private static final MyOutInterceptor OUT_INTERCEPTOR = new MyOutInterceptor(); private static final MyOutFaultInterceptor OUT_FAULT_INTERCEPTOR = new MyOutFaultInterceptor(); private static final Collection<QName> ASSERTION_TYPES; static { ASSERTION_TYPES = new ArrayList<QName>(); ASSERTION_TYPES.add(new QName("www.mycompany.org", "myassertion")); } public MyInterceptorProvider() { super(ASSERTION_TYPES); getInInterceptors().add(IN_INTERCEPTOR); getOutInterceptors().add(OUT_INTERCEPTOR); getOutFaultInterceptors().add(OUT_FAULT_INTERCEPTOR); } }
Since version 2.5.2, Assertion builder and policy interceptor provider can be registered using CXF bus extension mechanism: just create a file META-INF/cxf/bus-extensions.txt containing the following:
org.company.MyInterceptorProvider::true org.company.MyAssertionBuilder::true
Boolean value at the end specifies lazy loading strategy.
CXF automatically recognizes the assertion builder and policy interceptor provider and store them into registries: AssertionBuilderRegistry and PolicyInterceptorProviderRegistry.
Since CXF 2.6.0 it is possible to register multiple interceptor providers for single assertion.
Implementing a Policy-Aware Conduit/Destination
Initialisation
Conduits/Destinations have access to the EndpointInfo object in their their constructors,. Assuming they also have access to the bus, they can at any time in their lifecycle obtain the effective policy for the endpoint as follows:
class MyPolicyAwareConduit { static final QName assertionType = new QName("http://mycompany.com}", "MyType"}); ... void init() { PolicyEngine engine = bus.getExtenation(PolicyEngine.class); if (null != engine && engine.isEnabled()) { EffectiveEndpointPolicy ep = engine.getEndpointPolicy(endpoint, this); Collection<Assertion> as = ep.getChosenAlternative(); for (Assertion a : as) { if (assertType.equals(a.getName()) { // do something with it ... } } ... } }
and similarly for a Destination.
Policy-Aware Message Sending
Given access to the Message object, a conduit can, in its send method, proceed the same way as an interceptor in handleMessage. It can defer the updating of the assertion status in the AssertionInfo objects until called upon by the PolicyVerificationOutInterceptor, i.e. implement the status update in the assertMessage method. If the status update takes place inside of the send method itself, assertMessage, at least for outbound messages, can be implemented as a no-op.
Implementing the Assertor Interface
With canAssert, the conduit/destination simply informs the framework if it understands a given type of assertions. In assertMessage on the other hand, the conduit/destination expresses support (or the lack thereof) for specific assertion instances. See Verification for a description of how this API is used by the verifying policy interceptors in the POST_STREAM or PRE_INVOKE phases.
HTTPConduit is an exmaple of a policy aware Conduit. It supports assertions of type HTTPClientPolicy, which are represented in the runtime as JaxbAssertion<HTTPClientPolicy> objects. HTTPConduit also has a data member of type HTTPClientPolicy. It implements assertMessage as follows: for outbound messages, it asserts all JaxbAssertion<HTTPClientPolicy> that are compatible with this data member. For inboun d messages, all HTTPClientPolicy assertions are asserted regardless their attributes. The rationale for this is that the sematics of the HTTPClientPolicy assertion effectively does not mandate any specific action on the inbound message.
Similary, on its inbound path, the HTTPDestination asserts all HTTPServerPolicy assertions that are equal to the HTTPServerPolicy assertion configured for the destination, and all assertions of that type on the outbound path.
class MyPolicyAwareConduit implements Assertor { static final QName MYTYPE = new QName("http://mycompany.com}", "MyType"}); public boolean canAssert(QName name) { return MTYPE.equals(name); } public void assertMessage(Mesage message) { AssertionInfoMap = message.get(AssertioninfoMap.class); ... } }