Community
Participate
Working Groups
During the plugin initialization of the plugin com.ibm.etools.websphere.tools.v4, a resource change listener has been added to the workspace. When the workbench restarts, the IResourceChangeListener.resourceChanged() is called. This causes the IResourceChangeEvent.getDelta() to be called but this method is never returned. This problem causes the workbench hangs there and fail to startup. I have added some system out print statements to the resource change listener: ====================================================================== ... public void resourceChanged(IResourceChangeEvent event) { System.out.println("Resource changed: " + event); // Print the stack trace. try { String hello = null; hello.toString(); } catch (Exception e) { e.printStackTrace(); } IResourceDelta delta = event.getDelta(); System.out.println("Resource delta is: " + delta); if (delta == null) return; // search for changes to remote file transfer instance using a visitor try { ... ====================================================================== Here is the system out that I get when I restart the eclipse workbench: ===================================================================== Resource changed: org.eclipse.core.internal.events.ResourceChangeEvent[source=org.eclipse.core.int ernal.resources.Workspace@41570edc] java.lang.NullPointerException at com.ibm.etools.websphere.tools.EARConfigChangeManager$EARConfigChangeListener.re sourceChanged(EARConfigChangeManager.java:77) at org.eclipse.core.internal.events.NotificationManager$1.run(NotificationManager.j ava:123) at org.eclipse.core.internal.runtime.InternalPlatform.run(InternalPlatform.java:817 ) at org.eclipse.core.runtime.Platform.run(Platform.java:395) at org.eclipse.core.internal.events.NotificationManager.notify(NotificationManager. java:138) at org.eclipse.core.internal.events.NotificationManager.broadcastChanges(Notificati onManager.java:62) at org.eclipse.core.internal.resources.Workspace.broadcastChanges(Workspace.java:13 4) at org.eclipse.core.internal.resources.Workspace.endOperation(Workspace.java:748) at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:1343) at org.eclipse.debug.internal.core.BreakpointManager$3.run(BreakpointManager.java:5 64) at java.lang.Thread.run(Thread.java:498) ===================================================================== From the system out, you can see that the "Resource changed" line is printed out and the stack trace is available. The "Resource delta is: ..." line is not displayed means that the IResourceChangeEvent.getDelta( ) method never gets returned. It simply hangs in the event.getDelta( ) method and the CPU usage is 0%. Therefore, it does not looks like it goes to an infinite loop of some kind. Here is the whole resource change listener class EARConfigChangeListener for reference: ================================================================= /** * EARConfigChangeListener -- tracks the changes in the application.xml * in an EAR project. */ public class EARConfigChangeListener implements IResourceChangeListener { /** * Create a new EARConfigChangeListener. */ public EARConfigChangeListener() { super(); } /** * Listen for projects being added or removed and act accordingly. * * @param event org.eclipse.core.resources.IResourceChangeEvent */ public void resourceChanged(IResourceChangeEvent event) { System.out.println("Resource changed: " + event); // Print the stack trace. try { String hello = null; hello.toString(); } catch (Exception e) { e.printStackTrace(); } IResourceDelta delta = event.getDelta(); System.out.println("Resource delta is: " + delta); if (delta == null) return; // search for changes to remote file transfer instance using a visitor try { delta.accept(new IResourceDeltaVisitor() { public boolean visit(IResourceDelta delta2) { IResource resource = delta2.getResource(); if (resource == null) return true; if (EAR_APPLICATION_XML_FILENAME.equals(resource.getName())) { earConfigChanged(resource.getProject()); } else if (resource instanceof IProject) { projectChanged(delta2); return false; } return true; } }); } catch (Exception e) {} } /** * React to a change within a project. * * @param delta org.eclipse.core.resources.IResourceDelta */ protected void projectChanged(IResourceDelta delta) { IResourceDelta[] children = delta.getAffectedChildren(); int size = children.length; for (int i = 0; i < size; i++) { // IResourceDelta child = children[i]; // look for server instances and configurations try { delta.accept(new IResourceDeltaVisitor() { public boolean visit(IResourceDelta delta2) { IResource resource = delta2.getResource(); // String resourceName = resource.getName(); if (EAR_APPLICATION_XML_FILENAME.equals(resource.getName())) { earConfigChanged(resource.getProject()); return false; } else return true; } }); } catch (Exception e) {} } } } ===========================================================
Created attachment 546 [details] code.txt
Created attachment with code for improved readability.
Which build are you using?
The driver is based on the M4 driver (based on 0321 stable build)
Your listener code contains an infinite loop. In projectChanged, you recursively create another visitor on the same project delta. I think you meant to create a vistor on the child delta here. Note: IResourceChangeEvent.getDelta() is just an accessor method, it doesn't do any work.
Created attachment 547 [details] Dump stack when eclipse startup hangs
I have changed the projectChanged() method to prevent the infinite loop. The problem still exists. That part of the code never gets executed. I did a dump stack and attached it with the defect. It looks like the plugin class loaders have dead locked. If you take a look at the dump stack, you'll notice that the main thread and the debug/breakpoint manager thread both have a lock on a different plugin class loader and are locking when trying to obtain a lock on the other's class loader. Plugins should not have to worry about the class loader's behaviour or possible deadlocks created by threads started by the workbench, so I am reopening this defect.
Any attempt to createExecutableExtension during plugin startup will likely cause deadlock in the current implementation. Only simple bookkeeping operations can be done safely. The ServerPlugin is doing much more than this. There is an attempt to improve the robustness of the startup code evaluation, but it is a wise design move to eliminate complex processing from startup.
Changed title to reflect real problem. See bug 5875 for a more complete discussion of this issue. See also "Note 3" in the javadoc for Plugin.startup. The culprit seems to be: com.ibm.etools.server.core.internal.ServerFactory.getDelegate called from: com.ibm.etools.server.core.internal.plugin.ServerPlugin.startup We realize this is a limitation of the platform, and we'd like to solve it in the future if we can. In the short term, it would be best to follow JohnW's advice and avoid heavy initialization code in Plugin.startup() methods.
John(s): 1) If this is a temporary limitation of the platform, can you give us a quick idea of the scope of the problem/fix and whether it is likely to be fixed in this release? 2) Is calling createExecutableExtension() the only case that might cause deadlock? Are there any other method calls that might also hang the workbench during startup? Since the penalty for leaving in one of these calls is severe, we'd like to know exactly what circumstances could trigger the problem. 3) This restriction is not public knowledge, and we know of other plugins that may also load extension points on startup. In a large product with many cross dependancies, it's hard to completely ensure that none of these methods are being called during plugin startup. (a "simple" method call to a required plugin may turn around and load other plugins via another method call) In light of when a fix might be available and not doing a lot of temporary work, is it reasonable for you to detect these situations and explicitly disallow them in current builds? (For instance, can you throw an exception when createExecutableExtension() is called during plugin startup?) This would cleanly enforce this restriction and ensure that other plugins do not run into the same problem.
1) I don't think it is likely this will be addressed in the 2.0 timeframe. This is mainly a function of the risk involved with significantly modifying the classloading mechanism. The fix, if feasible, would be localized within the classloader code, so it would be transparent to all users. 2) No. Any two orthogonal locking mechanisms can combine to cause deadlock. The classloader introduces one lock, so if you try to acquire any other lock within the startup() code, deadlock will be possible. Quite simply, if the startup() blocks while waiting on a lock, and the thread that holds that lock attempts a classload in the same plugin, you'll get deadlock. We will consider your suggestion for disallowing createExecutableExtension during startup(). I don't know how difficult this would be to implement. Unfortunately this is only one instance of a general problem, so it won't be a great help.
*** Bug 46438 has been marked as a duplicate of this bug. ***
If we don't fix for 3.0, we should minimally document the behavior (for example in Plugin.startup javadoc).
These deadlock cases have been adressed in the osgi based runtime.