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 197308 - MultifileSystem cache problem with overridden attributes (MulitFileObject)
Summary: MultifileSystem cache problem with overridden attributes (MulitFileObject)
Status: RESOLVED FIXED
Alias: None
Product: platform
Classification: Unclassified
Component: Filesystems (show other bugs)
Version: 6.x
Hardware: PC Other
: P2 normal (vote)
Assignee: Jaroslav Tulach
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-04-01 06:24 UTC by arittner
Modified: 2011-04-07 08:47 UTC (History)
2 users (show)

See Also:
Issue Type: DEFECT
Exception Reporter:


Attachments
Tests that try, but fail to simulate the problem (4.25 KB, patch)
2011-04-04 16:05 UTC, Jaroslav Tulach
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description arittner 2011-04-01 06:24:38 UTC
Hi!

This is a very strange bug and IMHO with a very big spread all over the NetBeans platform configurations. 

If you read an attribute twice you get first a changed attribute. With the second read you get the standard attribute value from the layer file. 

This issue kills many scenarios to use the SystemFileSystem with MultifileSystem as a changeable registry. More, the problem appears only randomly (if the developer needs to read the attribute twice and the user have changed the attribute).

Here an example.

A sample entry in the layer.xml:

<filesystem>
    <folder name ="org-sepix">
      <folder name= "Panes">
            <file name="Demo.txt">
                <attr name="position" intvalue="100"/>
            </file>
      </folder>
    </folder>
</filesystem>

and a minimized example class:

public class Demo {
  public static void demo () {
    FileObject folder = FileUtil.getConfigFile("org-sepix/Panes/");
    
    for (FileObject fileObject : folder.getChildren()) {
      try {
        System.out.println(fileObject.getNameExt());
        System.out.println("position = " + fileObject.getAttribute("position") + " should 100");
        
        fileObject.setAttribute("position", new Integer (200));
        System.out.println("position = " + fileObject.getAttribute("position") + " should 200");
        
        // Here the problem:
        System.out.println("position = " + fileObject.getAttribute("position") + " should 200");
        
        
      } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
      }
    }
  }
}

And this is the output:

Demo.txt
position = 100 should 100
position = 200 should 200
position = 100 should 200


The last line is the wrong output.

This is reproducible with all attributes. It's hard to debug, the debugger also changes the value. 

IMHO it's a special coded block to optimize reads for attributes in the MultiFileObject class:

  private final Object getAttribute(String attrName, String path) {
        // Look for attribute in any file system starting at the front.
        // Additionally, look for attribute in root folder, where
        // the relative path from the folder to the target file is
        // prepended to the attribute name, all separated with slashes.
        // This search scheme permits writable front systems to set file
        // attributes on deeply buried files in back systems without
        // actually creating copies of the files or even their parent folders.
        // [PENDING] consider the effects of mask files
        String prefixattr = ((path.length() == 0) ? null : (path.replace('/', '\\') + '\\' + attrName));

        { /*There was proved that this block enhances performance*/

            Object oPerf;
            FileObject localFo = getAttributeCache().getDelegate();
            String cachedAttrName = getAttributeCache().getAttributeName();

            if ((localFo != null) && !localFo.equals(this) && cachedAttrName.equals(attrName)) {
                if (localFo.isRoot() && (prefixattr != null)) {
                    try {
                        FileSystem foFs = localFo.getFileSystem();
                        if (!foFs.isReadOnly() || getMultiFileSystem().canHaveRootAttributeOnReadOnlyFS(prefixattr)) {
                            localFo = foFs.getRoot();
                            oPerf = getAttribute(localFo, prefixattr, ""); // NOI18N

                            if (oPerf != null) {
                                return devoidify(oPerf);
                            }
                        }
                    } catch (FileStateInvalidException fiex) {
                        //then continue
                    }
                }

                /** There is no chance to cache localFo.getPath(), because every
                 *  rename up to it in hierarchy makes this cache invalid.
                 */
                oPerf = getAttribute(localFo, attrName, localFo.getPath());

                if (oPerf != null) {
                    return devoidify(oPerf);
                }
            }
        }


The line (792)

    String cachedAttrName = getAttributeCache().getAttributeName();

returns a valid (pre-read) name ("position") and the first if-statement is true. 

The line (814):

    oPerf = getAttribute(localFo, attrName, localFo.getPath());

returns the wrong value (the default from the layer.xml, not the changed attribute value)


From my feeling it's an P1 in case of the unexpected runtime results.

As developer I've found two workarounds: 

  Read a different attribute before 
or 
  cast the FileObject to FileChangeListener and call fileRenamed (null) 
  to delete the internal cache via updateAll/mfo.freeAttribCache();

but this is only a well known implementation hack and doesn't have an effect to the existing code in the platform.

best regards,
  josh.
Comment 1 arittner 2011-04-01 17:01:06 UTC
Same source code in NB 7.0rc1. Is it possible to fix it before 7 goes final?
Comment 2 Jaroslav Tulach 2011-04-04 15:43:53 UTC
Unit test is better than demo application.
Comment 3 arittner 2011-04-04 16:01:58 UTC
(In reply to comment #2)
> Unit test is better than demo application.

Sorry, I will make this the next time.
Comment 4 Jaroslav Tulach 2011-04-04 16:05:31 UTC
Created attachment 107480 [details]
Tests that try, but fail to simulate the problem
Comment 5 arittner 2011-04-05 07:56:07 UTC
Hi!

Cool, in the Dev-Builds the test works.

But with 6.9.1 (I've downloaded the release691_fixes_base) the test fails with an assertion error. 

Testcase: testMultiFileSystemWithOverridenAttributes(org.openide.filesystems.XMLFileSystemTestHid):	FAILED
should be 200 still expected:<200> but was:<100>
junit.framework.AssertionFailedError: should be 200 still expected:<200> but was:<100>
	at org.openide.filesystems.XMLFileSystemTestHid.testMultiFileSystemWithOverridenAttributes(XMLFileSystemTestHid.java:357)
	at org.netbeans.junit.NbTestCase.access$200(NbTestCase.java:95)
	at org.netbeans.junit.NbTestCase$2.doSomething(NbTestCase.java:365)
	at org.netbeans.junit.NbTestCase$1Guard.run(NbTestCase.java:294)
	at org.netbeans.junit.NbTestCase.runBare(NbTestCase.java:384)
	at org.netbeans.junit.NbTestCase.run(NbTestCase.java:228)
	at org.openide.filesystems.MultiThreadedTestCaseHid.run(MultiThreadedTestCaseHid.java:85)
	at junit.extensions.TestDecorator.basicRun(TestDecorator.java:24)
	at org.netbeans.junit.NbTestSetup$1.protect(NbTestSetup.java:64)
	at org.netbeans.junit.NbTestSetup$1.protect(NbTestSetup.java:64)
	at org.netbeans.junit.NbTestSetup.run(NbTestSetup.java:68)

(your patch can be imported into release691_fixes_base, only fix imports for List/ArrayList is needed)

However, I have no idea what was done in Dev, so it will work again. For a back-port it would be important to find it out.

br, josh.
Comment 6 Jaroslav Tulach 2011-04-05 16:46:06 UTC
OK, I integrated the test then: ergonomics#617757c16f7e
 can you verify the issue is fixed in trunk?
Comment 7 Quality Engineering 2011-04-07 08:47:24 UTC
Integrated into 'main-golden', will be available in build *201104070400* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress)
Changeset: http://hg.netbeans.org/main/rev/617757c16f7e
User: Jaroslav Tulach <jtulach@netbeans.org>
Log: #197308: Test that seem to work in 7.0 but were broken in 6.9