PolicyExecutionStage, or the seasons of a ws-policy assertion
Today I discovered that apparently the Microsoft.Web.Services2.Policy.PolicyExecutionStage type lives behind some sort of event horizon: no mention of it on the net (nor the use-net), if you exclude the usual msnd entry. Yet it's a very useful compass while you implement custom policy assertions, so I'm going to tell you what it is about. The following comes from observation of the WSE behaviour while handling a custom assertion.
I'm not going in big details here, WSE documentation, quickstarts and good articles around supply excellent coverage of the syntactic sugar; however I need to contextualize the following, so I have to set up the stage: but promise me you'll go check serious sources later.
A custom assertion is simply a class deriving from PolicyAssertion. Among other things, there are two methods you want to override:
- bool IsSatisfiedBy(SoapEnvelope message)
That goes without saying. You check the message, you decide if it's a go or a no-go.
- bool CanEnforce(ref IPolicyEnforcer enforcer)
This one is trickier. Your code here must decide if it can do something to make a message comply to the policy: if it is the case, it should then assign the enforcer with a suitable enforcer instance (more about it below). A policy can be enforced only on the sending side, and that's only reasonable: what is less advertised is that if you are not on the sending side the policy framework seems to avoid calling the method at all.
- the constructor
I know, I said TWO. I forgot the costructor: there's where you parse the XML fragment representing your assertion.
An enforcer is simply a class implementing IPolicyEnforcer: an hymn to the saying "simple but effective", the interface exhibit a single member:
- void Enforce(SoapEnvelope message)
This method is responsible for mangling in the message the directives imposed by the specific policy. Can very well implemented by the assertion class itself, as demonstrated by the WSE quickstart sample.
Now, a reasonable sequence with which the above are invoked looks pretty much like that:
- Constructor - At the first web service call, parse the policyCache.config hence call the constructor of every assertion (including yours)
- CanEnforce - but only if you are on a sending side (you are a client sending a request, or a service sending a response). The info will come handy at the next step
- IsSatisfiedBy - the moment of the truth. If you are on the receiving end, returning a false will always storm the place (i.e.: throw an exception); if you are a sender and you answered true to the question CanEnforce, you will proceed to the next step.
- Enforce - here you do your best to weave your stuff in the envelope. If you succeed the message will go on to the next assertion (and you don't know what the next one is, though the probability that is the next one in the declaration order is very very high)
Fairly simple model, very usable: great object model, good work. So far, however, it's not clear how a custom implementation may tell if a method is executing on the sending or the receiving sides: there are situations in which such a knowledge may come useful. Let's forget about the constructor, and let's consider all other methods:
- CanEnforce - easy. If it gets called, we are on sending side. On the receiving side, no check.
- Enforce - Same here.
- IsSatisfiedBy- hmmmmmmmmmmm............
OK, I'll get to the point. The stages through which policy processing goes through are formalized in a static variable of type PolicyExecutionStage, which you can find under the current policy context: PolicyContext.Current.Stage.
PolicyExecutionStage is an enum of four values:
- Parsing - policy is being parsed from the file
- Compilation - policy is being compiled in its memory format
- Enforcement - the message is being modified in order to make it compliant
- Verification - the message is being checked for compliance
For eliminating the ambiguity of the IsSatisfiedBy execution environment I've added a trace inside every custom assertion method, displaying the state in which they were executing; and I applied my custom assertion to request and response on both the client and the web service.
The result is that IsSatisfiedBy runs in the Compilation stage when on a sending side, while it is in Verification stage when it is running on a receiving end. Below my traditional OneNote snapshot.