The Source for Java Technology Collaboration
User: Password:



Jacob Hookom's Blog

February 2006 Archives


New Feature for JSF 1.2

Posted by jhook on February 25, 2006 at 09:03 AM | Permalink | Comments (19)

First off, thanks to the JCP and JEE EG for allowing us to add this feature so late in the game. The system *does* work! Now, on to the details...

The core of JSF views are mutable component trees, uniquely identified, black boxes of encapsulated features that can be dropped in and combined in the true academic definition of a 'component'. Each component can participate not only in rendering, but all other phases of MVC including updating, validation, invoking actions, etc as part of an event system. One way to look at it is Action chaining to the Nth degree in the form of widgets that will coordinate/handle MVC request processing.

So here we have trees of components. How do I ask for a particular component in the tree? Well, currently the JSF API only had UIComponent.findComponent(FacesContext, String id), which used a specialized logic of interpreting that passed String id to find the associated component. Developers immediately assumed that the ids JSF produces work for the findComponent() method. Sorry to say, they don't.

The reason why findComponent() doesn't work is in how JSF works with iterative contexts-- something traditionally done with a c:forEach, h:dataTable, or d:displayTag. If you have a parent component who will render its children a hundred times, it doesn't mean there's a hundred children. The parent component simply evaluates its one or two children 100 times, saving on memory. Makes sense doesn't it? So, now we have to uniquely identify each child for each iteration, so JSF produces ids (Client Ids) in the form of mytable:3:text, mytable:4:text, mytable:5:text, etc. You get the picture.

Now, lets say you wanted to take that identifier of mytable:4:text and operate on it later, such as re-rendering part of the page without evaluating the rest of the page. We know that UIComponent.findComponent doesn't work since it returns a UIComponent instance-- but for what row, what state-- there's only one instance in the component tree! And, findComponent knows nothing of the client identifiers your custom UIComponents produce. What do we do?

To solve this, we've added boolean UIComponent.invokeOnComponent(FacesContext faces, String clientId, ContextCallback callback). This allows, in a Chain of Responsibility fashion, a callback to be passed into the tree and when a UIComponent decides to handle it, it invokes the callback and returns true, telling the process that we're done.

This allows black box components, like UIData (h:dataTable) to allow you to operate on a specific component within a given row. Keep in mind, each iteration, just like with a for loop in Java is volatile, so by passing a callback, as in an event system, you can operate on state within a given iteration without collision.

Here's a simple example of partially rendering a JSF component model for things like AJAX. JSF renders your page and produces a series of client identifiers in the page. Now, you want to refresh only part of the rendered page for a known client identifier. Simple!


public static final ContextCallback RENDER = new ContextCallback() {
   public void invokeContextCallback(FacesContext ctx, UIComponent c) {
      c.renderAll(ctx);
   }
};

// custom ajax request
String clientId = paramMap.get("renderId");
UIViewRoot root = faces.getViewRoot();

boolean found = root.invokeOnComponent(faces, clientId, RENDER);

if (!found) throw new FacesException(clientId + " not found!");

Notice, we didn't need to process or render the whole tree and mess with special content buffering or wasteful evaluation. We just pick out what we want to operate on. The RENDER ContextCallback was a simple example, but could be extended for any number of things. Since each UIComponent knows how it produces client identifiers, you can optimize the invokeOnComponent seach to jump to the correct component state in just a few hops, avoiding visiting or evaluating 100 child component iterations when you know the client id was for index 101.

Fantastic stuff in my opinion. At the 30,000 foot view, here we've broken through the traditional URI/resource in request/action based frameworks such as RoR, where each thing you want to partially handle has to be separated out and coordinated as a specialized endpoint. Not so with JSF since we've extended the concept of a URI into the page itself, allowing you to uniquely operate on resources embeded into the page. The best part is that your components don't need to know that they are processed as part of a whole or part. In truth, the same applies to your domain model-- refresh that list of employees or modify just one employee without needing to coordinate actions or special cases.



JSF More Performant than Action Frameworks?

Posted by jhook on February 08, 2006 at 02:48 PM | Permalink | Comments (23)

With traditional Action MVC frameworks (Struts), you have a specific set of scopes to store state in over transactions: request, session, and application. Often, while developing applications with Action MVC frameworks, you have to make a decision to store objects/state in the request or the session. There can be consequences to the the performance of your application in either case. You can go the transient route and just retain state for a request cycle, or you can live with and manage it in your session.

Let's disect a request to an MVC application-- when you compare the time your in-memory Java code takes compared to DB calls, often times, the time spent by the MVC framework is marginal-- JSF or Struts. So we go back to trying to fix performance with the DB by either specializing operations in the request or storing results in the session.

JSF has acted as a foundation for frameworks like JBoss Seam and Oracle ADF because it provides context to user interactions. These contexts can be used to scope cached data in more meaningful processes without committing to a full transient (request) or persistent (session) solution. It becomes much more practical to store results from the DB within the context of a particular paging table component, avoiding the 600ms->20sec DB queries. I hope you see my point.

Take a look at your caching strategies and ORM mappings-- what's driving those fetch policies and associations? Are you making decisions to not-lazy fetch an association because you display it with the parent object? I would say that a lot of your decisions around caching/fetching policies is formulated by what's being coordinated in the presentation. Doesn't it make a lot of sense then to have a MVC solution that retains and manages those contextual associations for you?

Now, pair that meaningful, contextual state management with encapsulated AJAX components that can communicate back to that managed state without 'requiring' DB calls for partial requests....





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds