Batik IllegalStateException when resizing the JCom

2019-07-27 22:51发布

My program seems to work quite well, but I keep getting "IllegalStateExceptions: RunnableQueue not started or has exited" from time to time, when I try to resize my component. I have set the documentState to ALWAYS_DYNAMIC and I have read that you are supposed to use the JSVGCanvas' UpdateManager and call invokelater(). I understand that it is available after the first time that

gvtBuildCompleted(GVTTreeBuilderEvent e)

is called, so I check whether it is running before I use it but I still get the exception. The following method is called by a thread repeatedly and seems to cause the exception:

private void updateDomTree(final SVGComponent component, final Document doc)
{
if(component.getSvgCanvas().getUpdateManager() != null && component.getSvgCanvas().getUpdateManager().isRunning())
{
    component.getSvgCanvas().getUpdateManager().getUpdateRunnableQueue().invokeLater(new Runnable() 
    {
        public void run() 
        {
            final Node newNode = doc.getChildNodes().item(0).getFirstChild();
            //could be easier to get this value, but ... it works.
            String newNodeId = newNode.getAttributes().getNamedItem("id").getFirstChild().getNodeValue();

            NodeList nodes = component.getSvgDocument().getDocumentElement().getChildNodes();
            Node updateNode = findElementById(nodes, newNodeId);
            resizeComponent(component, doc);
            component.getSvgCanvas().getSVGDocument().adoptNode(newNode);
            component.getSvgCanvas().getSVGDocument().getDocumentElement().replaceChild(newNode, updateNode);
            component.refreshSVGCanvas();
        }
    });
}
}

The actual resizing is done here:

protected void resizeComponent(SVGComponent component, Document doc)
{
    Element svgRoot = doc.getDocumentElement();
    final int svgWidth = Integer.parseInt(svgRoot.getAttribute("width"));
    final int svgHeight = Integer.parseInt(svgRoot.getAttribute("height"));
    String[] viewBox = svgRoot.getAttribute("viewBox").split(" ");
    int viewBoxLeft = Integer.parseInt(viewBox[0]);
    int viewBoxTop = Integer.parseInt(viewBox[1]);
    final float factor = component.getScaleFactor();
    String[] viewBoxOld = component.getSvgDocument().getDocumentElement().getAttribute("viewBox").split(" ");
    int viewBoxLeftOld = Integer.parseInt(viewBoxOld[0]);
    int viewBoxTopOld = Integer.parseInt(viewBoxOld[1]);
    int xDiff = (int) ((viewBoxLeftOld - viewBoxLeft)*factor);
    int yDiff = (int) ((viewBoxTopOld - viewBoxTop)*factor);

    if ( viewBoxLeftOld != viewBoxLeft ) //If there is additional content left
    {
        component.setLocation(component.getLocation().x - xDiff, component.getLocation().y);
    }
    if ( viewBoxTopOld != viewBoxTop ) //If there is additional content right)
    {
        component.setLocation(component.getLocation().x, component.getLocation().y - yDiff);
    }

    component.getSvgDocument().getDocumentElement().setAttribute("width",""+svgWidth);
    component.getSvgDocument().getDocumentElement().setAttribute("height",""+svgHeight);
    component.getSvgDocument().getDocumentElement().setAttribute("viewBox", ""+viewBoxLeft+" "+viewBoxTop+" "+svgWidth+" "+svgHeight);
    component.setSize((int)(svgWidth*factor),(int)(svgHeight*factor));
}

The method

refreshJSVGCanvas()

calls

JSVGCanvas.setDocument(Document);
JSVGCanvas.setSize(int, int);

Here's the full stack trace:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: RunnableQueue not started or has exited
at org.apache.batik.util.RunnableQueue.invokeLater(RunnableQueue.java:277)
at org.apache.batik.swing.svg.AbstractJSVGComponent.updateRenderingTransform(AbstractJSVGComponent.java:1057)
at org.apache.batik.swing.gvt.AbstractJGVTComponent$1.componentResized(AbstractJGVTComponent.java:237)
at java.awt.AWTEventMulticaster.componentResized(Unknown Source)
at java.awt.Component.processComponentEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

Thanks in advance, I have searched everywhere and tried a lot, but could not find a solution.

Edit: This is the invokeLater-Method of Batik where the Exception is actually thrown:

public void invokeLater(Runnable r) {
    if (runnableQueueThread == null) {
        throw new IllegalStateException
            ("RunnableQueue not started or has exited");
    }
    synchronized (list) {
        list.push(new Link(r));
        list.notify();
    }
}

runnableQueueThrad is set inside that class' run()-Method and set to null at the end. So I guess I have to do some kind of synchronization.

1条回答
你好瞎i
2楼-- · 2019-07-27 23:20

Hazard a guess, the "public void run()" code should not be inside another method and really is a thread class/interface objects so called constructor(interface version actually).

Remove it to its own class(e.g. nested subclass to preserve scope) and implement the "thread runnable" interface on the class to place the "run()" method in it to use.

Stack trace says the run method is not available because it does not actually have such a method(or at least not properly declared) so it is in an "illegal state".

查看更多
登录 后发表回答