diff --git a/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java b/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java --- a/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java +++ b/projectapi/src/org/netbeans/modules/projectapi/SimpleFileOwnerQueryImplementation.java @@ -57,6 +57,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.BackingStoreException; @@ -127,6 +128,12 @@ public Project getOwner(FileObject f) { + try { + // wait until prefs deserialize + externalLatch.await(); + } catch (InterruptedException ex) { + LOG.log(Level.INFO, ex.getMessage(), ex); + } while (f != null) { synchronized (this) { if (lastFoundKey != null && lastFoundKey.get() == f) { @@ -218,9 +225,20 @@ private static final Map deserializedExternalOwners = Collections.synchronizedMap(new HashMap()); + /** + * Latch released when the serialized external roots load. All file queries + * must wait until the preferences reads. + */ + private static final CountDownLatch externalLatch = new CountDownLatch(1); + private static boolean externalRootsIncludeNonFolders = false; - + /** + * Deserializes stored cross-reference of external files to their projects. + * It is called from @OnStart, which runs asynchronously/in parallel, but + * getOwner() queries require that the cross-ref is laoded so the answers are + * consistent in time. + */ static void deserialize() { try { Preferences p = NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners"); @@ -236,6 +254,8 @@ NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners").removeNode(); } catch (BackingStoreException ex) { LOG.log(Level.INFO, null, ex); + } finally { + externalLatch.countDown(); } }