 |
Experiment: Going Stateless with JSF
Posted by jhook on January 14, 2006 at 04:26 PM | Comments (11)
In one my last blogs on the concepts behind Avatar involved trying to gain performance via AJAX by partially processing a UIComponent tree in JSF.
I'd like to bring another possible idea up with JSF: what about treating all components as stateless? Not stateless as in Servlet-stateless, but stateless as in request scoped.
As mentioned in the Avatar blog, component tree creation is dirt cheap (especially with Facelets). What's expensive is state saving and rendering. So here we have this idea that evolved out of Avatar about partially rendering component trees, but then there's still a state saving issue. Even though we would render only part of the document for AJAX requests, theoretically, any part of the component tree could have changed-- so the whole tree has to be saved on every request.
JSF currently allows two different modes of state saving: client and server. In current implementations of Avatar (such as Galen Dunkleberger's), state saving only works in server mode. This is because there isn't a requirement to update the client document on every request because the server manages that state for you. With client state saving, you are required to maintain state change in the client document via hidden variables and inputs. This is a big hurdle to say the least.
Well, what if we were to throw that requirement of state saving out the window? This way partial rendering, restful communication, and server load wouldn't be so much of a problem. Maybe because I've spent too much time in the 'boiler room' of JSF to see the importance of retaining state in UIComponents, but I'd like to explain some of my reasoning behind this approach in hopes of getting feedback from other developers.
There's two ways to manage state. Actually Store the result of the process, or be able to consistently repeat the process to be able to conclude the same result. Think about JSF's tree building process and the the fact that it's clearly repeatable-- structually the same[1] and expressions can be bound on every request. So why store the same stuff for every user on your site?
[1] With JSF's tree building process, imperative control tags, such as JSTL, force a mark'n'sweep process on tree creation. While this does allow the page to be structually different from the previous request, it's easily avoidable by using JSF counter parts. In some cases, one could say that re-evaluating the structure of the tree on every request would be preferred.
With the new EL-API and webtier alignment with JSP, there's no longer a disconnect on variable management and attribute assignment between the evaluated JSP document and the UIComponent tree. Again, this only leans towards the fact that retaining variable/attribute state across requests (document evaluations) could be unecessary.
Compared to other frameworks, like WebWork or Struts ActionForms, you have request-scoped state to operate on in different ways. Example, you can take a WebWork action and invoke multiple methods on that object such as update, validate, execute, etc. Hey, wait! So could JSF. Why couldn't you produce a JSF UIComponent tree at the start of every request, then update it from the incoming request, possibly validate, update the model, invoke actions, and then render?
Statesaving in JSF is [really] bad in implementation. It's very much like JSP tags where all possible attributes have to be processed. Same thing with any variable state for JSF-- both within the UIComponent itself and the whole tree structure. It's not like EJB3 or Hibernate where the framework can freely managage or optimize state deltas for you since JSF lets you explicitly implement state saving, even if you don't want to. I've thought about doing automatic state saving deltas, but then I'd lose any contract with invoking the state saving template methods on UIComponent.
JSF probably does the best job (out of the MVC frameworks) of clearly separating the View from the Model and Controller via EL. Again, this introduces an interesting point such that anything we would want to retain reference to within the JSF lifecycle and state saving-- is defined by a [static] EL expression that can be assigned and evaluated, disjoint from the UIComponent's state.
As we move into more client-side functionality with JSF and AJAX or simply client side DOM, it may introduce a disconnect between the captured state and the actual state the client sees. Then being able to 'lighter' requests back to the server becomes much more important (Avatar) and state saving of the perceived server state becomes [unecessary] overhead.
At this point you may think I'm either really dense or just maybe have a point. Again, I haven't had a chance to look at other people's components and what they require for state saving. Please let me know your thoughts and requirements where not saving state would cause problems for your components.
Truthfully, I see some issues with this approach, but they aren't without their own work arounds...
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
i see it
Posted by: kakujx on January 16, 2006 at 05:10 PM
-
Doesn't making every component an "immediate" component accomplish the same thing?
Posted by: fbolander on January 19, 2006 at 10:47 AM
-
Hi Jacob,
First, I like the direction, and I think it fits with the design intent of JSF.
As you know, during one of many iterations of the Tree Creation proposal for JSF 1.2, we did try to build the tree up front during RestoreView, but had to abandon that approach due to some complexities encountered in getting it to work with JSP. However, that was before facelets. From the beginning of the state management design, I was trying to enable a system where the tree structure could be created from a static file and the component state could be applied to that tree. This was the whole reason for the separate "tree structure" and "component state" concepts in the State Saving API for JSF 1.0. We see now that the contract we have where every component is guaranteed to have its save/restsore state methods called at the right time does effectively hobble this approach. However, let's assume we can build the static tree during restore view somehow. I think it's possible to encode more information in the incoming request to identify the complete set of components whose state has changed from the default, along with the new values comprising that state, and only call the restore state methods on *those* components.
Ed (JSR-252 spec-co-lead)
Posted by: edburns on February 02, 2006 at 04:02 AM
-
I think the state saving is now needed only for complex components like a tree or grid. Here state is used to preserve, for instance, expanded nodes or current page. But we can hold this information inside models in a user beans also (we already use user beans to hold part of state). Therefore all “state” will be in user beans. And UIComponent tree will be completely defined by facelets and beans.
Posted by: denis7 on February 03, 2006 at 08:30 AM
-
Jacob, could you post facelets on maven2 ? It would be useful because people are interested in using maven 2 because of transitive dependencies and facelets distribution doesn't state clearly which versions of its own dependencies it uses....
Gilles Philippart
Posted by: gphilipp on February 06, 2006 at 03:12 PM
-
Jacob,
I implemented a view handler similar to facelets before facelets came out, using xhtml to define views.
However it implemented it so that every tag is a component (including normal html tags). This gives you amazing power, as the jsf component tree effectively becomes a DOM.
The problem of course is that there are way too many components to save the state on the client side.
In order to improve scalability, I did a lot of memory profiling ( I think you might have commented on a couple of my posted jsf bugs). But a large view might still take 50K, and is stored per user in their session.
This lead me to investigate the possibility of stateless components. The solution I came up with involves the application keeping a pool of views. When a post comes in, a view is got out of the pool, goes through the jsf phases, and is then cleaned, and returned to the pool.
I found that the only thing that really needs cleaning is submittedValue on UIInput. I set this to null before the view goes back into the pool. Almost all important state is saved on backing beans, via EL expressions.
For exception components, like a tree, I can mark an individual component as stateMode="session" .
The other thing I do is save a hash of each components state at creation, so that I can check that the structure has not been changed before it goes back in the pool.
All this seems to work quite well, but I guess it will take my next production app to iron out the wrinkles.
Cheers,
Jon Nermut
Posted by: jnermut on March 01, 2006 at 04:24 PM
-
I guess as soon as you have to externally declare a state mode, then I think we begin to lose out on what makes JSF so easy to use-- we don't want to worry about state, in whatever form or interpretation as a user of the framework. I still think there's some validity to stateless components, possibly doing more with Facelet ComponentHandlers that can produce proxy instances that are lazy referenced by the tree, to optimize things like AJAX and partial rendering. I swear I almost have this conversation weekly with Adam Winer (ADF) about going stateless with JSF, but he always finds some condition or component that would need to retain state across requests. As soon as we start going case by case or require the component to figure out how to render its state in hidden fields, etc, we've again lost the intentions of JSF's component model. It's as if JSF is the perfect idea, but we have to work out some of the kinks yet.
Posted by: jhook on March 01, 2006 at 05:12 PM
-
One other possible problem I see when pushing more state to the user model and out of the JSF state is this would probably cause problems with the "back" button. A lot of work has gone in to the 1.2-RI's Session state saver to keep a history of the page states. Though this could become an application developer's question it is another thing to keep in mind when thinking about going completely stateless.
I personally think the idea expressed by Ed Burns above is the way to go. Only the state is saved where it differs from the default tree. Ed's comment regarding the fact this couldn’t be done because of the problem it creates for the JSP view implementation is unfortunate. Hindsight is 20x20 but I wonder if when these decisions were being made since keeping tree structure separate from tree state was a limitation of .JSP, perhaps restoring the structure of the tree should have also fallen to the responsibility of the view. I think perhaps it would have made more sense for tree structure recreation to be the view’s responsibility since I believe currently it is the view’s responsibility to create the initial tree. If it were the views responsibility to recreate the tree then JSP’s view implementation would have to save the tree in some form of state and Facelets could simply recreate the tree when needed. Anyway, it is unfortunate that storing the structure of the tree along with the state of the component became the job of the component, the one part of JSF that isn't very pluggable. :)
Mike
Posted by: youngm on March 04, 2006 at 09:46 AM
-
(Same comment as above with a bit of formatting. :)
One other possible problem I see when pushing more state to the user model and out of the JSF state is this would probably cause problems with the "back" button. A lot of work has gone in to the 1.2-RI's Session state saver to keep a history of the page states. Though this could become an application developer's question it is another thing to keep in mind when thinking about going completely stateless.
I personally think the idea expressed by Ed Burns above is the way to go. Only the state is saved where it differs from the default tree. Ed's comment regarding the fact this couldn’t be done because of the problem it creates for the JSP view implementation is unfortunate. Hindsight is 20x20 but I wonder if when these decisions were being made since keeping tree structure separate from tree state was a limitation of .JSP, perhaps restoring the structure of the tree should have also fallen to the responsibility of the view. I think perhaps it would have made more sense for tree structure recreation to be the view’s responsibility since I believe currently it is the view’s responsibility to create the initial tree. If it were the views responsibility to recreate the tree then JSP’s view implementation would have to save the tree in some form of state and Facelets could simply recreate the tree when needed. Anyway, it is unfortunate that storing the structure of the tree along with the state of the component became the job of the component, the one part of JSF that isn't very pluggable. :)
Mike
Posted by: youngm on March 04, 2006 at 09:49 AM
-
It is a good thing this is an old blog so that few will be able to see my potentially dumb ideas. :)
Anyway, this separation of Tree structure and state data has got me thinking. I've been bothered lately about how UIData fits into the whole structure of JSF (perhaps you’ve seen my probing all over the internet about it). It has bothered me the way it kind of goes outside of some basic JSF constructs to get things done. Well, if tree structure creation is separated from state data and only state as it differs from the default is saved then this allows for some interesting different ways of thinking about UIData.
First off with tree structure and component state separated you have to understand that processRestoreState and processSaveState now have the job of simply restoring and saving its own delta of the default tree. So, it is now no longer necessary for all restore and saveState calls up the tree to proceed without interruption. Components can now interrupt these calls where they see fit.
If a ViewRoot represents the beginning of a view and the place where the lifecycle processor begins all tree processes and state saving/restoring (not tree recreation) begins then why not think of a UIData as something similar like a Sub-View (no relation to the current JSF sub-view). UIData would short circuit all processes that move up the tree, like it currently does except it would also short circuit restore and saveState calls. However, the difference would be when a UIData gets a process command, like processDecodes, instead of going outside of current component state saving and restoring constructs it would simply use the standard constructs that already exist and call restoreState for its children (passing the state delta it stored for the given iteration) prior to executing the current lifecycle process. UIData would then simply call its children’s saveState and store the data it returns (a delta for the default) for that iteration. Nested UIData components would use the exact same processing method as the outer UIData.
This would help solve a number of problems with UIData. For example, currently UIData only saves EditableValueHolder properties of it's child components. So it is currently not possible for components to maintain individual row state inside of a UIData outside of it's value properties. This would allow (for example) a UIData full of a tree component that would all remember their own current view. That limitation would no longer exist because the components inside of a UIData would simply be a subview of the tree functioning exactly the way it would outside of a UIData. In addition, because the component state is only the delta of the default values of the tree, very little more data (if any) would be saved in the state compared to the current implementation of UIData.
Well, anyway, I don't know if any of this made sense to you but after a long search I think you have helped me finally find UIData Nirvana. :) Now that I’ve found UIData Nirvana what do I do about it? Sorry for the long posts but thanks in advance for reading them.
Mike
Posted by: youngm on March 04, 2006 at 11:48 AM
-
Mike, I agree-- lets use UIData as an example. Here it's acceptable to only store EditableValueHolder properties, relatively limiting compared to what every other component stores. How would the amount of data stored change if in the restore view phase, the page state/instructions were re-applied? So JSF would re-build the view in restore view, then apply the stored state.
Posted by: jhook on March 04, 2006 at 08:14 PM
|