[Ecm] cpsskins design notes (part 2) - about caching

Jean-Marc Orliaguet jmo at ita.chalmers.se
Mon Sep 18 21:56:52 CEST 2006


Hi!

I've done further experiments with JSF/facelets/Seam, to see how these 
technologies fit with the concept of a dynamic rendering engine.

So far the implementation is in phase with the design, that is, things 
fit well - with no surprises, but there is a design issue that makes me 
feel that I'm attempting to shove a square peg into a round hole. I'm 
going to expose it now.

I also took a quick look at NXActions and I see that they same issue is 
present there too, apparently unsolved.

This is really all about separating data and presentation layers and 
about deciding whether to use a data-driven or a presentation-driven 
design. Eventually this is also about caching data efficiently.

Background
==========

1) content or "markup" providers
--------------------------------

In the Zope2 design of CPSSkins/CPSPortlets  all visual elements 
(templets, portlets) return *HTML markup*.
This is a plain and simple design in which implementing a portlet means 
implementing something that returns HTML, the rest is simply decoration.

This is the way the JSR-168 API generates content:

the doView(), doEdit(), doHelp() methods in javax.portlet.* produce some 
markup that get written to the response with

    response.getWriter().write("....")

or they can dispatch the request to another page (using 
PortletRequestDispatcher) and insert it into the response, but in the 
end this is still markup that gets generated by portlets.

This is also the way "viewlets" work in Zope3 (see 
zope.contentprovider.interfaces.IContentProvider::render).

In these cases, the portlet's core and the portlet's presentation layer 
are identical, there is no distinction between model and view.


2) the model-driven approach
----------------------------

In order to separate model and view (data and presentation of data), it 
is possible to do as in CPSSchemas by saying that there is:

  - a data structure with fields, a storage layer, a data model that 
acts as a facade for the data structure making abstraction of the 
storage and other internals, and
  - a presentation layer with layouts, widgets dealing with the 
presentation of data.

In the particular case of visual elements such as portlets however, this 
can be simplified to having
  - a transient structure holding data and
  - a widget that can interpret the data,

i.e. we don't have to deal with storage or validation issues at that level.

This is by the way the concept found behind separating "presentation 
beans" and JSP/JSF templates: presentation beans hold transient 
presentation data obtained from the application's model and business 
logic and the presentation layer is responsible for producing markup, 
without having to deal with the details of how the application is built.

That is exactly the kind of logical separation that I'm thinking of 
between portlet's data and widgets. It is very much "model-driven". The 
presentation bean acts as a mediator between the business logic and the 
presentation layer, in that case the portlet would also act as a mediator.


3) the template-driven approach
-------------------------------

Then comes JBoss Seam with the concept of "bijection", that is, of 
"injection" and of "outjection" of data.

"Outjection" is very much in the spirit of what is described in point 2) 
[ "bean data => template"] but "injection" is just the opposite: if a 
variable is referenced in a page template, Seam will intercept and 
injected data into the bean. This will occur when a user *navigates* to 
a given page depending on the context (session, conversation, page, ..). 
Also there is not necessarily any data in the bean prior to that.

Putting it simply: this is highly "template-driven" or "event-driven", 
the data gets loaded into the presentation bean only on-demand depending 
on the user's actions. And this is done in a *very contextual way* based 
on the component's own scope.


Comparisons
-----------

To compare the two approaches, the model-driven approach is very generic 
(ought to work in a rich-client environment), while the template-driven 
approach is Seam-specific, i.e. it will work only in a web environment.

The model used in NXAction is the one described in 2), one can also read 
in NXAction/src/org/nuxeo/ecm/actions/ejb/ActionManagerBean.java

public class ActionManagerBean {
    ...
    // TODO: the seam context cannot be retrieved here
    // - need to find a method to pass the seam context
    public Collection<Action> getActions(String category) {
        ActionContext context = new ActionContext();
        // TODO setup the context
        return actionService.getActions(context, category);
    }


Back to cpsskins
----------------

In fact, I encountered exactly the same problem in the core component of 
cpsskins, namely how to set the context in a component that knows 
nothing about JSF/Seam in the first place?

I first added it "from the outside" by letting the JSF layer do a

    info.setContext(facesContext);

on the data structure, but something just didn't feel right.


The idea when developing a presentation bean is to either:
- passively let Seam inject data into it or
- to actively set up a data structure which the presentation layer will 
depend on (not the other way around)

But it is difficult to mix both approaches and be consistent.

So I came to the following design decisions:

* the data structures created by portlets cannot be "Seam contextual", 
they have to be available directly by "mechanical computation" from the 
portlet's own configuration data, for instance an RSS feed producing a 
list of items as a data structure is OK

* contextual data is managed by Seam at the presentation (widget) level.

I don't see how to implement the data/presentation logical separation 
otherwise.


About caching
=============

Since the management of contextual data is now delegated to Seam (or any 
other context management framework) this simplifies caching issues *a 
lot*. Namely it is Seam's problem to deal with session data management.

What the cpsskins rendering engine provides however is a nearly static 
template which can be contextual of course but within a more controlled 
scope (i.e based on the current perspective, based on the name of the 
page that is being viewed, etc.).

The cpsskins widgets, instead of producing final HTML, will provide JSF 
markup such as:

    <h:commandLink value="#{item.name}"
            action="#{workspaceActions.selectItem}" />

but not the final rendering, which is managed by JSF or Seam.


Static vs dynamic content
-------------------------

Anyone familiar with Edge-Side-Includes 
(http://www.esi.org/overview.html) knows that the content of a page 
template is divided into:

- static content (logotype, footer, etc.)
- dynamic content (contextual, time-based data, ...)

cpsskins currently implements such a distinction, that is, it is 
possible to mark a portlet as 'static' or as 'dynamic'.

- if a portlet is 'static' it will be part of the theme with no 
possibility for its markup to change over time
- if a portlet is 'dynamic' its markup will be rendered at each request 
(with the possibility to add a second-level cache of course)

it is clear that static content is rendered in a much faster way than 
dynamic content since there is virtually no memory or CPU overhead for 
getting the data (typically one gets 300-400 req/s even on large pages).

dynamic content however is rendered in threw steps:

1) in the first step, a JSF tag is rendered instead of the final HTML 
markup, for instance:

  <cpsskins:portlet uid="1234" />

  this gets cached in the page's template ad vitam eternam.

2) in the second step, the tag is rendered by the cpsskins rendering 
engine as some HTML or JSF markup, for instance:

    <h:commandLink value="#{item.name}"
      action="#{workspaceActions.selectItem}" />

3) in the third step, the JSF markup is rendered as HTML:

    <a href="...">item</a>


Of course if the second step always produces the same JSF markup, then 
the portlet can be marked as 'static' and step 2 becomes step 1. So 
'static' does not mean that the final markup never changes.


I did some benchmarks, and the 3-step rendering gives very good results 
already even without optimizations (typically 100-250 req/s). My guess 
is that most portlets can be marked as 'static' if JSF/Seam is used.

================

All for now :-)

/JM







This list archive provided by Nuxeo, the leaders of open source ECM. Check out the Nuxeo 5 open source, standards-based ECM project.