Table of Contents
Maverick.NET is a .NET port of Maverick, a Model-View-Controller (aka "Model 2") framework for web publishing. It is a minimalist framework which focuses solely on MVC logic, allowing you to generate presentation using a variety of templating and transformation technologies.
Core features include:
Pluggable view templating technologies.
Pluggable transformation technologies.
Support for iterative transformations.
Configuration using an XML sitemap.
Framework support for internationalization, customization of content based on browser, and WML.
The ability to use standard aspx pages as Maverick controllers. There is full support for Server Controls, Postback forms, and View State.
A fairly exhaustive set of sample applications using a variety of templating languages and controller types.
Depending on what templating technology you choose, you may be interested in one or more of the following features:
Automatic XML serialization of arbitrary objects so that XSLT transformations can be used without the effort and processing overhead of generating and parsing text XML.
The ability to halt iterative transformations at any step and obtain the intermediate result. In the case of XSLT and DVSL, this would produce static XML. This allows designers to build template files offline with simple tools.
"Wrapping" transformations using basic templating languages like ASPX. This allows varying content to be easily encapsualted within a common layout, look-and-feel, etc.
NFop transformations which can produce PDF documents on-the-fly.
NVelocity and DVSL (an XSLT-like language based on Velocity) templates are now supported through the Opt-NFop package.
An excellent quick introduction of MVC aka "Model 2" concepts can be found here.
TODO: It would be worthwhile to put a more customized explanation here.
This is a short explanation of how Maverick (and your web application) processes requests. A detailed explanation can be found in a later chapter.
Maverick processes commands that are mapped to the Dispatcher Handler through extension mapping. For example, all URIs which end with *.m can be mapped to Maverick. The URI, minus the webapp context and the extension is the command. For example, lets say you have a webapp mounted on /foo and a request comes in for http://your.site.com/foo/runQuery.m. The command is "runQuery".
What Maverick does with a command is determined by the Maverick configuration file. Here is a complete (albeit simple) example:
<maverick version="2.0"> <commands> <command name="runQuery"> <controller class="Bar.Foo.Query, Foobar"/> <view name="success" type="document" path="queryResult.aspx"> <transform type="xslt" path="lookAndFeel.xsl"/> </view> <view name="error" type="document" path="queryFailed.aspx"> <transform type="xslt" path="lookAndFeel.xsl"/> </view> </command> </commands> </maverick>
This file defines a single command, runQuery, which has two possible outcomes (views): "success" and "error".
When the runQuery command is requested, the following steps occur:
An instance of the controller class Bar.Foo.Query is created. 
The Perform() method on the controller is called. Depending on the type of controller used, this may result in properties of the controller being populated with the http request parameters prior to execution application logic.
During execution, the Perform() method can define an object which will be used as the model.
The return value from Perform() specifies the name of the view to render. This example will assume "success".
The model is placed in the HttpContext.Items collection with the key "model".
The ASPX page queryResult.aspx is executed, which uses the model to generate XML.
The XML is transformed with the XSLT file lookAndFeel.xsl, which applies the title bar, navigation bar, and footer. The result is sent back to the client.
There are many other possible configurations, including other types of views (for instance, redirects) and other types of transforms (including "wrapping" transforms and DVSL). Transforms themselves are unnecessary. Furthermore, it is possible to define controllers in a variety of ways. Just about every aspect of Maverick is pluggable and configurable.
 It is also possible to have singleton controllers with form objects similar to the java mvc framework Struts.
Maverick includes an example application which illustrates how to set up a basic web application. It would be helpful to examine it as you go through this documentation.
The first step in building a Maverick application is to configure iis to redirect all commands with a particular extention, say "*.m", to the aspnet_isapi.dll.
After you have created a folder somwhere under your webroot to hold your application, right click on the folder in the iis management console. On the directory tab, under Application Settings, if the application has not yet been created, click create.
Next, click on the "Configuration" button. Click add under the list of application mappings. In the dialog that appears, choose aspnet_isapi.dll as the executable (this should live somewhere under winnt/Microsoft.net/framework). Put .m (or another extension if you prefer) as the extension. Make sure that "check that file exists" is unchecked, and click ok.
The second step in building a Maverick application is to configure the Dispatcher handler in your Web.config. All commands with a particular extention, say "*.m", are mapped to this handler:
<configSections> <sectionGroup name="Maverick"> <section name="Dispatcher" type="System.Configuration.NameValueSectionHandler,system, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, Custom=null" /> </sectionGroup> </configSections> <Maverick> <Dispatcher> <add key="configFile" value="maverick.config" /> <add key="currentConfigCommand" value="currentConfig" /> <add key="reloadCommand" value="reload" /> <add key="limitTransformsParam" value="maxTransforms" /> <add key="commandCaseSensitivity" value="insensitive" /> </Dispatcher> </Maverick>
There are several init-parameters for the Dispatcher:
configFile. The path to the maverick XML config file. If not specified, this defaults to maverick.config.
configTransform. Path to an XSL file which is used to transform the Maverick configuration file prior to loading. If left unspecified, no transformation is executed.
currentConfigCommand. If defined, this command outputs the current configuration XML, including any transformation specified by the configTransform. If you have sensitive information in your config file, be sure to disable this command on your production system.
reloadCommand. If defined, this command causes Maverick to reload the configuration file and all associated cached data (xsl stylesheets, etc). This does not stop Maverick from servicing commands; execution continues with the old configuration until the new data is completely loaded and ready. Because this operation is processor intensive, you probably want to disable it (leave it undefined) or name it something obscure on a production system.
limitTransformParam. If defined, this specifies the name of a special request query parameter which, when added to a request, limits the number of transforms which will be applied. This allows any page rendering to be halted midstream and the intermediate result sent to the browser.
commandCaseSensitivity. If defined, this specifies whether command name matching should be case sensitive or insensitive. Acceptable values are "sensitive" or "insensitive". If this param is not specified, the default setting "insensitive" will be used.
The heart of a Maverick application is the maverick.config file. This file contains all the information about what commands are available, what code (controllers) are associated with specific commands, and what views can result from a command.
By default, this file resides in your web application as maverick.config. The location is configurable as a an init-param to the Dispatcher handler.
The schema of the configuration file is quite flexible because much of it is interpreted by pluggable modules. Each of the view types and transform types are defined by instances of special factory classes which can be configured in the modules element. The documentation for individual view or transform types is found in the api docs for the factory.
By way of convention, the following constructs are usually considered equivalent and can be used interchangably:
In addition, these flexible elements can also be overriden by an environment variable. The example above can be overriden with an environment variable named maverick.element.attr.
See the appendix for an explanation of the options available in the Maverick configuration file.
Using ASPX with Maverick is straightforward. Use the "document" view type to reference your ASPX documents. The model will be placed in the HttpContext.Items collection so that your ASPX can access it. The default key is "model", but both the default and individual view keys can be configured.
Document views allow transforms. The friendbook sample application demonstrates the use of "document" transforms, which provides content from previous steps to subsequent steps in the form of a String in the HttpContext.Items collection.
There are several ways of working with XSLT in Maverick. XSLT itself is available as a transform type, not a view. This means you can apply XSLT transformation to any view type that supports transforms.
The traditional way is to use document views such as ASPX to generate text XML which will then be transformed.
A much more elegant mechanism is the XmlSerializingView, which automatically serializes the model into XML.
This approach allows designers to work immediately with XSLT as a templating language. Since Maverick's XSLT transform allows the transformation process to be halted at any step, it is easy to obtain the raw XML so that designers can work offline.
Included in the standard distribution is a friendbook-xsl sample application. The controller classes are the same as those used for friendbook; only the maverick.config and the templates are different.
There are many, many ways to internationalize a web application, many of which are dependent on the specific templating language you are using. Nevertheless, Maverick offers considerable assistance with this task in a way which is useful for performing other kinds of "view customization", such as providing browser-specific behavior or delivering WML.
The two important concepts that Maverick adds are "modes" and "shunts". Modes allow you to define multiple views with the same name. Shunts are pluggable Maverick modules that determine at runtime which view's mode should be executed.
You can set up a Maverick application to use shunts by configuring a shunt-factory in the modules section. The factory will be used to create a Shunt for each named view. Here is a simple example of a Maverick application that uses the org.infohazard.maverick.shunt.LanguageShuntFactory:
<maverick version="2.0" default-view-type="document"> <modules> <shunt-factory provider="Maverick.Shunt.LanguageShuntFactory, Maverick"/> </modules> <commands> <command name="runQuery"> <controller class="Bar.Foo.Query, Foobar"/> <view name="success" path="en/queryResults.aspx"/> <view name="success" mode="fr" path="fr/queryResults.aspx"/> <view name="success" mode="de" path="de/queryResults.aspx"/> <view name="error" path="en/queryError.aspx"/> <view name="error" mode="fr" path="fr/queryError.aspx"/> <view name="error" mode="de" path="de/queryError.aspx"/> </command> </commands> </maverick>
The LanguageShuntFactory builds shunts which determine mode based on the value of the Accept-Language header of the HTTP request. Your controller (Foo.Query) determines which view name to render; the shunt determines which mode should be used.
Currently, Maverick provides only the LanguageShuntFactory, but the Shunt and ShuntFactory interfaces are very simple so it is easy to implement your own. You can use this facility to perform automatic customization based on browser, or to offer WML content to WAP devices.
There are two basic phases in the life of an executing Maverick application. The first is the "load" phase which (usually) occurs once and uses the config file to construct a tree of workflow objects. The second is the "execution" phase in which http requests are serviced. Maverick is designed to perform as much work as possible during the load phase so that the execution phase can be as fast as possible.
The workflow tree constructed during the load phase represents all of the possible execution paths for servicing an http request. It consists of objects which implement various interfaces, and many of the objects are created by pluggable factories. After the workflow tree is built, the overhead Maverick itself adds to a running web application should be little more than a couple IDictionary lookups and a handful of virtual method calls.
The top level object is an ICommand object, which corresponds to a single defined command in the config file. Other than special implementations like the reload command, there are two basic implementations of the ICommand interface: CommandSingleView and CommandMultipleViews. The CommandFactory will automatically build the correct instance depending on how many views are available; the only difference is that CommandSingleView can eschew the IDictionary lookup to deterimine which view to render.
There is always one IController associated with every command; a simple null controller is generated by the ControllerFactory when no controller is specified by the user. In addition, it should be noted that from the perspective of the command objects, controllers always look like singleton controllers; a special ThrowawayAdapter is used for "normal" controllers.
The command object chooses an IView to render. IViews are built by the flexible IViewFactory system. At runtime, execution passes from the ICommand to the IView identified by the IController.Go() return value; the IView is responsible for "dealing with" the request from here out. There are many types of views.
If an IShuntFactory was defined in the modules part of the config file, the IView instances held by a ICommand will actually be instances of the decorator ViewShunted, which holds an IDictionary of real views and adds the mode switching behavior.
Actual IView implementations are responsible for tasks like sending HTTP redirects, or rendering the model using an ASPX page. Output is rendered through an ITransformStep object obtained from the IViewContext passed into Go(). Depending on the configuration in maverick.config, the ITransformStep may be the entry point for a sequence of transformations or it may actually dump the results directly to the response output stream.
Thus, at execution time, an HTTP request is serviced like this:
The Dispatcher receives the request and looks in an IDictionary for the ICommand associated with the particular URL. ICommand.go() is called.
The ICommand calls Controller.Go(). The controller optionally sets the model object (using a method on IControllerContext) and returns a string indicating the name of the view to render.
The ICommand looks in an IDictionary for the IView to render. IView.go() is called and passed a IViewContxt object.
If shunting was enabled, the IView will actually be an instance of ViewShunted. This object uses the IShunt to find the actual view to render; the actual mechanism for this is specific to the shunt.
The IView generates some content and sends it to the ITransformStep obtained from IViewContext.NextStep. If there were no transforms defined, this step will actually be a special implementation that sends output directly to the real response.
ITransformStep objects are chained together such that one passes information to the next. Eventually the data gets passed to the special implementation which sends output to the real response.
Maverick was designed with pluggability and extendability in mind. There are normally four ways of extending Maverick: custom view factories, custom transform factories, custom shunt factories, and custom controllers. Maverick itself uses these mechanisms to provide the base functionality.
Anyone can add a custom view type to Maverick by plugging in their own implementation of Maverick.Flow.IViewFactory. If a Maverick config file defines the factory as a view-factory in the modules section, the factory object will be created and initialized with the XML which defined the factory. This way child elements and attributes can be used to configure the factory.
When processing the configuration file, Maverick identifies which factory to use to generate IView objects based on the type attribute. The factory is then asked to create an IView instance based on the snippet of XML which defined the view.
Creating a custom ITransformFactory is nearly identical to creating a custom IViewFactory. The framework automatically recognizes transform elements within view elements and builds the appropriate ITransform chain.
Downloads are available here.
For help using Maverick and discussion of the future direction of Maverick, subscribe to the mavnet-user mailing list.
For more information about the orgininal java version of Maverick, visit the Maverick page, also hosted on sourceforge.
Maverick is an Open Source project distributed with an Apache-style license. It was created by Jeff Schnitzer and Scott Hernandez. The first public release was 17 May 2001.
Maverick.NET is an Open Source project distributed with an Apache-style license. It was ported by Jim Moore. The first public release was 3 August 2002.
The Maverick logo is courtesy of Mike Moulton and the talented artists at http://www.meltmedia.com.
Table of Contents
maverick — The root element of Maverick configuration
maverick ::= (modules?, views*, commands+)
|version||CDATA||Required. Must be "2.0"|
maverick is the root element of Maverick configuration. It contains everything.
version. The version of the schema. Must be 2.0.
default-view-type. The assumed type of view nodes which do not have an explicit type attribute.
default-transform-type. The assumed type of transform nodes which do not have an explicit type attribute.
<maverick version="2.0" default-view-type="document" default-transform-type="xslt"> <modules> ... </modules> <views> ... </views> <commands> ... </commands> </maverick>
modules — Contains entries for pluggable modules.
modules ::= (view-factory*, transform-factory*, shunt-factory?)
The modules element acts as a container for pluggable Maverick modules, such as view factories and transform factories. It is otherwise unremarkable.
<maverick version="2.0" default-transform-type="xslt"> <modules> <transform-factory type="xslt" provider="Maverick.Transform.XsltTransformFactory, Maverick"> <lazy-load-templates value="true"/> </transform-factory> <shunt-factory provider="Maverick.Shunt.LanguageShuntFactory, Maverick"/> </modules> ... </maverick>
view-factory — Configures a pluggable view type
view-factory ::= [Special]
A view-factory is a pluggable module which defines a particular presentation scheme. These factories build the Maverick workflow tree which processes requests at runtime. Examples include the "redirect", "document", "trivial", and "null" view factories.
View factory classes must implement the Maverick.Flow.IViewFactory interface. The factory instance will be passed the XML fragment of the view-factory element and is free to interpret the XML in any manner. Thus, full documentation for what are the allowable contents of a view-factory element can be found by examining the api documentation of the particular factory being instantiated.
Note that it is possible to override the base view factories by explicitly defining them. This is useful for altering the default parameters of a particular view type.
type. The unique key for this view type, to be used as the type attribute of view elements. A duplicate will override the previous factory.
provider. The full classname and assembly of a class that implements Maverick.Flow.IViewFactory.
transform-factory — Configures a pluggable transform type
transform-factory ::= [Special]
A transform-factory is a pluggable module which allows content from a view to be manipulated in some way. These factories build the Maverick workflow tree which processes requests at runtime. The word "transform" is commonly associated with XSLT, but Maverick allows many other types, including "wrapping" transforms.
Transform factory classes must implement the Maverick.Flow.ITransformFactory interface. The factory instance will be passed the XML fragment of the transform-factory element and is free to interpret the XML in any manner. Thus, full documentation for what are the allowable contents of a transform-factory element can be found by examining the api documentation of the particular factory being instantiated.
Note that it is possible to override the base transform factories by explicitly defining them. This is useful for altering the default parameters of a particular transform type.
type. The unique key for this transform type, to be used as the type attribute of transform elements. A duplicate will override the previous factory.
provider. The full classname and assembly of a class that implements Maverick.Flow.ITransformFactory.
shunt-factory — Configures a shunt factory
shunt-factory ::= [Special]
A shunt-factory is a pluggable module which allows views to be specified with modes. The Shunt automatically chooses the correct mode based on some characteristic of the HTTP request. This is useful for internationalizing an application or providing browser-specific behavior.
Shunt factory classes must implement the Maverick.Flow.IShuntFactory interface. The factory instance will be passed the XML fragment of the shunt-factory element and is free to interpret the XML in any manner. Thus, full documentation for what are the allowable contents of a shunt-factory element can be found by examining the api documentation of the particular factory being instantiated.
There can be only one instance of a shunt-factory defined for a webapp.
provider. The full classname and assembly of a class that implements Mverick.Flow.IShuntFactory.
views — Contains views which can be referenced from any command
views ::= (view+)
The views element acts as a container for view elements which can be referenced from any Maverick command. All views defined within a views element must use the id attribute rather than the name attribute.
mode. All views contained within the element will be considered to have this mode unless an explicit mode is specified on the individual view.
<maverick version="2.0"> <views> <view id="loginRequired" path="en/loginRequired.aspx"/> <view id="loginFailed" path="en/loginFailed.aspx"/> </views> <views mode="de"> <view id="loginRequired" path="de/loginRequired.aspx"/> <view id="loginFailed" path="de/loginFailed.aspx"/> </views> ... </maverick>
commands — Contains commands
commands ::= (command+)
The commands element acts as a container for command elements.
<maverick version="2.0"> <commands> <command name="welcome"> ... </command> <command name="signup"> ... </command> </commands> </maverick>
command — Defines the behavior of one command
command ::= (controller?, view+)
A command is the basic unit of work in Maverick. When an HTTP request is processed by Maverick, the URI is examined to determine which command to execute. If no command matches the URI, Maverick returns 404.
The contents of a command element determine the runtime behavior. You can define a controller class and some number of views, one of which will be rendered for every request.
Note that the controller child element is optional, but if it is left out, there can be only one view element since a controller is needed to select from the available views.
name. The URI to associate with this command, minus the context root, extension, or query parameters. For example, "welcome" and "protected/reportDetail".
<maverick version="2.0" default-view-type="document"> <commands> <command name="welcome"> <view path="hello.html"/> </command> </commands> </maverick>
controller — Associates a controller with a particular command
controller ::= (param*,[Special])
A controller is a user class which "controls" how the request is handled. Here is where user code is executed, the model is prepared, and which view to render is chosen.
param child elements populate the context params available from IControllerContext.Params prior to execution of the controller.
There are two types of controller classes, determined by whether or not the class implements Maverick.Flow.IController or Maverick.Flow.IControllerSingleton.
Normal controller objects are instantiated fresh for every request and disposed of afterwards. Typically you will want to extend one of the abstract base classes in Maverick.Ctl such as ThrowawayForm which provides helpful additional features such as populating object properties from the request parameters.
Singleton controllers resemble Struts controllers. A single instance of each controller is instantiated for the entire webapp. The XML contents of the controller element are passed to the controller object so that Form objects or other parameters may be configured. For singletons, the structure of the XML contents of the controller node is arbitrary and freely interpreted by the controller instance.
An ISingletonController helper base class is provided with Maverick: FormUser. This is just one example which provides a very Struts-like action pattern.
class. The full name of the class which will act as the controller. This class must implement Maverick.Flow.IController or Maverick.Flow.IControllerSingleton.
<maverick version="2.0" default-view-type="document"> <commands> <command name="viewReport"> <controller class="Bar.Foo.Report, Foobar"> <param name="color" value="red"/> </controller> <view name="success" path="good.aspx"/> <view name="error" path="bad.aspx"/> </command> </commands> </maverick>
view — Defines a view as either part of a command or a globally referencable view
view ::= (param*,[Special])
|type||One of the view factory types||The value of the default-view-type attribute on the maverick element, or "document" if there is no explicit default.|
|id||CDATA||Required if parent is views|
|name||CDATA||The value of ref|
|ref||A previously defined view id||None|
A view renders the model to the response. There are many different types of views, defined by the view factories registered in the modules section. The interpretation of a view element is determined by the type attribute, or the default registered with Maverick.
There are two places view elements can appear. The first is as a global view defined under a views element. In this case, the view must have an id attribute and cannot have a name or ref.
The second place view elements can appear is under a command element. Here they are named (with the name attribute) and designate the options available in the command for rendering the model. A view here can either completely define a new view or it can reference one of the globally defined views in a views section. To link to a global view, simply use the global view's id as the value of the ref attribute. As a convenience, the name defaults to the same value as the ref.
Note that if a command defines only a single view, it does not need to be named. Regardless of what the controller returns, the one view will be rendered.
param child elements populate the context params available from IViewContext.Params prior to execution of the view. The meaning of a param is specific to the particular IView type.
Other attributes and child elements are determined by the type of view. Nevertheless, there are some common conventions which IViewFactory implementors are strongly encouraged to follow:
Path-like values should use the attribute path.
See the chapter on Internationalization and Shunting for a discussion of the mode attribute.
type. One of the types registered as a view-factory, or one of the defaults (redirect, document, trivial, null).
id. Must be present for global views defined in the views element. Allows commands to reference the id of global views with the ref attribute.
name. For views defined in command elements, this is the string that the controller will return to identify which view to render. Defaults to the value of ref. Can be omitted if there is only one view for a command.
ref. Links to the id of a global view.
mode. See the chapter on Internationalization and Shunting.
<maverick version="2.0" default-view-type="document"> <views> <view id="ugly" path="ugly.aspx"/> <view id="fugly" path="fugly.aspx"/> </views> <commands> <command name="viewReport"> <controller class="Bar.Foo.Report, Foobar"/> <view name="good" path="good.aspx"> <param name="color" value="red"/> </view> <view name="bad" path="bad.aspx"/> <view ref="ugly"/> <view name="explicitname" ref="fugly"/> </command> </commands> </maverick>
transform — Defines a transform on views that support them
transform ::= (param*,[Special])
|type||One of the transform factory types||The value of the default-transform-type attribute on the maverick element, or "document" if there is no explicit default.|
A transform is some sort of arbitrary manipulation of view content performed immediately prior to dumping it to the response. Most view types support transforms, but some (such as redirect) do not. The canonical example of a transform is an XSLT transform, but there are other options such as simple text "wrapping" transformations.
Transforms are pluggable very much like views. Factories are associated with types in the modules section, and transform elements within view element have a type attribute. The content of a transform element (attributes, child elements) is determined by the type.
By convention, path-like values should be expressed using a path attribute.
Multiple transforms can be associated with a view by simply defining multiple transform elements. All transforms need not be of the same type.
param child elements populate the context params available from ITransformContext.Params prior to execution of the view. The meaning of a param is specific to the transform type, but should be pretty obvious.
Further documentation about the allowable options for various transform types is available in the api documentation for each factory.
type. One of the types registered as a transform-factory, or one of the defaults (document, xslt).
<maverick version="2.0" default-view-type="document" default-transform-type="xslt"> <commands> <command name="runQuery"> <controller class="Bar.Foo.Query, Foobar"/> <view name="success" path="queryResults.aspx"> <transform path="firstStage.xsl"/> <transform path="lookAndFeel.xsl"/> </view> <view name="error" path="queryError.aspx"> <transform path="errorStage.xsl"> <param name="color" value="red"/> </transform> <transform path="lookAndFeel.xsl"/> </view> </command> </commands> </maverick>
Why did you choose the name Maverick.NET?
Cause it's a .NET port of Maverick. If you wanna know where the name Maverick came from, see the Maverick faq.
How do I initialize log4net?
Why answer here, when you can get it directly from the horse's mouth?
How can I split my maverick.config into multiple files?
TODO: explain how to use entities to make the parser do the work.