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

(-)a/projectapi/manifest.mf (-1 lines)
Lines 1-6 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.modules.projectapi/1
2
OpenIDE-Module: org.netbeans.modules.projectapi/1
3
OpenIDE-Module-Install: org/netbeans/modules/projectapi/Installer.class
4
OpenIDE-Module-Specification-Version: 1.33
3
OpenIDE-Module-Specification-Version: 1.33
5
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/projectapi/Bundle.properties
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/projectapi/Bundle.properties
6
OpenIDE-Module-Layer: org/netbeans/modules/projectapi/layer.xml
5
OpenIDE-Module-Layer: org/netbeans/modules/projectapi/layer.xml
(-)a/projectapi/src/org/netbeans/api/project/ProjectManager.java (-9 / +1 lines)
Lines 47-53 Link Here
47
import java.io.IOException;
47
import java.io.IOException;
48
import java.lang.ref.Reference;
48
import java.lang.ref.Reference;
49
import java.util.Arrays;
49
import java.util.Arrays;
50
import java.util.Collection;
51
import java.util.Collections;
50
import java.util.Collections;
52
import java.util.HashSet;
51
import java.util.HashSet;
53
import java.util.Iterator;
52
import java.util.Iterator;
Lines 60-66 Link Here
60
import javax.swing.Icon;
59
import javax.swing.Icon;
61
import org.netbeans.modules.projectapi.SimpleFileOwnerQueryImplementation;
60
import org.netbeans.modules.projectapi.SimpleFileOwnerQueryImplementation;
62
import org.netbeans.modules.projectapi.TimedWeakReference;
61
import org.netbeans.modules.projectapi.TimedWeakReference;
63
import org.netbeans.spi.project.FileOwnerQueryImplementation;
64
import org.netbeans.spi.project.ProjectFactory;
62
import org.netbeans.spi.project.ProjectFactory;
65
import org.netbeans.spi.project.ProjectFactory2;
63
import org.netbeans.spi.project.ProjectFactory2;
66
import org.netbeans.spi.project.ProjectState;
64
import org.netbeans.spi.project.ProjectState;
Lines 571-583 Link Here
571
                    if (!removedProjects.add(p)) {
569
                    if (!removedProjects.add(p)) {
572
                        LOG.log(Level.WARNING, "An attempt to call notifyDeleted more than once. Project: {0}", p.getProjectDirectory());
570
                        LOG.log(Level.WARNING, "An attempt to call notifyDeleted more than once. Project: {0}", p.getProjectDirectory());
573
                    }
571
                    }
574
                    //#111892
572
                    SimpleFileOwnerQueryImplementation.resetLastFoundReferences(); // #111892
575
                    Collection<? extends FileOwnerQueryImplementation> col = Lookup.getDefault().lookupAll(FileOwnerQueryImplementation.class);
576
                    for (FileOwnerQueryImplementation impl : col) {
577
                        if (impl instanceof SimpleFileOwnerQueryImplementation) {
578
                            ((SimpleFileOwnerQueryImplementation)impl).resetLastFoundReferences();
579
                        }
580
                    }
581
                    return null;
573
                    return null;
582
                }
574
                }
583
            });
575
            });
(-)a/projectapi/src/org/netbeans/modules/projectapi/Installer.java (-66 lines)
Lines 1-66 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2010 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
 * Contributor(s):
28
 *
29
 * The Original Software is NetBeans. The Initial Developer of the Original
30
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
31
 * Microsystems, Inc. All Rights Reserved.
32
 *
33
 * If you wish your version of this file to be governed by only the CDDL
34
 * or only the GPL Version 2, indicate your decision by adding
35
 * "[Contributor] elects to include this software in this distribution
36
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
37
 * single choice of license, a recipient has the option to distribute
38
 * your version of this file under either the CDDL, the GPL Version 2 or
39
 * to extend the choice of license to its licensees as provided above.
40
 * However, if you add GPL Version 2 code and therefore, elected the GPL
41
 * Version 2 license, then the option applies only if the new code is
42
 * made subject to such option by the copyright holder.
43
 */
44
45
package org.netbeans.modules.projectapi;
46
47
import org.openide.modules.ModuleInstall;
48
49
/**
50
 * Manages a module's lifecycle. Remember that an installer is optional and
51
 * often not needed at all.
52
 */
53
public class Installer extends ModuleInstall {
54
55
    @Override
56
    public void restored() {
57
        //#125582
58
        SimpleFileOwnerQueryImplementation.deserialize();
59
    }
60
61
    @Override
62
    public void close() {
63
        //#125582
64
        SimpleFileOwnerQueryImplementation.serialize();
65
    }
66
}
(-)a/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java (-216 / +107 lines)
Lines 49-63 Link Here
49
import java.lang.ref.WeakReference;
49
import java.lang.ref.WeakReference;
50
import java.net.MalformedURLException;
50
import java.net.MalformedURLException;
51
import java.net.URI;
51
import java.net.URI;
52
import java.net.URISyntaxException;
53
import java.net.URL;
52
import java.net.URL;
54
import java.util.Collection;
55
import java.util.Collections;
56
import java.util.HashMap;
57
import java.util.LinkedList;
58
import java.util.Map;
59
import java.util.Set;
60
import java.util.WeakHashMap;
61
import java.util.logging.Level;
53
import java.util.logging.Level;
62
import java.util.logging.Logger;
54
import java.util.logging.Logger;
63
import java.util.prefs.BackingStoreException;
55
import java.util.prefs.BackingStoreException;
Lines 69-271 Link Here
69
import org.openide.filesystems.FileStateInvalidException;
61
import org.openide.filesystems.FileStateInvalidException;
70
import org.openide.filesystems.URLMapper;
62
import org.openide.filesystems.URLMapper;
71
import org.openide.util.NbPreferences;
63
import org.openide.util.NbPreferences;
72
import org.openide.util.Utilities;
64
import org.openide.util.lookup.ServiceProvider;
73
import org.openide.util.WeakSet;
74
65
75
/**
66
/**
76
 * Finds a project by searching the directory tree.
67
 * Finds a project by searching the directory tree.
77
 * @author Jesse Glick
68
 * @author Jesse Glick
78
 */
69
 */
79
@org.openide.util.lookup.ServiceProvider(service=org.netbeans.spi.project.FileOwnerQueryImplementation.class, position=100)
70
@ServiceProvider(service=FileOwnerQueryImplementation.class, position=100)
80
public class SimpleFileOwnerQueryImplementation implements FileOwnerQueryImplementation {
71
public class SimpleFileOwnerQueryImplementation implements FileOwnerQueryImplementation {
81
    private static final Logger LOG = Logger.getLogger(SimpleFileOwnerQueryImplementation.class.getName());
72
    private static final Logger LOG = Logger.getLogger(SimpleFileOwnerQueryImplementation.class.getName());
82
    
73
    
83
    /** Do nothing */
74
    private static URI lastFoundKey = null;
84
    public SimpleFileOwnerQueryImplementation() {}
75
    private static Reference<Project> lastFoundValue = null;
85
    
76
    
86
    public Project getOwner(URI fileURI) {
77
    public static synchronized void resetLastFoundReferences() { // #111892
87
        // Try to find a FileObject for it.
78
        lastFoundValue = null;
88
        URI test = fileURI;
79
        lastFoundKey = null;
89
        FileObject file;
90
        do {
91
            file = uri2FileObject(test);
92
            test = goUp(test);
93
        } while (file == null && test != null);
94
        if (file == null) {
95
            return null;
96
        }
97
        return getOwner(file);
98
    }
80
    }
99
    
81
    
100
    private final Set<FileObject> warnedAboutBrokenProjects = new WeakSet<FileObject>();
82
    public @Override Project getOwner(FileObject f) {
101
        
83
        return getOwner(fileObject2URI(f), f);
102
    private Reference<FileObject> lastFoundKey = null;
84
    }
103
    private Reference<Project> lastFoundValue = null;
85
104
    
86
    public @Override Project getOwner(URI u) {
105
    /**
87
        return getOwner(u, uri2FileObject(u));
106
     * 
88
    }
107
     * #111892
89
108
     */
90
    private static Project getOwner(URI u, FileObject f) {
109
    public void resetLastFoundReferences() {
91
        synchronized (SimpleFileOwnerQueryImplementation.class) {
110
        synchronized (this) {
92
            if (u.equals(lastFoundKey)) {
111
            lastFoundValue = null;
93
                Project p = lastFoundValue.get();
112
            lastFoundKey = null;
94
                if (p != null) {
95
                    return p;
96
                }
97
            }
113
        }
98
        }
99
        Project p = findOwner(u.toString(), f);
100
        if (p != null) {
101
            synchronized (SimpleFileOwnerQueryImplementation.class) {
102
                lastFoundKey = u;
103
                lastFoundValue = new WeakReference<Project>(p);
104
            }
105
        }
106
        return p;
114
    }
107
    }
115
    
108
116
    
109
    private static Project findOwner(String u, FileObject f) {
117
    public Project getOwner(FileObject f) {
110
        // Would be nice to maintain a SortedMap<String,URI> of external owners and use headMap
118
        while (f != null) {
111
        // to jump directly to the right spot. Unfortunately we also need to inspect intermediate
119
            synchronized (this) {
112
        // existing folders (if they exist on disk) to see if they are project directories.
120
                if (lastFoundKey != null && lastFoundKey.get() == f) {
113
        if (f != null && f.isFolder()) {
121
                    Project p = lastFoundValue.get();
114
            try {
115
                Project p = ProjectManager.getDefault().findProject(f);
116
                if (p != null) {
117
                    return p;
118
                }
119
            } catch (IOException x) {
120
                LOG.log(Level.FINE, "Cannot load project", x);
121
                return null;
122
            }
123
        }
124
        String externalOwner = storage().get(u, null);
125
        if (externalOwner != null) {
126
            FileObject externalOwnerFolder = uri2FileObject(URI.create(externalOwner));
127
            if (externalOwnerFolder != null && externalOwnerFolder.isFolder()) {
128
                try {
129
                    Project p = ProjectManager.getDefault().findProject(externalOwnerFolder);
122
                    if (p != null) {
130
                    if (p != null) {
123
                        return p;
131
                        return p;
124
                    }
132
                    }
133
                } catch (IOException x) {
134
                    LOG.log(Level.FINE, "Cannot load external owner", x);
125
                }
135
                }
126
            }
136
            }
127
            boolean folder = f.isFolder();
137
            // Registration is stale - no such folder, no such project, or broken.
128
            if (folder) {
138
            storage().remove(u);
129
                Project p;
139
        }
130
                try {
140
        // OK, check next level up.
131
                    p = ProjectManager.getDefault().findProject(f);
141
        int slash = u.lastIndexOf('/');
132
                } catch (IOException e) {
142
        if (slash == u.length() - 1) { // folder
133
                    // There is a project here, but we cannot load it...
143
            slash = u.lastIndexOf('/', slash - 1);
134
                    if (warnedAboutBrokenProjects.add(f)) { // #60416
144
        } else if (slash == -1) { // at root?
135
                        LOG.log(Level.FINE, "Cannot load project.", e); //NOI18N
145
            return null;
136
                    }
146
        } // else file
137
                    return null;
147
        String u2 = u.substring(0, slash + 1); // file:/tmp/foo/ or file:/tmp/foo -> file:/tmp/
138
                }
148
        FileObject f2 = null;
139
                if (p != null) {
149
        if (f != null) {
140
                    synchronized (this) {
150
            f2 = f.getParent();
141
                        lastFoundKey = new WeakReference<FileObject>(f);
151
            if (f2 == null) {
142
                        lastFoundValue = new WeakReference<Project>(p);
152
                // At filesystem root, no matches.
143
                    }
153
                return null;
144
                    return p;
145
                }
146
            }
154
            }
147
            
155
        } else {
148
            if (!externalOwners.isEmpty() && (folder || externalRootsIncludeNonFolders)) {
156
            f2 = uri2FileObject(URI.create(u2)); // may still be null
149
                URI externalOwnersURI = externalOwners.get(fileObject2URI(f));
150
151
                if (externalOwnersURI != null) {
152
                    FileObject externalOwner = uri2FileObject(externalOwnersURI);
153
154
                    if (externalOwner != null && externalOwner.isValid()) {
155
                        try {
156
                            // Note: will be null if there is no such project.
157
                            Project p = ProjectManager.getDefault().findProject(externalOwner);
158
                            synchronized (this) {
159
                                lastFoundKey = new WeakReference<FileObject>(f);
160
                                lastFoundValue = new WeakReference<Project>(p);
161
                            }
162
                            return p;
163
                        } catch (IOException e) {
164
                            // There is a project there, but we cannot load it...
165
                            LOG.log(Level.FINE, "Cannot load project.", e); //NOI18N
166
                            return null;
167
                        }
168
                    }
169
                }
170
            }
171
            if (!deserializedExternalOwners.isEmpty() && (folder || externalRootsIncludeNonFolders)) {
172
                FileObject externalOwner = deserializedExternalOwners.get(fileObject2URI(f));
173
                if (externalOwner != null && externalOwner.isValid()) {
174
                    try {
175
                        // Note: will be null if there is no such project.
176
                        Project p = ProjectManager.getDefault().findProject(externalOwner);
177
                        synchronized (this) {
178
                            lastFoundKey = new WeakReference<FileObject>(f);
179
                            lastFoundValue = new WeakReference<Project>(p);
180
                        }
181
                        return p;
182
                    } catch (IOException e) {
183
                        // There is a project there, but we cannot load it...
184
                        LOG.log(Level.FINE, "Cannot load project.", e); //NOI18N
185
                        return null;
186
                    }
187
                }
188
            }
189
            
190
            f = f.getParent();
191
        }
157
        }
192
        return null;
158
        return findOwner(u2, f2);
193
    }
159
    }
194
    
160
    
195
    /**
161
    private static Preferences storage() {
196
     * Map from external source roots to the owning project directories.
162
        return NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners");
197
     */
198
    private static final Map<URI,URI> externalOwners =
199
        Collections.synchronizedMap(new HashMap<URI,URI>());
200
    
201
    private static final Map<URI,FileObject> deserializedExternalOwners =
202
        Collections.synchronizedMap(new HashMap<URI,FileObject>());
203
    
204
    private static boolean externalRootsIncludeNonFolders = false;
205
    
206
    
207
    static void deserialize() {
208
        try {
209
            Preferences p = NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners");
210
            for (String name : p.keys()) {
211
                URL u = new URL(p.get(name, null));
212
                URI i = new URI(name);
213
                deserializedExternalOwners.put(i, URLMapper.findFileObject(u));
214
            }
215
        } catch (Exception ex) {
216
            LOG.log(Level.INFO, null, ex);
217
        }
218
        try {
219
            NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners").removeNode();
220
        } catch (BackingStoreException ex) {
221
            LOG.log(Level.INFO, null, ex);
222
        }
223
    }
224
    
225
    static void serialize() {
226
        try {
227
            Preferences p = NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners");
228
            for (URI uri : externalOwners.keySet()) {
229
                URI ownerURI = externalOwners.get(uri);
230
                p.put(uri.toString(), ownerURI.toString());
231
            }
232
            p.sync(); // #184310
233
        } catch (Exception ex) {
234
            LOG.log(Level.WARNING, null, ex);
235
        }
236
        
237
    }
163
    }
238
    
164
    
239
    /** @see FileOwnerQuery#reset */
165
    /** @see FileOwnerQuery#reset */
240
    public static void reset() {
166
    public static void reset() {
241
        externalOwners.clear();
167
        try {
168
            storage().removeNode();
169
        } catch (BackingStoreException x) {
170
            assert false : x;
171
        }
172
    }
173
    
174
    /** @see FileOwnerQuery#markExternalOwner */
175
    public static void markExternalOwnerTransient(FileObject root, Project owner) {
176
        try {
177
            markExternalOwnerTransient(root.getURL().toString(), owner);
178
        } catch (FileStateInvalidException x) {
179
            throw new IllegalArgumentException(x);
180
        }
181
    }
182
    
183
    /** @see FileOwnerQuery#markExternalOwner */
184
    public static void markExternalOwnerTransient(URI root, Project owner) {
185
        markExternalOwnerTransient(root.toString(), owner);
186
    }
187
188
    private static void markExternalOwnerTransient(String root, Project owner) {
189
        if (owner != null) {
190
            storage().put(root, fileObject2URI(owner.getProjectDirectory()).toString());
191
        } else {
192
            storage().remove(root);
193
        }
242
    }
194
    }
243
    
195
    
244
    private static URI fileObject2URI(FileObject f) {
196
    private static URI fileObject2URI(FileObject f) {
245
        try {
197
        try {
246
            return URI.create(f.getURL().toString());
198
            return URI.create(f.getURL().toString());
247
        } catch (FileStateInvalidException e) {
199
        } catch (FileStateInvalidException x) {
248
            throw (IllegalArgumentException) new IllegalArgumentException(e.toString()).initCause(e);
200
            throw new IllegalArgumentException(x);
249
        }
201
        }
250
    }
202
    }
251
    
203
252
    /** @see FileOwnerQuery#markExternalOwner */
253
    public static void markExternalOwnerTransient(FileObject root, Project owner) {
254
        markExternalOwnerTransient(fileObject2URI(root), owner);
255
    }
256
    
257
    /** @see FileOwnerQuery#markExternalOwner */
258
    public static void markExternalOwnerTransient(URI root, Project owner) {
259
        externalRootsIncludeNonFolders |= !root.getPath().endsWith("/");
260
        if (owner != null) {
261
            FileObject fo = owner.getProjectDirectory();
262
            externalOwners.put(root, fileObject2URI(fo));
263
            deserializedExternalOwners.remove(root);
264
        } else {
265
            externalOwners.remove(root);
266
        }
267
    }
268
    
269
    private static FileObject uri2FileObject(URI u) {
204
    private static FileObject uri2FileObject(URI u) {
270
        URL url;
205
        URL url;
271
        try {
206
        try {
Lines 278-325 Link Here
278
        return URLMapper.findFileObject(url);
213
        return URLMapper.findFileObject(url);
279
    }
214
    }
280
    
215
    
281
    private static URI goUp(URI u) {
282
        assert u.isAbsolute() : u;
283
        assert u.getFragment() == null : u;
284
        assert u.getQuery() == null : u;
285
        // XXX isn't there any easier way to do this?
286
        // Using getPath in the new path does not work; nbfs: URLs break. (#39613)
287
        // On the other hand, nbfs: URLs are not really used any more, so do we care?
288
        String path = u.getPath();
289
        if (path == null || path.equals("/")) { // NOI18N
290
            return null;
291
        }
292
        String us = u.toString();
293
        if (us.endsWith("/")) { // NOI18N
294
            us = us.substring(0, us.length() - 1);
295
            assert path.endsWith("/"); // NOI18N
296
            path = path.substring(0, path.length() - 1);
297
        }
298
        int idx = us.lastIndexOf('/');
299
        assert idx != -1 : path;
300
        if (path.lastIndexOf('/') == 0) {
301
            us = us.substring(0, idx + 1);
302
        } else {
303
            us = us.substring(0, idx);
304
        }
305
        URI nue;
306
        try {
307
            nue = new URI(us);
308
        } catch (URISyntaxException e) {
309
            throw new AssertionError(e);
310
        }
311
        if (WINDOWS) {
312
            String pth = nue.getPath();
313
            // check that path is not "/C:" or "/"
314
            if ((pth.length() == 3 && pth.endsWith(":")) ||
315
                (pth.length() == 1 && pth.endsWith("/"))) {
316
                return null;
317
            }
318
        }
319
        assert nue.isAbsolute() : nue;
320
        assert u.toString().startsWith(nue.toString()) : "not a parent: " + nue + " of " + u;
321
        return nue;
322
    }
323
    private static final boolean WINDOWS = Utilities.isWindows();
324
    
325
}
216
}
(-)a/projectapi/test/unit/src/org/netbeans/api/project/FileOwnerQueryTest.java (+21 lines)
Lines 44-49 Link Here
44
44
45
package org.netbeans.api.project;
45
package org.netbeans.api.project;
46
46
47
import java.io.File;
47
import java.lang.ref.Reference;
48
import java.lang.ref.Reference;
48
import java.lang.ref.WeakReference;
49
import java.lang.ref.WeakReference;
49
import java.net.URI;
50
import java.net.URI;
Lines 269-274 Link Here
269
        //XXX: unmarking files.
270
        //XXX: unmarking files.
270
    }
271
    }
271
272
273
    public void testExternalOwnerNonexistentFile() throws Exception {
274
        URI unbuilt = FileUtil.urlForArchiveOrDir(new File(getWorkDir(), "unbuilt")).toURI();
275
        assert unbuilt.toString().endsWith("/");
276
        FileOwnerQuery.markExternalOwner(unbuilt, p, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT);
277
        URI subfolder = unbuilt.resolve("subfolder/");
278
        assert subfolder.toString().endsWith("/");
279
        Project p2 = ProjectManager.getDefault().findProject(subprojdir);
280
        FileOwnerQuery.markExternalOwner(subfolder, p2, FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT);
281
        assertEquals(p, FileOwnerQuery.getOwner(unbuilt));
282
        assertEquals(p, FileOwnerQuery.getOwner(unbuilt.resolve("stuff")));
283
        assertEquals(p2, FileOwnerQuery.getOwner(subfolder));
284
        assertEquals(p2, FileOwnerQuery.getOwner(subfolder.resolve("stuff")));
285
        FileUtil.createFolder(scratch, "unbuilt/stuff/testproject");
286
        assertEquals(p, FileOwnerQuery.getOwner(unbuilt));
287
        Project other = FileOwnerQuery.getOwner(unbuilt.resolve("stuff"));
288
        assertNotNull(other);
289
        assertEquals("stuff", other.getProjectDirectory().getName());
290
        assertEquals(null, FileOwnerQuery.getOwner(URI.create("file:/completely/nonexistent/")));
291
    }
292
272
    public void testExternalOwnerDisappearingProject() throws Exception {
293
    public void testExternalOwnerDisappearingProject() throws Exception {
273
        FileObject ext1 = scratch.getFileObject("external1");
294
        FileObject ext1 = scratch.getFileObject("external1");
274
        FileObject tempPrjMarker = FileUtil.createFolder(scratch, "tempprj/testproject");
295
        FileObject tempPrjMarker = FileUtil.createFolder(scratch, "tempprj/testproject");

Return to bug 186024