1

Wicket throws StalePageException only if some Ajax action has been performed on a page before duplicating the page.

Example wicket project can be found at, along with steps to reproduce the exception.

https://github.com/rjngshn/java-wicket-testing/tree/main

Is there a way to ensure this exception is not thrown?

Thanks

Rajani

  • You would have to make your pages stateless to get rid of the Stale-Page problem completly (Which is users operating with a state of the page that is no longer up-to-date). There is a wicket-extensions for stateless wicket components including ajax requests, but i don't know if this is still being kept workable with the current wicket versions: https://mvnrepository.com/artifact/org.wicketstuff/wicketstuff-stateless – OH GOD SPIDERS Aug 12 '21 at 10:49
  • 1
    WicketStuff Stateless had been merged to Apache Wicket since 7.4.0 – martin-g Aug 12 '21 at 12:12
  • As I understand from wicket documentation, stateless pages are essentially used where user session is not relevant. Although it might be possible to make the page in test example stateless, that option is not feasible in real life scenario. I need to display list of items to signed in user. User can refresh this list after providing selection criteria. User can then make copy of this page using 'Duplicate' option provided by browser. After opening a copy if user navigates to original page, clicks on any item to edit/view details. StalePageException is thrown. I hope this clarifies my problem. – Rajani Sohoni Aug 16 '21 at 13:54
  • I will also like to bring to your notice that in the provided example HomePage is stateless. – Rajani Sohoni Aug 16 '21 at 14:06
  • Committed latest code to git. It now has AjaxRequestTarget.IListener implementation, and logging value of 'isStateless', which when I tested was true for HomePage. – Rajani Sohoni Aug 16 '21 at 14:20

1 Answers1

2

The default behavior provided in Wicket is too queue AJAX requests as they come in. Let’s say you have a button with a callback that does some work when clicked and updates the UI. This means that if the button is quickly clicked three times successively, the last two requests will be queued up and processed after the first request finishes.

A simple solution is to change the behavior of the AjaxChannel from queueing to active. This means that if any AJAX requests are received while there is an active(unfinished) request being processed, they will be ignored.

So how do we override Wicket’s default behavior in one spot and ensure all AjaxChannel‘s are modified? We use a custom AjaxRequestTarget.IListener.

public class ActiveAjaxListener implements AjaxRequestTarget.IListener {
 
 private static final AjaxChannel ACTIVE_CHANNEL = new AjaxChannel(AjaxChannel.DEFAULT_NAME, AjaxChannel.Type.ACTIVE);

 @Override
 public void updateAjaxAttributes(AbstractDefaultAjaxBehavior behavior, AjaxRequestAttributes attributes) {
    attributes.setChannel(ACTIVE_CHANNEL);
 }
}

Our ActiveAjaxListener class will modify every AJAX behavior and make sure it uses the active channel. To register it, we simple insert this line into our WebApplication init() method:

getAjaxRequestTargetListeners().add(new ActiveAjaxListener());

(I've copied this explanation from https://www.coderdreams.com/wicket-quick-tip-4-change-default-ajaxchannel-to-active/)

Another way is to use a veil that prevents the double clicks via JS/CSS. More details about this approach could be found at JavaScript / Wicket : How to display an overlay which prevents user interaction

martin-g
  • 17,243
  • 2
  • 23
  • 35
  • My problem is not related to user clicking the button before receiving response. As explained in the problem, user clicks on button, receives response. After receiving response user makes copy of current page using browser provided 'Duplicate' option, whereupon copy of the page is opened in new tab. After that if user returns to original page and clicks on button, 'StalePageException' is thrown. Even then I tried the suggestion, adding the listener as suggested, resulting in same exception being thrown. – Rajani Sohoni Aug 16 '21 at 13:40
  • 1
    In this case you need to use AjaxNewWindowNotifyingBehavior in your page(s). This behavior will render a new instance of the page so that it won't affect the usage of the old instance. – martin-g Aug 16 '21 at 20:47
  • 1
    `add(new AjaxNewWindowNotifyingBehavior(){ @Override protected void onNewWindow(AjaxRequestTarget target) { System.out.println("HomePage:AjaxNewWindowNotifyingBehavior:onNewWindow" + "render count:" + currentCount); if (currentCount > 1) setResponsePage(HomePage.class); } });` Duplicating page in another tab in the browser, increases render count of the page being copied. Navigating to this original page, continues throwing exception even after suggested behavior is added to the page. – Rajani Sohoni Aug 19 '21 at 10:40
  • 1
    This is because you don't call `super.onNewWindow(target);` – martin-g Aug 19 '21 at 12:15
  • Adding it did not change the outcome. My changed code: `protected void onNewWindow(AjaxRequestTarget target) { System.out.println("Before calling super.onNewWindow" + "render count:" + currentCount); super.onNewWindow(target); setResponsePage(HomePage.class); }` WARN RequestCycleExtra - Handling the following exception|#] org.apache.wicket.core.request.mapper.StalePageException: A request to page '[Page class = uk.ac.ox.ndph.ckb.scratch.HomePage, id = 0, render count = 2]' has been made with stale 'renderCount'. The page will be re-rendered – Rajani Sohoni Aug 23 '21 at 10:05