More JSF 2 Ideas
While sitting in the concurrency pitfalls session at javaone (nothing new and I totally disagreed with his use of annotations as pure documentation)-- anyways, my mind wandered and I started thinking about Seam, WebBeans, and some of the byte code stuff the JPA is using. So why can't we do the same with JSF 2?
A while back, I wrote a bit on some initial ideas for JSF 2, but was stuck on how to propagate state changes between components without explicit assignment. Swing suffers from this same issue and the bean binding spec blows in its current state. Anyways, two things are going for JSF on this front in the current specs:
- All components must be created by some factory-- yay, we can byte code enhance them. Since 98.4% of all JSF UIComponents in apps today come from templates (Facelets, JSP, Shale, etc), the factory creation hook really isn't an issue
- The current idea behind UIComponent has tons of metadata which can be hidden from the end user in JSF 2
Where I think we can differ from something like Seam/JPA, is that all you want to do in your code is say, "This field is injectable, and leave the 'what' is injected up to some other resource." I actually appreciate this separation a bit as I believe both Tapestry 5 and Google Guice promotes this to some extent, but here's an example:
@Attribute
private Iterable value;
In the above example, all I want to say is that this field is an attribute, leaving a separate set of metadata as provided by a template to dictate what is actually bound:
<h:dataTable value="#{dept.employees}"/>
Where things get difficult is now in marrying the two parts: the developer's object and the metadata from the template. Often this is done with byte-code enhancements in common ORM frameworks, but to do this better, we take a feather from Seam's ideas around bijection-- such that:
@Attribute
private Iterable value;
Is two way-- set it and the value propagates back into the variable-scope while fetching it pulls the value from some variable-scope. Here's a more elaborate example:
<h:dataTable value="#{dept.employees}" var="emp">
<h:column>#{emp}</h:column>
</h:dataTable>
The Java code for the HtmlDataTable would look something like:
public class HtmlDataTable extends UIComponent {
@Attribute
private Object var;
@Attribute
private Iterable value;
@Children
private Collection<UIColumn> columns;
public void onEncode(FacesContext faces) {
for (Object i : value) {
this.var = i;
for (UIColumn c : columns) {
c.onEncode(faces)
}
}
}
}
The conundrum is that we'd like to preserve the evaluation flexibility from JSF 1.x, but how does the child UIColumn able to resolve the variable "emp" when we move to an annotated binding solution? Since bindings are two ways, doing this.var = i
, actually is byte-code enhanced to push whatever i
is back out into the variable-scope as "emp". So when you turn around and call c.onEncode(faces)
, the UIColumn will inject-on-invoke any possible state required for resolution, in this case, "emp".
If we weren't going to do byte-code enhancement and just look at an EJB 3 interceptor, the pseudo code for lifecycle methods would look something like:
public Object intercept(InvocationContext ctx) {
// exit if already on stack for eval
if (is lifecycle method) {
Object component = ctx.getBean();
UIComponentMetaModel meta = // resolve metadata from template
StateManager stateMngr = // get threadlocal statemanager
for (Stateful state : // find stateful members) {
Object value = stateMngr.restore(clientId, component, state.getName());
setState(component, state.getName(), value);
}
for (Attributes attr : // find attributes) {
Object value = meta.getValue(attr.getName(), attr.getType());
setState(component, attr.getName(), value);
}
Object result = ctx.proceed();
for (Stateful state : // find stateful members) {
Object value = getState(component, state.getName());
stateMngr.save(clientId, component, state.getName(), value);
}
}
}
I threw in stateful crap, because we need to handle it-- but no longer mess with saving state for everything when in reality, only in niche cases would you ever have to save state-- like some kind of counter component or preferences, but we could be looking at only a couple primitive values for a very large view-- making things much more performant.
Going back to the marriage of the UIComponentMetaModel and the class that the end developer wrote, I think you'd have a single JSF byte-code enhanced class per type-- which gets the metamodel pre-assigned before returning back to the client of the factory. It could be more efficient if we did byte-code per USE of the class such that each invocation wouldn't have to 'ask' the metamodel for information, it could just have everything explicitly scripted out for it without little iterations/for-loops. At that point, things would be a pure performance implementation.
Also, for this to work, returned component trees would have to be constructed from the bottom up like Facelets does such that metamodel data passed to parents is already child aware. BTW, what if Swing was able to work this way-- no more listeners and propertychange blah blah blah. Much of these use cases in Java in general would be so much simpler if properties were actual first class citizens instead of the bean 'hack/convention' everyone is accustomed to.
Where this differs from Tapestry 5 is that we must retain the direct parent/child operability-- that programatic freedom that's allowed in JSF over other component frameworks. While Tapestry 5 is a huge step forward and a new benchmark to shoot for, I think we can do a little better with JSF 2 in retaining the flexability it provides from a component composition standpoint. To many, this is probably a niche case for them, but extremely important in producing a commodity solution.