This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.
I am not sure if this is an execution bug or a documentation bug. The bug has to do with clarification of the contract for the Node.destroy() method. This bug will provide a module which demonstrates the problem with an example. The bug is as follows: 1: call node.destroy() on a given node. 2: The result of step 1 is that the node is left in a state where it can not be called again. 3: After event 2 has completed the getIcon() method of the node is invoked. In order to see this behavior, expand and mount the sample directory as a file system. Execute the jar (assumes api support). Open the node "openme" Delete any of the inner nodes in openme and you will see the stack trace below at the end of this section of the report. The interesting bits of code around this problem are: public synchronized void destroy() { isDestroyed = true; parent.dobj.removeChild(getName()); parent.addNotify(); } public synchronized Image getIcon(int type) { if (isDestroyed) { throw new RuntimeException("This node has been destroyed. Why are we asking for this icon?"); } return super.getIcon(type); } As can be seen in the code below, getIcon is called after the node has been destroyed. This is obviously a contrived example. In the real code, from which this example was created from, the destroy method removes something from the data object that the getIcon code needs to read in order to provide the correct icon. Questions: 1: What should the contract be for Node.destroy 2: Is it reasonable to assume that the node will not again be invoked by the infrastructure once destroyed? 3: If the answer to question 2 is no, can the javadoc be updated appropriately to reflect this. This appears to be a change in behavior since NetBeans 3.3. The code from which the example was derrived worked fine under NetBeans 3.3. ---------------- Stack trace from getIcon called after destroy() has been called --------- Annotation: Root C:\java\j2sdk1.4.1\jre\classes does not exist. java.lang.RuntimeException: This node has been destroyed. Why are we asking for this icon? at childrentester.MyDataNode$MyChildNode.getIcon (MyDataNode.java:73) at org.openide.nodes.FilterNode.getIcon (FilterNode.java:362) at org.openide.explorer.view.NodeRenderer$Tree.getTreeCellRend ererComponent(NodeRenderer.java:257) at org.openide.explorer.view.NodeRenderer.getTreeCellRendererC omponent(NodeRenderer.java:103) at javax.swing.plaf.basic.BasicTreeUI.paintRow (BasicTreeUI.java:1375) at javax.swing.plaf.basic.BasicTreeUI.paint (BasicTreeUI.java:1171) at javax.swing.plaf.metal.MetalTreeUI.paint (MetalTreeUI.java:143) at javax.swing.plaf.ComponentUI.update (ComponentUI.java:142) at javax.swing.JComponent.paintComponent (JComponent.java:541) at javax.swing.JComponent.paint (JComponent.java:808) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JViewport.paint(JViewport.java:707) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at org.netbeans.core.windows.frames.CloseButtonTabbedPane.pain t(CloseButtonTabbedPane.java:161) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JLayeredPane.paint (JLayeredPane.java:552) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintWithOffscreenBuffer (JComponent.java:4771) at javax.swing.JComponent.paintDoubleBuffered (JComponent.java:4724) at javax.swing.JComponent._paintImmediately (JComponent.java:4668) at javax.swing.JComponent.paintImmediately (JComponent.java:4477) at javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:410) [catch] at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest. run(SystemEventQueueUtilities.java:117) at java.awt.event.InvocationEvent.dispatch (InvocationEvent.java:178) at java.awt.EventQueue.dispatchEvent (EventQueue.java:448) at java.awt.EventDispatchThread.pumpOneEventForHierarchy (EventDispatchThread.java:197) at java.awt.EventDispatchThread.pumpEventsForHierarchy (EventDispatchThread.java:150) at java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:144) at java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:136) at java.awt.EventDispatchThread.run (EventDispatchThread.java:99)
Created attachment 8818 [details] Tar archive of test module which demonstrates the case.
This is causing a bug in S1S. I have created a tracking bug in BugTraq 4814837.
AFAIK this is just a doc bug - your getIcon must be written to gracefully handle the case that the node has been destroyed. E.g. return null or something. The effect of calling setKeys with a smaller set of keys (i.e. of deleting the child node) is not likely to be synchronous, though it should take effect immediately from the user perspective. In the meantime, some AWT redraw might happen. If the Nodes API worked exclusively in the AWT thread and did everything synchronously then of course this would not happen, but I think it uses a request processor for handling key changes.
A couple of comments. Node.getIcon() does not provide for the possibility of null as a return value. The effect of doing so is a predictable null pointer exception elsewhere in the IDE. For whatever reason, the behavior has changed going from "sierra" branch to the trunk. Once a node is destroyed, all of the state that it needs to represent itself is gone. It is obvious that I am going to have to go back and redo code that worked to accomidate new behavior. Prior to doing so it would be nice to know some more things about the life cycle of Nodes, mostly pertaining to what is synchronus and what is not. I guess this falls under the more general, and off discussed topic of what is the threading model for NetBeans. -----Stack Trace of NPE if null is returned-------- java.lang.NullPointerException at javax.swing.ImageIcon.<init>(ImageIcon.java:161) at org.openide.explorer.view.NodeRenderer.getIcon (NodeRenderer.java:191) at org.openide.explorer.view.NodeRenderer$Tree.getTreeCellRend ererComponent(NodeRenderer.java:257) at org.openide.explorer.view.NodeRenderer.getTreeCellRendererC omponent(NodeRenderer.java:103) at javax.swing.plaf.basic.BasicTreeUI.paintRow (BasicTreeUI.java:1375) at javax.swing.plaf.basic.BasicTreeUI.paint (BasicTreeUI.java:1171) at javax.swing.plaf.metal.MetalTreeUI.paint (MetalTreeUI.java:143) at javax.swing.plaf.ComponentUI.update (ComponentUI.java:142) at javax.swing.JComponent.paintComponent (JComponent.java:541) at javax.swing.JComponent.paint (JComponent.java:808) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JViewport.paint(JViewport.java:707) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at org.netbeans.core.windows.frames.CloseButtonTabbedPane.pain t(CloseButtonTabbedPane.java:161) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JLayeredPane.paint (JLayeredPane.java:552) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintChildren (JComponent.java:647) at javax.swing.JComponent.paint (JComponent.java:817) at javax.swing.JComponent.paintWithOffscreenBuffer (JComponent.java:4771) at javax.swing.JComponent.paintDoubleBuffered (JComponent.java:4724) at javax.swing.JComponent._paintImmediately (JComponent.java:4668) at javax.swing.JComponent.paintImmediately (JComponent.java:4477) at javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:410) [catch] at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest. run(SystemEventQueueUtilities.java:117) at java.awt.event.InvocationEvent.dispatch (InvocationEvent.java:178) at java.awt.EventQueue.dispatchEvent (EventQueue.java:448) at java.awt.EventDispatchThread.pumpOneEventForHierarchy (EventDispatchThread.java:197) at java.awt.EventDispatchThread.pumpEventsForHierarchy (EventDispatchThread.java:150) at java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:144) at java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:136) at java.awt.EventDispatchThread.run (EventDispatchThread.java:99)
Sorry, by "return null" I was speaking loosely; "return some arbitrary dummy value legal according to the method contract". E.g. super.getIcon(). I don't think there is any documented contract as to when a node is "really" destroyed. It may well be that the implementation behavior was different in 3.3, but it was never correct to fail in a hard sense if some method is called on a node after it is removed from a parent. Code which was not robust in this sense may have worked only by luck before. There is no strongly specified threading model for nodes, other than that node methods might be accessed from various threads, and that Children.MUTEX can be used to lock hierarchy changes (though in practice it is not necessary for module code to touch this mutex). Timing behavior for Children.Keys etc. is not documented so no assumptions can be made. addNotify will be called when a Children object is expected to generate its children (perhaps asynchronously), and removeNotify when children are no longer needed.
what is the status of this issue? Thx
For S1S, I have coded around this problem by coding "defensively" as was suggested earlier in this thread. BugTraq 4814837 has been closed. If he effect of calling setKeys with a smaller set of keys (i.e. of deleting the child node) is not likely to be synchronous, as Jesse indicates: <QUOTE> The effect of calling setKeys with a smaller set of keys (i.e. of deleting the child node) is not likely to be synchronous, though it should take effect immediately .... </QUOTE> then that should be documented as well. It should be safe to assume synchronous method execution unless otherwise documented. As to the meaning of destroy, if Nodes are intended to be invoked after destroy, that certainly needs to be documented. Doing so seems to be analogous to using a DataObject after it has been invalidated.
So this is a API doc issue. I lower prio to P3
The analogy with invalidated data objects is not exact, but it is instructive. Should consider whether visualizer impl can be changed in such a way that it will not look up properties of a node (icon e.g.) which has been posted for removal via setKeys but not yet removed... perhaps some kind of better use of the mutex? I don't know.
Will add notice to Node.destory Javadoc that the node needs to respond with some reasonable temporary values even after the destory method is called. The idea with VisualiserNode enhancement probably deserves own enhancement in Explorer. However Nodes should be independent on Explorer so the notice in destroy's Javadoc will still be valid.
"However Nodes should be independent on Explorer so the notice in destroy's Javadoc will still be valid." - not necessarily; if we could in fact guarantee that the Explorer code never called methods on dead nodes, then it would make sense to document in Node that no one may call methods on a node after it is destroyed, and that node impls need not write code to provide temporary dummy values.
Taking over the node bugs from phrebejk.
Probably by fixed by patch in issue #35833.
Probably P4; don't know of user-visible problems caused by this.
Agreed
Reassigning to new module owner Tomas Holy.
This bug was reported against NetBeans IDE 6.0 or an older release, or against a non-maintained module. NetBeans team does not have enough resources to get to this issue, therefore we are closing the issue as a WONTFIX. If you are interested in providing a patch for this bug, please see our NetFIX guidelines for how to proceed. We apologize for any inconvenience. Thank you. The NetBeans Team
Noting in Javadoc for destroy() that other methods may be called too: core-main #9fe397f79548
Integrated into 'main-golden', will be available in build *200912230201* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress) Changeset: http://hg.netbeans.org/main/rev/9fe397f79548 User: Jesse Glick <jglick@netbeans.org> Log: Improving destroy() Javadoc. See also #30702.