Hi Curt,

Thanks for your detailed response.  I have some more questions, if you can 
spare the time.  Perhaps I can help you improve your design (once I fully 
understand it, that is) :) since you did say there were some shortcomings...

At 05:44 PM 1/24/01 -0800, you wrote:
>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.

OK, this is clear.


> > - 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.

Do you mean that you allow screen A to be cached by the browser?  In other 
words, you aren't setting the no-cache stuff in the HTML header to prevent 
caching?

> > 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.)

LookupForm is a subclass of ActionForm...ok.  What is the variable 
lkupform? an instance of LookupForm?  I'm confused.  Then you call 
oldform.processAction()...don't get this either.  What I would imagine you 
want to do from a purely logical perspective is to recover the saved 
ActionForm associated with screen A and use RequestDispatcher.forward() to 
forward to the JSP that displays screen A.  That will reproduce screen A 
for the user with the same data the user last saw.  How does the above code 
do that?  Or am I misunderstanding what needs to happen here?


> > 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.

Sure, makes sense.


> > 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=4&amp;code=CAC&amp;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.)

Questions:
1. caller is "a reference to the cached calling form" - meaning the token 
used to recover the cached info, to use your previous terminology?
2. What happens to the "name" attribute of app:lookupResult? It doesn't 
seem to make it into the <a href=...
3. Where does the "caller" come from in the <a href=...?  Is this stored by 
the search Action as a request attribute before forwarding to screen B, and 
then obtained by the app:lookupResult tag handler from the PageContext?
4. Are you saying that "code" is the actual value that will be written to 
the ActionForm representing screen A data?  Or that "code" is essentially 
the key, perhaps a primary key representing the data?  If it is the former, 
then you lose the ability to reference the object which it represents, on 
screen A, but this probably doesn't matter since all you really want in 
this situation is the value.
5. Can you give an example of when "id" might be needed?

> >> 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.

A caution...ActionServlet.processActionForm() reuses the ActionForm object 
if the ActionForm subclass required by the new request is the same class as 
the last one it cached. From a very cursory inspection of the ActionServlet 
code, I believe it caches 1 ActionForm object in an instance variable 
called "instance".  I suppose this theoretically could allow your saved 
ActionForm object to be reused and overwritten by accident, but probably 
not a problem given your data flow.  I suppose you could clone() the 
ActionForm subclass for more safety, but at the cost of additional CPU cycles.

It appears that your code doesn't save the HttpServletRequest and 
HttpServletResponse objects.  I think I recall that they are invalid after 
processing a request, so perhaps that would not work anyway.  However, by 
not saving the information contained in the HttpServletRequest, does it 
mean that your approach can only be used from an HTML form?  What do you do 
for a situation in which the user is redirected through an intermediate 
page as the result of clicking on an HREF link?  For example, say you're on 
screen X of a website and you click an href link that takes you to a 
section of the site that requires login.  What do you do in this situation? 
-- there's no ActionForm received by the Action.perform() method, because 
ActionForm objects are only associated with HTML forms, not href 
links.  There could, however, ActionForm objects that were used to display 
data into other forms on screen X or other beans containing data used to 
populate areas of screen X.  If these beans were stored under request scope 
by the Action that originally forwarded to screen X, they would be lost 
after leaving screen X to redirect to the login page.  What happens if the 
user cancels the login page; you need to redisplay screen X, but now the 
beans that fed the data are gone - all the data in screen X  gone with 
them.  True?  I can't think of any way to deal with this problem, other 
than to return the user to a previous page (that doesn't require request 
scope data) if they cancel out of the login.

On another related (and perhaps more important note) using the above 
example...if the user is redirected to a login page when they click an href 
link to get from screen X to screen Y, and the user successfully logs in, 
the original request must be restored and passed to the Action processor 
that will process the originally targeted href link...correct?.  This 
includes request parameters received from the original call. Do you save 
request parameters in this case?  If so, how?


>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 a submission, going to
>a lookup form, if that's what the user has clicked, and displaying
>the output, which may be the same .jsp or a different one,
>depending on the circumstances.  I'm not 100% happy with this
>pattern, and I'm sure that others will point out its shortcomings,
>but it's been working for me so far.

Not that I get the feeling that you're accusing me of this (at all), but 
I'm definitely not trying to point out shortcomings of your model for the 
purpose of pointing out shortcomings...just want you to be clear on 
that.  I'm simply trying to understand what you have built to help me in my 
design. Hopefully, I'll be able to help you improve on it, if necessary, to 
return the favor.


>I'll see what I can do about posting some code.
>
> > By "moves forward", do you mean possibly moving up the stack
> > of screens, e.g. from screen C to screen A?
>
>I mean "moves forward" in the set of screens on the browser.
>On the server, we'd be moving back up the stack of screens.
>
> > 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?
>
>I think that yes, this was my motivation.
>
> > For example, if we cache screen A's request information and
> > then when we return the user to screen A after a successful
> > search, we obtain that cached information (via the token stored on
> > screen C), how will we know that cached information exists?  I
> > would have thought that the token (which should have disappeared
> > after we processed screen C's request) was the only way to get
> > the cached information from screen A's request.
>
>I don't understand this.  As long as the value still exists in
>the cache, and the key to the value is stored someplace, we
>can still get at the value.  The key is just something that
>we can pass back and forth to the browser as cheaply as possible.

Do you use a separate thread to clean out the cache, or just remove timed 
out elements when a new request comes in by checking if current time has 
exceeded the timer?  Seems like the latter would be sufficient.

Sorry for the length of this note... Thanks again for sharing your ideas 
and design.

Rick Horowitz


_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com

Reply via email to