Hi all,

I have an interesting case, which I will admit I lost some sleep over 😉

The ListView API is not stable WRT its model.

We have not been able to reproduce this, but this is what we get in
production:

Caused by: java.lang.NullPointerException: Cannot invoke
"java.util.List.get(int)" because the return value of
"org.apache.wicket.markup.html.list.ListView.getModelObject()" is null
at
org.apache.wicket.markup.html.list.ListItemModel.getObject(ListItemModel.java:55)
~[wicket-core-9.19.0.jar:9.19.0]
at ....SectionsPanel.createSection(SectionsPanel.java:57) ~[-]
at ....SectionsPanel$1.populateItem(SectionsPanel.java:48) ~[-]
at
org.apache.wicket.markup.html.list.ListView.onPopulate(ListView.java:523)
~[wicket-core-9.19.0.jar:9.19.0]
at
org.apache.wicket.markup.repeater.AbstractRepeater.onBeforeRender(AbstractRepeater.java:124)
~[wicket-core-9.19.0.jar:9.19.0]
at org.apache.wicket.Component.beforeRender(Component.java:949)
~[wicket-core-9.19.0.jar:9.19.0]
at
org.apache.wicket.MarkupContainer.onBeforeRenderChildren(MarkupContainer.java:1759)
~[wicket-core-9.19.0.jar:9.19.0]

The simplest way to explain this:

We have a ListView in a stateless page.
The ListView gets a list model mapped from an LDM on the page:

var parentModel = LoadableDetachableModel.of(() ->
fetchItemFromCacheAndExternalApi());
… = new ListView(“id”, parentModel.map(Parent::getItems)) { … }

For a reason unknown at this time, which only happens under high load, this
stateless page is re-rendered.

On the re-render of the ListView, it walks through its onPopulate method.

First it calls getViewSize() to check if there are items to render (parent
model is non-null and says: here’s a list of X items)
Reuse items is off, so it calls removeAll => all children are removed and
detached => which also detaches the list model and the page model
It then assumes the “size” is still correct and starts creating and
appending children (ListItem instances).
Once those populate and call their ListItemModel.getObject(), the parent
model reattaches and now returns null => Boom, there’s the NPE.

The reason that our page model returns non-null and null in quick
succession is spam bots and thus cache issues; An external API we load data
from also seems to misbehave and sometimes return empty results under load.
This particular issue caught me by suprise, as normally a LDM is only
loaded once during render cycle; Here it loads *twice*: for getViewSize()
and then for the first ListItemModel, because it got detached by
ListView#removeAll.

But more to the point: the ListView proceeds, after calling removeAll(), as
if nothing can have changed. And that is not correct, the ListView model
may have been detached by removing children and thus have a different value
now.

Should ListView not re-calculate/query getViewSize() after calling
removeAll()? That would remove this edge case?

Met vriendelijke groet,
Kind regards,

Bas Gooren

Reply via email to