You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@wicket.apache.org by Martin Grigorov <mg...@apache.org> on 2012/07/01 16:36:31 UTC

Re: setResponsePage swallows my session feedback messages

Hi,

Here is my later response.

I see two ways to solve this:

1) Use setResponsePage(pageInstance) as I described in my previous response:

setMyCookie();
PageB pageB = new PageB();
pageB.info("some text");
setResponsePage(pageB);

This way the feedback is associated with a component (pageB) and wont
be rendered when PageA is rendered.
The drawback is that this way you will get a url like 'wicket/page?42'
instead of the nice looking one (e.g. /customers/12)

2) the second way is to use non-resetting RestartResponsePage
I've rolled this out several months ago for our app:

public class NonResettingRestartException extends ReplaceHandlerException {

    public NonResettingRestartException(final Class<? extends Page<?>>
pageClass, final PageParameters params, final RequestCycle cycle) {
        super(createRequestHandler(pageClass, params), true);

        Response response = cycle.getResponse();
        if (response instanceof IMetaDataBufferingWebResponse) {
            IMetaDataBufferingWebResponse bufferingWebResponse =
(IMetaDataBufferingWebResponse) response;
            bufferingWebResponse.writeMetaData((WebResponse)
cycle.getOriginalResponse());
        }
    }

    private static IRequestHandler createRequestHandler(Class<?
extends Page<?>> pageClass, PageParameters params) {
        return new RenderPageRequestHandler(new
PageProvider(pageClass, params));
    }
}

and use it:
setMyCookie();
throw new NonResettingRestartException(PageB.class, params);

I guess you will prefer option 2)

See inline below.

On Fri, Jun 29, 2012 at 12:11 AM, Bertrand Guay-Paquet
<be...@step.polymtl.ca> wrote:
> Hi,
>
> I have 2 pages, each with a feedback panel. Page2 does the following:
> setACookie();
> Session.get().info("blah");
> setResponsePage(Page1.class);
>
> The problem I have is that "blah" is never displayed in the feedback panel
> of Page1. I stepped in the request processing code and found that
> setResponsePage() actually renders the full current page before throwing
> that away and issuing a 302 redirect. During this first (unused) rendering
> of Page2, its feedback panel consumes all the session messages. After the
> client follows the 302 and requests Page1, there are no more session
> messages to display.
>
> Instead of setResponsePage, I can use a RestartResponseException to redirect
> to Page1 and the session messages will then be displayed on Page1 because
> Page2 is never rendered. However this has the important downside of also
> throwing away all header information (e.g. cookies like setACookie() above).
>
> Is there a way to both set a cookie and display the session message on the
> response page?
>
> Bonus!
> I'm also wondering why, when setResponsePage() is used, the current page is
> still rendered. I can think of 3 reasons why this should be avoided:
> 1-Performance; it's wasteful to render components to discard them right away
>
> 2-Components hierarchy; even if setResponsePage() is used in a page
> constructor (e.g. when redirecting depending on page parameters), since the
> page is rendered, all of its components or some substitutes must be added to
> the page. Otherwise Wicket will throw a missing component exception in dev
> mode.
>
> 3-My use case; feedback messages registered in the session can be swallowed
> by a feedback panel in the thrown away page rendering.
>
> Of course, I definitely don't have the complete picture and am just now
> making some sense of how Wicket handles redirects and responses.

I think this is a good opportunity for an improvement! Please file a
ticket and we will investigate it.

Ajax support in Wicket works using this way: a
ListenerInterfaceRequestHandler (LIRH) is executed and
AjaxRequestHandler(/Target) (ART) is scheduled immediately and passed
to the callback function (e.g. onClick(AjaxRequestTarget)) so the user
code can use it and add components to it. Later when LIRH is ready
Wicket starts ARA which just renders whatever is already collected in
it (JavaScript and components) to <ajax-response>.

The comments in WebPageRenderer (related to WICKET-4358) also say that
a IRequestHandler can be scheduled during rendering, but I see no
reason why to render at all if there is already a scheduled
IRequestHandler before starting the rendering.

>
> Regards,
> Bertrand
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>



-- 
Martin Grigorov
jWeekend
Training, Consulting, Development
http://jWeekend.com

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
For additional commands, e-mail: users-help@wicket.apache.org


Re: setResponsePage swallows my session feedback messages

Posted by Bertrand Guay-Paquet <be...@step.polymtl.ca>.
Hi Martin,

Following Andrea's comments from issue Wicket-4637, I modified the 
NonResettingRestartException to fix the error I reported.

Here's the modified version:
public class NonResettingRestartException extends ReplaceHandlerException {

     public NonResettingRestartException(final Class<? extends Page> 
pageClass,
             final PageParameters params, final RequestCycle cycle) {
         super(createRequestHandler(pageClass, params), true);

         Response response = cycle.getResponse();
         if (response instanceof IMetaDataBufferingWebResponse) {
             WebResponse originalResponse = (WebResponse) cycle
                     .getOriginalResponse();
             if (originalResponse != response) {
                 IMetaDataBufferingWebResponse bufferingWebResponse = 
(IMetaDataBufferingWebResponse) response;
                 bufferingWebResponse.writeMetaData(originalResponse);
             }
         }
     }

     private static IRequestHandler createRequestHandler(
             Class<? extends Page> pageClass, PageParameters params) {
         return new RenderPageRequestHandler(new PageProvider(pageClass, 
params));
     }
}

Bertrand

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
For additional commands, e-mail: users-help@wicket.apache.org


Re: setResponsePage swallows my session feedback messages

Posted by Bertrand Guay-Paquet <be...@step.polymtl.ca>.
Hi,

Issues WICKET-4636 and WICKET-4637 were created.

Bertrand

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
For additional commands, e-mail: users-help@wicket.apache.org


Re: setResponsePage swallows my session feedback messages

Posted by Martin Grigorov <mg...@apache.org>.
Hi Bertrand,

On Mon, Jul 2, 2012 at 11:21 PM, Bertrand Guay-Paquet
<be...@step.polymtl.ca> wrote:
> Hi Martin,
>
> Thanks for your answer and code. Indeed, in my case, option 2 is most
> desirable because the session message is set in a completely different place
> than where the redirect is done.
>
> NonResettingRestartException works great when thrown from a page
> constructor. However, when thrown from a link or form handler, a list
> concurrent modification exception occurs.
>
> This email is rather long so I divided it in 2 parts.
>
> Part 1 - NonResettingRestartException problem
> =========================================
>
> The list is the "actions" list of the BufferedWebResponse with the following
> stack trace:
> BufferedWebResponse.addCookie(Cookie) line: 421
> HeaderBufferingWebResponse.addCookie(Cookie) line: 72
> BufferedWebResponse$AddCookieAction.invoke(WebResponse) line: 240
> BufferedWebResponse.writeMetaData(WebResponse) line: 75
> HeaderBufferingWebResponse.writeMetaData(WebResponse) line: 205
> NonResettingRestartException.<init>(Class<Page>, PageParameters,
> RequestCycle) line: 28
> LinkPage$4.onClick() line: 38
>
> Analysis:
> 1) HeaderBufferingWebResponse delegates #writeMetaData() to its
> bufferedResponse (a BufferedWebResponse instance).
> 2) BufferedWebResponse iterates its action list with the single
> "AddCookieAction" and invoke()s it
> 3) BufferedWebResponse$AddCookieAction.invoke does
> "response.addCookie(cookie)"
> 4) The addCookie is executed with this==BufferedWebResponse from 2) which
> modifies the action list illegally
>
> I created a set of test pages to test the different ways to redirect to a
> page from a page constructor, a link handler and a form submit handler. They
> all set a cookie and a session message to validate the behaviors. I will
> gladly share this if desired.

Yes, please attach the quickstart to a separate ticket.
NonResettingRestartException is not part of Wicket but it will be good
to have this functionality working in all cases.

>
> In conclusion, with Wicket 1.5.7, NonResettingRestartException works in a
> page constructor, but setResponsePage must be used in a link or form
> handler.
>
> So there is a workable alternative, but the difference in behavior depending
> on where the exception is thrown is not so good. And maybe other code paths
> are at risk of triggering this exception.
>
> Part 2 - Cookie problem with Jetty
> ==============================
> A heads up for anybody using redirects, cookies and Jetty 7.5.0, 7.5.4,
> 7.6.4 or 8.1.4 (so I guess all versions essentially).
>
> Normally, throwing a RestartResponseException throws away anything added to
> the response before that point. This is not always the case for cookies when
> using Jetty! If a cookie is set before the exception is thrown AND it
> already exists on the client (with any value), Jetty will erroneously think
> the new value is set on the client.
>
> Here is what happens: when a cookie already exists, CookieUtils#save()
> updates its value instead of creating a new one. However, Jetty's
> CookieCutter class caches the client's parsed cookies across requests
> (server-side) so the cached copy is updated with the new value. Afterwards,
> Jetty does not check properly whether the cookies sent by the client match
> the cached values so the cookie is considered modified with the new "phantom
> value" until a new session is created.
>
> This bug had me scratching my head for a while when testing the different
> ways to redirect clients! This does not happen with Tomcat 7.0.27
>
> This definitely seems like a bug in Jetty but I don't have time to file it
> and follow up since I'm only using Jetty for quickstarts. I can give more
> info on request.
>
>
>
> On 01/07/2012 10:36 AM, Martin Grigorov wrote:
>>
>> Hi,
>>
>> Here is my later response.
>>
>> I see two ways to solve this:
>>
>> 1) Use setResponsePage(pageInstance) as I described in my previous
>> response:
>>
>> setMyCookie();
>> PageB pageB = new PageB();
>> pageB.info("some text");
>> setResponsePage(pageB);
>>
>> This way the feedback is associated with a component (pageB) and wont
>> be rendered when PageA is rendered.
>> The drawback is that this way you will get a url like 'wicket/page?42'
>> instead of the nice looking one (e.g. /customers/12)
>>
>> 2) the second way is to use non-resetting RestartResponsePage
>> I've rolled this out several months ago for our app:
>>
>> public class NonResettingRestartException extends ReplaceHandlerException
>> {
>>
>>      public NonResettingRestartException(final Class<? extends Page<?>>
>> pageClass, final PageParameters params, final RequestCycle cycle) {
>>          super(createRequestHandler(pageClass, params), true);
>>
>>          Response response = cycle.getResponse();
>>          if (response instanceof IMetaDataBufferingWebResponse) {
>>              IMetaDataBufferingWebResponse bufferingWebResponse =
>> (IMetaDataBufferingWebResponse) response;
>>              bufferingWebResponse.writeMetaData((WebResponse)
>> cycle.getOriginalResponse());
>>          }
>>      }
>>
>>      private static IRequestHandler createRequestHandler(Class<?
>> extends Page<?>>  pageClass, PageParameters params) {
>>          return new RenderPageRequestHandler(new
>> PageProvider(pageClass, params));
>>      }
>> }
>>
>> and use it:
>> setMyCookie();
>> throw new NonResettingRestartException(PageB.class, params);
>>
>> I guess you will prefer option 2)
>
>
> I'll also file a ticket as you suggested regarding rendering a throw-away
> response.

Thanks!

>
>
> Bertrand
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>



-- 
Martin Grigorov
jWeekend
Training, Consulting, Development
http://jWeekend.com

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
For additional commands, e-mail: users-help@wicket.apache.org


Re: setResponsePage swallows my session feedback messages

Posted by Bertrand Guay-Paquet <be...@step.polymtl.ca>.
Hi Martin,

Thanks for your answer and code. Indeed, in my case, option 2 is most 
desirable because the session message is set in a completely different 
place than where the redirect is done.

NonResettingRestartException works great when thrown from a page 
constructor. However, when thrown from a link or form handler, a list 
concurrent modification exception occurs.

This email is rather long so I divided it in 2 parts.

Part 1 - NonResettingRestartException problem
=========================================

The list is the "actions" list of the BufferedWebResponse with the 
following stack trace:
BufferedWebResponse.addCookie(Cookie) line: 421
HeaderBufferingWebResponse.addCookie(Cookie) line: 72
BufferedWebResponse$AddCookieAction.invoke(WebResponse) line: 240
BufferedWebResponse.writeMetaData(WebResponse) line: 75
HeaderBufferingWebResponse.writeMetaData(WebResponse) line: 205
NonResettingRestartException.<init>(Class<Page>, PageParameters, 
RequestCycle) line: 28
LinkPage$4.onClick() line: 38

Analysis:
1) HeaderBufferingWebResponse delegates #writeMetaData() to its 
bufferedResponse (a BufferedWebResponse instance).
2) BufferedWebResponse iterates its action list with the single 
"AddCookieAction" and invoke()s it
3) BufferedWebResponse$AddCookieAction.invoke does 
"response.addCookie(cookie)"
4) The addCookie is executed with this==BufferedWebResponse from 2) 
which modifies the action list illegally

I created a set of test pages to test the different ways to redirect to 
a page from a page constructor, a link handler and a form submit 
handler. They all set a cookie and a session message to validate the 
behaviors. I will gladly share this if desired.

In conclusion, with Wicket 1.5.7, NonResettingRestartException works in 
a page constructor, but setResponsePage must be used in a link or form 
handler.

So there is a workable alternative, but the difference in behavior 
depending on where the exception is thrown is not so good. And maybe 
other code paths are at risk of triggering this exception.

Part 2 - Cookie problem with Jetty
==============================
A heads up for anybody using redirects, cookies and Jetty 7.5.0, 7.5.4, 
7.6.4 or 8.1.4 (so I guess all versions essentially).

Normally, throwing a RestartResponseException throws away anything added 
to the response before that point. This is not always the case for 
cookies when using Jetty! If a cookie is set before the exception is 
thrown AND it already exists on the client (with any value), Jetty will 
erroneously think the new value is set on the client.

Here is what happens: when a cookie already exists, CookieUtils#save() 
updates its value instead of creating a new one. However, Jetty's 
CookieCutter class caches the client's parsed cookies across requests 
(server-side) so the cached copy is updated with the new value. 
Afterwards, Jetty does not check properly whether the cookies sent by 
the client match the cached values so the cookie is considered modified 
with the new "phantom value" until a new session is created.

This bug had me scratching my head for a while when testing the 
different ways to redirect clients! This does not happen with Tomcat 7.0.27

This definitely seems like a bug in Jetty but I don't have time to file 
it and follow up since I'm only using Jetty for quickstarts. I can give 
more info on request.


On 01/07/2012 10:36 AM, Martin Grigorov wrote:
> Hi,
>
> Here is my later response.
>
> I see two ways to solve this:
>
> 1) Use setResponsePage(pageInstance) as I described in my previous response:
>
> setMyCookie();
> PageB pageB = new PageB();
> pageB.info("some text");
> setResponsePage(pageB);
>
> This way the feedback is associated with a component (pageB) and wont
> be rendered when PageA is rendered.
> The drawback is that this way you will get a url like 'wicket/page?42'
> instead of the nice looking one (e.g. /customers/12)
>
> 2) the second way is to use non-resetting RestartResponsePage
> I've rolled this out several months ago for our app:
>
> public class NonResettingRestartException extends ReplaceHandlerException {
>
>      public NonResettingRestartException(final Class<? extends Page<?>>
> pageClass, final PageParameters params, final RequestCycle cycle) {
>          super(createRequestHandler(pageClass, params), true);
>
>          Response response = cycle.getResponse();
>          if (response instanceof IMetaDataBufferingWebResponse) {
>              IMetaDataBufferingWebResponse bufferingWebResponse =
> (IMetaDataBufferingWebResponse) response;
>              bufferingWebResponse.writeMetaData((WebResponse)
> cycle.getOriginalResponse());
>          }
>      }
>
>      private static IRequestHandler createRequestHandler(Class<?
> extends Page<?>>  pageClass, PageParameters params) {
>          return new RenderPageRequestHandler(new
> PageProvider(pageClass, params));
>      }
> }
>
> and use it:
> setMyCookie();
> throw new NonResettingRestartException(PageB.class, params);
>
> I guess you will prefer option 2)

I'll also file a ticket as you suggested regarding rendering a 
throw-away response.

Bertrand

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
For additional commands, e-mail: users-help@wicket.apache.org