Thread: RE: [Webwork-devel] Sessionable Actions
Brought to you by:
baldree,
rickardoberg
|
From: Jason C. <jas...@no...> - 2001-11-28 21:36:58
|
> -----Original Message----- > From: Rickard Oberg [mailto:ri...@xp...] > > > Hm.. I'm not sure this is the way you'd want to do it. First of all I > can see problems with this and the ClientServletDispatcher, where the > action is being sent back and forth between clients and server. Second, > why put the action itself into the session? Why not put a wizard object > representation into the session? It should be simpler to just do that, > and then extract it using a utility class, or a utility action. This is really to skin the cat that ClientServletDispatcher also skins, i.e. managing state and not having to send all of the params with each request. In this case, don't save the Actions in the session. The ClientServletDispatcher lets you keep the Action and use it multiple times, just like this does, only in a different presentation type. I haven't looked at ClientServletDispatcher except at the high level that you explained it, so I don't know what the impact would be... Possibly instead of the Sessionable Interface, it could be declarative in the action mapping as to whether this particular action alias is saved in the session. You've already got the Action, and it's already got the data from the form, so why not use it? It also makes development much easier, because instead of having to have your Action be session aware and save things into the session, the Action itself is persisted in the session and available for the next request. This makes Actions more reusable, since they aren't tied to saving their data out into a session, so for instance, if you have the Action being fed all by a huge one page form, you can very easily separate this into multiple pages without having to implement the session state saving with the wizard object. > > About double-submitting, that should be easily solved by using > timestamps. I.e. in each form you include a number that is increased for > each submit. In the action you compare the number received with the > current number in the wizard object from the session. If its valid, then > go ahead, but if its not, then return an error. That should always work. > This only works with the synchronization I mentioned. Otherwise, you could have the setters being called during the execution of the Action from a previous request, since it's multithreaded. Jason |
|
From: Rickard <ri...@xp...> - 2001-11-29 09:58:33
|
Jason Carreira wrote:
> This is really to skin the cat that ClientServletDispatcher also skins, i.e.
> managing state and not having to send all of the params with each request.
> In this case, don't save the Actions in the session. The
> ClientServletDispatcher lets you keep the Action and use it multiple times,
> just like this does, only in a different presentation type. I haven't looked
> at ClientServletDispatcher except at the high level that you explained it,
> so I don't know what the impact would be... Possibly instead of the
> Sessionable Interface, it could be declarative in the action mapping as to
> whether this particular action alias is saved in the session.
The problem is that the action is instantiated on the client with CSD.
So, you have no way of intercepting it.
This might change in the future so that one may really call
ActionFactory.getAction() even on the client, which would go to the
server and get it (in order to perform prepare() properly). But then
you'd send a lot of state back and forth three times for each action
that is invoked.
I want the actions to be as shortlived and stateless *as possible*.
Are there any problems with having the action save the state in a
session? Remember that it *can* (if it so chooses) store itself into a
session, and then get it and use BeanUtil.copy to perform pretty much
the same thing. The difference is that the framework doesn't have to
know about it, or be changed to accomodate it.
> You've already got the Action, and it's already got the data from the form,
> so why not use it? It also makes development much easier, because instead of
> having to have your Action be session aware and save things into the
> session, the Action itself is persisted in the session and available for the
> next request.
I would perform this by having a generic action that can be used for
these kinds of things instead. I.e. in your action that wants to save
itself it would do:
StateAction state = (StateAction)ActionFactory.getAction(StateAction.class);
state.load(this, changeCount); // Copy stored action into this action
.. do things..
state.store(this); // Store action back into session, and have it
increase changeCount
Then only StateAction would have to be SessionAware. The changeCount is
incremented for each run, so that if double submitting is done the
changeCount in load() is less then what is in the state store (=session)
then the load fails, since another action has already "used" up the
state of that changeCount(/timestamp).
> This makes Actions more reusable, since they aren't tied to
> saving their data out into a session, so for instance, if you have the
> Action being fed all by a huge one page form, you can very easily separate
> this into multiple pages without having to implement the session state
> saving with the wizard object.
Hm.. this might be idealistic since there's often a difference between
non-wizard and wizard mode in terms of process state changes and
awareness. The above should work better, and also be possible to put in
a base class, e.g.:
protected String doExecute()
{
StateAction state = (StateAction)ActionFactory.getAction(StateAction.class);
state.load(this, changeCount);
doWizard();
state.store(this);
}
Something like that. Now all you have to do is implement doWizard(),
which in turn may delegate to methods for each wizard step.
This approach needs to be tested, but IMHO it will solve the same
problem in a better way since it will not make actions themselves
long-lived (which I really really want to avoid. Gut feeling tells me
its the right thing to do).
> This only works with the synchronization I mentioned. Otherwise, you could
> have the setters being called during the execution of the Action from a
> previous request, since it's multithreaded.
Hm.. what if the prepare() method did the check? Then it could barf at
that point.
Also, if the action itself is not longlived, who cares if the setters
are called? Big deal. It's thrown away anyway. :-)
/Rickard
--
Rickard Öberg
|
|
From: Jason C. <jas...@no...> - 2001-11-29 14:11:36
|
> -----Original Message-----
> From: Rickard Öberg [mailto:ri...@xp...]
>
> The problem is that the action is instantiated on the client with CSD.
> So, you have no way of intercepting it.
>
> This might change in the future so that one may really call
> ActionFactory.getAction() even on the client, which would go to the
> server and get it (in order to perform prepare() properly). But then
> you'd send a lot of state back and forth three times for each action
> that is invoked.
This is not something you'd use with CSD. Lets take an example that's a form
that's got 200 fields. With CSD, you could instantiate the Action on the
client side, then show the form to the user 50 fields at a time, and put the
data into the Action as they finish each page, with maybe some page-by-page
data validation.
In this case you've got one Action, multiple pages. You keep using the
Action across all of those pages, until you're ready to submit the data,
then you send the Action over the wire to the server.
On the web side, things aren't nearly so nice. Multiple pages means multiple
page submits, multiple actions being created, and having to find a way to
keep the previous data ready for them to go back to. Maybe you can create a
framework to make this easier, but my question is, why? If you just kept
using the same Action, like you would in the case of CSD, wouldn't things
just be an order of magnitude easier? The data's there because the Action is
the same action as last time, and when you're ready to go back, it's still
there. No new objects, no copying data field by field, it's just there.
Think about how much easier this could make things like remembering the last
tab selected in a tabbed pane was, 5 minutes and 4 pages ago, or which
column to sort a table by. Yes, this could all be manually saved to the
session and pulled back out, but it's so much easier to just automate it and
reuse the same Action.
>
> I want the actions to be as shortlived and stateless *as possible*.
>
> Are there any problems with having the action save the state in a
> session? Remember that it *can* (if it so chooses) store itself into a
> session, and then get it and use BeanUtil.copy to perform pretty much
> the same thing. The difference is that the framework doesn't have to
> know about it, or be changed to accomodate it.
The problem is that I have to do it, and re-do it, etc. It's a lot of code
to have to develop and maintain if it's done in the application layer,
instead of the webwork framework layer.
> I would perform this by having a generic action that can be used for
> these kinds of things instead. I.e. in your action that wants to save
> itself it would do:
> StateAction state =
> (StateAction)ActionFactory.getAction(StateAction.class);
> state.load(this, changeCount); // Copy stored action into this action
> .. do things..
> state.store(this); // Store action back into session, and have it
> increase changeCount
>
> Then only StateAction would have to be SessionAware. The changeCount is
> incremented for each run, so that if double submitting is done the
> changeCount in load() is less then what is in the state store (=session)
> then the load fails, since another action has already "used" up the
> state of that changeCount(/timestamp).
This seems like a lot of extra work compared to what I had to do to get this
working with the ActionFactoryProxy framework.
>
>
> Hm.. this might be idealistic since there's often a difference between
> non-wizard and wizard mode in terms of process state changes and
> awareness. The above should work better, and also be possible to put in
> a base class, e.g.:
>
> protected String doExecute()
> {
> StateAction state =
> (StateAction)ActionFactory.getAction(StateAction.class);
> state.load(this, changeCount);
> doWizard();
> state.store(this);
> }
>
> Something like that. Now all you have to do is implement doWizard(),
> which in turn may delegate to methods for each wizard step.
>
> This approach needs to be tested, but IMHO it will solve the same
> problem in a better way since it will not make actions themselves
> long-lived (which I really really want to avoid. Gut feeling tells me
> its the right thing to do).
How about if I send you the stuff I built and you can take a look? We need
an example of multi-page forms anyway, so maybe this is a good time to work
through this. I took the form test action, extended it, made the subclass
implement Sessionable, and added a command operation (doPage2), and overrode
doValidation() to null out the errors before calling super.doValidation
because otherwise the error messages carry over, and that was all it took to
make that form action be saved in the session and support multiple pages
(except of course copying the JSP and creating 2 pages from it, and adding
some view mappings).
>
>
> > This only works with the synchronization I mentioned.
> Otherwise, you could
> > have the setters being called during the execution of the Action from a
> > previous request, since it's multithreaded.
>
> Hm.. what if the prepare() method did the check? Then it could barf at
> that point.
>
> Also, if the action itself is not longlived, who cares if the setters
> are called? Big deal. It's thrown away anyway. :-)
I think even if you start setting "in use" flags, you have synchronization
issues unless you synchronize all of the accessors and doXXX methods. If
it's a big deal that it not be submitted twice, then you have to
synchronize. At least if you are using the same Action and saving it in the
session, it gives you something to synchronize on.
Jason
|
|
From: Stefan M. <Ste...@Dy...> - 2001-11-29 10:55:56
|
<>About double-submitting, that should be easily solved by using >>timestamps. I.e. in each form you include a number that is increased for >>each submit. In the action you compare the number received with the >>current number in the wizard object from the session. If its valid, then >>go ahead, but if its not, then return an error. That should always work. >> >> > > This only works with the synchronization I mentioned. Otherwise, you could > have the setters being called during the execution of the Action from a > previous request, since it's multithreaded. I am rather new to webwork, but having built an own framework before i need to say that synchronizing is not what you need. You have to lock the action as long as the first request is running. Otherwise you ent up havin one thread setting one property and the other a second before execute is called. We did this in our framework, and it worked for the rare cases a user hits reload. When the urls are in a frame the situation is completely different, as two or more hits come in at the same time. In this situation it is best to create a second action and use this one. You store the action which becomes available first. A completely different question, having looked at webwork only at topmost level. - Are there any hooks one can place some initialisation. We set up a lot of services upon startup? - Is it possible to add RequestInterceptors, which get called when a browser hits the server and just before the call leaves the server? - Do you have SessionInterceptors wich will be called, when a session terminates? Stefan -- Dynaware Systemberatung GmbH Tel: +49 89 743130-15 Am Westpark 7 Fax: +49 89 743130-05 81373 München mobil: +49 174 3019644 http://www.dynaware.de mailto:Ste...@Dy... |
|
From: Rickard <ri...@xp...> - 2001-11-29 11:20:10
|
Stefan Mainz wrote: > I am rather new to webwork, but having built an own framework before i > need to say that synchronizing is not what you need. You have to lock > the action as long as the first request is running. Otherwise you ent up > havin one thread setting one property and the other a second before > execute is called. Good point, which is one reason I really don't want to have actions that are long-lived and stateful. > A completely different question, having looked at webwork only at > topmost level. > > - Are there any hooks one can place some initialisation. We set up a > lot of services upon startup? No, currently you'd do that as a servlet that is loaded on startup. It would be entirely possible to allow some actions to be configured to run on startup though. Any idea of how you'd want this to work? > - Is it possible to add RequestInterceptors, which get called when > a browser hits the server and just before the call leaves the server? Not in the framework, no, but typically you do this by making action base classes that overload execute() and do things there before delegating to the usual handling. I.e. you do this as action internal functionality. > - Do you have SessionInterceptors wich will be called, when a session > terminates? No, same as above, you'd use regular servlet session event listeners for that. /Rickard -- Rickard Öberg |