Chrysalis Architecture
The document describes the architecture and design philosophy
behind the Chrysalis Web Framework.
Motivating Design Patterns
Chrysalis is based on one simple idea: that servlet invocations
resemble Remote Procedures Calls (RPC). Form values can be
considered parameters of a procedure call with the servlet as the
procedure being invoked.
Servlet Invoked with the parameters id, name
and stock
The above operation could be mapped to a method like the
following:
void saveItem(long id, String name, int stock) ...
Once we perform this mapping successfully, we can follow the
standard object-oriented pattern of putting related methods into a
single class, with instance variables for the data values
manipulated by those methods. This makes the servlet controllers
less like RPC and more like Distributed Objects.
public class CatalogController extends Controller {
private Item currentItem;
void saveItem(long id, String name, int stock) ...
void removeItem(long id) ...
// Other methods ...
}
Chrysalis combines this idea with the
Model-View-Controller (MVC) pattern used by many web
frameworks:
-
The Model: Components that store business data. In
Chrysalis, these are JavaBeans. The Model should not know anything
about the web environment.
-
The View: Components that display information to the
user. In Chrysalis, these are JSP pages and custom tags. The JSP
display information from the JavaBeans.
-
The Controller: Components that react to user input. In
a sense, controllers mediate between the model and the view,
because they take data entered by the user in the view and insert
those values into the model. In Chrysalis these controllers are
called "controllers" (subclasses of the
Controller
class).
Chrysalis also uses the Front Controller pattern to limit
entry points into the system.
Basic Architecture
The following diagram illustrates the basic Chrysalis
architecture.
All invocations go through one of two Front
Controllers:
-
CommandFilter: For command invocations (URLs ending with
*.cmd
).
-
ViewFilter: For view invocations (
*.jsp
and
URLs ending with *.view
).
The CommandFilter invokes the appropriate controller
method of a controller class. The ViewFilter displays a JSP.
Both controllers and JSP invoke JavaBeans to manipulate application
data.
Command Invocations
The flow for command invocations is as follows:
- The browser invokes the command with its URL:
Cart.placeOrder.cmd
.
- The command extension (
*.cmd
) is mapped to the
CommandFilter.
- The command filter invokes the ControllerMapper.
- The ControllerMapper parses the URL to determine the
correct controller and controller method.
- The ControllerMapper retrieves the controller from the
servlet session. If no controller exists, a new one is
created.
- The ControllerMapper invokes the correct controller
method, converting request parameters to method
parameters.
View Invocations
The flow for command invocations is as follows:
- The browser invokes the view with its URL:
showOrder.jsp
.
- The invocation passes through the ViewFilter to
initialize the Chrysalis environment.
- The servlet engine executes the appropriate JSP.
- The JSP may use a tag to load object data from controllers:
<jutil:use
var="
order
"
controller="
Cart
" />
.
- This tag retrieves the controller from the session and invokes
the appropriate initializer method to load object data.
- The object data is stored in the JSP
pageContext
,
so its information can be displayed in the page.
If the page has a template, things are a bit more
complicated:
- The ViewFilter first calls the template page.
- The template page uses the
<ji18n:includeBody
/>
tag to invoke the original target JSP.
- The message tags in the template use the same configuration
data as the target page, allowing template contents like page title
to be customized for a particular page through the page
configuration.
- The target JSP is invoked, and its output is inserted
(included) into the template page.
Application Flow
Chrysalis uses two kinds of links between commands:
-
Command Invocations (
*.cmd
): Invokes the
appropriate controller method.
-
View Invocations (
*.jsp/*.view
): Display
the appropriate page.
Unlike many frameworks, object data is not pre-loaded and
forwarded to the JSP. The JSP can invoke controllers directly to
load data (via the <jutil:use>
tag). This means
that it is possible to link directly to a view page and test them
as self-contained components.
Links between components should be as follows:
-
JSP to JSP
: Simple
hyperlinks to the next view component.
-
JSP to Controller
: Command
invocations in hyperlinks or form actions.
-
Controller to JSP
:
Redirection to the appropriate view after the command invocation is
complete.
-
Controller to Controller: Direct Java invocation using
the
Controller.getController()
method (possible, but
not recommended).
The flow diagram of the sample application illustrates these
principles.
Note: Since the template.jsp is embedded in every
page, its links are available globally.
Servlet 2.2 Filter Support
The key components in Chrysalis are
javax.servlet.Filter
classes. For backwards
compatibility with the Servlet 2.2 standard, Chrysalis has a Filter
Micro-Container to add partial filter support to older web
containers. The implementation classes for the Filter
Micro-Container are in the org.chwf.servlet.filter
package.
The heart of the Filter Micro-Container is the
FilterServlet
, which invokes filters based on the
configuration information in the /WEB-INF/filter.xml file.
The FilterServlet must be assigned the full set of URL patterns
handled by all of its filters; individual filters may be assigned
more specific filter patterns.
The Filter Micro-Container is independent of much of the rest of
the Chrysalis framework, and can be used as a generic mechanism for
adding filter support to older web applications.
Resource Mapper
The filter specifications state that when the end of a filter
chain is reached, the original target resource should be invoked.
The component that fulfills this function in the Filter
Micro-Container is the ResourceMapper
class. When the
filter chain is finished, the ResourceMapper
invokes
the target resource using the
RequestDispatcher.include()
method.
Unfortunately, the ResourceMapper
(and filters)
cannot directly filter the target resource on Servlet 2.2
containers. Suppose, for example, a filter was mapped to the URL
pattern "*.jsp". This means that the FilterServlet
must also be mapped to the URL pattern "*.jsp". When the
ResourceMapper
tries to invoke the JSP page, the
servlet engine will invoke the FilterServlet
instead,
which will re-start the filter chain and trapped the system in an
endless loop.
To circumvent this problem, the ResourceMapper
matches "mapped" extensions to "resource" extensions. The mapped
extension acts as an alias for the file extension of the actual
resource. For example, the ".view" extension is mapped to the
".jsp" extension in the standard Chrysalis configuration. To invoke
the filters in front of a JSP, the browser's URL must replace
".jsp" with the ".view" extension. The ResourceMapper
converts ".view" back to ".jsp" before invoking the resource.
This means that on older web containers, Chrysalis view URLs
must use a different extension (.view) from the file extension
(.jsp). This is annoying, but this is the best that can be done on
older web containers without rebuilding the entire servlet engine
from scratch.
Also note that the same issue prevents the
FilterServlet
from being mapped to the "/*" pattern;
the FilterServlet
cannot filter everything, because it
becomes impossible for the ResourceMapper
to invoke
the target resources.
The exact resource mappings can be controlled in the
/WEB-INF/filter.xml configuration file.
<web-app>
<!-- Other configuration ... -->
<resource-mapping>
<mapped-extension>*.view</mapped-extension>
<resource-extension>*.jsp</resource-extension>
</resource-mapping>
</web-app>
Composite Filters
To simplify configuration for common filter chains, the Filter
Micro-Container has a CompositeFilter
that can be used
to group other filters together. When a
CompositeFilter
(or one of its subclasses) is invoked,
all of the composed filters are invoked in turn, then the filter
chain continues. All of the composed filters effectively match the
same URL pattern as the composite filter.
Both the CommandFilter
and ViewFilter
at the heart of the Chrysalis engine are composite filters, to
facilitate future expansion.
Filters on Servlet 2.3+ Containers
On Servlet 2.3+ containers, the Chrysalis filters can be
specified in the standard /WEB-INF/web.xml configuration
file. This allows the filters to have URL patterns matching file
resource extensions, such as ".jsp". This makes the web application
much easier to understand, since file extensions and URL extensions
will be the same for view pages.
If backwards compatibility with Servlet 2.2 containers is
necessary, you can add the ResourceMapper
filter to
the end of the filter chain to perform the necessary extension
mappings. The necessary steps are discussed in the Chrysalis
configuration guidelines for the web.xml and filter.xml files.