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

(-)a/masterfs/apichanges.xml (+16 lines)
Lines 49-54 Link Here
49
        <apidef name="masterfs">MasterFileSystem API</apidef>
49
        <apidef name="masterfs">MasterFileSystem API</apidef>
50
    </apidefs>
50
    </apidefs>
51
    <changes>
51
    <changes>
52
        <change id="org.netbeans.io.suspend">
53
            <api name="masterfs"/>
54
            <summary>A property to suspend native listeners</summary>
55
            <version major="2" minor="42"/>
56
            <date day="22" month="11" year="2012"/>
57
            <author login="jtulach"/>
58
            <compatibility addition="yes" binary="compatible" 
59
                source="compatible" semantic="compatible" 
60
                deprecation="no" deletion="no" modification="no"
61
            />
62
            <description>
63
                A <a href="architecture-summary.html#systemproperty-org.netbeans.io.suspend">
64
                way</a> to temporarily suspend native listeners.
65
            </description>
66
            <issue number="222193"/>
67
        </change>
52
        <change id="fileLocked.io.exception">
68
        <change id="fileLocked.io.exception">
53
            <api name="masterfs"/>
69
            <api name="masterfs"/>
54
            <summary>fileLocked method can throw <code>IOException</code></summary>
70
            <summary>fileLocked method can throw <code>IOException</code></summary>
(-)a/masterfs/arch.xml (+30 lines)
Lines 533-538 Link Here
533
    consumption, not for consumption by other parts of the system. The name and
533
    consumption, not for consumption by other parts of the system. The name and
534
    meaning of this property may change in any release.
534
    meaning of this property may change in any release.
535
    </api>
535
    </api>
536
    
537
    <api name="org.netbeans.io.suspend" category="devel" group="systemproperty" type="export">
538
        <p>
539
    Native listeners check the <code>org.netbeans.io.suspend</code> property. 
540
    If it is set to integer greater than zero, they stop delivering file change events.
541
    The list of modified directories is recorded (its size is made available
542
    by setting its string value into <code>org.netbeans.io.pending</code> property), 
543
    but its processing is suspended. Events are delivered when 
544
    <code>org.netbeans.io.suspend</code> property changes its value to <code>0</code>
545
    or becomes empty.
546
    </p>
547
    <p>
548
    I/O intensive operations in other NetBeans modules are advised
549
    to honour the <code>org.netbeans.io.suspend</code> property as well and
550
    suspend their I/O activities too.
551
    </p>
552
    <p>
553
    In order to properly communicate changes to the property between multiple
554
    <em>receivers</em> and multiple <em>controllers</em> it is suggested to
555
    only manipulate the value under <code>synchronized("org.netbeans.io.suspend".intern())</code>
556
    lock. Those changing the value are supposed to increment it by one when they
557
    request the suspend and decrement it by one when they want to resume their
558
    own suspend.
559
    </p>
560
    <p>
561
    Whenever a change to the state of the property is made,
562
    <em>controllers</em> are supposed to 
563
    <code>"org.netbeans.io.suspend".intern().notifyAll()</code>.
564
    </p>
565
    </api>
536
</answer>
566
</answer>
537
567
538
568
(-)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.41
4
OpenIDE-Module-Specification-Version: 2.42
5
OpenIDE-Module-Recommends: org.netbeans.modules.masterfs.providers.Notifier
5
OpenIDE-Module-Recommends: org.netbeans.modules.masterfs.providers.Notifier
6
OpenIDE-Module-Provides: org.openide.filesystems.FileUtil.toFileObject
6
OpenIDE-Module-Provides: org.openide.filesystems.FileUtil.toFileObject
7
AutoUpdate-Show-In-Client: false
7
AutoUpdate-Show-In-Client: false
(-)a/masterfs/src/org/netbeans/modules/masterfs/watcher/Watcher.java (-4 / +21 lines)
Lines 57-65 Link Here
57
import org.netbeans.modules.masterfs.providers.ProvidedExtensions;
57
import org.netbeans.modules.masterfs.providers.ProvidedExtensions;
58
import org.openide.filesystems.FileObject;
58
import org.openide.filesystems.FileObject;
59
import org.netbeans.modules.masterfs.providers.AnnotationProvider;
59
import org.netbeans.modules.masterfs.providers.AnnotationProvider;
60
import org.openide.util.Exceptions;
60
import org.openide.util.Lookup;
61
import org.openide.util.Lookup;
61
import org.openide.util.Lookup.Item;
62
import org.openide.util.Lookup.Item;
62
import org.openide.util.RequestProcessor;
63
import org.openide.util.RequestProcessor;
64
import org.openide.util.WeakSet;
63
import org.openide.util.lookup.ServiceProvider;
65
import org.openide.util.lookup.ServiceProvider;
64
import org.openide.util.lookup.ServiceProviders;
66
import org.openide.util.lookup.ServiceProviders;
65
67
Lines 354-360 Link Here
354
        }
356
        }
355
    }
357
    }
356
358
357
    private final Object lock = new Object();
359
    private final Object lock = "org.netbeans.io.suspend".intern();
358
    private Set<FileObject> pending; // guarded by lock
360
    private Set<FileObject> pending; // guarded by lock
359
    private static RequestProcessor RP = new RequestProcessor("Pending refresh", 1);
361
    private static RequestProcessor RP = new RequestProcessor("Pending refresh", 1);
360
362
Lines 364-373 Link Here
364
            Set<FileObject> toRefresh;
366
            Set<FileObject> toRefresh;
365
            synchronized(lock) {
367
            synchronized(lock) {
366
                toRefresh = pending;
368
                toRefresh = pending;
367
                pending = null;
368
                if (toRefresh == null) {
369
                if (toRefresh == null) {
369
                    return;
370
                    return;
370
                }
371
                }
372
                for (;;) {
373
                    int cnt = Integer.getInteger("org.netbeans.io.suspend", 0); // NOI18N
374
                    if (cnt <= 0) {
375
                        break;
376
                    }
377
                    final String pndngSize = String.valueOf(toRefresh.size());
378
                    System.setProperty("org.netbeans.io.pending", pndngSize); // NOI18N
379
                    LOG.log(Level.FINE, "Suspend count {0} pending {1}", new Object[]{cnt, pndngSize});
380
                    try {
381
                        lock.wait(1500);
382
                    } catch (InterruptedException ex) {
383
                        LOG.log(Level.FINE, null, ex);
384
                    }
385
                }
386
                System.getProperties().remove("org.netbeans.io.pending"); // NOI18N
387
                pending = null;
371
            }
388
            }
372
            LOG.log(Level.FINE, "Refreshing {0} directories", toRefresh.size());
389
            LOG.log(Level.FINE, "Refreshing {0} directories", toRefresh.size());
373
390
Lines 391-397 Link Here
391
        synchronized(lock) {
408
        synchronized(lock) {
392
            if (pending == null) {
409
            if (pending == null) {
393
                refreshTask.schedule(1500);
410
                refreshTask.schedule(1500);
394
                pending = new HashSet<FileObject>();
411
                pending = new WeakSet<FileObject>();
395
            }
412
            }
396
            pending.add(fo);
413
            pending.add(fo);
397
        }
414
        }
Lines 404-410 Link Here
404
        synchronized(lock) {
421
        synchronized(lock) {
405
            if (pending == null) {
422
            if (pending == null) {
406
                refreshTask.schedule(1500);
423
                refreshTask.schedule(1500);
407
                pending = new HashSet<FileObject>();
424
                pending = new WeakSet<FileObject>();
408
            }
425
            }
409
            pending.addAll(fos);
426
            pending.addAll(fos);
410
        }
427
        }
(-)59d630fe8661 (+193 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2011 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.masterfs.watcher;
43
44
import java.io.File;
45
import org.netbeans.modules.masterfs.providers.Notifier;
46
import java.io.IOException;
47
import java.lang.ref.Reference;
48
import java.lang.ref.WeakReference;
49
import java.util.LinkedList;
50
import java.util.List;
51
import java.util.Queue;
52
import java.util.concurrent.ArrayBlockingQueue;
53
import java.util.concurrent.BlockingQueue;
54
import org.netbeans.junit.MockServices;
55
import org.netbeans.junit.NbTestCase;
56
import org.netbeans.modules.masterfs.filebasedfs.fileobjects.FileObjectFactory;
57
import org.netbeans.modules.masterfs.filebasedfs.naming.NamingFactory;
58
import org.openide.filesystems.FileChangeAdapter;
59
import org.openide.filesystems.FileEvent;
60
import org.openide.filesystems.FileObject;
61
import org.openide.filesystems.FileUtil;
62
import org.openide.util.Lookup;
63
64
/** Test the behavior of Watcher.
65
 *
66
 * @author Jaroslav Tulach <jtulach@netbeans.org>
67
 */
68
public class WatcherSuspendTest extends NbTestCase {
69
    private TestNotifier notify;
70
    private L listener;
71
    private Watcher watcher;
72
    
73
    public WatcherSuspendTest(String s) {
74
        super(s);
75
    }
76
77
    @Override
78
    protected void setUp() throws Exception {
79
        clearWorkDir();
80
        MockServices.setServices(TestNotifier.class);
81
        listener = new L();
82
        watcher = Lookup.getDefault().lookup(Watcher.class);
83
        notify = Lookup.getDefault().lookup(TestNotifier.class);
84
        notify.start();
85
    }
86
87
    @Override
88
    protected void tearDown() throws Exception {
89
    }
90
    
91
    public void testSuspendRequests() throws Exception {
92
        FileObject root = FileUtil.toFileObject(getWorkDir());
93
        FileObject folder = root.createFolder("dir");
94
        File dir = FileUtil.toFile(folder);
95
        
96
        FileObject[] arr = folder.getChildren();
97
        folder.addFileChangeListener(listener);
98
        assertEquals("Empty ", 0, arr.length);
99
        
100
        final String prop = "org.netbeans.io.suspend".intern();
101
        synchronized (prop) {
102
            int prev = Integer.getInteger(prop, 0);
103
            System.setProperty(prop, "" + (prev + 1));
104
            prop.notifyAll();
105
        }
106
        
107
        new File(dir, "data.txt").createNewFile();
108
        notify.event.offer(dir.getPath());
109
        
110
        assertProperty("One path waiting", "1", "org.netbeans.io.pending");
111
        listener.assertEvents("No event yet", 0, 200);
112
113
        synchronized (prop) {
114
            int prev = Integer.getInteger(prop, 0);
115
            System.setProperty(prop, "" + (prev - 1));
116
            prop.notifyAll();
117
        }
118
        assertProperty("Pending status is cleared", null, "org.netbeans.io.pending");
119
        
120
        listener.assertEvents("One event delivered", 1, 5000);
121
        
122
        arr = folder.getChildren();
123
        assertEquals("One child", 1, arr.length);
124
        assertEquals("data.txt", arr[0].getNameExt());
125
    }
126
127
    private void assertProperty(String msg, String expVal, String propName) throws InterruptedException {
128
        String val = null;
129
        for (int i = 0; i < 100; i++) {
130
            val = System.getProperty(propName);
131
            if (expVal == null && val == null) {
132
                return;
133
            }
134
            if (expVal != null && expVal.equals(val)) {
135
                return;
136
            }
137
            Thread.sleep(100);
138
        }
139
        fail(msg + " exp: " + expVal + " was: " + val);
140
    }
141
142
    
143
    private static final class L extends FileChangeAdapter {
144
        private int cnt;
145
146
        @Override
147
        public synchronized void fileDataCreated(FileEvent fe) {
148
            cnt++;
149
        }
150
151
        @Override
152
        public void fileFolderCreated(FileEvent fe) {
153
            cnt++;
154
        }
155
156
        private synchronized void assertEvents(
157
            String msg, int cnt, int timeOut
158
        ) throws InterruptedException {
159
            if (this.cnt != cnt) {
160
                wait(timeOut);
161
            }
162
            assertEquals(msg, cnt, this.cnt);
163
        }
164
        
165
    }
166
    
167
    public static final class TestNotifier extends Notifier<Integer> {
168
        final List<String> registered = new LinkedList<String>();
169
        final BlockingQueue<String> event = new ArrayBlockingQueue<String>(10);
170
171
        @Override
172
        public Integer addWatch(String path) throws IOException {
173
            int size = registered.size();
174
            registered.add(path);
175
            return size;
176
        }
177
178
        @Override
179
        public void removeWatch(Integer key) throws IOException {
180
            registered.set(key, null);
181
        }
182
183
        @Override
184
        public String nextEvent() throws IOException, InterruptedException {
185
            return event.take();
186
        }
187
188
        @Override
189
        protected void start() throws IOException {
190
            registered.clear();
191
        }
192
    }
193
}

Return to bug 222193