Maverick

Jeff Schnitzer

Scott Hernandez

Jim Moore

$Date: 2004/06/27 17:42:15 $

$Revision: 1.27 $


Table of Contents

Preface
1. Concepts
What means MVC?
Command Processing Overview
2. Using Maverick
Configuring web.xml
Configuring maverick.xml
3. Templating Technologies
JSP
XSLT
Velocity
4. Internationalization and Shunting
5. Optional Packages
opt-domify
opt-betwixt
opt-fop
opt-velocity
opt-perl
opt-struts
6. How Maverick Works
7. Extending Maverick
Custom View Types
Custom Transform Types
8. More Information
9. Credits
A. Configuration Schema Reference
B. Frequently Asked Questions

Preface

Maverick is a Model-View-Controller (aka "Model 2") framework for web publishing using Java and J2EE. It is a minimalist framework which focuses solely on MVC logic, allowing you to generate presentation using a variety of templating and transformation technologies.

In principle it combines the best features of Struts, WebWork, and Cocoon2, however:

  • Maverick is simple to use - this is a minimalist framework that anyone can understand easily. This is not a "kitchen sink" framework that tries to provide everything you need to build a web application; there are plenty of great database connection pools, application servers, validation frameworks, templating languages, etc already out there.

  • Maverick is simple to understand - the code is easy to understand, there's not a lot of it, and it's designed with pluggability and extendability in mind. The idea of a Controller that builds a Model that gets rendered by a View is very simple and straightforward, so the framework should be too.

  • Maverick is agnostic about view technologies - you can use any templating engine you like with Maverick. Examples are provided for JSP (with JSTL - no need for special tag libaries), Velocity, and Domify/XSLT. The developers of Maverick actively use all three of these in their "real life" to build web applications.

  • You can run your view output through a pipeline of transformations. Maverick-supplied transformations include XSLT, DVSL, "wrapping" layout transformations, FOP, and Perl. You can efficiently chain many transformations of various types together, and you can specify this on a per-view basis. Of course, transformation technologies are pluggable and you can easily define your own.

    In addition you can halt the transformation process at any point and output the intermediate content. If you're using XSLT, this is a great way to produce static XML and build your templates offline with standard tools.

  • Your commands, controllers, views, and transforms are configured with an easy-to-understand XML sitemap. For even more flexibility, you can preprocess it with XSLT.

  • Maverick will automagically pick from different views based on user language, browser type, or any other characteristic of the request. Of course, this behavior is pluggable.

  • Maverick supports both Struts-style singleton Controllers (aka Actions) and Webwork-style "throwaway" Controllers.

  • Maverick is multi-platform; it has been ported to both .NET and PHP.

Depending on what templating technology you choose, you may be interested in one or more of the following features:

  • Maverick can automatically "domify" (or "saxify") arbitrary Java objects so that XSLT can be used without the effort and processing overhead of generating and parsing text XML. XSLT can be used as a templating language directly on your model just like JSP.

  • For text-based templating engines like JSP or Velocity, an elegant way to apply a common "look and feel" and layout to a set of views is to use the "wrapping" transformation. The output of the previous step is made available to subsequent steps as a String variable which can be placed anywhere on the page.

  • FOP transformations allow your application to produce PDF, Postscript, PCL, and a half-dozen other document formats on-the-fly.

  • An interesting alternative to XSLT is DVSL. This is a declarative templating language patterned after XSLT but based on Velocity.

If you like Maverick, but also would like to use additional features like Webwork and Struts provide, you might want to check out Baritus. Baritus is an extension of Maverick that provides a boosted version of the FormBeanUser controller. It focusses on fine grained population, validation and error reporting, has several utilities for things like formatting output and supports the concept of interceptors.

This is not a framework designed by people who build frameworks; Maverick is designed and built by people who build web applications for a living and were disappointed with the complexity and invasiveness of existing open source tools.

Chapter 1. Concepts

What means MVC?

An excellent quick introduction of MVC aka "Model 2" concepts can be found here.

Command Processing Overview

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 servlet 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="com.bar.foo.Query"/>
      <view name="success" type="document" path="queryResult.jsp">
        <transform type="xslt" path="lookAndFeel.xsl"/>
      </view>
      <view name="error" type="document" path="queryFailed.jsp">
        <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:

  1. An instance of the controller class com.bar.foo.Query is created. [1]

  2. The perform() method on the controller is called. Depending on the type of controller used, this may result in bean properties of the controller being populated with the http request parameters prior to execution application logic.

  3. During execution, the perform() method can define an object which will be used as the model.

  4. The return value from perform() specifies the name of the view to render. This example will assume "success".

  5. The model is placed in the servlet request attributes with the key "model".

  6. The JSP page queryResult.jsp is executed, which uses the model to generate XML.

  7. 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.



[1] It is also possible to have singleton controllers with form beans similar to Struts.

Chapter 2. Using Maverick

Maverick includes many example applications which illustrate how to set up a basic web application. It would be helpful to examine them as you go through this documentation.

Configuring web.xml

The first step in building a Maverick application is to configure the Dispatcher servlet in your web.xml. All commands with a particular extention, say "*.m", are mapped to this servlet:

<web-app>
  <display-name>Friendbook Web Application</display-name>
	
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <display-name>Maverick Dispatcher</display-name>
    <servlet-class>org.infohazard.maverick.Dispatcher</servlet-class>
    <load-on-startup>2</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.m</url-pattern>
  </servlet-mapping>
</web-app>
			

There are several init-parameters for the Dispatcher:

configFile.  The path to the maverick XML config file. If not specified, this defaults to /WEB-INF/maverick.xml. Can be overriden by setting an application-context attribute with the key Dispatcher.KEY_CONFIG_FILE.

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. Can be overriden by setting an application-context attribute with the key Dispatcher.KEY_CONFIG_TRANSFORM.

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 (templates, 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.

limitTransformsParam.  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.

defaultRequestCharset.  This allows web applications to override the charset used to decode HTTP request parameters. [2] If not defined, the servlet container's default (typically ISO-8859-1) is used.

reuseMaverickContext.  If set to "true", the Maverick context object will be persisted through multiple Maverick invocations within the same request. This may be useful for chaining several command invocations by forwarding (with a document view) to a maverick command. Because this causes problems with SiteMesh, the default is false.

Configuring maverick.xml

The heart of a Maverick application is the maverick.xml 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 WEB-INF/maverick.xml. The location is configurable as a an init-param to the Dispatcher servlet.

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 javadocs for the factory.

By way of convention, the following constructs are usually considered equivalent and can be used interchangably:

  • <element attr="blah"/>

  • <element><attr value="blah"/></element>

In addition, these flexible elements can also be overriden by a system property. The example above can be overriden with a property constructed like this: maverick.element.attr. An example argument to the java command like would be: -Dmaverick.element.attr=overrideblah

See the appendix for an explanation of the options available in the Maverick configuration file.



[2] In general, browsers encode submissions using the same charset used to encode the document which contains the form. However, this bit of information (the encoding charset) is *not* sent along with the HTTP request by any existing browsers, so the server has to guess at what charset to use to decode the submission.

In fact, that "guess" usually (for Tomcat and Orion, at least) means that web containers default to ISO-8859-1 unless the servlet specifically calls HttpServletRequest.setCharacterEncoding() *before* any data is read, including accessing any of the parameters. Some web containers offer other strategies (like guessing based on the user's locale, which *is* submitted), but they all have serious problems (like does jp mean Kanji or UTF-8?).

Specifying a defaultRequestCharset causes HttpServletRequest.setCharacterEncoding() to be called. When building internationalized applications, you will probably be best off if you pick a charset which accomodates all your languages (like UTF-8) and sticking to it religiously.

Chapter 3. Templating Technologies

Table of Contents

JSP
XSLT
Velocity

JSP

Using JSP with Maverick is straightforward. Use the "document" view type to reference your JSP documents. The model will be placed in the servlet request attribute collection so that your JSP can access it. The default key is "model", but both the default and individual view keys can be configured. Params (either defined with param elements in the config file or defined by your application calling ControllerContext.setParam()) are also placed in the request context.

Document views allow transforms. The friendbook-jsp 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 servlet request attribute collection.

XSLT

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 JSP to generate text XML which will then be transformed. This works just fine, but it is cumbersome and adds a significant amount of processing overhead.

A much more elegant mechanism is included in Maverick's opt-domify package. Rather than using a templating language to generate textual XML from the model, Domify uses Java reflection to automagically adapt the model to a DOM facade. The "domify" view type wraps the model with a Domify adapter and then hands the DOM source off to the transform. The DOM tree is lazy-loaded for efficiency; the tree is built as the processor navigates the DOM.

This approach is not only more efficient (no need to generate or parse XML), but it 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.

The opt-domify package contains a domify version of the friendbook sample application. The controller classes are the same as those used for friendbook-jsp; only the maverick.xml and the templates are different.

Domify was created for Maverick, but it has been spun off into a separate sourceforge project. Development continues there.

Velocity

In most ways, using Velocity with Maverick is just like using JSP. Thanks to the Jakarta Struts integration effort, the Velocity folks have produced a VelocityViewServlet which allows your web container to handle Velocity templates in the same way that your web container handles JSP templates. All requests for Velocity files (*.vm) are mapped to this servlet, which masquerades the servlet attribute collections as the Velocity Context.

To use Maverick with Velocity, simply register the VelocityViewServlet in your web.xml deployment descriptor and then use the normal Maverick "document" view type. The model will be available in the context with the key "model" (just as with JSP, this is configurable). You can also use any type of transform with these views, including "document" transforms. You can use Velocity to write these "document" transforms.

The opt-velocity package is not necessary to use Velocity with Maverick, however it does provide some assistance. It includes a version of the friendbook sample application which uses Velocity. It includes a recent version of the VelocityViewServlet. It also includes a new transform type based on the experimental DVSL technology recently developed by the Velocity team.

DVSL is a technology very similar in form and purpose to XSLT. It is a declarative, "matching" language with a syntax based on XSLT but which uses Velocity to generate output. This carries with it a number of advantages and disadvantages that are out of scope for this document.

Chapter 4. Internationalization and Shunting

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="org.infohazard.maverick.shunt.LanguageShuntFactory"/>
  </modules>
  
  <commands>
    <command name="runQuery">
      <controller class="org.foo.Query"/>
      
      <view name="success" path="en/queryResults.jsp"/>
      <view name="success" mode="fr" path="fr/queryResults.jsp"/>
      <view name="success" mode="de" path="de/queryResults.jsp"/>
      
      <view name="error" path="en/queryError.jsp"/>
      <view name="error" mode="fr" path="fr/queryError.jsp"/>
      <view name="error" mode="de" path="de/queryError.jsp"/>
    </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 (org.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.

Chapter 5. Optional Packages

Maverick is an extendable framework, and many features are implemented as pluggable modules. Here is an overview of the various packages available for download on the Maverick website:

opt-domify

Domify is a Java library that adapts arbitrary JavaBeans (and collections) to a W3C DOM representation. It was originally created as part of Maverick but has since been separated out into a separate project.

This optional package includes a Maverick view type for "domify" views and a version of the Friendbook sample application which uses Domify and XSLT for presentation.

opt-betwixt

Betwixt is an alternative to Domify. It can generate a series of SAX events from a JavaBean model.

Note that this option is less well tested than Domify.

opt-fop

Apache FOP is a processor that can transform XSL-FO (formatting objects) into a variety of presentation formats, including PDF and Postscript. The opt-fop package provides FOP services as a Maverick transform type.

For example, you can use XSLT to transform your model into XSL-FO, then use the FOP transform to emit PDF back to the user.

opt-velocity

Using Velocity with Maverick does not require a new view type. Instead, normal "document" views are used in conjunction with the VelocityViewServlet that was originally developed for Struts. Using Velocity with Maverick is very much like using JSP.

This package includes three things:

  • A relatively recent build of VelocityViewServlet.
  • A Velocity version of the Friendbook sample application.
  • A Maverick transform type for DVSL. DVSL is an experimental XML transformation language patterned after XSLT but based on Velocity.

opt-perl

Offers a Maverick transform type that runs output through Perl.

opt-struts

Provides a number of tools to help you migrate a Struts application to Maverick.

Chapter 6. How Maverick Works

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 Map lookups and a handful of virtual method calls.

The top level object is a Command 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 Command 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 Map lookup to deterimine which view to render.

There is always one Controller 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 a View to render. Views are built by the flexible ViewFactory system. At runtime, execution passes from the Command to the View identified by the Controller.go() return value; the View is responsible for "dealing with" the request from here out. There are many types of views.

If a ShuntFactory was defined in the modules part of the config file, the View instances held by a Command will actually be instances of the decorator ViewShunted, which holds a Map of real views and adds the mode switching behavior.

Actual View implementations are responsible for tasks like sending HTTP redirects, or rendering the model using a JSP page. Output is rendered through a TransformStep object obtained from the ViewContext passed into go(). Depending on the configuration in maverick.xml, the TransformStep 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:

  1. The Dispatcher receives the request and looks in a Map for the Command associated with the particular URL. Command.go() is called.

  2. The Command calls Controller.go(). The controller optionally sets the model object (using a method on ControllerContext) and returns a String indicating the name of the view to render.

  3. The Command looks in a Map for the View to render. View.go() is called and passed a ViewContxt object.

  4. If shunting was enabled, the View will actually be an instance of ViewShunted. This object uses the Shunt to find the actual view to render; the actual mechanism for this is specific to the shunt.

  5. The View generates some content and sends it to the TransformStep obtained from ViewContext.getNextStep(). If there were no transforms defined, this step will actually be a special implementation that sends output directly to the real response.

  6. TransformStep 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.

Chapter 7. Extending Maverick

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.

Custom View Types

Anyone can add a custom view type to Maverick by plugging in their own implementation of org.infohazard.maverick.flow.ViewFactory. 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 View objects based on the type attribute. The factory is then asked to create a View instance based on the snippet of XML which defined the view.

Custom Transform Types

Creating a custom TransformFactory is nearly identical to creating a custom ViewFactory. The framework automatically recognizes transform elements within view elements and builds the appropriate Transform chain.

Chapter 8. More Information

Maverick is hosted by SourceForge. For updated information, visit the website or the project page.

Downloads are available here.

For help using Maverick and discussion of the future direction of Maverick, subscribe to the mav-user mailing list.

For a comprehensive sample J2EE application built with Maverick, XSLT, and EJBs, examine the Punk Image Gallery.

For more information about Domify, visit the domify home page.

For more information about Velocity, visit the velocity page at the jakarta apache project.

For more information about the Maverick extension Baritus, visit the Baritus home page.

Chapter 9. Credits

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.

The Maverick logo is courtesy of Mike Moulton and the talented artists at http://www.meltmedia.com.

This product includes software developed by the Apache Software Foundation (http://www.apache.org/).

Appendix A. Configuration Schema Reference

Table of Contents

maverick - The root element of Maverick configuration
modules - Contains entries for pluggable modules.
view-factory - Configures a pluggable view type
transform-factory - Configures a pluggable transform type
shunt-factory - Configures a shunt factory
controller-factory - Configures a controller factory
views - Contains views which can be referenced from any command
commands - Contains commands
command - Defines the behavior of one command
controller - Associates a controller with a particular command
view - Defines a view as either part of a command or a globally referencable view
transform - Defines a transform on views that support them

Name

maverick — The root element of Maverick configuration

Synopsis

Content

maverick ::= (modules?, views*, commands+)

Attributes

NameTypeDefault
versionCDATARequired. Must be "2.0"
default-view-typeCDATA "document".
default-transform-typeCDATA "document".

Descripton

maverick is the root element of Maverick configuration. It contains everything.

Attributes

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.

Examples

<maverick version="2.0" default-view-type="document" default-transform-type="xslt">
  <modules>
    ...
  </modules>
  <views>
    ...
  </views>
  <commands>
    ...
  </commands>
</maverick>
				

Name

modules — Contains entries for pluggable modules.

Synopsis

Content

modules ::= (view-factory*, transform-factory*, shunt-factory?)

Attributes

None

Descripton

The modules element acts as a container for pluggable Maverick modules, such as view factories and transform factories. It is otherwise unremarkable.

Attributes

None

Examples

<maverick version="2.0" default-view-type="domify" default-transform-type="xslt">
  <modules>
    <view-factory type="domify" provider="org.infohazard.maverick.opt.view.DomifyViewFactory/>
    <transform-factory type="dvsl" provider="org.infohazard.maverick.opt.transform.DVSLTransformFactory/>
    <transform-factory type="xslt" provider="org.infohazard.maverick.transform.XSLTransformFactory>
      <lazy-load-templates value="true"/>
    </transform-factory>
    <shunt-factory provider="org.infohazard.maverick.shunt.LanguageShuntFactory"/>
    <controller-factory provider="org.infohazard.maverick.flow.DefaultControllerFactory">
  </modules>
  
  ...
</maverick>
				

Name

view-factory — Configures a pluggable view type

Synopsis

Content

view-factory ::= [Special]

Attributes

NameTypeDefault
typeCDATARequired
providerCDATARequired

Descripton

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 org.infohazard.maverick.flow.ViewFactory 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 javadocs 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.

Attributes

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 of a class that implements org.infohazard.maverick.flow.ViewFactory.

Examples

See modules.


Name

transform-factory — Configures a pluggable transform type

Synopsis

Content

transform-factory ::= [Special]

Attributes

NameTypeDefault
typeCDATARequired
providerCDATARequired

Descripton

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 DVSL (similar to XSLT but uses Velocity), "wrapping" transforms, and FOP.

Transform factory classes must implement the org.infohazard.maverick.flow.TransformFactory 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 javadocs 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.

Attributes

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 of a class that implements org.infohazard.maverick.flow.TransformFactory.

Examples

See modules.


Name

shunt-factory — Configures a shunt factory

Synopsis

Content

shunt-factory ::= [Special]

Attributes

NameTypeDefault
providerCDATARequired

Descripton

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 org.infohazard.maverick.flow.ShuntFactory 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 javadocs of the particular factory being instantiated.

There can be only one instance of a shunt-factory defined for a webapp.

Attributes

provider.  The full classname of a class that implements org.infohazard.maverick.flow.ShuntFactory.

Examples

See modules.


Name

controller-factory — Configures a controller factory

Synopsis

Content

controller-factory ::= [Special]

Attributes

NameTypeDefault
providerCDATARequired

Descripton

A controller-factory is a pluggable module that is responsible for creating the controllers on startup.

Controller factory classes must implement the org.infohazard.maverick.flow.ControllerFactory interface. The factory instance will be passed the XML fragment of the controller-factory element and is free to interpret the XML in any manner.

There can be only one instance of a controller-factory defined for a webapp. If no controller-factory is defined, the default controller factory org.infohazard.maverick.flow.DefaultControllerFactory is used.

When providing a custom implementation, it is advisable to extend org.infohazard.maverick.flow.AbstractControllerFactory and just override what you need.

Note that in order to provide a custom controller-factory, you need to know the Maverick internals to some extend.

Attributes

provider.  The full classname of a class that implements org.infohazard.maverick.flow.ControllerFactory.

Examples

A custom controller factory:


/**
 * Custom implementation of the controller factory that, in case attribute
 * 'allways-reload' is true, creates and initializes a new instance of
 * controllers even if they are of type ControllerSingleton. This can
 * be usefull when testing controllers; eg when you make changes in the init method,
 * you want these changes be loaded while in a production environment, you
 * want the initialization just done once.
 */
public class AllwaysReloadControllerFactory extends AbstractControllerFactory {
    public final static String ATTRIB_ALLWAYS_RELOAD = "allways-reload";
    private boolean allwaysReload = false;
    private static Log log = LogFactory.getLog(AllwaysReloadControllerFactory.class);

    public void init(Element factoryNode, ServletConfig servletCfg) throws ConfigException {
        String allwaysReloadS = XML.getValue(factoryNode, ATTRIB_ALLWAYS_RELOAD);
        if(allwaysReloadS != null) {
            allwaysReload = Boolean.valueOf(allwaysReloadS).booleanValue();
            log.info("allwaysReload set to " + allwaysReload);
        } else {
            log.info("attribute " + ATTRIB_ALLWAYS_RELOAD + " not set");
        }
    }

    protected Controller decorateController(Element controllerNode, Controller controller) throws ConfigException {
        if(allwaysReload) {
            controller = new AllwaysReloadControllerAdapter(controller.getClass());
        }
        // let the framework do any additional decorating (ControllerWithParameters)
        controller = super.decorateController(controllerNode, controller);
        return controller;
    }
}
				

That uses a custom adapter:


/**
 * This adapter masquerades as a singleton controller but actually
 * creates single-use instance controllers AND initializes the controllers
 * if they are of type ControllerSingleton.
 */
public class AllwaysReloadControllerAdapter implements ControllerSingleton {
	protected Class controllerClass;
	protected Map params = null;
	private Element controllerNode;

	public AllwaysReloadControllerAdapter(Class controllerClass) {
		this.controllerClass = controllerClass;
	}

	public void init(Element controllerNode) throws ConfigException {
	    this.controllerNode = controllerNode;
	}

	public String go(ControllerContext cctx) throws ServletException {
		try {
			Controller instance = (Controller)this.controllerClass.newInstance();
			if(instance instanceof ControllerSingleton) {
			    ((ControllerSingleton)instance).init(controllerNode);
			}
			return instance.go(cctx);
		}
		catch (Exception ex) {
			throw new ServletException(ex);
		}
	}
}
				

Is defined in the maverick configuration document like:


<maverick version="2.0">
	<modules>
		<controller-factory provider="nl.openedge.util.maverick.CustomControllerFactory">
		    <allways-reload value="true"/>
		</controller-factory>
	</modules>
  
  ...
</maverick>
				


Name

views — Contains views which can be referenced from any command

Synopsis

Content

views ::= (view+)

Attributes

NameTypeDefault
modeCDATANone

Descripton

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.

Attributes

mode.  All views contained within the element will be considered to have this mode unless an explicit mode is specified on the individual view.

Examples

<maverick version="2.0">
  <views>
    <view id="loginRequired" path="en/loginRequired.jsp"/>
    <view id="loginFailed" path="en/loginFailed.jsp"/>
  </views>

  <views mode="de">
    <view id="loginRequired" path="de/loginRequired.jsp"/>
    <view id="loginFailed" path="de/loginFailed.jsp"/>
  </views>
  
  ...
</maverick>
				

Name

commands — Contains commands

Synopsis

Content

commands ::= (command+)

Attributes

None

Descripton

The commands element acts as a container for command elements.

Attributes

None

Examples

<maverick version="2.0">
  <commands>
    <command name="welcome">
      ...
    </command>
    <command name="signup">
      ...
    </command>
  </commands>
</maverick>
				

Name

command — Defines the behavior of one command

Synopsis

Content

command ::= (controller?, view+)

Attributes

NameTypeDefault
nameCDATARequired

Descripton

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.

Attributes

name.  The URI to associate with this command, minus the context root, extension, or query parameters. For example, "welcome" and "protected/reportDetail".

Examples

<maverick version="2.0" default-view-type="document">
  <commands>
    <command name="welcome">
      <view path="hello.html"/>
    </command>
  </commands>
</maverick>
				

Name

controller — Associates a controller with a particular command

Synopsis

Content

controller ::= (param*,[Special])

Attributes

NameTypeDefault
classCDATARequired

Descripton

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 ControllerContext.getControllerParams() prior to execution of the controller.

There are two types of controller classes, determined by whether or not the class implements org.infohazard.maverick.flow.Controller or org.infohazard.maverick.flow.ControllerSingleton.

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 org.infohazard.maverick.ctl such as ThrowawayBean which provides helpful additional features such as populating bean 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 bean classes 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.

A SingletonController helper base class is provided with Maverick: FormBeanUser. This is just one example which provides a very Struts-like action pattern.

Attributes

class.  The full name of the class which will act as the controller. This class must implement org.infohazard.maverick.flow.Controller or org.infohazard.maverick.flow.ControllerSingleton.

Examples

<maverick version="2.0" default-view-type="document">
  <commands>
    <command name="viewReport">
      <controller class="org.foo.Report">
        <param name="color" value="red"/>
      </controller>
      <view name="success" path="good.jsp"/>
      <view name="error" path="bad.jsp"/>
    </command>
  </commands>
</maverick>
				

Name

view — Defines a view as either part of a command or a globally referencable view

Synopsis

Content

view ::= (param*,[Special])

Attributes

NameTypeDefault
typeOne 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.
idCDATARequired if parent is views
nameCDATA The value of ref
refA previously defined view idNone
modeCDATANone
[Special]  

Descripton

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 ViewContext.getViewParams() prior to execution of the view. The meaning of a param is specific to the particular View type.

Other attributes and child elements are determined by the type of view. Nevertheless, there are some common conventions which ViewFactory 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.

Attributes

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.

Examples

<maverick version="2.0" default-view-type="document">
  <views>
    <view id="ugly" path="ugly.jsp"/>
    <view id="fugly" path="fugly.jsp"/>
  </views>
  <commands>
    <command name="viewReport">
      <controller class="org.foo.Report"/>
      <view name="good" path="good.jsp">
      	<param name="color" value="red"/>
      </view>
      <view name="bad" path="bad.jsp"/>
      <view ref="ugly"/>
      <view name="explicitname" ref="fugly"/>
    </command>
  </commands>
</maverick>
				

Name

transform — Defines a transform on views that support them

Synopsis

Content

transform ::= (param*,[Special])

Attributes

NameTypeDefault
typeOne 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.
[Special]  

Descripton

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 DVSL and 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 TransformContext.getTransformParams() 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 javadocs for each factory.

Attributes

type.  One of the types registered as a transform-factory, or one of the defaults (document, xslt).

Examples

<maverick version="2.0" default-view-type="document" default-transform-type="xslt">
  <commands>
    <command name="runQuery">
      <controller class="org.foo.Query"/>
      
      <view name="success" path="queryResults.jsp">
        <transform path="firstStage.xsl"/>
        <transform path="lookAndFeel.xsl"/>
      </view>
      
      <view name="error" path="queryError.jsp">
        <transform path="errorStage.xsl">
          <param name="color" value="red"/>
        </transform>
        <transform path="lookAndFeel.xsl"/>
      </view>
    </command>
  </commands>
</maverick>
				

Appendix B. Frequently Asked Questions

B.1. Why did you choose the name Maverick?
B.2. Huh?
B.3. How can I split my maverick.xml into multiple files?
B.4. How can I use a Maverick command as the welcome page, like index.jsp?
B.5. Can I chain command invocations together?
B.6. Does Maverick work with Tomcat 3.x?
B.7. How does Maverick compare to Struts?
B.8. How does Maverick compare to WebWork?
B.9. Why do I get a java.lang.NoClassDefFoundError when I start a Maverick application?
B.10. What jars does Maverick require?
B.11. What public websites are using Maverick?
B.1.

Why did you choose the name Maverick?

Because it is a better name than Ambivalence.

B.2.

Huh?

grep -i m.*v.*c /usr/share/dict/words

B.3.

How can I split my maverick.xml into multiple files?

You can make the XML parser do the work for you. Read this thread from the mailing list archives:

http://www.mail-archive.com/mav-user@lists.sourceforge.net/msg00462.html

B.4.

How can I use a Maverick command as the welcome page, like index.jsp?

You might have noticed that web containers won't let you specify a servlet as a welcome page. The reason is that the container goes through a list of files, checking to see if they exist on the disk and if not trying the next one. There are two solutions to this problem:

  • Create an index.jsp which contains <jsp:forward page="yourCommand.m"/>. This adds the overhead of a forward.

  • Create an empty file called yourCommand.m. The container will see this file and allow the request to process... which will then be handled normally as a servlet call. Cheesey.

B.5.

Can I chain command invocations together?

Yes. See the documentation for the "reuseMaverickContext" init-param to the Dispatcher.

B.6.

Does Maverick work with Tomcat 3.x?

It is not officially supported, but it can be made to work with some limitation. In general, avoid transforming documents, although transformations of Node sources will work. Also, since ThrowawayBean relies on getParameterMap(), you must derive your controllers from one of its superclasses, such as Throwaway.

B.7.

How does Maverick compare to Struts?

TODO

B.8.

How does Maverick compare to WebWork?

TODO

B.9.

Why do I get a java.lang.NoClassDefFoundError when I start a Maverick application?

You are missing one of the jars that Maverick depends on. See the FAQ regarding what jars Maverick uses.

B.10.

What jars does Maverick require?

Here is a list of all the jars currently used by the core (all available in the lib directory of the Maverick distribution):

  • commons-logging-1.0.3.jar

  • jdom.jar

  • commons-beanutils.jar

  • commons-collections.jar

Note that other pluggable view or transform types may require additional jars; the best way is to open up the sample WARs and see what is there.

Of couse, your environment will need the JAXP API and Servlet 2.3 API classes.

B.11.

What public websites are using Maverick?

This list is not especially comprehensive:

  • http://www.similarity.com - Jeff's pet project that spawned the original need for Maverick. Currently down for extensive revision. The view layer is built with Velocity and "document" transforms.

  • http://player.thesimsonline.ea.com - the player community website for The Sims Online. This is actually built as a hybrid MVC approach; while all form submissions go through the Maverick dispatcher, most pages use a special taglib to instantiate standard Maverick controllers inside the JSP. This was necessary to satisfy the requirement that producers be able to add pages without "developer" interaction. The view layer is built with JSP and JSTL.

  • http://www.fluidiom.com - An interesting experiment in memetics.

  • http://www.richdad.com

  • http://www.burgerweeshuis.nl - A Public website for a pop culture platform with a custom content management interface. Furthermore the website has a reservations module that is used by several other dutch culture platforms.