RE: Any way to dynamically change flow-of-control?
Hi Curt, I just read through the thread for this message, and your approach sounds very interesting. I'd like to ask you some questions, interspersed below if you wouldn't mind...thanks in advance. At 04:20 PM 1/12/01 -0800, you wrote: I bring this up because struts seems to have gone partway--better than any other framework I've seen--but not all the way in its support of this dialog box metaphor (it uses form beans). What is now needed is a navigation stack of some kind--perhaps even something that is written/phrased in terms of "traditional" UI metaphors. I've implemented something like this as follows. I make frequent use of a metaphor whereby the form contains an edit field with an adjacent button (text: "...") that allows lookup of values in the edit field. This allows the user to enter the value directly if they know it, or to click on the "..." button to perform a search. (This presumes that there are too many choices for a select field to make sense.) Assume the user starts on screen A, and clicks the search button that takes them to screen B which displays a search form. The user fills out the search form, clicks OK, and is presented with screen C, which displays the search results. The user then selects from among the search results by clicking an href link or a form button. The user is then returned to screen A with the original screen A form data and the search results filled in. Question: I'm wondering how you move the user to the appropriate screens, given your approach described below. The transition from screens A to B is easy. This can be hard-coded in the struts-config.xml ActionMappings. How do you determine the transitions for the other screens, though? When the user is presented with screen B, he fills out the search form and selects either: - OK: we process the search request and transition to screen C if search is successful, and return to screen B with errors if search is unsuccessful. - Cancel: we obtain the data from the cache, set things up, and return the user to screen A - Hits the browser back button: ?? When the user is presented with screen C, he either: - Selects a choice from the search results: we obtain the data from the cache, set things up, and return the user to screen A - Hits the browser back button: ?? So this leaves me with a couple of questions: 1. How do we differentiate the transitions when processing the OK or Cancel from screen B? The OK transition to screen C can be specified in the ActionMappings in struts-config.xml, but the Cancel request must obtain the information from the cache. I suppose this means the token used to store screen A's information is written to screen B's form, and retrieved IFF the user decides to Cancel screen B's action. True? 2. What happens when the user selects a search result choice on screen C? We want to return the user to screen A. Does this mean that screen C should also be implemented as a HTML form, and that we also write the token for screen A to this form? How do you know which token to write onto screen C's form? Do you just "know" that screen B needs to create a new cache entry, and that screen C needs to use the cache entry token that is submitted with screen B's request, and therefore code it that way? Or do you have some other mechanism that you've designed? more below... I want to be able to perform the search and return to this screen with both the existing data and the search results filled in. I want the user to be able to do this in multiple browser windows without them interfering with each other. I want the "back" button to work as expected, because I know that as a user, I get very annoyed with the inability to use this button. When the back button is pressed, doesn't the previous page's request just get resent? What are the implications of this...any special processing required? When the user clicks the "..." search, the entire calling state, including the ActionForm bean, is copied into a cache which is stored on a per-user basis. Can you share your code showing how you obtain all of the calling state? Also, your cache code, as well, if possible...thanks. From the cache, I get a unique token to refer to the calling state. This token is then stored in a hidden "caller" element on the new form. If the user moves forward from this screen by pressing either "OK" or "Cancel", By "moves forward", do you mean possibly moving up the stack of screens, e.g. from screen C to screen A? the original form is restored from the cached information, the cached value becomes eligible for destruction, and after a short grace period, the form is removed from the cache. Why do you have this short grace period? Is this cached information still relevant? Perhaps if the user hits forward and back again on the browser? Or something else? I'm very unclear on how you would access this cached information. For example, if we cache screen
RE: Any way to dynamically change flow-of-control?
Rick- When the user is presented with screen B, he fills out the search form and selects either: - OK: we process the search request and transition to screen C if search is successful, and return to screen B with errors if search is unsuccessful. Actually, screen B and screen C are implemented in the same (.jsp) file. When first moving from screen A to screen B, there are no results stored in the context of the request. When screen B is submitted to the Action, one of three things will happen. - The results are stored in a "requestList" attribute - The errors are stored in an "errorList" attribute - A boolean "true" is stored in a "noResults" attribute The .jsp displays the results as appropriate (using Struts logic tags) under the same (form) header that was used to collect the search criteria. This also allows the user to immediately do a different search without hitting the "Back" button. - Cancel: we obtain the data from the cache, set things up, and return the user to screen A Yes. - Hits the browser back button: ?? The browser simply restores screen A without any input from the server. We never can directly discover whether or not this has happened without the type of JavaScript I'm trying to avoid. When the user is presented with screen C, he either: - Selects a choice from the search results: we obtain the data from the cache, set things up, and return the user to screen A Yes. - Hits the browser back button: ?? Again, the browser simply restores screen B without any input from the server. So this leaves me with a couple of questions: 1. How do we differentiate the transitions when processing the OK or Cancel from screen B? I look directly at the request. The relevant code is: if (request.getParameter("cancel") != null) { LookupForm oldform = lkupform.getSavedForm(); return oldform.processAction(request, response); } (LookupForm is a derivative of ActionForm that has the small amount of additional state information needed to implement the stack.) I suppose this means the token used to store screen A's information is written to screen B's form, and retrieved IFF the user decides to Cancel screen B's action. True? It's also used when the user selects a search result choice on screen C. 2. What happens when the user selects a search result choice on screen C? We want to return the user to screen A. Does this mean that screen C should also be implemented as a HTML form, and that we also write the token for screen A to this form? How do you know which token to write onto screen C's form? Do you just "know" that screen B needs to create a new cache entry, and that screen C needs to use the cache entry token that is submitted with screen B's request, and therefore code it that way? Or do you have some other mechanism that you've designed? Knowing that screens B and C share a .jsp and (by extension) an ActionForm should make this a little clearer. The actual selection of a search result from screen C is done with a custom tag that references a "LookupResult" action which I reuse for all such lookups. This tag app:lookupResult name="product" code="code" id="id" produces this HTML a href="LookupResult.do?caller=4amp;code=CACamp;id=36" The caller is, again, a reference to the cached calling form. The "code" and "id" are fields that identify the search result chosen -- in this case, "code" is a human-readable possibly unique value representing the record, and "id" is a surrogate key to that record (and guaranteed unique). The code is what will populate the field on the original form (screen A) that we're looking up. The id allows for any required disambiguation. (Usually, there is none.) When the back button is pressed, doesn't the previous page's request just get resent? What are the implications of this... any special processing required? Back followed by submit will resend the previous request, yes. I don't do anything special to handle this, and I don't think there are any ramifications. (I'm pretty sure I worked it out so that there is no memory leaked, but I'm fuzzy on the details.) When the user clicks the "..." search, the entire calling state, including the ActionForm bean, is copied into a cache which is stored on a per-user basis. Can you share your code showing how you obtain all of the calling state? Also, your cache code, as well, if possible I think I made that sound more sophisticated than it really is. The calling state is just the calling ActionForm and Action. The calling Action is what checks to see if the user has selected the lookup in addition to its "real" processing job. It also knows how to redraw the original screen. I make frequent use of a pattern whereby there is a single action and .jsp which are responsible for initially displaying the form, displaying the form with any errors after
Re: Any way to dynamically change flow-of-control?
- Original Message - From: [EMAIL PROTECTED] This should return you to the first screen, with all of the values entered up to that point intact (okay, so that means its not a link but a form post), PLUS the value of the customer field. The 'Find Customer' screen should be able to be invoked from a range of other screens. Right; the "Find Customer" screen in this case is really a dialog box (or would be in a normal client-server application, most likely). I guess that perhaps this could be done by an application in Struts quite easily, but some standard approach enforced by the framework itself would be nice. Yes. I'm not suggesting, BTW, that there be a whole set of UI objects that should be shoved into the struts framework (that would be kind of silly and overblown), but I am suggesting that whatever mechanism ends up being put in place force the user to construct modular screens, i.e. to build the Find Customer screen such that it can be invoked from many places. Most web sites I've seen or worked with are built assuming a rigid flow--if you pluck the existing Find Customer screen out of the flow and want to invoke it somewhere else you typically have to change a lot of form action=xxx tags (and you typically break stuff). Cheers, Laird -- [EMAIL PROTECTED] http://www.amherst.edu/~ljnelson/ Good, cheap, fast: pick two.
RE: Any way to dynamically change flow-of-control?
I bring this up because struts seems to have gone partway--better than any other framework I've seen--but not all the way in its support of this dialog box metaphor (it uses form beans). What is now needed is a navigation stack of some kind--perhaps even something that is written/phrased in terms of "traditional" UI metaphors. I've implemented something like this as follows. I make frequent use of a metaphor whereby the form contains an edit field with an adjacent button (text: "...") that allows lookup of values in the edit field. This allows the user to enter the value directly if they know it, or to click on the "..." button to perform a search. (This presumes that there are too many choices for a select field to make sense.) I want to be able to perform the search and return to this screen with both the existing data and the search results filled in. I want the user to be able to do this in multiple browser windows without them interfering with each other. I want the "back" button to work as expected, because I know that as a user, I get very annoyed with the inability to use this button. When the user clicks the "..." search, the entire calling state, including the ActionForm bean, is copied into a cache which is stored on a per-user basis. From the cache, I get a unique token to refer to the calling state. This token is then stored in a hidden "caller" element on the new form. If the user moves forward from this screen by pressing either "OK" or "Cancel", the original form is restored from the cached information, the cached value becomes eligible for destruction, and after a short grace period, the form is removed from the cache. If the user presses the "Back" button, the form remains in the cache longer, although it, too, will eventually time out and be destroyed. In any event, once the user's session is terminated, any saved forms are destroyed. This approach works very well for me. A stack is implicitly defined by the succession of "caller" elements without having to maintain anything that looks like a stack on a global level. I don't know how well this approach scales, but short of keeping all of the state information in the user's browser, I can't think of any way to reduce its footprint. -- Curt Hagenlocher [EMAIL PROTECTED]
Re: Any way to dynamically change flow-of-control?
see interspersed comments below... "Laird J. Nelson" wrote: - Original Message - I like to think of this as the "dialog box problem" that web UIs face. That is, each case above, in a "traditional" client-server program, would be implemented, usually, as a dialog box. What's unique about a dialog box? It pops up and gets in the way of where you (a) are or (b) were trying to go. Only when you dismiss it are you "returned" to either (a) or (b). On the web, of course, with our primitive HTML-based UIs, there are no such things as dialog boxes (unless you use javascript). So "screen flow" becomes all important. I bring this up because struts seems to have gone partway--better than any other framework I've seen--but not all the way in its support of this dialog box metaphor (it uses form beans). What is now needed is a navigation stack of some kind--perhaps even something that is written/phrased in terms of "traditional" UI metaphors. I'm thinking about some kind of stack--and believe me, I haven't thought this all the way through yet--that can be accessed by Actions, but any given Action would typically say something like "OK, go to the next screen that I'm being told to go to, or if I'm not being told to go anywhere, go to this default location". Or the Action might be able to say "oops, conditions aren't right to advance; pop the stack and go back to where we were a moment ago but display the following message". Things on the stack would be *roughly* equivalent to screens--though I hesitate to use "screen" as I think that's one of the major shortcomings of the Turbine framework, because a "screen" on the web often exists as a separate entity only because the marketing department said it has to. Actually, I've implemented just such a construct at work for the struts based prototype of a significant financial web-application shell. There was some discussion on this list some time ago about something called a StateManager, which is essentially what this is. And yes, the concept of "screen" is fuzzy -- in my situation, a "screen" is really a [at times] complicated and changing frameset. The app/shell's flow follows a (process/state update/redraw) pattern in a [con]federation of sub-applications (and thus, controllers). See my attached diagram with numbered steps as follows: 1) Application Request Client-side user triggers a request to the server. 2) Sub-application Controller Dispatch Dispatches the request to the appropriate action handler. 3) Action Request Processing The actions process the request, invokes relevant business logic, sets up data and state beans. 4) Redirect Response Initial request response is _always_ a redirect to a single URL to redraw the application window. 5) Window Redraw Request There is one global window redraw request handled by the main controller servlet. This request "repaints" the entire browser window based on the current state of the application. 6) Main Controller Dispatch Dispatches the request to the appropriate action handler. 7) Action Request Processing This sets up the request object and determines the correct views to "forwards" to based on what's on top of the page stack. 8) Request Forwarded (dispatched) to Views 9) JSP Views Render Response 10) Final Result Response Response to the redraw request. (apologies for the detail level in these steps -- I lifted this diagram and description from the system overview documentation I'm working on at work (geared towards newbie developers in my group)) one side benefit of this indirected approach is that the application has _total_ control over what the user sees...because all requests _always_ result in a redirect to the same _global_ repaint action, the browser's navigation buttons are effectively disabled; the user always gets presented what is currently at the top of the pagestack -- be it error page, form, interjected "dialog" window, etc. The framework effectively hides the implementing JSP views from the browser's URL location making it more difficult for the user to bookmark intermediate pages or the browser to track page history (something which is usually not desirable, especially with sensitive apps/sites). I hope this was helpful... e -- ___ Elod Horvath ('e') / ITFAIS Records (http://www.itfais.com/) Unknown Document
Re: Any way to dynamically change flow-of-control?
"Felciano, Ramon" wrote: - Confirm pages ("Are you sure that ?" with "Continue" and "Cancel" buttons) - Pick lists ("We couldn't find the product ID you entered; please select from the following list") - Alert pages for (possibly unexpected) server-side events that occured since the last client-server traffic passed through. Just a wild idea here: An action is not required to forward to a path or action, but has everything it needs to answer the request itself (and return a null action forwarding). So how about if the action wrote pages like this dynamically, the old-fashioned servlet way, perhaps using a standard "message box" type library? The responses in the dynamic page could then be set to forward to whatever actions or paths were appropriate to the circumstances, without having to write a physical JSP for every conceivable combination. -- Ted Husted, Husted dot Com, Fairport NY USA. -- Custom Software ~ Technical Services. -- Tel 716 425-0252; Fax 716 223-2506. -- http://www.husted.com/
Re: Any way to dynamically change flow-of-control?
This is a very interesting discussion; this sort of functionality appears to be missing from all of the servlet based frameworks out there (with possibly the exception of Hammock?). An example of what I would like struts to be able to support (and this example may have already been put forward, but here's my take on it anyway) is a screen (okay, 'form') that has multiple fields, plus say a customer field. You can either enter the name of the customer, or click on a link next to the field called 'Find Customer...'. This takes you to the 'Find Customer' form where you can enter search parameters and then see a list of customers matching those parameters (the 'Customer List' form). The 'List Customer' form may let you return to the 'Find Customer' form for another type of search, or click on a link next to one of the matching customers. This should return you to the first screen, with all of the values entered up to that point intact (okay, so that means its not a link but a form post), PLUS the value of the customer field. The 'Find Customer' screen should be able to be invoked from a range of other screens. I guess that perhaps this could be done by an application in Struts quite easily, but some standard approach enforced by the framework itself would be nice. Regards, James W. -- This e-mail is from Cards Etc Pty Ltd (ACN: 069 533 302). It may contain privileged and confidential information. It is intended for the named recipient(s) only. If you are not an intended recipient, please notify us immediately by reply e-mail or by phone on +61 2 9212 7773 delete this e-mail from your system. --
Re: Any way to dynamically change flow-of-control?
Hi Ramon, See interspersed comments below. "Felciano, Ramon" wrote: Hi -- I'm evaluating Struts (+Turbine+Rocks) as a framework for our web apps We often want to insert certain UI actions based on exceptional user behavior. A simple example is an authentication screen that gets inserted "just in time". For example, if the normal flow of control is from page A -- page B, where page B requires authentication, then 98% of the time, the user goes A -- B. However, the very first time this happens we want to insert an authentication page: A -- Logon -- B. Other situations where this type of intermittent insertion of additional UI and flow logic would be helpful include: - Confirm pages ("Are you sure that ?" with "Continue" and "Cancel" buttons) - Pick lists ("We couldn't find the product ID you entered; please select from the following list") - Alert pages for (possibly unexpected) server-side events that occured since the last client-server traffic passed through. That is an interesting approach. I realize that these can all be implemented on a case-by-case basis, with each Action class deciding whether to insert a new flow element or not. A's Action class could check the authentication state and decide whether to go to Login or B accordingly. In the pick list example, this could be done by returning the user to the original form and using JSP logic to notice the error and display a list of options to choose from. However, if the page is a complex one, displaying a specialized form for just this input value can be useful. I guess I'm wondering whether there is any generalized support for this that might better separate the nature of these exception events? For example, in the login example, it doesn't make sense to me that A's Action class make the decision as to whether to show the login form, since the authentication is bound to B. In particular, I'd like to be able to reroute to Logon when needed, but otherwise continue with the "normal" flow of logic. Thus, if at a later date the "normal" flow of logic dictates A -- C, I only want to change this in one place (A's success forward property), and still benefit from the just-in-time authentication. Same thing for confirm pages, etc... Within Struts 1.0, an approach to handling these things generically would be to subclass the ActionServlet controller servlet, and override a method like processActionPerform (which actually calls the selected Action) or processActionForward (which forwards control to the returned logical page name). You could then inspect the current state of the world, and inject flow-of-control changes like this while still utilizing the superclass's basic functionality. To make the types of things you inject somewhat configurable, you might also want to use your own custom ActionMapping subclass, with extra bean properties that describe (to your override method) what exceptional things to do and when to do them -- the selected ActionMapping instance is made available to most of the processing calls, so this information would be easily accessible. In future versions, one of the directions I really want to look is a more "workflow" like organization of processing within Struts (both for the basic processing that the controller servlet does for you, and for letting you glue together finer grained tasks, in addition to (or instead of) the relatively coarse grained Actions that are currently supported. Doing the sorts of dynamic flow-of-control changes you are talking about would seem to fit into that model pretty well, especially if you could "script" them in the struts-config file. Finally, and on a different topic, can someone please point me to the most recent discussion (or docs) regarding how to do Form validation with struts? The [outdated?] user guide first suggests not doing it in the ActionForm class, then provides a ValidatingActionForm that seems to encourage the practice... The user guide is under active development to bring it up to the 1.0 APIs -- I would expect the version you get with tonight's nightly build to be pretty up to date. On the particular issue of validation, the ActionForm class (you don't need ValidatingActionForm any more) has a method: public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) that is used for form beans that want to validate. You return null if no errors are encountered, or an ActionErrors object that is a collection of error message keys (along with the field names the errors are assocaited with). Thanks in advance for your time. Ramon Craig McClanahan