Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 340983 - Should use preference service API rather than plugin preferences in core.resources
Summary: Should use preference service API rather than plugin preferences in core.reso...
Status: CLOSED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Resources (show other bugs)
Version: 3.7   Edit
Hardware: PC Linux-GTK
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: James Blackburn CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
Depends on: 340993
Blocks:
  Show dependency tree
 
Reported: 2011-03-25 12:38 EDT by James Blackburn CLA
Modified: 2019-11-08 04:38 EST (History)
1 user (show)

See Also:


Attachments
patch (8.34 KB, patch)
2011-03-28 17:09 EDT, DJ Houghton CLA
no flags Details | Diff
Back ListenerRegistry by HashMap (4.29 KB, patch)
2011-03-28 17:42 EDT, James Blackburn CLA
no flags Details | Diff
FSRM core.resources patch to use non-deprecated prefs API (3.06 KB, patch)
2011-03-29 16:03 EDT, James Blackburn CLA
no flags Details | Diff
core.resources.tests patch (1.53 KB, patch)
2011-03-29 17:14 EDT, James Blackburn CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description James Blackburn CLA 2011-03-25 12:38:40 EDT
In bug 303517 I tried to use eclipse preferences for the new lightweight preference rather than deprecated plugin preferences.  I did this:

InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).addPreferenceChangeListener(this);
lightweightAutoRefreshEnabled = Platform.getPreferencesService().getBoolean(ResourcesPlugin.PI_RESOURCES, RefreshManager.PREF_LIGHTWEIGHT_AUTO_REFRESH, PreferenceInitializer.PREF_LIGHTWEIGHT_AUTO_REFRESH, null);


And this works when running the individual tests. However when running the full testsuite, by the time we get to the new tests, the listener list for the ResourcesPlugin node doesn't contain my listener.

Bug 80314 this should work, so either I'm doing something wrong, or there's a bug in the preferences (as AFAICS there isn't a workaround).
Comment 1 Szymon Brandys CLA 2011-03-28 05:09:37 EDT
I think you should ping Dj about it.
Comment 2 DJ Houghton CLA 2011-03-28 11:10:18 EDT
I'm trying to trace the history of the EclipsePreferences class but it is difficult since the version in HEAD has not only changed repositories, but also changed locations in the original repo since being created. 

But if you browse the changes between 1.56 and 1.57 in:
  org.eclipse.core.runtime/src/org/eclipse/core/internal/preferences/EclipsePreferences.java
you will see that the fix to bug 80314 was to create a separate listener registry. But in HEAD that change seems to be missing and the code was reverted to each node keeping track of its own set of listeners.

Ah, found the change. This was changed between versions 1.65 and 1.66 of the file. The CVS commit comment says it was related to bug 89262. We had a case where listeners were not GC'd when nodes were being removed and not added back. 

In my defense I made the change 6 years ago so no wonder I didn't remember. :-) 

I'm open to suggestions for alternative ways of keeping track of listeners.
Comment 3 James Blackburn CLA 2011-03-28 12:00:01 EDT
Thanks for the historical perspective DJ :)

I'm bumping this to major, as a quick call-hierarchy code inspection (in the projects I've got in my workspace) shows some of the call-sites which have migrated to the non-deprecated API may be susceptible to this bug:

  - core.variables.StringVariableManager#initialize
  - debug.internal.core.Preferences#addPreferenceListener
  - ui.internal.WorkbenchPreferenceInitializer#initializeDefaultPreferences
  - ui.editors.DefaultEncodingSupport#initialize
  - cdt.IndexerPreferences#addListener
  - cdt.ui.CNavigatorContentProvider
  - cdt.ui.EclipsePreferencesAdapter#addPropertyChangeListener
  - cdt...

(In reply to comment #2)
> Ah, found the change. This was changed between versions 1.65 and 1.66 of the
> file. The CVS commit comment says it was related to bug 89262. We had a case
> where listeners were not GC'd when nodes were being removed and not added back. 

Interesting. To me it's not clear that listeners should need to be aware that their current node may disappear and may naively expect (as I did) that instance/org.eclipse.core.resources wouldn't disappear without the plugin being shutdown...  It's also not obvious how to register a node change listener at the parent of instance scope.  I guess as this has changed a couple of times, the behaviour is 'implementation defined'.

One place that gets this right is org.eclipse.ui.internal.UIPreferenceInitializer. Unfortunately the amount of boiler plate here is pretty prohibitive:

IEclipsePreferences rootNode = (IEclipsePreferences) Platform
		.getPreferencesService().getRootNode()
		.node(InstanceScope.SCOPE);
final String uiName = UIPlugin.getDefault().getBundle()
		.getSymbolicName();
try {
	if (rootNode.nodeExists(uiName)) {
		((IEclipsePreferences) rootNode.node(uiName))
				.addPreferenceChangeListener(PlatformUIPreferenceListener
				.getSingleton());
	}
} catch (BackingStoreException e) {
	IStatus status = new Status(IStatus.ERROR, UIPlugin.getDefault()
			.getBundle().getSymbolicName(), IStatus.ERROR, e
			.getLocalizedMessage(), e);
	UIPlugin.getDefault().getLog().log(status);
}

rootNode.addNodeChangeListener(new IEclipsePreferences.INodeChangeListener() {
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.core.runtime.preferences.IEclipsePreferences
	 * .INodeChangeListener
	 * #added(org.eclipse.core.runtime.preferences
	 * .IEclipsePreferences.NodeChangeEvent)
	 */
	public void added(NodeChangeEvent event) {
		if (!event.getChild().name().equals(uiName)) {
		return;
	}
	((IEclipsePreferences) event.getChild())
		.addPreferenceChangeListener(PlatformUIPreferenceListener
			.getSingleton());

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.core.runtime.preferences.IEclipsePreferences
	 * .INodeChangeListener
	 * #removed(org.eclipse.core.runtime.preferences
	 * .IEclipsePreferences.NodeChangeEvent)
	 */
	public void removed(NodeChangeEvent event) {
	// Nothing to do here
	}
	});
}

That's like 50+ additional lines (admittedly a lot of comment)!

> In my defense I made the change 6 years ago so no wonder I didn't remember. :-) 
> 
> I'm open to suggestions for alternative ways of keeping track of listeners.

I think listeners expect to be persisted and should be in control of their own lifecycle.  While we could do something clever with references, this would just lead to arbitrary and random problems in the future. 

Certainly in the rest of the SDK we hold a strong reference to listeners. For example: the existing deprecated plugin preferences API, resource change listeners, etc.  

If there is huge churn on the nodes then it's possible leaked listeners from removed nodes could be a significant leak.  Was there a use case behind bug 89262?

There are a few solution I see:
  1) Re-add the central root node registry
  2) Ensure that the provided scopes: instance ; default ; configuration ; ... and their immediate children don't get removed and re-added as they correspond to the namespace root of the plugins (or fix them such that they persist their listeners across removal / creation)...
  3) Add API to make listeners transitive: I've often wanted to listen to changes on org.eclipse.myplugin/my_node/... and children.  If it was possible to register a transitive listener then I would only need to register one listener rather than a listener per child node => resulting in smaller listener lists.
Comment 4 DJ Houghton CLA 2011-03-28 13:33:05 EDT
I can't remember the motivation but chances are that bug 89262 was created as a knee-jerk reaction to a potential problem rather than a real problem that was exhibited in the field. I might be inclined to add back the central registry and see how that goes.
Comment 5 DJ Houghton CLA 2011-03-28 17:09:31 EDT
Created attachment 192044 [details]
patch

I think this patch ports all the changes back to keep track of all node and pref change listeners outside of the current node.
Comment 6 James Blackburn CLA 2011-03-28 17:29:34 EDT
(In reply to comment #5)
> Created attachment 192044 [details]
> patch
> 
> I think this patch ports all the changes back to keep track of all node and
> pref change listeners outside of the current node.

Looks good to me. Taking a quick look at the implementation, the ListenerRegistry map seems backed by an array.  Won't this perform badly in a large eclipse as lookups will be O(n) in the size of the preference node namespace?
Comment 7 James Blackburn CLA 2011-03-28 17:42:21 EDT
Created attachment 192047 [details]
Back ListenerRegistry by HashMap

Simple patch to replace the custom map with a HashMap
Comment 8 James Blackburn CLA 2011-03-28 18:09:42 EDT
As an aside, I pinned down where the listeners are getting lost.  The '/instance' node is removed when preferences are imported...

Daemon Thread [Thread-1] (Suspended (breakpoint at line 875 in EclipsePreferences))	
	InstancePreferences(EclipsePreferences).removeNode() line: 875	
	PreferencesService$1.visit(IEclipsePreferences) line: 140	
	ExportedPreferences(EclipsePreferences).accept(IPreferenceNodeVisitor) line: 106	
	ExportedPreferences(EclipsePreferences).accept(IPreferenceNodeVisitor) line: 110	
	PreferencesService.applyPreferences(IExportedPreferences) line: 196	
	PreferencesService.importPreferences(InputStream) line: 685	
	Preferences.importPreferences(IPath) line: 388	
	WorkspacePreferencesTest.testImportExport() line: 209
Comment 9 DJ Houghton CLA 2011-03-29 15:19:36 EDT
I'm not sure the original motivation for the array-based registry but the map-based one looks fine.

Yeah that makes sense re: import being the one to remove the nodes. 

What test failures were you getting? I've modified the code locally to use the new APIs (and keep the existing per-node list of listeners) and I'm getting 6 problems. The first one looks interested and potentially related but the last 5 I would say are unrelated. (but they still fail... not sure why though)


All Resources Tests
org.eclipse.core.tests.resources.AutomatedTests
org.eclipse.core.tests.resources.AllTests
org.eclipse.core.tests.resources.CharsetTest
testBug186984(org.eclipse.core.tests.resources.CharsetTest)
org.eclipse.core.internal.resources.ResourceException: Resource is out of sync with the file system: '/7068aa01305a0010124df048ff7a14b2/file.xml'.
	at org.eclipse.core.internal.resources.File.getContentDescription(File.java:269)
	at org.eclipse.core.tests.resources.CharsetTest.testBug186984(CharsetTest.java:429)

org.eclipse.core.tests.resources.regression.AllTests
org.eclipse.core.tests.resources.regression.Bug_303517
testExists(org.eclipse.core.tests.resources.regression.Bug_303517)
junit.framework.AssertionFailedError: 1.4
	at junit.framework.Assert.fail(Assert.java:47)
	at junit.framework.Assert.assertTrue(Assert.java:20)
	at junit.framework.Assert.assertFalse(Assert.java:34)
	at org.eclipse.core.tests.resources.regression.Bug_303517.testExists(Bug_303517.java:79)

testGetContents(org.eclipse.core.tests.resources.regression.Bug_303517)
junit.framework.AssertionFailedError: 1.1
	at junit.framework.Assert.fail(Assert.java:47)
	at junit.framework.Assert.assertTrue(Assert.java:20)
	at org.eclipse.core.tests.resources.regression.Bug_303517.testGetContents(Bug_303517.java:90)

testIsSynchronized(org.eclipse.core.tests.resources.regression.Bug_303517)
junit.framework.AssertionFailedError: 1.1
	at junit.framework.Assert.fail(Assert.java:47)
	at junit.framework.Assert.assertTrue(Assert.java:20)
	at org.eclipse.core.tests.resources.regression.Bug_303517.testIsSynchronized(Bug_303517.java:156)

testGetContentsTrue(org.eclipse.core.tests.resources.regression.Bug_303517)
junit.framework.AssertionFailedError: 1.1
	at junit.framework.Assert.fail(Assert.java:47)
	at junit.framework.Assert.assertTrue(Assert.java:20)
	at org.eclipse.core.tests.resources.regression.Bug_303517.testGetContentsTrue(Bug_303517.java:123)

testChangeResourceGender(org.eclipse.core.tests.resources.regression.Bug_303517)
junit.framework.AssertionFailedError: 1.1
	at junit.framework.Assert.fail(Assert.java:47)
	at junit.framework.Assert.assertTrue(Assert.java:20)
	at org.eclipse.core.tests.resources.regression.Bug_303517.testChangeResourceGender(Bug_303517.java:177)
Comment 10 James Blackburn CLA 2011-03-29 15:29:25 EDT
(In reply to comment #9)
> What test failures were you getting?

Exactly the ones you've listed :)

>  I've modified the code locally to use the
> new APIs (and keep the existing per-node list of listeners) and I'm getting 6
> problems. The first one looks interested and potentially related but the last 5
> I would say are unrelated. (but they still fail... not sure why though)

All those tests are the ones I touched for Bug_303517 and rely on the preference being setup correctly --  relying on the listener FileSystemResourceManager getting poked correctly.  See: Bug_303517#setup and testBug186984 itself.

I'm guess if they're all failing there's something wrong with the wiring of the preference listener?
Comment 11 James Blackburn CLA 2011-03-29 16:03:40 EDT
Created attachment 192127 [details]
FSRM core.resources patch to use non-deprecated prefs API

The core.resources tests look good to me with the above patches applied.
Comment 12 James Blackburn CLA 2011-03-29 16:14:53 EDT
Actually got a couple NPEs in SpecificContextTest:

testIsAssociatedWith(org.eclipse.core.tests.resources.content.SpecificContextTest)
java.lang.NullPointerException
	at org.eclipse.core.internal.preferences.EclipsePreferences.getNodeChangeListenerRegistry(EclipsePreferences.java:1166)
	at org.eclipse.core.internal.preferences.EclipsePreferences.fireNodeEvent(EclipsePreferences.java:668)
	at org.eclipse.core.internal.preferences.EclipsePreferences.internalNode(EclipsePreferences.java:543)
	at org.eclipse.core.internal.preferences.EclipsePreferences.node(EclipsePreferences.java:664)
	at org.eclipse.core.internal.content.ContentTypeSettings.getFileSpecs(ContentTypeSettings.java:49)
	at org.eclipse.core.internal.content.ContentType.hasFileSpec(ContentType.java:371)
	at org.eclipse.core.internal.content.ContentType.internalIsAssociatedWith(ContentType.java:470)
	at org.eclipse.core.internal.content.ContentType.isAssociatedWith(ContentType.java:520)
	at org.eclipse.core.internal.content.ContentTypeHandler.isAssociatedWith(ContentTypeHandler.java:156)
	at org.eclipse.core.tests.resources.content.SpecificContextTest.testIsAssociatedWith(SpecificContextTest.java:101)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Comment 13 James Blackburn CLA 2011-03-29 17:14:28 EDT
Created attachment 192143 [details]
core.resources.tests patch

The test with the NPE uses default EclipsePreferences constructor.  Fixed up the test to provide listener registries.
Comment 14 James Blackburn CLA 2011-03-29 17:18:23 EDT
(In reply to comment #10)
> I'm guess if they're all failing there's something wrong with the wiring of the
> preference listener?

Sorry I misread your comment, I understand that when the tests failed you weren't using the central registry patch.
Comment 15 Lars Vogel CLA 2019-11-08 04:38:10 EST
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

If the bug is still relevant please remove the stalebug whiteboard tag.