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.

Bug 179977 - File masking does not work in dynamically added layers
Summary: File masking does not work in dynamically added layers
Status: RESOLVED WORKSFORME
Alias: None
Product: platform
Classification: Unclassified
Component: Module System (show other bugs)
Version: 6.x
Hardware: PC Windows XP
: P3 normal (vote)
Assignee: Jaroslav Tulach
URL:
Keywords: REGRESSION
Depends on:
Blocks: 169892
  Show dependency tree
 
Reported: 2010-01-28 23:41 UTC by _ tboudreau
Modified: 2010-07-29 03:18 UTC (History)
2 users (show)

See Also:
Issue Type: DEFECT
Exception Reporter:


Attachments
Module that adds one menu and removes the Edit menu (10.52 KB, application/zip)
2010-01-28 23:48 UTC, _ tboudreau
Details

Note You need to log in before you can comment on or make changes to this bug.
Description _ tboudreau 2010-01-28 23:41:52 UTC
Probably related to stacking order.  Try the attached module.
Comment 1 _ tboudreau 2010-01-28 23:48:47 UTC
Created attachment 93642 [details]
Module that adds one menu and removes the Edit menu

Run this module.  Note the assertion error in
assert FileUtil.getConfigFile ("Menu/Edit") == null;
after the layer is merged.
Comment 2 _ tboudreau 2010-01-29 00:32:42 UTC
The bug is here in SystemFileSystem lines 231 to 234, in core.startup - the code that creates the system filesystem:
FileSystem[] arr = new FileSystem[home == null ? 1 : 2];
arr[0] = new ModuleLayeredFileSystem(user, true, new FileSystem[0], false);
if (home != null) { //running with no userdir
   arr[1] = new ModuleLayeredFileSystem(home, false, extras, true);
}
return new SystemFileSystem (arr);

Notice the false which is passed if the userdir does not exist.  This calls:

    ModuleLayeredFileSystem (FileSystem writableLayer, boolean userDir, FileSystem[] otherLayers, boolean mgr) throws IOException {
        this(writableLayer, userDir, otherLayers, LayerCacheManager.manager(mgr));
    }

Notice the name of the boolean parameter is "userDir".  Now look at the name of that variable in the private constructor it invokes - it is now "addLookup"
    
    private ModuleLayeredFileSystem(FileSystem writableLayer, boolean addLookup, FileSystem[] otherLayers, LayerCacheManager mgr) throws IOException {
        this(writableLayer, addLookup, otherLayers, mgr, mgr.loadCache());
    }

and the real constructor sets the variable "addLayerBefore" to whether or not the userdir exists on disk (in a unit test environment it will not).

addLayerBefore controls whether dynamically added layers are added to the bottom of the system filesystem or the top
    
    private ModuleLayeredFileSystem(FileSystem writableLayer, boolean addLookup, FileSystem[] otherLayers, LayerCacheManager mgr, FileSystem cacheLayer) throws IOException {
        super(
            appendLayers(
                writableLayer, addLookup, otherLayers,
                cacheLayer == null ? mgr.createEmptyFileSystem() : cacheLayer,
                addLookup
            )
        );
...

appendLayers() inserts all FileSystems in the default lookup to the bottom of the layer stack if addLookup is true, and at the top if it is false.

This clearly looks like an accident.  The presence or absence of the userdir should have no effect on the way dynamically added layers are merged.  Either the constructor needs a separate argument (if it is ever actually useful to merge them underneath everything else - I suspect not), or the value should simply be defaulted to true.  It looks like somebody just used code completion and didn't notice they were passing the wrong variable value to appendLayers().

Since dynamically adding layer contents will almost always be to mask or unmask some files, I can't think of a use case for ever adding dynamic SFS contents below all other SFS contents.

Additionally, for some reason appendLayers() will only add a dynamic filesystem to the *top* if Boolean.TRUE.equals(fs.getRoot().getAttribute("fallback")) (this is very awkward, since you cannot set attributes of an FS root in an XML filesystem definition).  So it looks like the fix for bug 169892 caused some sort of regression here - there seem to be four problems:
1.  Presence or absence of userdir determines what position in the stacking order dynamic layers get, because presence or absence of the userdir is accidentally used to decide where such layers are placed.
2.  If addLookupBefore is true, filesystems with the "fallback" attribute true will be added, but filesystems with it set to false will never be.
3.  If addLookupBefore is false, filesystems with the "fallback" attribute false will be added, but layers with it set to true will never be
4.  The "fallback" attribute cannot be set in an XML file - it must be done programmatically.

All that analysis being done, I still cannot get masking to work, so there remains another problem :-(
Comment 3 _ tboudreau 2010-01-29 00:47:21 UTC
Re "fallback":  Worse still, trying to set an attribute on the root of an XMLFileSystem *or* a MultiFileSystem will throw an IOException, because both are read-only.  So, to use this feature you have to create an extra MemoryFileSystem just to set an attribute on its root and merge it:

FileSystem mem = FileUtil.createMemoryFileSystem();
mem.getRoot().setAttribute("fallback", true);
setDelegates(new FileSystem[]{fs, mem});
Comment 4 Jesse Glick 2010-01-29 08:45:27 UTC
(In reply to comment #2)
> dynamically adding layer contents will almost always be to mask or unmask
> some files

That has never been the use case to date. It is normally used to insert new files under some conditions (see ActiveConfigAction.DynLayer for an example). I doubt that masking from a dynamic layer was ever even tested as a possibility. That said, it would be nice for it to work. Marking P3 since this is not a primary use case.

> I can't think of a use case for ever adding dynamic SFS contents
> below all other SFS contents.

Ergonomics relies on exactly this if I recall correctly (it wants its placeholder layer to be overridden by newly enabled modules), hence the fallback facility. Yarda would of course know better.

> you cannot set attributes of an FS root in an XML filesystem definition

Sure you can (as of the 1.1 DTD). See ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/common.xml for an example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
<filesystem>
    <attr name="fallback" boolvalue="true"/>
</filesystem>

> it looks like the fix for bug 169892 caused some
> sort of regression here

Marking a blocker accordingly. BTW did you confirm this problem in 6.8 or only 6.9 dev?

> 2.  If addLookupBefore is true, filesystems with the "fallback" attribute true
> will be added, but filesystems with it set to false will never be.
> 3.  If addLookupBefore is false, filesystems with the "fallback" attribute
> false will be added, but layers with it set to true will never be

It's been a while since I worked on this code but I think the intent was to add the fallback layers only, then static layers, then the non-fallback layers only (or the reverse).

> The "fallback" attribute cannot be set in an XML file

Again, this is not true.
Comment 5 Jaroslav Tulach 2010-02-01 08:52:07 UTC
Without investigating too deeply I suggest Tim to see MFS#setPropagateMask and use it in his MFS. There is some duplicate, but I am not going to seek for it.

Tim, if you want to improve some documentation, feel free to do so.
Comment 6 Quality Engineering 2010-07-29 03:18:24 UTC
Integrated into 'main-golden', will be available in build *201007290001* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress)
Changeset: http://hg.netbeans.org/main/rev/c42bfa57c9d6
User: Jesse Glick <jglick@netbeans.org>
Log: Moving documentation about dynamic layer injection to Javadoc where it belongs.
Rearranging some arch.xml entries.
#179977: clarifying that setPropagateMasks is necessary to dynamically inject *_hidden files.