Hi,
In RESTful applications which also generate continuation key (_k) links a
problem can arise if you open multiple tabs all sharing the same session.
To illustrate:
1) Enable cookies for http://localhost:8080/examples/counter.
2) Open the counter and increment the value.
3) Open http://localhost:8080/examples/counter in a new tab and decrement
the counter.
4) Return to the first counter and increment the value - you will see "-1"
rather then "2" !
What I think is happening:
If Seaside finds a session but there is no continuation key (_k) then
WASession>>#handleFiltered: calls WARenderLoopMain>>#start which in turn
creates a new root presenter and calls #initialRequest on the
presenter. When the second tab is opened a new WACounter is created and
stored in the session's #presenter property. When you return to the first
tab, the callback is executed on the first counter, but the second counter
is rendered.
Ideas for possible fixes:
* I thought that #presenter was backtracked so don't understand why the
original counter #presenter isn't restored for the callback. If the
presenter was backtracked correctly then the counter example should work as
anticipated.... however:
* in Pier (and potentially other RESTful apps) you don't want to create a
new presenter for every RESTful call within an active session. If you don't
create a new presenter then #initialRequest currently won't be called,
which then won't allow your RESTful app to derive state from the url. If
Seaside is modified to call #initialRequest do you need to differentiate
between a call on a newly created session and one on an existing session?
You could differentiate with a call to a new method on WAPresenter, pass in
a flag to #initialRequest, or set a WARequestContext property to indicate a
new session had been created. What to do with backtracked state. Do you nil
all state before handling the request, restore state from a stored "initial
value" snapshot or just use the state of the components from their last
handled state?
I bought this issue up before [1], but didn't pursue it as I was struggling
to understand the issues and ran out of time. Julian responded to my
previous email when I was proposing a problematic fix with:
I'm not sure it's a good fix in the general case. If you don't have a
> continuation key, you shouldn't (by definition) have any existing
> (backtracked) state. Relying on the root presenter currently in the
> Session is problematic since that value is backtracked for each
> request; you'll simply get whichever root happened to be used by
> whatever request was last handled by that session. If you don't happen
> to be ever changing the root, then it may well be what you want, but
> it's not right in the general case.
> Incidentally, this also touches on the flaw I was discussing the other
> week with our snapshots: because we have no continuation key, we
> really ought to nil out any backtracked data before processing the
> request (at least that way we have a consistent state while handling
> the request). We don't run into the problem with the root presenter
> because #start reinitializes it, but for other backtracked state,
> you'd just get random stuff in there based on the last-handled
> request.
> A more appropriate general fix (at least in this case) could be to
> take the last-used continuation and apply its state before handling a
> request with no _k. However, we don't always know the last-used
> (depending on the caching mechanism in place) and I'm not convinced
> that is actually always what people want. I still feel pretty
> confident that the "right" semantics of an Application/Session request
> with no _k is to start a new main/render-loop.
Certainly for Pier as it is currently designed I don't think that creating
a new render-loop for session requests with the _k is the right thing to
do, as it created a new root #presenter. I think ideally Seaside would for
existing session with no _k
1) Keep the existing #presenter
2) call #initialRequest with a flag to indicate that a new session hadn't
been created.
3) restore the state to an "initial render state" which is snapshotted (?)
when the initial render occurs on a new session.
Thoughts
Nick
[1]
http://forum.world.st/sessions-continuation-keys-restful-urls-and-component…
If each tabs starts with a RESTful url then a new presenter will be
created.
Hi
Together with Denis Defreyne I was working on Issue 724: add
.pushState to ajaxifier [1]. We ran into an issue that the current
Ajaxifier implementation has as well. Namely that the redirect from
the action to the render continuation happens totally transparently to
the JavaScript. There is so way to find out the URL to which the
redirect happened. So Ajaxifier ended up saving the action URL. To
counter this we changed WARenderPhaseContinuation to add a custom HTTP
header if the request was made with the Ajaxifier. Thinking about it
again I'm a bit uncomfortable with putting specific knowledge about
the Ajaxifier into WARenderPhaseContinuation. Maybe a custom subclass
would be a better option, however that would have to reside in
Seaside-RenderLoop otherwise JQuery-Core would have to depend on
Seaside-RenderLoop. Opinions?
Also I would appreciate it if somebody knowledgeable in the area
(Lukas?) could review our changes, the affected packages are
Seaside-RenderLoop and JQuery-Core.
[1] http://code.google.com/p/seaside/issues/detail?id=724
Cheers
Philippe