This issue has been created
 
 
XWiki Platform / cid:jira-generated-image-avatar-db5765a5-cac0-4c75-bc8c-015fbec60482 XWIKI-23013 Open

The instance filter stream overwrites previous revisions when versions are not ordered

 
View issue   ·   Add comment
 

Issue created

 
cid:jira-generated-image-avatar-cbccb480-1d4b-405a-8859-b010e90acbfa Raphaël Jakse created this issue on 21/Mar/25 16:52
 
Summary: The instance filter stream overwrites previous revisions when versions are not ordered
Issue Type: cid:jira-generated-image-avatar-db5765a5-cac0-4c75-bc8c-015fbec60482 Bug
Affects Versions: 17.1.0
Assignee: Unassigned
Attachments: buggyhistory.xml
Components: Filter - Instance
Created: 21/Mar/25 16:52
Priority: cid:jira-generated-image-static-major-66fe5c64-a0c8-43f8-ae57-ab383fc867a6 Major
Reporter: Raphaël Jakse
Description:

When filtering a document with several revisions with unordered versions, some versions overwrite previous ones because they end up being assigned existing versions.

This can be reproduced with the buggyhistory.xml attachment and the Filter Stream Converters application. This file is to be given as the source of the input+xml filter, and the xwiki+instance filter is to be used.

The input is clearly buggy, but the filter streams should probably not overwrite revisions silently. I guess an acceptable outcome would be to throw a filter exception complaining about the wrong version order.

Investigation

That's because the save archive mechanism forces versions to be increasing. If the requested version is not higher than the previous one, a new version is actually created, incrementing the previous one.
Also, when saving a document, if its version already exists, a new one is not created, the existing one is updated.
Thus, when versions are not ordered, some versions will overwrite existing ones

When that happens, there is this stack trace (on a XWiki 17.0.0 instance):

createNextVersion:117, XWikiDocumentArchive (com.xpn.xwiki.doc)
updateArchive:294, XWikiDocumentArchive (com.xpn.xwiki.doc)
updateXWikiDocArchive:315, XWikiHibernateVersioningStore (com.xpn.xwiki.store)
saveXWikiDoc:664, XWikiHibernateStore (com.xpn.xwiki.store)
saveXWikiDoc:275, XWikiCacheStore (com.xpn.xwiki.store)
saveXWikiDoc:235, XWikiCacheStore (com.xpn.xwiki.store)
saveDocument:2110, XWiki (com.xpn.xwiki)
maybeSaveDocument:252, DocumentInstanceOutputFilterStream (com.xpn.xwiki.internal.filter.output)
endWikiDocumentRevision:164, DocumentInstanceOutputFilterStream (com.xpn.xwiki.internal.filter.output)
invoke:-1, GeneratedMethodAccessor138 (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:569, Method (java.lang.reflect)
invoke:90, FilterProxy (org.xwiki.filter.internal)
invoke:75, CompositeFilter (org.xwiki.filter.internal)
endWikiDocumentRevision:-1, $Proxy410 (jdk.proxy96)
invoke:-1, GeneratedMethodAccessor138 (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:569, Method (java.lang.reflect)
invoke:90, FilterProxy (org.xwiki.filter.internal)
invoke:75, CompositeFilter (org.xwiki.filter.internal)
endWikiDocumentRevision:-1, $Proxy411 (jdk.proxy96)
invoke:-1, GeneratedMethodAccessor138 (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:569, Method (java.lang.reflect)
fireEvent:228, DefaultXMLParser$Block (org.xwiki.filter.xml.internal.parser)
fireEndEvent:168, DefaultXMLParser$Block (org.xwiki.filter.xml.internal.parser)
endElement:440, DefaultXMLParser (org.xwiki.filter.xml.internal.parser)
endElement:558, XMLFilterImpl (org.xml.sax.helpers)
handleEndElement:211, SAXEventWriter (org.xwiki.xml.stax)
convertEvent:117, SAXEventWriter (org.xwiki.xml.stax)
sendEvent:91, SAXEventWriter (org.xwiki.xml.stax)
add:519, BaseXMLEventWriter (javanet.staxutils)
add:527, BaseXMLEventWriter (javanet.staxutils)
copy:383, XMLStreamUtils (javanet.staxutils)
read:62, AbstractXMLInputFilterStream (org.xwiki.filter.xml.internal.input)
runInternal:97, FilterStreamConverterJob (org.xwiki.filter.internal.job)
runInContext:246, AbstractJob (org.xwiki.job)
run:223, AbstractJob (org.xwiki.job)
run:75, ScriptFilterStreamConverterJob (org.xwiki.filter.script.internal)
runWorker:1136, ThreadPoolExecutor (java.util.concurrent)
run:635, ThreadPoolExecutor$Worker (java.util.concurrent)
run:840, Thread (java.lang)

Here's an excerpt of updateArchive: (https://github.com/xwiki/xwiki-platform/blob/33b296d403901034fdc09f498dc6f4a750bda4d9/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocumentArchive.java#L288-L296):

    public void updateArchive(XWikiDocument doc, String author, Date date, String comment, Version version,
        XWikiContext context) throws XWikiException
    {
        Version oldLatestVer = getLatestVersion();
        Version newVer = version;
        if (newVer == null || oldLatestVer != null && newVer.compareVersions(oldLatestVer) <= 0) {
            newVer = createNextVersion(oldLatestVer, doc.isMinorEdit());
        }

This if causes the decision to go with a new version instead of the requested one. Link to [`createNextVersion`](https://github.com/xwiki/xwiki-platform/blob/33b296d403901034fdc09f498dc6f4a750bda4d9/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/doc/XWikiDocumentArchive.java#L114)

in saveXWikiDoc, line 664 (https://github.com/xwiki/xwiki-platform/blob/33b296d403901034fdc09f498dc6f4a750bda4d9/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java#L662-L665):

                                    if (!containsVersion(doc, doc.getRCSVersion(), context)) {
                                        context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, false,
                                            context);
                                    }

This is the decision to update the doc archive only if the doc version is not already found.