Details
-
Bug
-
Resolution: Unresolved
-
Major
-
None
-
9.95.1
-
None
-
Unknown
-
Description
(Not sure about the version.)
A Confluence XML import can run into an endless loop. It makes importing affected spaces impossible.
We have not yet pinpointed the problem down exactly and don't know the affected page yet (we don't have direkt access to the affected XWiki instance), but the stacktrace when the importer got into the endless loop looks as follows:
java.lang.Thread.State: RUNNABLE at org.xwiki.rendering.listener.WrappingListener.onWord(WrappingListener.java:299) at org.xwiki.rendering.listener.chaining.EventType$51.fireEvent(EventType.java:645) at org.xwiki.rendering.listener.QueueListener.consumeEvents(QueueListener.java:96) at org.xwiki.contrib.confluence.filter.internal.input.ConfluenceConverterListener.endParagraph(ConfluenceConverterListener.java:252) at org.xwiki.rendering.listener.chaining.EventType$6.fireEvent(EventType.java:103) at org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceXWikiGeneratorListener.fireEvents(ConfluenceXWikiGeneratorListener.java:605) at org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceXWikiGeneratorListener.fireEvents(ConfluenceXWikiGeneratorListener.java:627) at org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceXWikiGeneratorListener.handleListItem(ConfluenceXWikiGeneratorListener.java:452) at org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceXWikiGeneratorListener.onMacroBlock(ConfluenceXWikiGeneratorListener.java:346) at org.xwiki.rendering.wikimodel.impl.InternalWikiScannerContext.onMacroBlock(InternalWikiScannerContext.java:1065) at org.xwiki.rendering.wikimodel.impl.WikiScannerContext.onMacroBlock(WikiScannerContext.java:602) at org.xwiki.rendering.wikimodel.impl.WikiScannerContext.onMacro(WikiScannerContext.java:595) at org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceListItemTagHandler.end(ConfluenceListItemTagHandler.java:51) at org.xwiki.rendering.wikimodel.xhtml.handler.TagHandler.endElement(TagHandler.java:85) at org.xwiki.rendering.wikimodel.xhtml.impl.TagContext.endElement(TagContext.java:81) at org.xwiki.rendering.wikimodel.xhtml.impl.TagStack.endElement(TagStack.java:123) at org.xwiki.rendering.wikimodel.xhtml.impl.XhtmlHandler.endElement(XhtmlHandler.java:199) at org.xml.sax.helpers.XMLFilterImpl.endElement(java.xml@25.0.3/XMLFilterImpl.java:558) at org.xwiki.rendering.wikimodel.xhtml.filter.XHTMLWhitespaceXMLFilter.endElement(XHTMLWhitespaceXMLFilter.java:190) at org.xwiki.contrib.confluence.parser.xhtml.internal.wikimodel.ConfluenceXHTMLWhitespaceXMLFilter.endElement(ConfluenceXHTMLWhitespaceXMLFilter.java:90) at org.xml.sax.helpers.XMLFilterImpl.endElement(java.xml@25.0.3/XMLFilterImpl.java:558) at org.xml.sax.helpers.XMLFilterImpl.endElement(java.xml@25.0.3/XMLFilterImpl.java:558) at org.xwiki.rendering.wikimodel.xhtml.filter.AccumulationXMLFilter.endElement(AccumulationXMLFilter.java:86) at org.xml.sax.helpers.XMLFilterImpl.endElement(java.xml@25.0.3/XMLFilterImpl.java:558) at org.xwiki.rendering.wikimodel.xhtml.filter.DTDXMLFilter.endElement(DTDXMLFilter.java:86)
Note that this stacktrace stays the same for all dumps while the endless loop is running - it actually was exactly the same for multiple dumps, so I suspect this endless loop is rather tight.
ConfluenceWrappingListener looks suspicious in this context, as queueEvents and dequeueEvents look asymmetric:
void queueEvents(Listener listener)
{
queuedListeners.push(listener);
super.setWrappedListener(listener);
}
/**
* Remove the given listener from the queue. When there are wrapping listeners around, rewire so that the removed
* listener doesn't receive events anymore.
* @param listenerToRemove the listener to remove
*/
void dequeueEvents(Listener listenerToRemove)
{
Listener prev = null;
Iterator<Listener> it = queuedListeners.iterator();
while (it.hasNext()) {
Listener cur = it.next();
if (cur == listenerToRemove) {
it.remove();
if (prev instanceof WrappingListener) {
Listener next = it.hasNext() ? it.next() : compositeListener;
((WrappingListener) prev).setWrappedListener(next);
}
break;
} else if (cur instanceof RightContextAnnotationFilter) {
/* FIXME: Calling stop for RightContextAnnotationFilter specifically doesn't feel completely right,
a more generic way of handling this would be nice.
This complexity mostly comes from the titles being replayed to generate their Confluence
anchors in ConfluenceConverterListener.
*/
((RightContextAnnotationFilter) cur).stop();
}
prev = cur;
}
if (queuedListeners.isEmpty()) {
super.setWrappedListener(compositeListener);
}
}
Just by reading the loop I didn't really understand the relationship between prev, cur, next and the way prev is then updated if it is a WrappingListener. Why does it then wrap next, which is in the same queue as prev, actually a direct sibling after cur was removed?
As far as I can currently tell, without having debugged it, is that ConfluenceConverterListener usually enqueues another QueueListener, no WrappingListeners, so I'm not sure when the instanceof will actually trigger. (Though I only looked at the code on GitHub so far, not with an IDE, and you can't easily follow the relationships in the code in the GitHub web UI.)
Also not sure if the following can happen: If listenerToRemove is the one currently wrapped by this, but queuedListeners is not empty after the loop, it will not be replaced by the compositeListener, and will stay wrapped even though it's not contained in queuedListeners any more.
Attachments
Issue Links
- is related to
-
CONFLUENCE-534 Endless loop when converting complex content combining several features involving queued listeners
-
- Open
-