Skip to main content

The New Servlet for MVC Frameworks

Posted by jhook on March 16, 2006 at 11:44 PM PST

Frameworks of Today

All MVC frameworks basically operate within the same 5 phases:

  1. Decode Interpreting state/parameters passed from the client
  2. Validate This involves validating the information decoded and possibly authorization/authentication
  3. Update Once the submitted information is validated, update the Java model
  4. Invoke Some controller behavior is handled here, such as an action once state is applied from the update phase.
  5. Encode Rendering the result of the action invocation and model state back to the client

Each of our favorite frameworks are implemented differently, but these phases always exist in some form or as an optional phase. Look at Struts w/ ActionForms and Actions or the hook methods and interceptors within WebWork.

Historically, all of these frameworks start their integration with an HttpServlet or Filter. This is their platform:

public void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    // start here
}

Wow, thanks JEE. You can begin to see why there's so much redundancy or one off approaches to MVC implementations.

A while back, Kito Mann started a WebTier Alignment Group of framework developers. While the group unfortunately never really took off, there were some great discussions and comments. The group did identify that there's common artifacts and points of integration between these frameworks. Maybe a common way of defining validators or converters-- or even a common way of integrating a project's domain model?

I'm going to go out on a limb and suggest that frameworks instead piggy back on the FacesServlet. I'm not saying that framework developers use JSF as end developers do, but being that it's part of the JEE 5 specification, use the bits and pieces you need. There's so much there that can be provided to frameworks that operate completely different.

Frameworks of Tomorrow

To elaborate on this, I'm going to template out a simple Action framework that sits on top of JSF, but only uses a little bit of the features, not including the overhead of stateful components. Ideally, we want to process URIs like this:

/order.addItem.do?item.id=41667&item.qty=3&item.code=SA

First, based on our familiarity with the HttpServlet, where do we start with our simple solution for JSF? The easiest route is to implement a PhaseListener which intercepts requests and does all the processing itself. Alternately we can look at simply providing a Lifecycle implementation.

Because we want to provide RoR/WebWork-like features, it would be great if we can simply invoke or set properties directly on our domain objects from an IoC container. Well, the Faces API has a scoped IoC container built in and has integration from both Spring and JBoss Seam to name a few. Sounds like we're already ahead of the game for our simple Action framework.

To implement our request processing, we'll break the URI into parts and evaluate them using JSF's built-in EL capabilities. The following is example code for basically implementing the 5 MVC phases with the help of JSF. While there's more to be added, this provides the basics required in pseudo code.

// get our JSF-provided FacesContext
FacesContext faces = FacesContext.getInstance();
Application app = faces.getApplication();

// JSF provides Portlet/Servlet wrapper
ExternalContext ext = faces.getExternalContext();

// EL support from JSF's API
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext el = faces.getELContext(); // local to thread

// to store messages
Map<String, String> msgs = new HashMap<String,String>();
ext.getRequestAttributeMap().put("messages", msgs);

// our view result to forward to when we're done
String result = null;

// 1: decode phase
String action = faces.getViewRoot().getId(); // w/ massaging
Map<String,String> params = ext.getRequestParameterMap();

// 2: validate phase
boolean valid = true;
Map<String,ValueExpression> paramEL = new HashMap();

// enable our ValidateResolver
el.putContext(ValidateResolver.class, Boolean.TRUE);

ValueExpression ve = null;
for (Map.Entry pair : params) {
   try {
     ve = elFact.createValueExpression(el,
                                       "#{"+pair.getKey()+"}",
                                       Object.class);
     paramEL.put(pair.getKey(), ve);

     // ValidateResolver could throw an error here
     ve.setValue(el, pair.getValue());
   } catch (ValidatorException ve) {
     valid = false;
     msgs.put(pair.getKey(), ve.getMessage());
   }
}

// disable our ValidateResolver
el.putContext(ValidateResolver.class, Boolean.FALSE);

// if we validated successfully
if (valid) {

   // 3: update phase
   for (Map.Entry pair : params) {
        ve = paramEL.get(pair.getKey());

        // set it for real
        ve.setValue(el, pair.getValue());
   }

   // 4: invoke phase
   String elAction = "#{"+action+"}";
   MethodExpression me = elFactory.createMethodExpression(el,
                             elAction, Object.class, null);

   Object invResult = me.invoke(el, null);
   result = (invResult != null) ? inResult.toString : null;

   if (result != null) {
      // delegate routing of result
      NavigationHandler nav = app.getNavigationHandler();
      nav.handleNavigation(faces, action, result);
   }
}

// 5: encode
faces.renderResponse();

While the above is only pseudo code, it does show that JSF could be used without needing to use stateful UIComponents with a custom, lightweight MVC framework. Because your framework would have a FacesContext always available, it would be much easier for you to include UIComponents down the road if you so choose.

Conclusion

I guess the point I'm making is that these new frameworks should take advantage of the JSF API, even though you're not using all of it. Facilitating other framework developers with the JSF API is something that should be pursued more vigorously by the JSF EG. With each phase of MVC outlined within JSF and the delegation of concerns around ViewHandlers, NavigationHandlers, ActionListeners, Converters, Validators, etc, the whole stack is very customizable. Ideally, developers could co-habitate MVC solutions on top of the JSF-API such that you can have the best of both worlds with action and component frameworks.

Related Topics >>