This is an automated email from the ASF dual-hosted git repository.

ecki pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-vfs.git


The following commit(s) were added to refs/heads/master by this push:
     new 709d099c [VFS-853] Fix FileListener on DelegateFileObject.
709d099c is described below

commit 709d099ce6bb98f1c781e18c9e211304188ce260
Author: Bernd Eckenfels <e...@apache.org>
AuthorDate: Mon May 27 17:25:57 2024 +0200

    [VFS-853] Fix FileListener on DelegateFileObject.
    
    This adds a new test JunctionTests#testEvent() which fails when
    the fix is not present.
---
 .../commons/vfs2/util/WeakRefFileListener.java     | 15 ++++-
 .../commons/vfs2/provider/test/JunctionTests.java  | 77 ++++++++++++++++++++++
 src/changes/changes.xml                            |  3 +
 3 files changed, 93 insertions(+), 2 deletions(-)

diff --git 
a/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/WeakRefFileListener.java
 
b/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/WeakRefFileListener.java
index 99e2303c..05c989f1 100644
--- 
a/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/WeakRefFileListener.java
+++ 
b/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/WeakRefFileListener.java
@@ -32,13 +32,24 @@ import org.apache.commons.vfs2.FileSystem;
 public class WeakRefFileListener implements FileListener {
 
     /**
-     * Installs the {@code listener} at the given {@code file}.
+     * Install the {@code listener} at the given {@code file}.
+     * <p>
+     * This installs a wrapper with a weak reference, so the listener can
+     * be collected. The reference to the listener is removed when the
+     * first event can't be delivered.
+     * <p>
+     * Warning: you cannot remove the listener with
+     * {@code fs.removeListener(file, listener)} as you do'nt have the wrapper
+     * instance at hand.
+     * <p>
+     * Method is used by {@link 
org.apache.commons.vfs2.provider.DelegateFileObject},
+     * as used for {@link org.apache.commons.vfs2.impl.VirtualFileSystem}.
      *
      * @param file The FileObject to listen on.
      * @param listener The FileListener
      */
     public static void installListener(final FileObject file, final 
FileListener listener) {
-        file.getFileSystem().addListener(file, new WeakRefFileListener(file, 
new WeakRefFileListener(file, listener)));
+        file.getFileSystem().addListener(file, new WeakRefFileListener(file, 
listener));
     }
 
     private final FileSystem fs;
diff --git 
a/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/test/JunctionTests.java
 
b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/test/JunctionTests.java
index 41bc76b9..9099b67c 100644
--- 
a/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/test/JunctionTests.java
+++ 
b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/test/JunctionTests.java
@@ -22,9 +22,13 @@ import static 
org.apache.commons.vfs2.VfsTestUtils.getTestDirectoryFile;
 import java.io.File;
 
 import org.apache.commons.vfs2.AbstractProviderTestCase;
+import org.apache.commons.vfs2.FileChangeEvent;
+import org.apache.commons.vfs2.FileListener;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.commons.vfs2.FileSystem;
 import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.provider.DelegateFileObject;
+import org.apache.commons.vfs2.util.WeakRefFileListener;
 import org.junit.Test;
 
 /**
@@ -92,6 +96,52 @@ public class JunctionTests extends AbstractProviderTestCase {
         }
     }
 
+    /**
+     * Checks that change events from delegatedd files are fired.
+     */
+    public void testEvent() throws Exception {
+        // we use the VirtualFileSystem to check change event propagation of 
DecoratedFileObject
+        final FileSystem fs = 
getManager().createVirtualFileSystem("vfs://").getFileSystem();
+        final FileObject baseDir = getBaseDir().resolveFile("junctiontest");
+
+        // Add the junction
+        fs.addJunction("/a", baseDir);
+
+        // Make sure the file at the junction point and its ancestors exist
+        FileObject file = fs.resolveFile("/a/hardref.txt");
+        assertSame("VirtualFileSystem does not use DelegateFO anymore?", 
file.getClass(), DelegateFileObject.class);
+
+        // Do with a hard reference listener
+        FileListener listener1 = new DebugFileListener();
+        file.getFileSystem().addListener(file, listener1);
+        FileObject real1 = baseDir.resolveFile("hardref.txt");
+        real1.createFile();
+        assertEquals("Strong Listener was not notified (create)", "Listener 
false true false", listener1.toString());
+        real1.delete();
+        assertEquals("Strong Listener was not notified (delete)", "Listener 
false true true", listener1.toString());
+
+        FileObject file2 = fs.resolveFile("/a/weakref.txt");
+        assertSame("VirtualFileSystem does not use DelegateFO anymore?", 
file2.getClass(), DelegateFileObject.class);
+        // repeat with Weak reference listener
+        FileListener listener2 = new DebugFileListener();
+        // since we hold the listener2 reference it should not get GC
+        WeakRefFileListener.installListener(file2, listener2);
+
+        // force the WekRefFileListener reference to (not) clear
+        System.gc();
+        Thread.sleep(1000);
+        System.gc();
+        Thread.sleep(1000);
+        System.gc();
+        Thread.sleep(1000);
+        System.gc();
+        Thread.sleep(1000);
+
+        FileObject real2 = baseDir.resolveFile("weakref.txt");
+        real2.createFile();
+        assertEquals("Weak Listener was abandoned", "Listener false true 
false", listener2.toString());
+    }
+
     // Check that file @ junction point exists only when backing file exists
     // Add 2 junctions with common parent
     // Compare real and virtual files
@@ -99,3 +149,30 @@ public class JunctionTests extends AbstractProviderTestCase 
{
     // Remove junctions
 
 }
+
+class DebugFileListener implements FileListener {
+    private boolean changed;
+    private boolean created;
+    private boolean deleted;
+
+    @Override
+    public void fileChanged(FileChangeEvent event) throws Exception {
+        changed = true;
+    }
+
+    @Override
+    public void fileCreated(FileChangeEvent event) throws Exception {
+        created = true;
+    }
+
+    @Override
+    public void fileDeleted(FileChangeEvent event) throws Exception {
+        deleted = true;
+    }
+    
+    @Override
+    public String toString() {
+        return "Listener " + changed + " " + created + " " + deleted;
+    }
+} // class DebugListener
+
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 6e529c61..79932deb 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -47,6 +47,9 @@ The <action> type attribute can be add,update,fix,remove.
   <body>
     <release version="2.10.0" date="YYYY-MM-DD" description="Maintenance 
release. Requires Java 8 or above.">
       <!-- FIX -->
+      <action type="fix" issue="VFS-853" dev="ecki" due-to="Bernd Eckenfels">
+        Double wrapped weak FileListener can lose change notification with 
DelegateFileObject.
+      </action>
       <action type="fix" dev="ggregory" due-to="Seth Falco">
         Replace package.html with package-info.java #206. 
       </action>

Reply via email to