Developing web app. with MyFaces
Hi, I've been working on an application based on MyFaces 1.0.8. Now, we are moving to MyFaces 1.0.9 and I would like to discuss discovered issues and some ideas (not only related to MyFaces but to JSF in general). AliasBean * This is a pretty useful component and I'm glad that MyFaces contains it! * There were a few bugs in this component in MyFaces 1.0.8 (processSaveState, queueEvent) but now they are fixed (1.0.9) - great Table * This is one of the most used components * It's definitely better than standard table (sorting support, scroller, …) BUT: * Suppose this (selecting a row while model has been concurrently modified by a different user): 1) Your table has 3 rows and you display a check box for each row so that user can select a row 2) User selects first row and submits but another user has concurrently deleted first row. So, model has now only 2 rows. 3) Submitted value (selected checkbox of the first row) will be applied to the first row of the model. So, actually the second row is selected :-(. (we've developed a quite simple solution, so if you are interested, let me know - and we definitely do not serialize whole model of the table!) * It's a common case that table rows have checkboxes and can be selected. So, we have to provide (for most of the tables) model where each row is wrapped in an extra class that has selected property - and that's tedious. DataScroller = * We've discovered following issue: uiData.setFirst(someNewValue) is used within the =decode= method of the HtmlDataScrollerRenderer - it is too early to modify uiData within =apply request values phase=. * If some rows of the table were modified too, they will be applied to wrong table rows :-( (because setFirst has already been called and table contains different rows now) * Our solution: DataScrollerRenderer fires an even that uiData should be updated, and they are updated later in =update model values= phase (just after model has been updated) Backing Bean's State, HttpSession, Concurrent Access within a Session === In my view, a reasonable web application should fulfill following requirements: * users can use browser's navigation buttons (back, forward, reload) * users can use an application from multiple browser tabs (tabs share the same session) Although these requirements look simple and unimportant, they present serious problems and have serious consequences on the whole architecture of an application. * navigation buttons * We use redirects. So, user can use the back button even when he's moved to a next page via HTTP POST * We use javax.faces.STATE_SAVING_METHOD=client. So, even if user has pressed the back button 10 times, a proper view will be restored when he submits form (or clicks link) on the old page (if state saving=server, this would obviously not work - you would have to click twice to perform an action :-)). * But who/when/how will store state of backing beans? - note that if you not store the state of your backing beans on a client, your application will not work. Suppose this: 1) Your page displays a category (it has name, description and sub-categories) 2) User selected category A and it is stored in a property =currentCategory= in your backing bean 3) User selected category B (sub-category of A), so =currentCategory= is now B 4) User uses browser's back button, so he can see detail of category A 5) User edits description of the category (A) and submits the page 6) Description of category B will be updated because =currentCategory= is still B :-( * Solution: a component (saveState) that accepts a value binding and saves the value within its state. E.g. y:saveState value=#{myConroller.pageState}/ * tabbed browsing * Any session scope backing bean is a potential race-condition * Moreover, if your data like currentItem, MyPageConroller have session scope, actions performed in one tab may influence behavior of the other tab Error Handling === * We created a special component similar to the Buffer component. We render a sub-tree of this component into the buffer and if and exception is thrown while processing the sub-tree, we render the exception. (in addition, we had to wrap ActionListener, HtmlDataTablePhaseListener, …). * It would be nice to see an errorHandling component in next MyFaces distribution. I hope that you will find some info from this mail useful and intelligible. Best regards, MN.
Re: Developing web app. with MyFaces
* Suppose this (selecting a row while model has been concurrently modified by a different user): Is the backing bean that supplies the List/DataModel/UIData in session scope? If so, this is a consequence of the design, not JSF. You'll get the same thing w/ Tapestry, or ASP.NET for that matter. 3) Submitted value (selected checkbox of the first row) will be applied to the first row of the model. So, actually the second row is selected :-(. (we've developed a quite simple solution, so if you are interested, let me know - and we definitely do not serialize whole model of the table!) Please share this w/ the list. This comes up a lot in my shop and it would be appreciated. I'm also looking for ideas for one of my side projects, http://forum.hibernate.org/viewtopic.php?t=945552 . Dennis Byrne
Re: Developing web app. with MyFaces
for 3) : use the preserveDataModel of the dataTable regards, Martin On 8/26/05, Dennis Byrne [EMAIL PROTECTED] wrote: * Suppose this (selecting a row while model has been concurrently modified by a different user): Is the backing bean that supplies the List/DataModel/UIData in session scope? If so, this is a consequence of the design, not JSF. You'll get the same thing w/ Tapestry, or ASP.NET for that matter. 3) Submitted value (selected checkbox of the first row) will be applied to the first row of the model. So, actually the second row is selected :-(. (we've developed a quite simple solution, so if you are interested, let me know - and we definitely do not serialize whole model of the table!) Please share this w/ the list. This comes up a lot in my shop and it would be appreciated. I'm also looking for ideas for one of my side projects, http://forum.hibernate.org/viewtopic.php?t=945552 . Dennis Byrne -- http://www.irian.at Your JSF powerhouse - JSF Trainings in English and German
Re: Developing web app. with MyFaces
Thank you for you opinion :-) On 8/26/05, Dennis Byrne [EMAIL PROTECTED] wrote: * Suppose this (selecting a row while model has been concurrently modified by a different user): Is the backing bean that supplies the List/DataModel/UIData in session scope? If so, this is a consequence of the design, not JSF. You'll get the same thing w/ Tapestry, or ASP.NET for that matter. I think that you are not right: - problem is that sequence number of the rows has changed (this problem arises whenever sequence number (position) of a row changes before the page with table is submitted) - we are displaying life data, so if user's had a break and after a while submits the page with the table and data (in db) has been modified concurrently, submitted values will be applied to bad rows :-( - if model were cached in the session, it would surprisingly work - because if the model is cached, it cannot be modified concurrently by a different user 3) Submitted value (selected checkbox of the first row) will be applied to the first row of the model. So, actually the second row is selected :-(. (we've developed a quite simple solution, so if you are interested, let me know - and we definitely do not serialize whole model of the table!) Please share this w/ the list. This comes up a lot in my shop and it would be appreciated. I'm also looking for ideas for one of my side projects, http://forum.hibernate.org/viewtopic.php?t=945552 . Dennis Byrne we've extended MyFaces table. Our table expects stronger data model, that extends DataModel but implementation of our model must: return id of any row, return value of a row by ID So, when the method saveState is called on our table, we store IDs of displayed rows (from first to first+rows). So, if table is scrolled (10 of 1000 rows is displayed), we will serialize only IDs of displayed (10) rows. When table is to be restored, we read serialized row IDs, ask for data (getRowDataById(id)) and we provide a temporary model - this model contains exactly the same data as when the page was previously rendered. After all the request values were applied and model is updated we just restore original model (maybe model contains more or less rows, maybe in a different order ...) Btw. I will definitely check your project :-)
Re: Developing web app. with MyFaces
- if model were cached in the session, it would surprisingly work - because if the model is cached, it cannot be modified concurrently by a different user The model can still be modified ... by the same user when they open a second browser window. Dennis Byrne