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.

View | Details | Raw Unified | Return to bug 170862
Collapse All | Expand All

(-)a/masterfs/manifest.mf (-1 / +1 lines)
Lines 1-7 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.modules.masterfs/2
2
OpenIDE-Module: org.netbeans.modules.masterfs/2
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/masterfs/resources/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/masterfs/resources/Bundle.properties
4
OpenIDE-Module-Specification-Version: 2.17
4
OpenIDE-Module-Specification-Version: 2.18
5
AutoUpdate-Show-In-Client: false
5
AutoUpdate-Show-In-Client: false
6
AutoUpdate-Essential-Module: true
6
AutoUpdate-Essential-Module: true
7
7
(-)a/masterfs/nbproject/project.xml (-1 / +1 lines)
Lines 60-66 Link Here
60
                    <build-prerequisite/>
60
                    <build-prerequisite/>
61
                    <compile-dependency/>
61
                    <compile-dependency/>
62
                    <run-dependency>
62
                    <run-dependency>
63
                        <specification-version>6.2</specification-version>
63
                        <specification-version>7.24</specification-version>
64
                    </run-dependency>
64
                    </run-dependency>
65
                </dependency>
65
                </dependency>
66
                <dependency>
66
                <dependency>
(-)a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/BaseFileObj.java (+16 lines)
Lines 203-208 Link Here
203
    public final boolean isRoot() {
203
    public final boolean isRoot() {
204
        return false;
204
        return false;
205
    }
205
    }
206
207
    public final java.util.Date lastModified() {
208
        final File f = getFileName().getFile();
209
        final long lastModified = f.lastModified();
210
        return new Date(lastModified);
211
    }
206
     
212
     
207
    @Override
213
    @Override
208
    public final FileObject move(FileLock lock, FileObject target, String name, String ext) throws IOException {
214
    public final FileObject move(FileLock lock, FileObject target, String name, String ext) throws IOException {
Lines 379-384 Link Here
379
        getEventSupport().remove(FileChangeListener.class, fcl);
385
        getEventSupport().remove(FileChangeListener.class, fcl);
380
    }
386
    }
381
387
388
    @Override
389
    public void addRecursiveListener(FileChangeListener fcl) {
390
        addFileChangeListener(fcl);
391
    }
392
    
393
    @Override
394
    public void removeRecursiveListener(FileChangeListener fcl) {
395
        removeFileChangeListener(fcl);
396
    }
397
382
    private Enumeration getListeners() {
398
    private Enumeration getListeners() {
383
        if (eventSupport == null) {
399
        if (eventSupport == null) {
384
            return Enumerations.empty();
400
            return Enumerations.empty();
(-)a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FileObj.java (-5 lines)
Lines 210-220 Link Here
210
        return super.canWrite();
210
        return super.canWrite();
211
    }
211
    }
212
        
212
        
213
    public final Date lastModified() {
214
        final File f = getFileName().getFile();
215
        return new Date(f.lastModified());
216
    }
217
218
    final void setLastModified(long lastModified) {
213
    final void setLastModified(long lastModified) {
219
        if (this.lastModified != 0) { // #130998 - don't set when already invalidated
214
        if (this.lastModified != 0) { // #130998 - don't set when already invalidated
220
            if (this.lastModified != -1 && !realLastModifiedCached) {
215
            if (this.lastModified != -1 && !realLastModifiedCached) {
(-)a9b41094a261 (+217 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 *
35
 * Contributor(s):
36
 *
37
 * Portions Copyrighted 2009 Sun Microsystems, Inc.
38
 */
39
40
package org.netbeans.modules.masterfs.filebasedfs.fileobjects;
41
42
import java.io.File;
43
import java.util.Enumeration;
44
import java.util.HashSet;
45
import java.util.List;
46
import java.util.Set;
47
import java.util.concurrent.CopyOnWriteArrayList;
48
import java.util.logging.Level;
49
import java.util.logging.Logger;
50
import org.netbeans.modules.masterfs.filebasedfs.fileobjects.FileObjectFactory.Caller;
51
import org.openide.filesystems.FileAttributeEvent;
52
import org.openide.filesystems.FileChangeListener;
53
import org.openide.filesystems.FileEvent;
54
import org.openide.filesystems.FileObject;
55
import org.openide.filesystems.FileRenameEvent;
56
57
/** Keeps list of fileobjects under given root. Adapted from Jan Lahoda's work
58
 * in issue 168237
59
 */
60
final class FileObjectKeeper implements FileChangeListener {
61
    private static final Logger LOG = Logger.getLogger(FileObjectKeeper.class.getName());
62
63
    private Set<FileObject> kept;
64
    private List<FileChangeListener> listeners;
65
    private final FolderObj root;
66
    private long timeStamp;
67
68
    public FileObjectKeeper(FolderObj root) {
69
        this.root = root;
70
    }
71
72
    public synchronized void addRecursiveListener(FileChangeListener fcl) {
73
        if (listeners == null) {
74
            listeners = new CopyOnWriteArrayList<FileChangeListener>();
75
            listenToAll();
76
        }
77
        listeners.add(fcl);
78
    }
79
80
    public synchronized void removeRecursiveListener(FileChangeListener fcl) {
81
        if (listeners == null) {
82
            return;
83
        }
84
        listeners.remove(fcl);
85
        if (listeners.isEmpty()) {
86
            listenNoMore();
87
        }
88
    }
89
90
    public void init(long previous, FileObjectFactory factory, boolean expected) {
91
        File file = root.getFileName().getFile();
92
        File[] arr = file.listFiles();
93
        long ts = 0;
94
        if (arr != null) {
95
            for (File f : arr) {
96
                if (f.isDirectory()) {
97
                    continue;
98
                }
99
                long lm = f.lastModified();
100
                LOG.log(Level.FINE, "  check {0} for {1}", new Object[] { lm, f });
101
                if (lm > ts) {
102
                    ts = lm;
103
                }
104
                if (lm > previous && factory != null) {
105
                    BaseFileObj who = factory.getValidFileObject(f, Caller.Others);
106
                    LOG.log(Level.FINE, "External change detected {0}", who);
107
                    who.fireFileChangedEvent(expected);
108
                }
109
            }
110
        }
111
        timeStamp = ts;
112
        LOG.log(Level.FINE, "Testing {0}, time {1}", new Object[] { file, timeStamp });
113
    }
114
115
    private final void listenToAll() {
116
        assert Thread.holdsLock(this);
117
        assert kept == null;
118
        kept = new HashSet<FileObject>();
119
        root.addFileChangeListener(this);
120
        Enumeration<? extends FileObject> en = root.getChildren(true);
121
        while (en.hasMoreElements()) {
122
            FileObject fo = en.nextElement();
123
            if (fo instanceof FolderObj) {
124
                FolderObj obj = (FolderObj)fo;
125
                obj.addFileChangeListener(this);
126
                kept.add(obj);
127
                obj.getKeeper();
128
            }
129
        }
130
    }
131
132
    private final void listenNoMore() {
133
        assert Thread.holdsLock(this);
134
        assert kept != null;
135
136
        root.removeFileChangeListener(this);
137
        for (FileObject fo : kept) {
138
            fo.removeFileChangeListener(this);
139
        }
140
        kept = null;
141
    }
142
143
    public void fileFolderCreated(FileEvent fe) {
144
        List<FileChangeListener> arr = listeners;
145
        if (arr == null) {
146
            return;
147
        }
148
        for (FileChangeListener l : arr) {
149
            l.fileFolderCreated(fe);
150
        }
151
        if (fe.getFile() instanceof FolderObj) {
152
            synchronized (this) {
153
                kept.add(fe.getFile());
154
            }
155
        }
156
    }
157
158
    public void fileDataCreated(FileEvent fe) {
159
        List<FileChangeListener> arr = listeners;
160
        if (arr == null) {
161
            return;
162
        }
163
        for (FileChangeListener l : arr) {
164
            l.fileDataCreated(fe);
165
        }
166
    }
167
168
    public void fileChanged(FileEvent fe) {
169
        List<FileChangeListener> arr = listeners;
170
        if (arr == null) {
171
            return;
172
        }
173
        for (FileChangeListener l : arr) {
174
            l.fileChanged(fe);
175
        }
176
    }
177
178
    public void fileDeleted(FileEvent fe) {
179
        List<FileChangeListener> arr = listeners;
180
        if (arr == null) {
181
            return;
182
        }
183
        for (FileChangeListener l : arr) {
184
            l.fileDeleted(fe);
185
        }
186
        if (fe.getFile() instanceof FolderObj) {
187
            synchronized (this) {
188
                kept.remove(fe.getFile());
189
            }
190
        }
191
    }
192
193
    public void fileRenamed(FileRenameEvent fe) {
194
        List<FileChangeListener> arr = listeners;
195
        if (arr == null) {
196
            return;
197
        }
198
        for (FileChangeListener l : arr) {
199
            l.fileRenamed(fe);
200
        }
201
    }
202
203
    public void fileAttributeChanged(FileAttributeEvent fe) {
204
        List<FileChangeListener> arr = listeners;
205
        if (arr == null) {
206
            return;
207
        }
208
        for (FileChangeListener l : arr) {
209
            l.fileAttributeChanged(fe);
210
        }
211
    }
212
213
    long childrenLastModified() {
214
        return timeStamp;
215
    }
216
217
}
(-)a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FolderObj.java (-5 / +30 lines)
Lines 49-54 Link Here
49
import java.io.SyncFailedException;
49
import java.io.SyncFailedException;
50
import java.util.ArrayList;
50
import java.util.ArrayList;
51
import java.util.Date;
51
import java.util.Date;
52
import java.util.Enumeration;
52
import java.util.HashSet;
53
import java.util.HashSet;
53
import java.util.Iterator;
54
import java.util.Iterator;
54
import java.util.LinkedList;
55
import java.util.LinkedList;
Lines 62-67 Link Here
62
import org.netbeans.modules.masterfs.filebasedfs.FileBasedFileSystem.FSCallable;
63
import org.netbeans.modules.masterfs.filebasedfs.FileBasedFileSystem.FSCallable;
63
import org.netbeans.modules.masterfs.filebasedfs.children.ChildrenCache;
64
import org.netbeans.modules.masterfs.filebasedfs.children.ChildrenCache;
64
import org.netbeans.modules.masterfs.filebasedfs.children.ChildrenSupport;
65
import org.netbeans.modules.masterfs.filebasedfs.children.ChildrenSupport;
66
import org.netbeans.modules.masterfs.filebasedfs.fileobjects.FileObjectFactory.Caller;
65
import org.netbeans.modules.masterfs.filebasedfs.naming.FileName;
67
import org.netbeans.modules.masterfs.filebasedfs.naming.FileName;
66
import org.netbeans.modules.masterfs.filebasedfs.naming.FileNaming;
68
import org.netbeans.modules.masterfs.filebasedfs.naming.FileNaming;
67
import org.netbeans.modules.masterfs.filebasedfs.naming.NamingFactory;
69
import org.netbeans.modules.masterfs.filebasedfs.naming.NamingFactory;
Lines 69-74 Link Here
69
import org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager;
71
import org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager;
70
import org.netbeans.modules.masterfs.filebasedfs.utils.FileInfo;
72
import org.netbeans.modules.masterfs.filebasedfs.utils.FileInfo;
71
import org.netbeans.modules.masterfs.providers.ProvidedExtensions;
73
import org.netbeans.modules.masterfs.providers.ProvidedExtensions;
74
import org.openide.filesystems.FileChangeListener;
72
import org.openide.filesystems.FileLock;
75
import org.openide.filesystems.FileLock;
73
import org.openide.filesystems.FileObject;
76
import org.openide.filesystems.FileObject;
74
import org.openide.util.Mutex;
77
import org.openide.util.Mutex;
Lines 82-88 Link Here
82
    private static final Mutex mutex = new Mutex(FolderObj.mp);
85
    private static final Mutex mutex = new Mutex(FolderObj.mp);
83
86
84
    private FolderChildrenCache folderChildren;
87
    private FolderChildrenCache folderChildren;
85
    boolean valid = true;    
88
    boolean valid = true;
89
    private FileObjectKeeper keeper;
86
90
87
    /**
91
    /**
88
     * Creates a new instance of FolderImpl
92
     * Creates a new instance of FolderImpl
Lines 333-338 Link Here
333
    public void refreshImpl(final boolean expected, boolean fire) {
337
    public void refreshImpl(final boolean expected, boolean fire) {
334
        final ChildrenCache cache = getChildrenCache();
338
        final ChildrenCache cache = getChildrenCache();
335
        final Mutex.Privileged mutexPrivileged = cache.getMutexPrivileged();
339
        final Mutex.Privileged mutexPrivileged = cache.getMutexPrivileged();
340
        final long previous = keeper == null ? -1 : keeper.childrenLastModified();
336
341
337
        Set oldChildren = null;
342
        Set oldChildren = null;
338
        Map refreshResult = null;
343
        Map refreshResult = null;
Lines 412-417 Link Here
412
                fireFileDeletedEvent(expected);
417
                fireFileDeletedEvent(expected);
413
            }
418
            }
414
        }
419
        }
420
421
        if (previous != -1) {
422
            assert keeper != null;
423
            keeper.init(previous, factory, expected);
424
        }
415
    }
425
    }
416
426
417
    @Override
427
    @Override
Lines 487-496 Link Here
487
        throw new IOException(getPath());
497
        throw new IOException(getPath());
488
    }
498
    }
489
499
490
    public final java.util.Date lastModified() {
491
        final File f = getFileName().getFile();
492
        return new Date(f.lastModified());
493
    }
494
500
495
    public final FileLock lock() throws IOException {
501
    public final FileLock lock() throws IOException {
496
        return new FileLock();
502
        return new FileLock();
Lines 508-513 Link Here
508
        return folderChildren;
514
        return folderChildren;
509
    }
515
    }
510
516
517
    synchronized FileObjectKeeper getKeeper() {
518
        if (keeper == null) {
519
            keeper = new FileObjectKeeper(this);
520
            keeper.init(-1, null, false);
521
        }
522
        return keeper;
523
    }
524
525
    @Override
526
    public final void addRecursiveListener(FileChangeListener fcl) {
527
        getKeeper().addRecursiveListener(fcl);
528
    }
529
530
    @Override
531
    public final void removeRecursiveListener(FileChangeListener fcl) {
532
        getKeeper().removeRecursiveListener(fcl);
533
    }
534
535
511
    public final class FolderChildrenCache implements ChildrenCache {
536
    public final class FolderChildrenCache implements ChildrenCache {
512
        public final ChildrenSupport ch = new ChildrenSupport();
537
        public final ChildrenSupport ch = new ChildrenSupport();
513
538
(-)a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/ReplaceForSerialization.java (-4 lines)
Lines 91-100 Link Here
91
            return false;
91
            return false;
92
        }
92
        }
93
93
94
        public Date lastModified() {
95
            return new Date(0L);
96
        }
97
98
        /* Test whether the file is valid. The file can be invalid if it has been deserialized
94
        /* Test whether the file is valid. The file can be invalid if it has been deserialized
99
        * and the file no longer exists on disk; or if the file has been deleted.
95
        * and the file no longer exists on disk; or if the file has been deleted.
100
        *
96
        *
(-)a9b41094a261 (+318 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * Contributor(s):
25
 *
26
 * The Original Software is NetBeans. The Initial Developer of the Original
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2009 Sun
28
 * Microsystems, Inc. All Rights Reserved.
29
 *
30
 * If you wish your version of this file to be governed by only the CDDL
31
 * or only the GPL Version 2, indicate your decision by adding
32
 * "[Contributor] elects to include this software in this distribution
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
34
 * single choice of license, a recipient has the option to distribute
35
 * your version of this file under either the CDDL, the GPL Version 2 or
36
 * to extend the choice of license to its licensees as provided above.
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
38
 * Version 2 license, then the option applies only if the new code is
39
 * made subject to such option by the copyright holder.
40
 */
41
package org.netbeans.modules.masterfs.filebasedfs.fileobjects;
42
43
import java.io.File;
44
import java.io.FileOutputStream;
45
import java.io.OutputStream;
46
import java.lang.ref.Reference;
47
import java.lang.ref.WeakReference;
48
import java.util.logging.Level;
49
import java.util.logging.Logger;
50
import org.netbeans.junit.NbTestCase;
51
import org.openide.filesystems.FileAttributeEvent;
52
import org.openide.filesystems.FileChangeAdapter;
53
import org.openide.filesystems.FileChangeListener;
54
import org.openide.filesystems.FileEvent;
55
import org.openide.filesystems.FileObject;
56
import org.openide.filesystems.FileRenameEvent;
57
import org.openide.filesystems.FileUtil;
58
59
public class ExternalTouchTest extends NbTestCase {
60
    private Logger LOG;
61
    private FileObject testFolder;
62
63
    public ExternalTouchTest(String testName) {
64
        super(testName);
65
    }
66
67
    @Override
68
    protected Level logLevel() {
69
        return Level.FINE;
70
    }
71
    
72
    @Override
73
    protected void setUp() throws Exception {
74
        clearWorkDir();
75
        
76
        LOG = Logger.getLogger("test." + getName());
77
        Logger.getLogger("org.openide.util.Mutex").setUseParentHandlers(false);
78
79
        File dir = new File(getWorkDir(), "test");
80
        dir.mkdirs();
81
        testFolder = FileUtil.toFileObject(dir);
82
        assertNotNull("Test folder created", testFolder);
83
84
    }
85
86
    public void testChangeInChildrenNoticed() throws Exception {
87
        long lm = System.currentTimeMillis();
88
        FileObject fileObject1 = testFolder.createData("fileObject1");
89
        assertNotNull("Just to initialize the stamp", lm);
90
        FileObject[] arr = testFolder.getChildren();
91
        assertEquals("One child", 1, arr.length);
92
        assertEquals("Right child", fileObject1, arr[0]);
93
94
        File file = FileUtil.toFile(fileObject1);
95
        assertNotNull("File found", file);
96
        Reference<FileObject> ref = new WeakReference<FileObject>(fileObject1);
97
        arr = null;
98
        fileObject1 = null;
99
        assertGC("File Object can disappear", ref);
100
101
        Thread.sleep(1000);
102
        
103
        class L extends FileChangeAdapter {
104
            int cnt;
105
            FileEvent event;
106
            
107
            @Override
108
            public void fileChanged(FileEvent fe) {
109
                LOG.info("file change " + fe.getFile());
110
                cnt++;
111
                event = fe;
112
            }
113
        }
114
        L listener = new L();
115
        testFolder.addRecursiveListener(listener);
116
117
        FileOutputStream os = new FileOutputStream(file);
118
        os.write(10);
119
        os.close();
120
121
        if (lm > file.lastModified() - 50) {
122
            fail("New modification time shall be at last 50ms after the original one: " + (file.lastModified() - lm));
123
        }
124
125
        testFolder.refresh();
126
127
        assertEquals("Change notified", 1, listener.cnt);
128
        assertEquals("Right file", file, FileUtil.toFile(listener.event.getFile()));
129
        assertEquals("Right source", file.getParentFile(), FileUtil.toFile((FileObject)listener.event.getSource()));
130
    }
131
    public void testNewChildNoticed() throws Exception {
132
        FileObject fileObject1 = testFolder.createData("fileObject1");
133
        FileObject[] arr = testFolder.getChildren();
134
        assertEquals("One child", 1, arr.length);
135
        assertEquals("Right child", fileObject1, arr[0]);
136
137
        File file = FileUtil.toFile(fileObject1);
138
        assertNotNull("File found", file);
139
        arr = null;
140
        fileObject1 = null;
141
        Reference<FileObject> ref = new WeakReference<FileObject>(fileObject1);
142
        assertGC("File Object can disappear", ref);
143
144
        Thread.sleep(100);
145
146
        class L extends FileChangeAdapter {
147
            int cnt;
148
            FileEvent event;
149
150
            @Override
151
            public void fileDataCreated(FileEvent fe) {
152
                cnt++;
153
                event = fe;
154
            }
155
156
        }
157
        L listener = new L();
158
        testFolder.addRecursiveListener(listener);
159
160
        File nfile = new File(file.getParentFile(), "new.txt");
161
        nfile.createNewFile();
162
163
        testFolder.refresh();
164
165
        assertEquals("Change notified", 1, listener.cnt);
166
        assertEquals("Right file", nfile, FileUtil.toFile(listener.event.getFile()));
167
    }
168
    public void testDeleteOfAChildNoticed() throws Exception {
169
        FileObject fileObject1 = testFolder.createData("fileObject1");
170
        FileObject[] arr = testFolder.getChildren();
171
        assertEquals("One child", 1, arr.length);
172
        assertEquals("Right child", fileObject1, arr[0]);
173
174
        File file = FileUtil.toFile(fileObject1);
175
        assertNotNull("File found", file);
176
        arr = null;
177
        fileObject1 = null;
178
        Reference<FileObject> ref = new WeakReference<FileObject>(fileObject1);
179
        assertGC("File Object can disappear", ref);
180
181
        Thread.sleep(100);
182
183
        class L extends FileChangeAdapter {
184
            int cnt;
185
            FileEvent event;
186
187
            @Override
188
            public void fileDeleted(FileEvent fe) {
189
                cnt++;
190
                event = fe;
191
            }
192
193
        }
194
        L listener = new L();
195
        testFolder.addRecursiveListener(listener);
196
197
        file.delete();
198
199
        testFolder.refresh();
200
201
        assertEquals("Change notified", 1, listener.cnt);
202
        assertEquals("Right file", file, FileUtil.toFile(listener.event.getFile()));
203
    }
204
205
    public void testRecursiveListener() throws Exception {
206
        FileObject obj = FileUtil.createData(testFolder, "my/sub/children/children.java");
207
        FileObject sub = obj.getParent().getParent();
208
209
        class L implements FileChangeListener {
210
            StringBuilder sb = new StringBuilder();
211
212
            public void fileFolderCreated(FileEvent fe) {
213
                LOG.info("FolderCreated: " + fe.getFile());
214
                sb.append("FolderCreated");
215
            }
216
217
            public void fileDataCreated(FileEvent fe) {
218
                LOG.info("DataCreated: " + fe.getFile());
219
                sb.append("DataCreated");
220
            }
221
222
            public void fileChanged(FileEvent fe) {
223
                LOG.info("Changed: " + fe.getFile());
224
                sb.append("Changed");
225
            }
226
227
            public void fileDeleted(FileEvent fe) {
228
                LOG.info("Deleted: " + fe.getFile());
229
                sb.append("Deleted");
230
            }
231
232
            public void fileRenamed(FileRenameEvent fe) {
233
                LOG.info("Renamed: " + fe.getFile());
234
                sb.append("Renamed");
235
            }
236
237
            public void fileAttributeChanged(FileAttributeEvent fe) {
238
                if (fe.getName().startsWith("DataEditorSupport.read-only.refresh")) {
239
                    return;
240
                }
241
                LOG.info("AttributeChanged: " + fe.getFile());
242
                sb.append("AttributeChanged");
243
            }
244
245
            public void assertMessages(String txt, String msg) {
246
                assertEquals(txt, msg, sb.toString());
247
                sb.setLength(0);
248
            }
249
        }
250
        L recursive = new L();
251
        L flat = new L();
252
253
        sub.addFileChangeListener(flat);
254
        LOG.info("Adding listener");
255
        sub.addRecursiveListener(recursive);
256
        LOG.info("Adding listener finished");
257
258
        Thread.sleep(1000);
259
260
        File fo = new File(FileUtil.toFile(obj.getParent()), "sibling.java");
261
        fo.createNewFile();
262
        LOG.info("sibling created, now refresh");
263
        FileUtil.refreshAll();
264
        LOG.info("sibling refresh finished");
265
266
// Right now it is DataCreatedChanged
267
//        recursive.assertMessages("Creation", "DataCreated");
268
        recursive.assertMessages("Creation", "DataCreatedChanged");
269
        flat.assertMessages("No messages in flat mode", "");
270
271
        Thread.sleep(1000);
272
273
        final OutputStream os = new FileOutputStream(fo);
274
        os.write(10);
275
        os.close();
276
        LOG.info("Before refresh");
277
        FileUtil.refreshAll();
278
        LOG.info("After refresh");
279
280
        flat.assertMessages("No messages in flat mode", "");
281
// Two changes detected now:
282
//        recursive.assertMessages("written", "Changed");
283
        recursive.assertMessages("written", "ChangedChanged");
284
285
        fo.delete();
286
        FileUtil.refreshAll();
287
288
        flat.assertMessages("No messages in flat mode", "");
289
        recursive.assertMessages("gone", "Deleted");
290
291
        new File(FileUtil.toFile(sub), "testFolder").mkdirs();
292
        FileUtil.refreshAll();
293
294
        flat.assertMessages("Direct Folder notified", "FolderCreated");
295
        recursive.assertMessages("Direct Folder notified", "FolderCreated");
296
297
        new File(FileUtil.toFile(sub.getParent()), "unimportant.txt").createNewFile();
298
        FileUtil.refreshAll();
299
300
        flat.assertMessages("No messages in flat mode", "");
301
        recursive.assertMessages("No messages in recursive mode", "");
302
303
        sub.removeRecursiveListener(recursive);
304
305
        new File(FileUtil.toFile(sub), "test.data").createNewFile();
306
        FileUtil.refreshAll();
307
308
// Double notification right now:
309
//        flat.assertMessages("Direct file notified", "DataCreated");
310
        flat.assertMessages("Direct file notified", "DataCreatedChanged");
311
        recursive.assertMessages("No longer active", "");
312
313
        WeakReference<L> ref = new WeakReference<L>(recursive);
314
        recursive = null;
315
        assertGC("Listener can be GCed", ref);
316
    }
317
318
}
(-)a/openide.filesystems/apichanges.xml (+17 lines)
Lines 46-51 Link Here
46
        <apidef name="filesystems">Filesystems API</apidef>
46
        <apidef name="filesystems">Filesystems API</apidef>
47
    </apidefs>
47
    </apidefs>
48
    <changes>
48
    <changes>
49
         <change id="recursive-listener">
50
            <api name="filesystems"/>
51
            <summary>Support for recursive listeners</summary>
52
            <version major="7" minor="24"/>
53
            <date day="1" month="9" year="2009"/>
54
            <author login="jtulach"/>
55
            <compatibility addition="yes" binary="compatible" source="compatible" semantic="compatible" deprecation="no" deletion="no" modification="no"/>
56
            <description>
57
                <p>
58
                    One can register a recursive listener on a file object by
59
                    calling
60
                    <a href="@TOP@/org/openide/filesystems/FileObject.html#addRecursiveListener(org.openide.filesystems.FileChangeListener)">FileObject.addRecursiveListener(FileChangeListener)</a>.
61
                </p>
62
            </description>
63
            <class package="org.openide.filesystems" name="FileObject"/>
64
            <issue number="170862"/>
65
        </change>
49
         <change id="add-fallback-content-to-sfs">
66
         <change id="add-fallback-content-to-sfs">
50
            <api name="filesystems"/>
67
            <api name="filesystems"/>
51
            <summary>Allow modules to dynamically add/remove layer content</summary>
68
            <summary>Allow modules to dynamically add/remove layer content</summary>
(-)a/openide.filesystems/nbproject/project.properties (-1 / +1 lines)
Lines 44-47 Link Here
44
javadoc.main.page=org/openide/filesystems/doc-files/api.html
44
javadoc.main.page=org/openide/filesystems/doc-files/api.html
45
javadoc.arch=${basedir}/arch.xml
45
javadoc.arch=${basedir}/arch.xml
46
javadoc.apichanges=${basedir}/apichanges.xml
46
javadoc.apichanges=${basedir}/apichanges.xml
47
spec.version.base=7.23.0
47
spec.version.base=7.24.0
(-)a/openide.filesystems/src/org/openide/filesystems/ExternalUtil.java (-1 / +1 lines)
Lines 112-118 Link Here
112
        return orig;
112
        return orig;
113
    }
113
    }
114
114
115
    private static Logger LOG = Logger.getLogger("org.openide.filesystems"); // NOI18N
115
    final static Logger LOG = Logger.getLogger("org.openide.filesystems"); // NOI18N
116
    /** Logs a text.
116
    /** Logs a text.
117
     */
117
     */
118
    public static void log(String msg) {
118
    public static void log(String msg) {
(-)a/openide.filesystems/src/org/openide/filesystems/FileObject.java (+49 lines)
Lines 58-64 Link Here
58
import java.util.LinkedList;
58
import java.util.LinkedList;
59
import java.util.List;
59
import java.util.List;
60
import java.util.StringTokenizer;
60
import java.util.StringTokenizer;
61
import java.util.logging.Level;
61
import org.openide.util.Enumerations;
62
import org.openide.util.Enumerations;
63
import org.openide.util.Exceptions;
62
import org.openide.util.NbBundle;
64
import org.openide.util.NbBundle;
63
import org.openide.util.UserQuestionException;
65
import org.openide.util.UserQuestionException;
64
66
Lines 413-418 Link Here
413
    */
415
    */
414
    public abstract void removeFileChangeListener(FileChangeListener fcl);
416
    public abstract void removeFileChangeListener(FileChangeListener fcl);
415
417
418
419
    /** Adds a listener to this {@link FileObject} and all its children and
420
     * children or its children.
421
     * It is guaranteed that whenever a change
422
     * is made via the FileSystem API itself under this {@link FileObject}
423
     * that it is notified to the <code>fcl</code> listener. Whether external
424
     * changes (if they make sense) are detected and
425
     * notified depends on actual implementation. As some implementations may
426
     * need to perform non-trivial amount of work during initialization of
427
     * listeners, this methods can take long time. Usage of this method may
428
     * consume a lot of system resources and as such it shall be used with care.
429
     * Traditional {@link #addFileChangeListener(org.openide.filesystems.FileChangeListener)}
430
     * is definitely preferred variant.
431
     * <p class="nonnormative">
432
     * If you are running with the MasterFS module enabled, it guarantees
433
     * that for files backed with real {@link File}, the system initializes
434
     * itself to detect external changes on the whole subtree.
435
     * This requires non-trivial amount of work and especially on slow
436
     * disks (aka networks ones) may take a long time to add the listener
437
     * and also refresh the system when {@link FileObject#refresh()}
438
     * and especially {@link FileUtil#refreshAll()} is requested.
439
     * </p>
440
     *
441
     * @param fcl the listener to register
442
     * @since 7.24
443
     */
444
    public void addRecursiveListener(FileChangeListener fcl) {
445
        try {
446
            getFileSystem().addFileChangeListener(new RecursiveListener(this, fcl));
447
        } catch (FileStateInvalidException ex) {
448
            ExternalUtil.LOG.log(Level.FINE, "Cannot remove listener from " + this, ex);
449
        }
450
    }
451
452
    /** Removes listener previously added by {@link #addRecursiveListener(org.openide.filesystems.FileChangeListener)}
453
     *
454
     * @param fcl the listener to remove
455
     * @since 7.24
456
     */
457
    public void removeRecursiveListener(FileChangeListener fcl) {
458
        try {
459
            getFileSystem().removeFileChangeListener(new RecursiveListener(this, fcl));
460
        } catch (FileStateInvalidException ex) {
461
            ExternalUtil.LOG.log(Level.FINE, "Cannot remove listener from " + this, ex);
462
        }
463
    }
464
416
    /** Fire data creation event.
465
    /** Fire data creation event.
417
    * @param en listeners that should receive the event
466
    * @param en listeners that should receive the event
418
    * @param fe the event to fire in this object
467
    * @param fe the event to fire in this object
(-)a9b41094a261 (+127 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 *
35
 * Contributor(s):
36
 *
37
 * Portions Copyrighted 2009 Sun Microsystems, Inc.
38
 */
39
40
package org.openide.filesystems;
41
42
import java.lang.ref.WeakReference;
43
44
/**
45
 *
46
 * @author Jaroslav Tulach <jtulach@netbeans.org>
47
 */
48
final class RecursiveListener extends WeakReference<FileObject>
49
implements FileChangeListener {
50
    private final FileChangeListener fcl;
51
52
    @Override
53
    public boolean equals(Object obj) {
54
        if (obj == null) {
55
            return false;
56
        }
57
        if (getClass() != obj.getClass()) {
58
            return false;
59
        }
60
        final RecursiveListener other = (RecursiveListener) obj;
61
        if (this.fcl != other.fcl && (this.fcl == null || !this.fcl.equals(other.fcl))) {
62
            return false;
63
        }
64
        final FileObject otherFo = other.get();
65
        final FileObject thisFo = this.get();
66
        if (thisFo != otherFo && (thisFo == null || !thisFo.equals(otherFo))) {
67
            return false;
68
        }
69
        return true;
70
    }
71
72
    @Override
73
    public int hashCode() {
74
        final FileObject thisFo = this.get();
75
        int hash = 3;
76
        hash = 37 * hash + (this.fcl != null ? this.fcl.hashCode() : 0);
77
        hash = 13 * hash + (thisFo != null ? thisFo.hashCode() : 0);
78
        return hash;
79
    }
80
81
    public RecursiveListener(FileObject source, FileChangeListener fcl) {
82
        super(source);
83
        this.fcl = fcl;
84
    }
85
86
    public void fileRenamed(FileRenameEvent fe) {
87
        FileObject thisFo = this.get();
88
        if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
89
            fcl.fileRenamed(fe);
90
        }
91
    }
92
93
    public void fileFolderCreated(FileEvent fe) {
94
        FileObject thisFo = this.get();
95
        if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
96
            fcl.fileFolderCreated(fe);
97
        }
98
    }
99
100
    public void fileDeleted(FileEvent fe) {
101
        FileObject thisFo = this.get();
102
        if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
103
            fcl.fileDeleted(fe);
104
        }
105
    }
106
107
    public void fileDataCreated(FileEvent fe) {
108
        FileObject thisFo = this.get();
109
        if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
110
            fcl.fileDataCreated(fe);
111
        }
112
    }
113
114
    public void fileChanged(FileEvent fe) {
115
        FileObject thisFo = this.get();
116
        if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
117
            fcl.fileChanged(fe);
118
        }
119
    }
120
121
    public void fileAttributeChanged(FileAttributeEvent fe) {
122
        FileObject thisFo = this.get();
123
        if (thisFo != null && FileUtil.isParentOf(thisFo, fe.getFile())) {
124
            fcl.fileAttributeChanged(fe);
125
        }
126
    }
127
}
(-)a/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java (+98 lines)
Lines 1569-1574 Link Here
1569
            assertNotNull(child);        
1569
            assertNotNull(child);        
1570
        }
1570
        }
1571
    }
1571
    }
1572
1573
    public void testRecursiveListener() throws IOException {
1574
        checkSetUp();
1575
1576
        FileObject folder1 = getTestFolder1 (root);
1577
        /** delete first time*/
1578
        try {
1579
            FileObject obj = FileUtil.createData(folder1, "my/sub/children/children.java");
1580
            FileObject sub = obj.getParent().getParent();
1581
1582
            class L implements FileChangeListener {
1583
                StringBuilder sb = new StringBuilder();
1584
1585
                public void fileFolderCreated(FileEvent fe) {
1586
                    sb.append("FolderCreated");
1587
                }
1588
1589
                public void fileDataCreated(FileEvent fe) {
1590
                    sb.append("DataCreated");
1591
                }
1592
1593
                public void fileChanged(FileEvent fe) {
1594
                    sb.append("Changed");
1595
                }
1596
1597
                public void fileDeleted(FileEvent fe) {
1598
                    sb.append("Deleted");
1599
                }
1600
1601
                public void fileRenamed(FileRenameEvent fe) {
1602
                    sb.append("Renamed");
1603
                }
1604
1605
                public void fileAttributeChanged(FileAttributeEvent fe) {
1606
                    sb.append("AttributeChanged");
1607
                }
1608
1609
                public void assertMessages(String txt, String msg) {
1610
                    assertEquals(txt, msg, sb.toString());
1611
                    sb.setLength(0);
1612
                }
1613
            }
1614
            L recursive = new L();
1615
            L flat = new L();
1616
1617
            sub.addFileChangeListener(flat);
1618
            sub.addRecursiveListener(recursive);
1619
1620
            FileObject fo = obj.getParent().createData("sibling.java");
1621
1622
            flat.assertMessages("No messages in flat mode", "");
1623
            recursive.assertMessages("Creation", "DataCreated");
1624
1625
            fo.setAttribute("jarda", "hello");
1626
1627
            flat.assertMessages("No messages in flat mode", "");
1628
            recursive.assertMessages("attr", "AttributeChanged");
1629
1630
            final OutputStream os = fo.getOutputStream();
1631
            os.write(10);
1632
            os.close();
1633
1634
            flat.assertMessages("No messages in flat mode", "");
1635
            recursive.assertMessages("written", "Changed");
1636
1637
            fo.delete();
1638
1639
            flat.assertMessages("No messages in flat mode", "");
1640
            recursive.assertMessages("gone", "Deleted");
1641
1642
            sub.createFolder("testFolder");
1643
1644
            flat.assertMessages("Direct Folder notified", "FolderCreated");
1645
            recursive.assertMessages("Direct Folder notified", "FolderCreated");
1646
1647
            sub.getParent().createData("unimportant.txt");
1648
1649
            flat.assertMessages("No messages in flat mode", "");
1650
            recursive.assertMessages("No messages in recursive mode", "");
1651
1652
            sub.removeRecursiveListener(recursive);
1653
1654
            sub.createData("test.data");
1655
1656
            flat.assertMessages("Direct file notified", "DataCreated");
1657
            recursive.assertMessages("No longer active", "");
1658
1659
            WeakReference<L> ref = new WeakReference<L>(recursive);
1660
            recursive = null;
1661
            assertGC("Listener can be GCed", ref);
1662
1663
        } catch (IOException iex) {
1664
            if (fs.isReadOnly() || root.isReadOnly()) return;
1665
            throw iex;
1666
        } finally {
1667
        }
1668
    }
1669
1572
    
1670
    
1573
    /** Test of delete method, of class org.openide.filesystems.FileObject. */    
1671
    /** Test of delete method, of class org.openide.filesystems.FileObject. */    
1574
    public void testCreateDeleteFolderCreate () throws IOException {
1672
    public void testCreateDeleteFolderCreate () throws IOException {

Return to bug 170862