Regarding some comments of my last post The Shadowfax Architecture, I will add some diagrams that will clarify some of the concepts presented in that post as well as getting into more detail about the internals of the pipeline.
Figure 1: The components of the Shadowfax services framework help expose a service interface over multiple channels with a “double” pipeline scheme.
In this diagram we can see a conceptual view of the architect that was described in the first post of this series (named above). Basically iterates through a list of handlers and manages the target activation. Recall that a handler is a block of code that satisfies some cross-cutting concern such as authentication, logging or transaction support (more of handlers later on this post).Note the multiple transports management at the beginning of each Pipeline. Here, the listeners transform the input message to the context object that can be handled in the pipeline. The reverse function is done to the output message. The context class contains all of the information for a request as it flows to the business action and then back to the client application. It contains an instance of the Request class, which contains information the client application passed in through the transport. If there is any response information from the business action, an instance of the Response class is stored in this object as well. When a pipeline iterates through a list of handlers, each handler is passed the context object (in fact through the IContext interface), which it uses to carry out its logic. Each handler may modify information in the context and in particular add a ContextItem. This class is used to store any additional information relevant to the request processing.The target of a pipeline implements the IPipelineTarget interface. In Shadowfax, only Dispatchers and Business Actions implement this interface. If it is a dispatcher, it instantiates a new pipeline otherwise a Business Action is activated.In either case, after the target of a pipeline has finished executing, any handler configured in the “after” stage is executed, and the response is returned back to · The client, in case of the service implementation (first) pipeline, or· The target of the service interface pipeline, in case of the service implementation (second) pipeline.The list of handlers is obtained from the configuration file ("ServiceFramework.config"). The handlers are executed in the order they appear in the file in the “<before>” and “<after>” groups in the pipeline configuration section. In the following figure we can see the two pipelines configuration sections.
<ra:pipelines> <ra:serviceInterfacePipelines> <ra:pipelinera:name="Default" ra:serviceActionName="*" ra:targetName="inproc"> <ra:before> <!-- (Authentication) Handlers --> <ra:handler ra:handlerName ="InfrastructureAuthentication"/> <!-- (Authorization) Handlers --> <ra:handler ra:handlerName ="InfrastructureAuthorization"> <ra:authzConfiguration ra:roles="NT AUTHORITY\Authenticated Users" ra:access="1"/> </ra:handler> </ra:before> </ra:pipeline> </ra:serviceInterfacePipelines> <ra:serviceImplementationPipelines> <ra:pipeline ra:name ="Default" ra:serviceActionName="MyBA" ra:targetName="businessAction"> <ra:before> <!-- Messaging Handlers --> <ra:handler ra:handlerName ="DuplicateHandlingHandler"/> <ra:handler ra:handlerName ="MessageTransformation"/> <ra:handler ra:handlerName ="SyntacticValidation"/> <! -- Instrumentation & Logging Handlers -- > <ra:handler ra:handlerName ="PublishBusinessEvent"/> <ra:handler ra:handlerName ="AppInstrumentationAround"/> <ra:handler ra:handlerName ="AppInstrumentation"/> <ra:handler ra:handlerName ="ClientTrace"/> <! -- Infrastructure Handlers (Transactions, Timeouts, others) --> <ra:handler ra:handlerName ="TxRequired"/> <ra:handler ra:handlerName ="ExecutionTimeout"> <ra:timeoutConfiguration ra:timeout ="10"/><!-- seconds --> </ra:handler> </ ra:before> <ra:after> <ra:handler ra:handlerName ="DuplicateHandlingHandler"/> <ra:handler ra:handlerName ="AppInstrumentationAround"/> </ ra:after > </ra:pipeline> </ra:serviceImplementationPipelines></ra:pipelines> |
In the Service Interface pipeline we have the authentication and authorization handlers placed in the “<before>” processing stage. Notice that each handler might have its own configuration settings. In this “Default” (see the pipeline name attribute) pipeline configuration, we have a set of handlers that will authenticate using the underlying operating system security infrastructure (IIS in case we are hosted in ASP.NET) and all account belonging to the “NT AUTHORITY\Authenticated Users“ group will be authorized to execute any business action (see the wild card * in the serviceActionName attribute). If we need a different set a different set of handlers to auth & authz a special business action or service, we might define another pipeline (inside the serviceInterfacePipelines group in this case) with the required handlers configuration. (Shadowfax comes with several auth & authz handlers as well as a collection of other handlers with varied functionality described in the Handlers section of this document.)
Dispatching Transports
The dispatching tansports classes process messages between Pipelines. All four of the dispatching transports implement the IPipelineTarget interface. The IPipelineTarget interface has a Submit() method which takes a Context as its parameter. On the other endpoint, that is the Service Implementation, the corresponding Dispatching Adapter receives the serialized context that was sent from the corresponding to the dispatcher transport.Each Dispatching Adapter class ultimately does the following:1. Recreates a Context from the interface pipeline Context, copying the references and values of one to the other.2. Creates an instance the Pipeline class and assigns arguments to local variable3. Calls the Execute() method of the Pipeline instance. The available dispatching transports corresponds to the same interface transports (WS, MSMQ and .NET Remoting) and it adds the Inproc dispatching transport that Submits a context to the Service Implementation pipeline in the same application domain.
Handlers
Handlers are simply pieces of logic that are executed in a certain order by a one pipeline. Handlers implement one of three interfaces, therefore we have three different kind of handlers. The most basic handler implements the IAtomicHandler interface, which has one method: Execute(). Handlers which use a stack approach to call other handlers in succession or chain, implement the IStatefulAroundHandler interface. When the list completes, the execution stack unwinds back to each previous handler. Finally, the last interface is the IStatelessAroundHandler interface. These handlers are called before and after the target execution. Every handler, when executed, receives an instance of Context which it uses to carry out its logic.The following table describes the interfaces shown above:
Class |
Description |
IAtomicHandler |
This interface is used for your basic, run-of-the-mill handler. It is executed just once, and then control is returned back to the pipeline. It can be placed in a list before the target, or after the target. |
IStatefulAroundHandler |
This interface is used to ensure that handlers call each other in a single stack frame. Unlike other handlers, execution is not immediately returned to the pipeline. Instead, each handler calls the next handler in the list. When the list is exhausted, the stack unwinds back to each previous handler, ultimately returning control back to the pipeline.The IStatefulAroundHandler Execute() method takes a delegate as its second parameter. As the pipeline iterates through the handlers in its InternalExecute() method, it checks to see if the next handler is an IStatefulAroundHandler. If so, it passes the InternalExecute() method as a delegate instance into the stack handler.The pipeline calls the handler which executes its logic and then calls the NextHandler() delegate, as shown in Figure 3. Because the InternalExecute() method was passed in as the next handler delegate, the stack handler calls it. The InternalExecute() method, now in the stack handler's stack frame, continues with the next handler in its list. |
IStatelessAroundHandler |
This interface is used for symmetric handlers. These type of handlers need to be called before and after the target in symmetric fashion as shown in figure 2 (see Handler (C) block). It has two methods, Before() and After(), which are used by the pipeline to manage their execution.The Before() method is called in the list of before handlers. When this list of handlers is exhausted, the pipeline invokes the target. After the target executes, the pipeline loads the after handlers, which include handlers of this type as well. This time though, the After() method is called. |
The following diagram describes the three kinds of handlers and its relation with the pipeline and the execution flow. Note that the only type of handler that must be present on both processing stages is the StatelessAround (Letter “C”).
The following table shows the list of handlers that are available out-of-the-box with Shadowfax:
Handler |
Description |
PasswordAuthentication |
This class implements the username/password based authentication by validating these credentials with some custom credentials storage. |
CustomAuthorization |
Used together with the PasswordAuthentication handler and provides access authorization with a roles based strategy. |
InfrastructureAuthentication |
Checks if the current Identity is authenticated, despite the underlying authentication mechanism selected. |
IdentityAuthentication |
This handler implements identity authentication by trusting the identity sent as user name from upstream subsystems and using this identity to set the current thread principal. |
InfrastructureAuthorization |
This scheme is used with the Authorization & Profiling Application Block and with the help of a SqlBasedAuthorzationProvider, validates the credentials against a SQL Server database repository. |
AppInstrumentation |
Reads the counter category information from the configuration file, and updates the counter values. |
AppInstrumentationAround |
Uses the IStatelessAroundHandler interface to record the elapsed time of execution for the request cycle. |
SignedMessageAuthentication |
Uses a public key to check the signature of the XML message that comes inside the payload property of the context object. |
ClientTrace |
Traces the execution of the request. |
TxRequired |
Starts a new COM+ transaction. This is a StatefulAroundHandler and precess the commit or rollback operation after the calling the nextHandler() delegate. |
ExecutionTimeout |
Monitors the execution beyond this handler, timing out the thread of execution if it exceeds a timeout value. |
SyntacticValidation |
Transforms a simple message into a strongly typed message. |
DuplicateHandlingHandler |
This handler handles duplicate requests to the same business action, rejecting those requests that are identical to the request executing at the time. |
PublishBusinessEvent |
Publishes a business event according to the information in the configuration file. |
MessageTransformation |
Uses an XSLT stylesheet to transform a message. |
Business Actions
The business action is the ultimate destination of the client's service request. The business action is executed by the BusinessActionTarget class, which is the target of the service implementation pipeline. The BusinessActionTarget class implements the IPipelineTarget interface. When a pipeline instantiates the BusinessActionTarget, it invokes the class implementation of the IPipelineTarget's Submit() method, passing in the Context. The BusinessActionTarget instance inspects the Context, and identifies the type of business action and the method of passing the request arguments to it, using information in the service request message and the configuration file ("ServiceFramework.config"). The following figure shows the Business Action configuration section with the three invocation methods.
<businessActionsDefinition> <businessActions> <businessAction serviceActionName ="MySerializationBA" type="Namespace.Class, Assembly" validate="false" validationMethod="MyValidate" invocationMethod="Serialization"> <request type ="Namespace.Class, Assembly"> <method name ="UpdateContact"/> </request> <response type ="Namespace.Class, Assembly"/> </businessAction> <businessAction serviceActionName="MyContextBA" type="Namespace.Class, Assembly" validate="true" invocationMethod="Context"/> <businessAction serviceActionName ="MyExplicitBA" type="Namespace.Class, Assembly" validate="true" validationMethod="MyValidateMethod" invocationMethod="Explicit"> <request type ="Namespace.Class, Assembly"> <method name ="MethodName"> <parameter name ="AccountId" propertyName="AccountId"/> <parameter name ="Amount" propertyName="Amount"/> <parameter name ="Date" propertyName="Date"/> </method> </request> </businessAction> </businessActions> </businessActionsDefinition> |
As the snippet above shows, the BusinessActionTarget uses the type name and .NET Reflection to instantiate an instance of the object. It uses the invocationMethod value to determine how to pass the arguments to the actual business action. With respect to passing the arguments to the business action, the BusinessActionTarget has three choices.The three options (or methods) for passing the request arguments to the business action are:
Argument Passing Option |
Description |
Context |
This option follows the Command Pattern which allows classes to parameterize client classes with different requests using the same method. This is accomplished by passing the entire Context object to the business action. The business action must implement the IBusinessAction interface. This interface declares the Execute() method which takes the Context instance as its only parameter. It is left entirely to the business action to examine the request payload included in the context.
This option is particularly useful when the structure of the request payload is unknown, that is, something other than XML. |
Serialization |
This is the recommended option for passing arguments to business actions. It assumes that the request payload is represented as XML that can be deserialized into an object. This object is passed in on the call to the business action. The business action is instantiated and invoked using.NET Reflection, and the deserialized payload object is passed in on this call. The BusinessActionTarget uses the name attribute in the "ServiceFramework.config" file to determine the method name. The business action can then use the arguments directly from the object passed in on the method call. |
Explicit |
This option uses .NET Reflection to instantiate the business action, just as it does in the Direct option. But it also uses reflection to identify each parameter of the method. It uses this information to create an array of parameters combining it with the values from the payload. These parameters are then passed in on the call using .NET Reflection. This method also assumes an XML Payload. |
If it is a business action, the business action is responsible for executing the service implementation. In either case, after the target of a pipeline has finished executing, any remaining around handler in the pipeline are executed, and the response is returned back to the client, in case of the service implementation (first) pipeline, or the target of the service interface pipeline, in case of the service implementation (second) pipeline.
Deployment and the “double” pipeline
The Shadowfax architecture has been designed to be flexible when deployed. It provides a number of different options. The basic configuration consists of two servers, with the externally accessible server sitting between two firewalls in the DMZ. In the logical diagram below, the first server is referred to as the Service Interface server (for the Service Interface Pipeline), and rests within the DMZ. The second server sits securely behind the second firewall, along with the configuration database and hosts the Service Implementation Pipeline.

Client applications running on desktops, laptops or external servers make a request across a channel, which goes through the first firewall, to the Service Interface Server. The server receives the request (invoking the Pipeline). The request continues through the second firewall to the Service Implementation Server, which ultimately invokes the business action. Of course that a single server scenario might be used in smaller installations or in development or demonstration environments. Single server deployment does not communicate across machines. All assemblies would be deployed on a single server. This type of deployment can use all of the ports (MSMQ, DCOM, InProc and Web Services) to communicate between the Pipeline and the Pipeline, though DCOM and InProc would be the recommended ports as they would perform best in this type of deployment. Use this post as a roadmap for the more detailed discussions that I'll dive into later. In future post we will see topics like performance, scalability and instrumentation management issues, exception handling and security design considerations as well as we‘ll analyze some of the handlers internals. The opinions expressed herein are personal and do not necessarily reflect the position of neither my employee nor Microsoft.