Wicket
  1. Wicket
  2. WICKET-4116

Ajax link reports weird error when session is expired

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.5.1
    • Fix Version/s: 1.5.2
    • Component/s: wicket
    • Labels:
      None

      Description

      Reproducing steps:

      1. Put below simple page into a Wicket application and get it mounted:

      TestPage.java:

      import org.apache.wicket.ajax.AjaxRequestTarget;
      import org.apache.wicket.ajax.markup.html.AjaxLink;
      import org.apache.wicket.markup.html.WebPage;

      @SuppressWarnings("serial")
      public class TestPage extends WebPage {

      public TestPage() {

      add(new AjaxLink<Void>("test") {

      @Override
      public void onClick(AjaxRequestTarget target) {
      }

      });

      }

      }

      TestPage.html:

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
      <?xml version="1.0" encoding="UTF-8"?>
      <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
      <title>Test Page</title>
      </head>
      <body>
      <a wicket:id="test">test</a>
      </body>
      </html>

      2. Access the page in browser via mounted url, the page will display a link.

      3. Wait until current session is expired (do not refresh the page or click the link while waiting).

      4. Hit the link and below exception will be thrown:
      Message: Cannot find behavior with id: 0 on component: [ [Component id = test]]. Perhaps the behavior did not properly implement getStatelessHint() and returned 'true' to indicate that it is stateless instead of returning 'false' to indicate that it is stateful.

      5. In wicket 1.5.0, this results in a PageExpiredException which is more comprehensive.

        Issue Links

          Activity

          Hide
          Martin Grigorov added a comment -

          With r1180580 if a request to listener interface is executed on expired page then a re-paint of the whole (and new) page is scheduled.
          Additionally a debug message is logged.

          Show
          Martin Grigorov added a comment - With r1180580 if a request to listener interface is executed on expired page then a re-paint of the whole (and new) page is scheduled. Additionally a debug message is logged.
          Hide
          Martin Grigorov added a comment -

          The change broke Wicket's StatelessForm.
          I tested only Jolira's stateless components yesterday.

          Show
          Martin Grigorov added a comment - The change broke Wicket's StatelessForm. I tested only Jolira's stateless components yesterday.
          Hide
          Martin Grigorov added a comment -

          Improved.
          Now listener interfaces again can be executed on stateless pages.

          Show
          Martin Grigorov added a comment - Improved. Now listener interfaces again can be executed on stateless pages.
          Hide
          Igor Vaynberg added a comment -

          fix broke a bunch of stuff

          for example we no longer redirect from /page to /page?0 which causes problems

          also querying request handlers for page instance no longer works. most
          queries look like this:

          if (pageHandler.isPageInstanceCreated())
          return (Page)pageHandler.getPage();

          public final boolean isPageInstanceCreated()

          { return !pageComponentProvider.isNewPageInstance(); }

          however, since isNewPageInstance() now caches the initial value of
          'true' even though the page provider later did create the instance we
          now erroneously assume the page instance was not created.

          isNewPageInstance() is a horrible name, it really should be
          isPageInstanceCreated(). if we need something like
          wasPageInstanceProvided() - to know whether or not page provider was
          created with or without a page instance to fix WICKET-4116 then we
          should add it

          or perhaps simpler names:

          Page hasPageInstance() and boolean createdPageInstance()

          Show
          Igor Vaynberg added a comment - fix broke a bunch of stuff for example we no longer redirect from /page to /page?0 which causes problems also querying request handlers for page instance no longer works. most queries look like this: if (pageHandler.isPageInstanceCreated()) return (Page)pageHandler.getPage(); public final boolean isPageInstanceCreated() { return !pageComponentProvider.isNewPageInstance(); } however, since isNewPageInstance() now caches the initial value of 'true' even though the page provider later did create the instance we now erroneously assume the page instance was not created. isNewPageInstance() is a horrible name, it really should be isPageInstanceCreated(). if we need something like wasPageInstanceProvided() - to know whether or not page provider was created with or without a page instance to fix WICKET-4116 then we should add it or perhaps simpler names: Page hasPageInstance() and boolean createdPageInstance()
          Hide
          Martin Grigorov added a comment -

          Can you attach a quickstart ?

          Show
          Martin Grigorov added a comment - Can you attach a quickstart ?
          Hide
          Igor Vaynberg added a comment -

          no need for a quickstart, check wicket-examples...

          Show
          Igor Vaynberg added a comment - no need for a quickstart, check wicket-examples...
          Hide
          Martin Grigorov added a comment -

          I have tested almost all wicket-examples for 1,5,2 and I didn't notice anything wrong...
          I'm traveling now and I wont be able to do anything until Monday so either cancel 1.5.2 or improve WICKET-4014/WICKET-4116.

          Show
          Martin Grigorov added a comment - I have tested almost all wicket-examples for 1,5,2 and I didn't notice anything wrong... I'm traveling now and I wont be able to do anything until Monday so either cancel 1.5.2 or improve WICKET-4014 / WICKET-4116 .
          Hide
          Igor Vaynberg added a comment -

          the examples will still work, but notice there is no redirect from /page to /page?0

          this also breaks cdi integration because that code only retrieves the page from the request handler if its created - it uses the isNewPageInstance() to check this, and now it never thinks it is created. i think this is the same root cause that creates the lack of the redirect.

          i already -1ed the release. im not sure if i will have time before monday myself...

          Show
          Igor Vaynberg added a comment - the examples will still work, but notice there is no redirect from /page to /page?0 this also breaks cdi integration because that code only retrieves the page from the request handler if its created - it uses the isNewPageInstance() to check this, and now it never thinks it is created. i think this is the same root cause that creates the lack of the redirect. i already -1ed the release. im not sure if i will have time before monday myself...
          Hide
          Emond Papegaaij added a comment -

          I consider rev #1184776 an API break. Even though you will not get a compilation error, it breaks methods that worked fine before: RenderPageRequestHandler.isPageInstanceCreated now throws an IllegalStateException for our custom PageProvider where it did not do so before this change. It is not optional to implement IIntrospectablePageProvider, because core functionality depends on it. This is just as bad, or even worse, as adding the methods directly to IPageProvider.

          Show
          Emond Papegaaij added a comment - I consider rev #1184776 an API break. Even though you will not get a compilation error, it breaks methods that worked fine before: RenderPageRequestHandler.isPageInstanceCreated now throws an IllegalStateException for our custom PageProvider where it did not do so before this change. It is not optional to implement IIntrospectablePageProvider, because core functionality depends on it. This is just as bad, or even worse, as adding the methods directly to IPageProvider.
          Hide
          Igor Vaynberg added a comment -

          fixed

          Show
          Igor Vaynberg added a comment - fixed
          Hide
          Martin Grigorov added a comment -

          Fixed with r1185315.

          Show
          Martin Grigorov added a comment - Fixed with r1185315.
          Hide
          Benjamin Klum added a comment -

          I would prefer if a PageExpiredException would be thrown in such a case. In our project, we need to show the user a special message if he clicks on an Ajax link but the session has expired. If the ListenerInterfaceRequestHandler just does a repaint, you have a hard time detecting what actually happened. Or am I missing something?

          Currently I use an ugly workaround on the page which is repainted to detect the timeout and throw a PageExpiredException:

          @Override
          protected void onInitialize() {
          super.onInitialize();
          if (wasRequestedAfterSessionExpiration())

          { throw new PageExpiredException("attempt to invoke request on expired listener interface"); }

          }

          private boolean wasRequestedAfterSessionExpiration()

          { return getSession().isTemporary() && isListenerInterfaceRequest(getRequest()); }

          private boolean isListenerInterfaceRequest(Request request)

          { return extractPageComponentInfo(request.getUrl()) != null; }

          private PageComponentInfo extractPageComponentInfo(Url url) {
          for (Url.QueryParameter p: url.getQueryParameters()) {
          if (Strings.isEmpty(p.getValue())) {
          PageComponentInfo i = PageComponentInfo.parse(p.getName());
          if (i != null)

          { return i; }

          }
          }
          return null;
          }

          Show
          Benjamin Klum added a comment - I would prefer if a PageExpiredException would be thrown in such a case. In our project, we need to show the user a special message if he clicks on an Ajax link but the session has expired. If the ListenerInterfaceRequestHandler just does a repaint, you have a hard time detecting what actually happened. Or am I missing something? Currently I use an ugly workaround on the page which is repainted to detect the timeout and throw a PageExpiredException: @Override protected void onInitialize() { super.onInitialize(); if (wasRequestedAfterSessionExpiration()) { throw new PageExpiredException("attempt to invoke request on expired listener interface"); } } private boolean wasRequestedAfterSessionExpiration() { return getSession().isTemporary() && isListenerInterfaceRequest(getRequest()); } private boolean isListenerInterfaceRequest(Request request) { return extractPageComponentInfo(request.getUrl()) != null; } private PageComponentInfo extractPageComponentInfo(Url url) { for (Url.QueryParameter p: url.getQueryParameters()) { if (Strings.isEmpty(p.getValue())) { PageComponentInfo i = PageComponentInfo.parse(p.getName()); if (i != null) { return i; } } } return null; }
          Hide
          Martin Grigorov added a comment -

          Hi,

          This will be possible with WICKET-4290.
          There is a new setting in IPageSettings which will switch that behavior.

          Show
          Martin Grigorov added a comment - Hi, This will be possible with WICKET-4290 . There is a new setting in IPageSettings which will switch that behavior.

            People

            • Assignee:
              Martin Grigorov
              Reporter:
              Robin Shine
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development