This issue has been created
There is 1 update.
 
 
XWiki Platform / cid:jira-generated-image-avatar-6e9048fd-3b7b-481f-a699-479d24cbbcc5 XWIKI-24779 Open

FeedPlugin.EntriesComparator never sorts anything because it reads both objects from the first entry

 
View issue   ·   Add comment
 

Issue created

 
cid:jira-generated-image-avatar-3b10afa6-69f0-49d5-9fa8-fc1d2e1afd5f Vincent Massol created this issue on 02/Sep/26 10:13
 
Summary: FeedPlugin.EntriesComparator never sorts anything because it reads both objects from the first entry
Issue Type: cid:jira-generated-image-avatar-6e9048fd-3b7b-481f-a699-479d24cbbcc5 Bug
Affects Versions: 17.10.12
Assignee: Unassigned
Components: Feed
Created: 02/Sep/26 10:13
Priority: cid:jira-generated-image-static-major-acc03af1-6521-48e6-91e3-320e4d3edce8 Major
Reporter: Vincent Massol
Description:

FeedPlugin.EntriesComparator (in com.xpn.xwiki.plugin.feed.FeedPlugin) reads both of the objects it is asked to compare from the same entry:

public int compare(com.xpn.xwiki.api.Object entry1, com.xpn.xwiki.api.Object entry2)
{
    BaseObject bobj1 = entry1.getXWikiObject();
    BaseObject bobj2 = entry1.getXWikiObject();   // should be entry2
    ...
    return (-bobj1.getDateValue("date").compareTo(bobj2.getDateValue("date")));
}

Two consequences:

  • com.xpn.xwiki.api.Object#getXWikiObject() returns the wrapped instance without copying it, so bobj1 and bobj2 are the same object. Every comparison compares the date property with itself and the comparator always returns 0. The Collections.sort(apiObjs, new EntriesComparator()) call in FeedPlugin#search(String, XWikiContext) is therefore a no-op: feed entries come back in whatever order the query produced them, rather than sorted by descending date as intended.
  • getXWikiObject() returns null when the caller does not have programming rights. In that case the comparator throws a NullPointerException on the first bobj1.getDateValue("date") instead of comparing, so the sort fails outright.

The typo dates back to commit d3ede03523a ("[misc] Introduce generics", 2009), which introduced the two bobj locals; before that the code correctly used entry1 and entry2.

Note that this is deprecated legacy code (the feed plugin), so the fix is low priority, but the comparator is public and can be used by third-party code.

 
 

1 update

 
cid:jira-generated-image-avatar-3b10afa6-69f0-49d5-9fa8-fc1d2e1afd5f Changes by Vincent Massol on 02/Sep/26 10:14
 
Description: {{FeedPlugin.EntriesComparator}} \ (in {{com.xpn.xwiki.plugin.feed.FeedPlugin}} \ ) reads both of the objects it is asked to compare from the *same* entry:

{code:
language= java}
public int compare(com.xpn.xwiki.api.Object entry1, com.xpn.xwiki.api.Object entry2)
{
    BaseObject bobj1 = entry1.getXWikiObject();
    BaseObject bobj2 = entry1.getXWikiObject();   // should be entry2
    ...
    return (-bobj1.getDateValue("date").compareTo(bobj2.getDateValue("date")));
}
{code}

Two consequences h2. What this breaks for users

{{FeedPlugin#search(String, XWikiContext)}} is the search over aggregated feed articles
: it collects the {{XWiki.FeedEntryClass}} objects matching the words the user typed and is meant to hand them back most-recent-first. Sorting them is the only thing {{EntriesComparator}} exists to do, and it does not do it.

*
*Search results over aggregated feeds come back in an arbitrary order.* {{com.xpn.xwiki.api.Object#getXWikiObject \ ( \ )}} returns the wrapped instance without copying it, so {{bobj1}} and {{bobj2}} are the same object . Every : every comparison compares the {{ one article's date }} property with itself and , the comparator always returns {{0}} . The , and {{Collections.sort \ (apiObjs, new EntriesComparator \ ( \ ) \ )}} call in leaves the list exactly as it was. The HQL query behind the search carries no {{ FeedPlugin#search\(String, XWikiContext\) ORDER BY }} either, so what the user actually gets is therefore raw database order: a no\ three - op: feed entries come back in whatever year-old article can sit above this morning's one, and the order can differ between two runs of the query produced them, rather than sorted by descending date as intended same search or between two databases . Anyone using the feed aggregator to find recent news has to read the whole result list instead of the top of it.
*
*The search fails outright for a caller without programming rights.* {{getXWikiObject \ ( \ )}} returns {{null}} when the caller does not have programming rights. In in that case , so the comparator throws a {{NullPointerException}} on the first {{bobj1.getDateValue \ ("date" \ )}} and the whole search errors out instead of comparing returning results.

What limits the blast radius: {{FeedPluginApi}} does not expose {{search}}
, so the method is only reachable from Java code (this plugin and third-party plugins), not from wiki scripts. The feed plugin is also deprecated legacy code. The fix is therefore low priority — but {{EntriesComparator}} is a {{public}} class that third-party code can use as a comparator in its own right, where it will silently sort fails outright nothing .

h2. Origin

The typo dates back to commit {{d3ede03523a}} \ ("\[misc\] Introduce generics", 2009 \ ), which introduced the two {{bobj}} locals; before that the code correctly used {{entry1}} and {{entry2}}.

Note that this is deprecated legacy code \(the feed plugin\), so the fix is low priority, but the comparator is {{public}} and can be used by third\-party code.