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 181684
Collapse All | Expand All

(-)a/masterfs/apichanges.xml (+17 lines)
Lines 46-51 Link Here
46
        <apidef name="masterfs">MasterFileSystem API</apidef>
46
        <apidef name="masterfs">MasterFileSystem API</apidef>
47
    </apidefs>
47
    </apidefs>
48
    <changes>
48
    <changes>
49
        <change id="recursive-control">
50
            <api name="masterfs"/>
51
            <summary><code>ProvidedExtensions.refreshRecursively</code> was added.</summary>
52
            <version major="2" minor="23"/>
53
            <date day="14" month="4" year="2010"/>
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
                    <code>ProvidedExtensions.refreshRecursively</code> allows
59
                    various version control providers to get better control
60
                    on behavior of recursive listener.
61
                </p>
62
            </description>
63
            <class package="org.netbeans.modules.masterfs.providers" name="ProvidedExtensions"/>
64
            <issue number="181684"/>
65
        </change>
49
        <change id="added-canWrite-friend-contract-for-versioning-systems">
66
        <change id="added-canWrite-friend-contract-for-versioning-systems">
50
            <api name="masterfs"/>
67
            <api name="masterfs"/>
51
            <summary><code>ProvidedExtensions.canWrite</code> was added.</summary>
68
            <summary><code>ProvidedExtensions.canWrite</code> was added.</summary>
(-)a/masterfs/src/org/netbeans/modules/masterfs/ProvidedExtensionsProxy.java (+19 lines)
Lines 45-50 Link Here
45
import java.io.IOException;
45
import java.io.IOException;
46
import java.util.Collection;
46
import java.util.Collection;
47
import java.util.Iterator;
47
import java.util.Iterator;
48
import java.util.List;
48
import java.util.concurrent.atomic.AtomicReference;
49
import java.util.concurrent.atomic.AtomicReference;
49
import java.util.logging.Level;
50
import java.util.logging.Level;
50
import java.util.logging.Logger;
51
import java.util.logging.Logger;
Lines 268-273 Link Here
268
        }
269
        }
269
        return null;
270
        return null;
270
    }
271
    }
272
273
    @Override
274
    public long refreshRecursively(File dir, long lastTimeStamp, List<? super File> children) {
275
        for (Iterator it = annotationProviders.iterator(); it.hasNext();) {
276
            AnnotationProvider provider = (AnnotationProvider) it.next();
277
            final InterceptionListener iListener = (provider != null) ? provider.getInterceptionListener() : null;
278
            if (iListener instanceof ProvidedExtensions) {
279
                ProvidedExtensions pe = (ProvidedExtensions)iListener;
280
                int prev = children.size();
281
                long ret = pe.refreshRecursively(dir, lastTimeStamp, children);
282
                assert ret != -1 || prev == children.size() : "When returning -1 from refreshRecursively, you cannot modify children: " + pe;
283
                if (ret != -1) {
284
                    return ret;
285
                }
286
            }
287
        }
288
        return super.refreshRecursively(dir, lastTimeStamp, children);
289
    }
271
    
290
    
272
    public static void checkReentrancy() {
291
    public static void checkReentrancy() {
273
        if (reentrantCheck.get() != null) {            
292
        if (reentrantCheck.get() != null) {            
(-)a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FileObjectKeeper.java (-48 / +67 lines)
Lines 41-48 Link Here
41
41
42
import java.io.File;
42
import java.io.File;
43
import java.util.Collection;
43
import java.util.Collection;
44
import java.util.Enumeration;
45
import java.util.HashSet;
44
import java.util.HashSet;
45
import java.util.LinkedList;
46
import java.util.List;
46
import java.util.Set;
47
import java.util.Set;
47
import java.util.concurrent.Callable;
48
import java.util.concurrent.Callable;
48
import java.util.concurrent.CopyOnWriteArraySet;
49
import java.util.concurrent.CopyOnWriteArraySet;
Lines 61-67 Link Here
61
final class FileObjectKeeper implements FileChangeListener {
62
final class FileObjectKeeper implements FileChangeListener {
62
    private static final Logger LOG = Logger.getLogger(FileObjectKeeper.class.getName());
63
    private static final Logger LOG = Logger.getLogger(FileObjectKeeper.class.getName());
63
64
64
    private Set<FileObject> kept;
65
    private Set<FolderObj> kept;
65
    private Collection<FileChangeListener> listeners;
66
    private Collection<FileChangeListener> listeners;
66
    private final FolderObj root;
67
    private final FolderObj root;
67
    private long timeStamp;
68
    private long timeStamp;
Lines 94-140 Link Here
94
        }
95
        }
95
    }
96
    }
96
97
97
    public void init(long previous, FileObjectFactory factory, boolean expected) {
98
    public List<File> init(long previous, FileObjectFactory factory, boolean expected) {
98
        File file = root.getFileName().getFile();
99
        File file = root.getFileName().getFile();
99
        File[] arr = file.listFiles();
100
        LinkedList<File> arr = new LinkedList<File>();
100
        long ts = 0;
101
        long ts = root.getProvidedExtensions().refreshRecursively(file, previous, arr);
101
        if (arr != null) {
102
        for (File f : arr) {
102
            for (File f : arr) {
103
            if (f.isDirectory()) {
103
                if (f.isDirectory()) {
104
                continue;
104
                    continue;
105
            }
105
                }
106
            long lm = f.lastModified();
106
                long lm = f.lastModified();
107
            LOG.log(Level.FINE, "  check {0} for {1}", new Object[] { lm, f });
107
                LOG.log(Level.FINE, "  check {0} for {1}", new Object[] { lm, f });
108
            if (lm > ts) {
108
                if (lm > ts) {
109
                ts = lm;
109
                    ts = lm;
110
            }
110
                }
111
            if (lm > previous && factory != null) {
111
                if (lm > previous && factory != null) {
112
                final BaseFileObj prevFO = factory.getCachedOnly(f);
112
                    final BaseFileObj prevFO = factory.getCachedOnly(f);
113
                if (prevFO == null) {
113
                    if (prevFO == null) {
114
                    BaseFileObj who = factory.getValidFileObject(f, Caller.Others);
114
                        BaseFileObj who = factory.getValidFileObject(f, Caller.Others);
115
                    if (who != null) {
115
                        if (who != null) {
116
                        LOG.log(Level.FINE, "External change detected {0}", who);  //NOI18N
116
                            LOG.log(Level.FINE, "External change detected {0}", who);  //NOI18N
117
                        who.fireFileChangedEvent(expected);
117
                            who.fireFileChangedEvent(expected);
118
                        } else {
119
                            LOG.log(Level.FINE, "Cannot get valid FileObject. File probably removed: {0}", f);  //NOI18N
120
                        }
121
                    } else {
118
                    } else {
122
                        LOG.log(Level.FINE, "Do classical refresh for {0}", prevFO);  //NOI18N
119
                        LOG.log(Level.FINE, "Cannot get valid FileObject. File probably removed: {0}", f);  //NOI18N
123
                        prevFO.refresh(expected, true);
124
                    }
120
                    }
121
                } else {
122
                    LOG.log(Level.FINE, "Do classical refresh for {0}", prevFO);  //NOI18N
123
                    prevFO.refresh(expected, true);
125
                }
124
                }
126
            }
125
            }
127
        }
126
        }
128
        timeStamp = ts;
127
        timeStamp = ts;
129
        LOG.log(Level.FINE, "Testing {0}, time {1}", new Object[] { file, timeStamp });
128
        LOG.log(Level.FINE, "Testing {0}, time {1}", new Object[] { file, timeStamp });
129
        return arr;
130
    }
130
    }
131
131
132
    private void listenTo(FileObject fo, boolean add) {
132
    private void listenTo(FolderObj fo, boolean add, Collection<? super File> children) {
133
        if (add) {
133
        if (add) {
134
            fo.addFileChangeListener(this);
134
            fo.addFileChangeListener(this);
135
            if (fo instanceof FolderObj) {
135
            if (fo instanceof FolderObj) {
136
                FolderObj folder = (FolderObj)fo;
136
                FolderObj folder = (FolderObj)fo;
137
                folder.getKeeper();
137
                folder.getKeeper(children);
138
                folder.getChildren();
138
                kept.add(folder);
139
                kept.add(folder);
139
            }
140
            }
140
            LOG.log(Level.FINER, "Listening to {0}", fo);
141
            LOG.log(Level.FINER, "Listening to {0}", fo);
Lines 147-157 Link Here
147
    private void listenToAll(Callable<?> stop) {
148
    private void listenToAll(Callable<?> stop) {
148
        assert Thread.holdsLock(this);
149
        assert Thread.holdsLock(this);
149
        assert kept == null;
150
        assert kept == null;
150
        kept = new HashSet<FileObject>();
151
        kept = new HashSet<FolderObj>();
151
        listenTo(root, true);
152
        LinkedList<File> it = new LinkedList<File>();
152
        Enumeration<? extends FileObject> en = root.getChildren(true);
153
        listenTo(root, true, it);
153
        while (en.hasMoreElements()) {
154
        FileObjectFactory factory = null;
154
            FileObject fo = en.nextElement();
155
        for (;;) {
156
            File f = it.poll();
157
            if (f == null) {
158
                break;
159
            }
160
            if (factory == null) {
161
                factory = FileObjectFactory.getInstance(f);
162
            }
163
            FileObject fo = factory.getValidFileObject(f, Caller.Others);
155
            if (fo instanceof FolderObj) {
164
            if (fo instanceof FolderObj) {
156
                FolderObj obj = (FolderObj) fo;
165
                FolderObj obj = (FolderObj) fo;
157
                Object shallStop = null;
166
                Object shallStop = null;
Lines 166-172 Link Here
166
                    LOG.log(Level.INFO, "addRecursiveListener to {0} interrupted", root); // NOI18N
175
                    LOG.log(Level.INFO, "addRecursiveListener to {0} interrupted", root); // NOI18N
167
                    return;
176
                    return;
168
                }
177
                }
169
                listenTo(obj, true);
178
                listenTo(obj, true, it);
170
            }
179
            }
171
        }
180
        }
172
    }
181
    }
Lines 174-184 Link Here
174
    private void listenNoMore() {
183
    private void listenNoMore() {
175
        assert Thread.holdsLock(this);
184
        assert Thread.holdsLock(this);
176
185
177
        listenTo(root, false);
186
        listenTo(root, false, null);
178
        Set<FileObject> k = kept;
187
        Set<FolderObj> k = kept;
179
        if (k != null) {
188
        if (k != null) {
180
            for (FileObject fo : k) {
189
            for (FolderObj fo : k) {
181
                listenTo(fo, false);
190
                listenTo(fo, false, null);
182
            }
191
            }
183
            kept = null;
192
            kept = null;
184
        }
193
        }
Lines 187-201 Link Here
187
    @Override
196
    @Override
188
    public void fileFolderCreated(FileEvent fe) {
197
    public void fileFolderCreated(FileEvent fe) {
189
        Collection<FileChangeListener> arr = listeners;
198
        Collection<FileChangeListener> arr = listeners;
190
        final FileObject f = fe.getFile();
199
        final FileObject folder = fe.getFile();
191
        if (f instanceof FolderObj) {
200
        if (folder instanceof FolderObj) {
201
            FolderObj obj = (FolderObj)folder;
192
            synchronized (this) {
202
            synchronized (this) {
193
                listenTo(f, true);
203
                LinkedList<File> it = new LinkedList<File>();
194
                Enumeration<? extends FileObject> en = f.getChildren(true);
204
                listenTo(obj, true, it);
195
                while (en.hasMoreElements()) {
205
                FileObjectFactory factory = null;
196
                    FileObject fo = en.nextElement();
206
                for (;;) {
207
                    File f = it.poll();
208
                    if (f == null) {
209
                        break;
210
                    }
211
                    if (factory == null) {
212
                        factory = FileObjectFactory.getInstance(f);
213
                    }
214
                    FileObject fo = factory.getValidFileObject(f, Caller.Others);
197
                    if (fo instanceof FolderObj) {
215
                    if (fo instanceof FolderObj) {
198
                        listenTo(fo, true);
216
                        listenTo((FolderObj)fo, true, it);
199
                    }
217
                    }
200
                }
218
                }
201
            }
219
            }
Lines 240-250 Link Here
240
        }
258
        }
241
259
242
        if (f instanceof FolderObj) {
260
        if (f instanceof FolderObj) {
261
            FolderObj obj = (FolderObj)f;
243
            synchronized (this) {
262
            synchronized (this) {
244
                if (kept != null) {
263
                if (kept != null) {
245
                    kept.remove(f);
264
                    kept.remove(obj);
246
                }
265
                }
247
                listenTo(f, false);
266
                listenTo(obj, false, null);
248
            }
267
            }
249
        }
268
        }
250
        if (arr == null) {
269
        if (arr == null) {
(-)a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/fileobjects/FolderObj.java (-5 / +11 lines)
Lines 48-53 Link Here
48
import java.io.OutputStream;
48
import java.io.OutputStream;
49
import java.io.SyncFailedException;
49
import java.io.SyncFailedException;
50
import java.util.ArrayList;
50
import java.util.ArrayList;
51
import java.util.Collection;
51
import java.util.HashSet;
52
import java.util.HashSet;
52
import java.util.LinkedList;
53
import java.util.LinkedList;
53
import java.util.List;
54
import java.util.List;
Lines 70-76 Link Here
70
import org.openide.filesystems.FileChangeListener;
71
import org.openide.filesystems.FileChangeListener;
71
import org.openide.filesystems.FileLock;
72
import org.openide.filesystems.FileLock;
72
import org.openide.filesystems.FileObject;
73
import org.openide.filesystems.FileObject;
73
import org.openide.util.Exceptions;
74
import org.openide.util.Mutex;
74
import org.openide.util.Mutex;
75
75
76
/**
76
/**
Lines 520-541 Link Here
520
        return folderChildren;
520
        return folderChildren;
521
    }
521
    }
522
522
523
    synchronized FileObjectKeeper getKeeper() {
523
    synchronized FileObjectKeeper getKeeper(Collection<? super File> arr) {
524
        if (keeper == null) {
524
        if (keeper == null) {
525
            keeper = new FileObjectKeeper(this);
525
            keeper = new FileObjectKeeper(this);
526
            keeper.init(-1, null, false);
526
            List<File> ch = keeper.init(-1, null, false);
527
            if (arr != null) {
528
                arr.addAll(ch);
529
            }
530
        } else if (arr != null) {
531
            List<File> ch = keeper.init(keeper.childrenLastModified(), null, false);
532
            arr.addAll(ch);
527
        }
533
        }
528
        return keeper;
534
        return keeper;
529
    }
535
    }
530
536
531
    @Override
537
    @Override
532
    public final void addRecursiveListener(FileChangeListener fcl) {
538
    public final void addRecursiveListener(FileChangeListener fcl) {
533
        getKeeper().addRecursiveListener(fcl);
539
        getKeeper(null).addRecursiveListener(fcl);
534
    }
540
    }
535
541
536
    @Override
542
    @Override
537
    public final void removeRecursiveListener(FileChangeListener fcl) {
543
    public final void removeRecursiveListener(FileChangeListener fcl) {
538
        getKeeper().removeRecursiveListener(fcl);
544
        getKeeper(null).removeRecursiveListener(fcl);
539
    }
545
    }
540
546
541
547
(-)a/masterfs/src/org/netbeans/modules/masterfs/providers/ProvidedExtensions.java (+24 lines)
Lines 43-48 Link Here
43
43
44
import java.io.File;
44
import java.io.File;
45
import java.io.IOException;
45
import java.io.IOException;
46
import java.util.Arrays;
47
import java.util.List;
46
import org.openide.filesystems.FileObject;
48
import org.openide.filesystems.FileObject;
47
49
48
/**
50
/**
Lines 187-190 Link Here
187
    public Object getAttribute(File file, String attrName) {
189
    public Object getAttribute(File file, String attrName) {
188
        return null;
190
        return null;
189
    }
191
    }
192
193
    /** Allows versioning system to exclude some children from recursive
194
     * listening check. Also notifies the versioning whenever a refresh
195
     * is required and allows the versiniong to provide special timestamp
196
     * for a directory.
197
     *
198
     * @param dir the directory to check timestamp for
199
     * @param lastTimeStamp the previously known timestamp or -1
200
     * @param children add children that shall be interated into this array
201
     * @return the timestamp that shall represent this directory, it will
202
     *   be compared with timestamps of all children and the newest
203
     *   one will be kept and next time passed as lastTimeStamp. Return
204
     *   0 if the directory does not have any special timestamp
205
     * @since 2.23
206
     */
207
    public long refreshRecursively(File dir, long lastTimeStamp, List<? super File> children) {
208
        final File[] arr = dir.listFiles();
209
        if (arr != null) {
210
            children.addAll(Arrays.asList(arr));
211
        }
212
        return 0;
213
    }
190
}
214
}
(-)a/masterfs/test/unit/src/org/netbeans/modules/masterfs/filebasedfs/FileUtilAddRecursiveListenerTest.java (+50 lines)
Lines 44-63 Link Here
44
import java.io.IOException;
44
import java.io.IOException;
45
import java.util.logging.Level;
45
import java.util.logging.Level;
46
import java.util.logging.Logger;
46
import java.util.logging.Logger;
47
import org.netbeans.junit.MockServices;
47
import org.netbeans.junit.NbTestCase;
48
import org.netbeans.junit.NbTestCase;
48
import org.netbeans.junit.RandomlyFails;
49
import org.netbeans.junit.RandomlyFails;
50
import org.netbeans.modules.masterfs.filebasedfs.fileobjects.BaseFileObj;
49
import org.openide.filesystems.FileLock;
51
import org.openide.filesystems.FileLock;
50
import org.openide.filesystems.FileObject;
52
import org.openide.filesystems.FileObject;
51
import org.openide.filesystems.FileUtil;
53
import org.openide.filesystems.FileUtil;
52
import org.openide.util.test.TestFileUtils;
54
import org.openide.util.test.TestFileUtils;
53
import org.netbeans.modules.masterfs.filebasedfs.FileUtilTest.EventType;
55
import org.netbeans.modules.masterfs.filebasedfs.FileUtilTest.EventType;
54
import org.netbeans.modules.masterfs.filebasedfs.FileUtilTest.TestFileChangeListener;
56
import org.netbeans.modules.masterfs.filebasedfs.FileUtilTest.TestFileChangeListener;
57
import org.netbeans.modules.masterfs.filebasedfs.fileobjects.FileObjectFactory;
55
import org.netbeans.modules.masterfs.filebasedfs.fileobjects.TestUtils;
58
import org.netbeans.modules.masterfs.filebasedfs.fileobjects.TestUtils;
59
import org.netbeans.modules.masterfs.providers.ProvidedExtensionsTest;
56
60
57
/**
61
/**
58
 * @author Jiri Skrivanek
62
 * @author Jiri Skrivanek
59
 */
63
 */
60
public class FileUtilAddRecursiveListenerTest extends NbTestCase {
64
public class FileUtilAddRecursiveListenerTest extends NbTestCase {
65
    static {
66
        MockServices.setServices(ProvidedExtensionsTest.AnnotationProviderImpl.class);
67
    }
68
61
    private final Logger LOG;
69
    private final Logger LOG;
62
70
63
    public FileUtilAddRecursiveListenerTest(String name) {
71
    public FileUtilAddRecursiveListenerTest(String name) {
Lines 66-71 Link Here
66
    }
74
    }
67
75
68
    @Override
76
    @Override
77
    protected void setUp() throws Exception {
78
        clearWorkDir();
79
    }
80
81
    @Override
69
    protected Level logLevel() {
82
    protected Level logLevel() {
70
        return Level.FINER;
83
        return Level.FINER;
71
    }
84
    }
Lines 276-279 Link Here
276
289
277
        LOG.info("OK");
290
        LOG.info("OK");
278
    }
291
    }
292
293
    public void testProvideExtensionsRefreshRecursively() throws Exception {
294
        File root = new File(getWorkDir(), "root");
295
        final File sub = new File(root, "sub");
296
        final File subsub = new File(sub, "subsub");
297
        File subsubdir = new File(subsub, "dir");
298
        subsubdir.mkdirs();
299
        File subfile = new File(sub, "file");
300
        subfile.createNewFile();
301
        File deepfile = new File(subsubdir, "deep");
302
        deepfile.createNewFile();
303
304
        ProvidedExtensionsTest.ProvidedExtensionsImpl.nextRefreshCall(
305
            sub,
306
            Long.MAX_VALUE - 10,
307
            subfile
308
        );
309
310
311
        TestFileChangeListener fcl = new TestFileChangeListener();
312
        FileObject rf = FileUtil.toFileObject(root);
313
        rf.addRecursiveListener(fcl);
314
315
        BaseFileObj noFO = FileObjectFactory.getInstance(root).getCachedOnly(subsubdir);
316
        assertNull("subsub directory has been skipped", noFO);
317
318
        assertEquals("No events", 0, fcl.checkAll());
319
        LOG.log(Level.INFO, "Touching subfile: {0}", deepfile);
320
        TestFileUtils.touch(deepfile, null);
321
        LOG.log(Level.INFO, "Will do refresh, lastModified: {0}", deepfile.lastModified());
322
        FileUtil.refreshFor(root);
323
        LOG.info("Refresh done");
324
        fcl.check(EventType.ATTRIBUTE_CHANGED); // ignore if any
325
326
        fcl.printAll(LOG);
327
        assertEquals("No other events", 0, fcl.checkAll());
328
    }
279
}
329
}
(-)a/masterfs/test/unit/src/org/netbeans/modules/masterfs/providers/ProvidedExtensionsTest.java (-1 / +24 lines)
Lines 49-54 Link Here
49
import java.io.InputStream;
49
import java.io.InputStream;
50
import java.io.OutputStream;
50
import java.io.OutputStream;
51
import java.util.ArrayList;
51
import java.util.ArrayList;
52
import java.util.Arrays;
52
import java.util.Collection;
53
import java.util.Collection;
53
import java.util.HashSet;
54
import java.util.HashSet;
54
import java.util.Iterator;
55
import java.util.Iterator;
Lines 450-455 Link Here
450
    }
451
    }
451
    
452
    
452
    public static class ProvidedExtensionsImpl extends ProvidedExtensions  {
453
    public static class ProvidedExtensionsImpl extends ProvidedExtensions  {
454
        private static File refreshCallForDir;
455
        private static long refreshCallRetValue;
456
        private static List<File> refreshCallToAdd;
453
        private int implsMoveCalls;
457
        private int implsMoveCalls;
454
        private int moveImplCalls;
458
        private int moveImplCalls;
455
        private int implsRenameCalls;
459
        private int implsRenameCalls;
Lines 522-528 Link Here
522
        public void beforeChange(FileObject f) {
526
        public void beforeChange(FileObject f) {
523
            assertNotNull(FileUtil.toFile(f));
527
            assertNotNull(FileUtil.toFile(f));
524
            implsBeforeChangeCalls++;
528
            implsBeforeChangeCalls++;
525
        }    
529
        }
530
        
531
        public static void nextRefreshCall(File forDir, long retValue, File... toAdd) {
532
            refreshCallForDir = forDir;
533
            refreshCallRetValue = retValue;
534
            refreshCallToAdd = Arrays.asList(toAdd);
535
        }
536
537
        @Override
538
        public long refreshRecursively(File dir, long lastTimeStamp, List<? super File> children) {
539
            if (dir.equals(refreshCallForDir)) {
540
                children.addAll(refreshCallToAdd);
541
                long r = refreshCallRetValue;
542
                refreshCallForDir = null;
543
                refreshCallToAdd = null;
544
                refreshCallRetValue = -1;
545
                return r;
546
            }
547
            return -1;
548
        }
526
        
549
        
527
        @Override
550
        @Override
528
        public ProvidedExtensions.DeleteHandler getDeleteHandler(File f) {
551
        public ProvidedExtensions.DeleteHandler getDeleteHandler(File f) {

Return to bug 181684