Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 111144 - WorkingCopyPreferences tries to flush removed pref nodes
Summary: WorkingCopyPreferences tries to flush removed pref nodes
Status: VERIFIED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.1   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.2 M6   Edit
Assignee: Tod Creasey CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 127986 (view as bug list)
Depends on:
Blocks:
 
Reported: 2005-09-29 16:57 EDT by Walter Harley CLA
Modified: 2006-04-18 19:06 EDT (History)
4 users (show)

See Also:


Attachments
patch for ui.workbench (1.74 KB, patch)
2006-03-23 12:07 EST, DJ Houghton CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Walter Harley CLA 2005-09-29 16:57:57 EDT
This bug is related to a cluster of bugs already reported, all to do with 
IllegalStateException thrown by EclipsePreferences.checkRemoved(); easiest way 
to see some of the cluster is to search on "checkRemoved" in comments.  The 
canonical one is perhaps Bug 68993.

This could be fixed (and in many places has been) in client code, but I am 
reporting this against Platform because I think there is an underlying Platform 
problem that is tripping up a number of downstream consumers.  I suggest 
solutions at the end of this bug report.

   *** REPRO ***

One repro scenario follows:
1. Start with a workspace that contains a Java project, and with JDT core 
settings all set to defaults (workspace and project).  
2. View workspace settings, go to the Java / Compiler pane, and change compiler 
compliance at the workspace level to 5.0 (so that there's a workspace node).  
3. Select "configure project specific settings" and select the project.  In 
project settings, uncheck "preserve unused local variables" (so there's a 
project node).  
4. Ok the project-specific dialog; ok the workspace dialog.  
5. Now, again view workspace settings.  Select "configure project specific 
settings" and select the project.  
6. In project settings, click "Restore Defaults".  
7. Ok the project-specific dialog; ok the workspace dialog.  

Note that the error log now contains two errors, an "unhandled loop exception" 
and "preference node org.eclipse.jdt.core has been removed".

   *** EXPLANATION ***

Here's why I think it's happening in this particular case.  For readability 
I've abbreviated some type names:

EP  = org.eclipse.core.internal.preferences.EclipsePreferences
PP  = org.eclipse.core.internal.resources.ProjectPreferences
WCM = org.eclipse.ui.internal.preferences.WorkingCopyManager
OCB = org.eclipse.jdt.internal.ui.preferences.OptionsConfigurationBlock

OCB is used to create preferences panes for certain JDT settings.  (We also use 
it as a base class for the jdt.apt settings, which is how I found this; see Bug 
106111.)  An OCB owns a WCM.  WCM owns a map of WCP.  Nothing is ever removed 
from this map.

OCB is used to create both the workspace and project-specific panes.  Because 
those dialogs are cross-linked, it is possible to nest a project-specific 
instance of OCB within a workspace-prefs instance of OCB, by first viewing a 
workspace pref pane and then linking to a project-specific pref pane.

When the "Configure project specific settings" link on the workspace-prefs 
instance is clicked, OCB.hasProjectSpecificOptions() is called.  In figuring 
out which projects have options, it inadvertently brings project-specific nodes 
into the WCM's map - that is, the WCM belonging to the parent (workspace) OCB.

Later, when the nested (project-specific) OCB is ok'ed, if the changes cause 
there to be no non-default settings under the project-specific node, then 
PP.save() deletes the whole project-specific preference file.  This results in 
the file's resource listener being called.  Ultimately, that results in a call 
to PP.deleted(IFile), which calls PP.removeNode() to remove the corresponding 
project-specific prefs node.

Even later, when the parent (workspace) OCB is ok'ed, it calls WCM.applyChanges
().  This flushes every WCP in its map, which includes the project-specific 
WCP.  When a WCP is flushed, it calls PP.flush() on the "original" node that 
backs it.  This results in a call to checkRemove(), which throws an exception 
if the node has been removed.

But in this case, the node that backs the project-specific WCP has already been 
removed.  So, the exception is thrown.

   *** SOLUTIONS ***

In a nutshell, the problem is that WCM is inadvertently caching project pref 
nodes, but those project nodes can get deleted by underlying code.

One workaround would be that in OCB, we could implement 
hasProjectSpecificSettings() differently, without using a WorkingCopyManager 
(since we're never interested in actually changing the settings discovered 
there, there's no point in wrapping them in WCM).  But that would only fix the 
problem in one place; Bugzilla contains many instances of people tripping over 
the same core problem, which is that it's bad to cache pref nodes.

I would suggest instead one of two fixes.

Minimally, WCM and WCP could be changed to install listeners on the nodes they 
cache, to remove the WCP from WCM's map if the underlying node is changed.  
This would solve the WCM problem, but it would still not solve other caching 
problems.

More deeply, I wonder whether it is right for EclipsePreferences to be throwing 
exceptions when removed nodes are flushed.  Perhaps it would be better to just 
silently ignore attempts to flush removed nodes?  If even the developers of 
WCM/WCP (in the platform code) make the mistake of caching preference nodes, it 
may be unreasonable to expect better of downstream consumers.
Comment 1 Martin Aeschlimann CLA 2005-11-10 05:20:46 EST
DJ, it would be good if you could have a look at this.
The quick summary is: The preference page builds a working copy preference
(WCP1) on top of the original preference store (PS). The page now opens a
property page out of the first page that builds a new, unrelated WCP2. That WCP2
is then commited, makes changes to the PS, in particular removing a node. WCS1
doesn't know of this and still holds on the removed node. Any attempt to change
the 'removed' node later result in IAExceptions.

This all comes from the scenario 'open a property page out of the preference
page'. The first question is, shouldn't we really have two different WCSs, or
should we share? Sharing would definitly have a benefit, although the nested
page has to be carful to only commit its own changes, and not yet committing the
other pages changes (would work in our scenario, but tricky in general). We
could also think of stacked WCP, where a WCP2 sits on top of WCP1. There it
isn't clear to me if the user would understand that pressiong ok on the nested
page would not really save the preferences until ok on the top page has been
pressed.
All that could be easely done in platform.UI by having a stack/instance of the
current WCM

Possibility 2 is to keep the current separation, but fix the removedNode problem.
A node can only be removed if no working copy node refers to it anymore. 
I guess as the WCP is currently in platform.ui, it has to become a remove
listener on the original. But why not using this opportunity to move the WCP
down to runtime :-)

Possibility 3 is that we change our code to force an apply before opening the
property page out of the preference page. This sounds like a bit nasty for the
user, but might be the right thing to do if we don't have sharing as suggested
in proposal 1.
(the remove bug should still be fixed, however)

Comment 2 DJ Houghton CLA 2006-01-18 16:20:55 EST
This problem is fixed in both 3.1.2 and 3.2. I can't find any core changes in these builds related to this, so it must have been with the property and preference pages themselves? Tod and I guess that the WCM was being queried for the wrong nodes.

btw, we can't change the behaviour of EclipsePreference#flush as it is spec'd to throw the exception if the node has been removed.

Martin do you feel that there is any more work to be done here or can this be closed? How many WCM instances do you use? Is it just one or multiple?
Comment 3 Martin Aeschlimann CLA 2006-01-18 17:42:48 EST
This seems indeed be fixed. I think there were more removed= true calls before. But i can't track back due to the move.

It would feel natural if a property page is opened out of a preference page they use the same WCS. But it's ok to me not not try that now.
Comment 4 DJ Houghton CLA 2006-01-18 17:52:37 EST
Ok, closing. We'll keep and eye open and if something else in this area surfaces we will open another report. Thanks.
Comment 5 Tod Creasey CLA 2006-01-19 07:55:38 EST
>It would feel natural if a property page is opened out of a preference page
>they use the same WCS. 

I would disagree as you may be modifying the same preference on a preference different node (that is exactly what you are doing now).
Comment 6 Martin Aeschlimann CLA 2006-01-19 09:40:03 EST
Yes, that's exacly the problem. Now two instances have different copies, so the one who flushes last, wins. 

In our case, when pressing 'default' on a project, we show you the settings of the workbench. But these are actually not the (unsaved) setting currenly in the preference dialog, but the ones on disk.

The fix for us is to always save the settings before opening a property page out of a preference page.

It's a minor bug, but in fact I think the right solution is to share the store. 
Comment 7 Martin Aeschlimann CLA 2006-02-16 03:54:15 EST
It seems that this bug is still alive, see bug 127986. I suggest to reopen.
Comment 8 Martin Aeschlimann CLA 2006-02-16 03:54:33 EST
*** Bug 127986 has been marked as a duplicate of this bug. ***
Comment 9 John Arthorne CLA 2006-03-17 15:10:49 EST
I am seeing this error in I20060315-1200 - same steps as Walter's original steps.  I reverted some project specific preferences to default, and then clicked ok in the preference dialog to get this error:

java.lang.IllegalStateException: Preference node "org.eclipse.jdt.core" has been removed.
at org.eclipse.core.internal.preferences.EclipsePreferences.checkRemoved(EclipsePreferences.java:157)
at org.eclipse.core.internal.preferences.EclipsePreferences.flush(EclipsePreferences.java:326)
at org.eclipse.core.internal.resources.ProjectPreferences.flush(ProjectPreferences.java:566)
at org.eclipse.ui.internal.preferences.WorkingCopyPreferences.flush(WorkingCopyPreferences.java:516)
at org.eclipse.ui.preferences.WorkingCopyManager.applyChanges(WorkingCopyManager.java:58)
at org.eclipse.ui.internal.dialogs.FilteredPreferenceDialog.okPressed(FilteredPreferenceDialog.java:385)
at org.eclipse.jface.preference.PreferenceDialog.buttonPressed(PreferenceDialog.java:225)
at org.eclipse.jface.dialogs.Dialog$3.widgetSelected(Dialog.java:652)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:90)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:925)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3340)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2960)
at org.eclipse.jface.window.Window.runEventLoop(Window.java:820)
at org.eclipse.jface.window.Window.open(Window.java:796)
at org.eclipse.ui.dialogs.PropertyDialogAction.run(PropertyDialogAction.java:156)
at org.eclipse.jface.action.Action.runWithEvent(Action.java:499)
at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:539)
at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:488)
at org.eclipse.jface.action.ActionContributionItem$5.handleEvent(ActionContributionItem.java:400)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:925)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3340)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2960)
at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:1909)
at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1873)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:418)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:143)
at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:107)
at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:78)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:92)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:68)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:374)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:169)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:85)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:58)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:60)
at java.lang.reflect.Method.invoke(Method.java:391)
at org.eclipse.core.launcher.Main.invokeFramework(Main.java:338)
at org.eclipse.core.launcher.Main.basicRun(Main.java:282)
at org.eclipse.core.launcher.Main.run(Main.java:977)
at org.eclipse.core.launcher.Main.main(Main.java:952)
Comment 10 DJ Houghton CLA 2006-03-23 12:07:00 EST
Created attachment 36815 [details]
patch for ui.workbench

Here is a patch for WorkingCopyManager which checks to see if any of the nodes in its cache have been removed from underneath it.

Another alternative is to register a listener on the original nodes. We will discuss this....after lunch. :-)
Comment 11 DJ Houghton CLA 2006-03-27 06:39:37 EST
Assigning to Tod to release the patch.
Comment 12 Tod Creasey CLA 2006-03-27 08:05:38 EST
Fixed for build >20060327
Comment 13 Tod Creasey CLA 2006-03-28 14:28:03 EST
Verified in 20060328-0100
Comment 14 Walter Harley CLA 2006-04-18 19:06:27 EDT
Please note very similar bug 137398.