diff -r 48531098cf7a projectui/src/org/netbeans/modules/project/ui/OpenProjectList.java --- a/projectui/src/org/netbeans/modules/project/ui/OpenProjectList.java Wed Jan 30 13:48:23 2008 +0000 +++ b/projectui/src/org/netbeans/modules/project/ui/OpenProjectList.java Wed Jan 30 14:58:29 2008 +0100 @@ -68,6 +68,14 @@ import java.util.Queue; import java.util.Queue; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; @@ -199,8 +207,11 @@ public final class OpenProjectList { } } + Future openProjectsAPI() { + return LOAD; + } - private final class LoadOpenProjects implements Runnable, LookupListener { + private final class LoadOpenProjects implements Runnable, LookupListener, Future { final RequestProcessor RP = new RequestProcessor("Load Open Projects"); // NOI18N final RequestProcessor.Task TASK = RP.create(this); private int action; @@ -209,6 +220,9 @@ public final class OpenProjectList { private List recentTemplates; private Project mainProject; private Lookup.Result currentFiles; + private int entered; + private final Lock enteredGuard = new ReentrantLock(); + private final Condition enteredZeroed = enteredGuard.newCondition(); public LoadOpenProjects(int a) { action = a; @@ -321,6 +335,70 @@ public final class OpenProjectList { } } + + final void enter() { + try { + enteredGuard.lock(); + entered++; + } finally { + enteredGuard.unlock(); + } + } + + final void exit() { + try { + enteredGuard.lock(); + if (--entered == 0) { + enteredZeroed.signalAll(); + } + } finally { + enteredGuard.unlock(); + } + } + + + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + public boolean isCancelled() { + return false; + } + + public boolean isDone() { + return TASK.isFinished() && entered == 0; + } + + public Project[] get() throws InterruptedException, ExecutionException { + TASK.waitFinished(); + try { + enteredGuard.lock(); + while (entered > 0) { + enteredZeroed.await(); + } + } finally { + enteredGuard.unlock(); + } + return getDefault().getOpenProjects(); + } + + public Project[] get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + long ms = unit.convert(timeout, TimeUnit.MILLISECONDS); + if (!TASK.waitFinished(timeout)) { + throw new TimeoutException(); + } + try { + enteredGuard.lock(); + if (entered > 0) { + if (!enteredZeroed.await(ms, TimeUnit.MILLISECONDS)) { + throw new TimeoutException(); + } + } + } finally { + enteredGuard.unlock(); + } + return getDefault().getOpenProjects(); + } } public void open( Project p ) { @@ -407,6 +485,9 @@ public final class OpenProjectList { assert !Arrays.asList(projects).contains(null) : "Projects can't be null"; LOAD.waitFinished(); + + try { + LOAD.enter(); boolean recentProjectsChanged = false; int maxWork = 1000; int workForSubprojects = maxWork / 2; @@ -502,7 +583,6 @@ public final class OpenProjectList { LogRecord[] addedRec = createRecord("UI_OPEN_PROJECTS", projectsToOpen.toArray(new Project[0])); // NOI18N log(addedRec); - Mutex.EVENT.readAccess(new Action() { public Void run() { pchSupport.firePropertyChange( PROPERTY_OPEN_PROJECTS, oldprjs.toArray(new Project[oldprjs.size()]), @@ -514,6 +594,9 @@ public final class OpenProjectList { return null; } }); + } finally { + LOAD.exit(); + } } public void close( Project projects[], boolean notifyUI ) { @@ -521,6 +604,10 @@ public final class OpenProjectList { if (!ProjectUtilities.closeAllDocuments (projects, notifyUI )) { return; } + + try { + LOAD.enter(); + logProjects("close(): closing project: ", projects); boolean mainClosed = false; boolean someClosed = false; @@ -583,6 +670,9 @@ public final class OpenProjectList { } LogRecord[] removedRec = createRecord("UI_CLOSED_PROJECTS", projects); // NOI18N log(removedRec); + } finally { + LOAD.exit(); + } } public synchronized Project[] getOpenProjects() { diff -r 48531098cf7a projectui/src/org/netbeans/modules/project/ui/OpenProjectsTrampolineImpl.java --- a/projectui/src/org/netbeans/modules/project/ui/OpenProjectsTrampolineImpl.java Wed Jan 30 13:48:23 2008 +0000 +++ b/projectui/src/org/netbeans/modules/project/ui/OpenProjectsTrampolineImpl.java Wed Jan 30 14:58:29 2008 +0100 @@ -44,6 +44,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; +import java.util.concurrent.Future; import org.netbeans.api.project.Project; import org.netbeans.api.project.ui.OpenProjects; import org.netbeans.modules.project.uiapi.OpenProjectsTrampoline; @@ -118,4 +119,8 @@ public final class OpenProjectsTrampolin OpenProjectList.getDefault().setMainProject(project); } + public Future openProjectsAPI() { + return OpenProjectList.getDefault().openProjectsAPI(); } + +} diff -r 48531098cf7a projectui/test/unit/src/org/netbeans/modules/project/ui/OpenProjectListTest.java --- a/projectui/test/unit/src/org/netbeans/modules/project/ui/OpenProjectListTest.java Wed Jan 30 13:48:23 2008 +0000 +++ b/projectui/test/unit/src/org/netbeans/modules/project/ui/OpenProjectListTest.java Wed Jan 30 14:58:29 2008 +0100 @@ -54,12 +54,15 @@ import java.util.Set; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.netbeans.api.project.FileOwnerQuery; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectManager; +import org.netbeans.api.project.ui.OpenProjects; import org.netbeans.junit.Log; import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; @@ -72,6 +75,7 @@ import org.openide.filesystems.URLMapper import org.openide.filesystems.URLMapper; import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; +import org.openide.util.RequestProcessor; import org.openide.util.lookup.Lookups; /** Tests fix of issue 56454. @@ -436,19 +440,37 @@ public class OpenProjectListTest extends } } - private static class TestProjectOpenedHookImpl extends ProjectOpenedHook { + private static class TestProjectOpenedHookImpl extends ProjectOpenedHook + implements Runnable { public static int opened = 0; public static int closed = 0; + private Object result; + protected void projectClosed() { closed++; + assertFalse("Working on", OpenProjects.getDefault().openProjects().isDone()); + RequestProcessor.getDefault().post(this).waitFinished(); + assertNotNull("some result computed", result); + assertEquals("It is time out exception", TimeoutException.class, result.getClass()); } protected void projectOpened() { opened++; + assertFalse("Working on", OpenProjects.getDefault().openProjects().isDone()); + RequestProcessor.getDefault().post(this).waitFinished(); + assertNotNull("some result computed", result); + assertEquals("It is time out exception", TimeoutException.class, result.getClass()); } + public void run() { + try { + result = OpenProjects.getDefault().openProjects().get(100, TimeUnit.MILLISECONDS); + } catch (Exception ex) { + result = ex; + } + } } private class ChangeListener implements PropertyChangeListener { diff -r 48531098cf7a projectui/test/unit/src/org/netbeans/modules/project/ui/ProjectsRootNodeTest.java --- a/projectui/test/unit/src/org/netbeans/modules/project/ui/ProjectsRootNodeTest.java Wed Jan 30 13:48:23 2008 +0000 +++ b/projectui/test/unit/src/org/netbeans/modules/project/ui/ProjectsRootNodeTest.java Wed Jan 30 14:58:29 2008 +0100 @@ -46,9 +46,14 @@ import java.util.EventObject; import java.util.EventObject; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import javax.swing.Action; import junit.framework.TestCase; +import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectManager; +import org.netbeans.api.project.ui.OpenProjects; import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.project.ui.actions.TestSupport; @@ -63,6 +68,7 @@ import org.openide.nodes.NodeMemberEvent import org.openide.nodes.NodeMemberEvent; import org.openide.nodes.NodeReorderEvent; import org.openide.util.Exceptions; +import org.openide.util.RequestProcessor; import org.openide.util.lookup.Lookups; /** @@ -111,7 +117,7 @@ public class ProjectsRootNodeTest extend super.tearDown(); } - public void testBehaviourOfProjectsLogicNode() throws InterruptedException { + public void testBehaviourOfProjectsLogicNode() throws Exception { Node logicalView = new ProjectsRootNode(ProjectsRootNode.LOGICAL_VIEW); L listener = new L(); logicalView.addNodeListener(listener); @@ -139,6 +145,10 @@ public class ProjectsRootNodeTest extend } listener.assertEvents("Goal is to receive no events at all", 0); + assertTrue("Finished", OpenProjects.getDefault().openProjects().isDone()); + assertFalse("Not cancelled, Finished", OpenProjects.getDefault().openProjects().isCancelled()); + Project[] arr = OpenProjects.getDefault().openProjects().get(); + assertEquals("30", 30, arr.length); } private static class L implements NodeListener { @@ -176,7 +186,8 @@ public class ProjectsRootNodeTest extend } - private static class TestProjectOpenedHookImpl extends ProjectOpenedHook { + private static class TestProjectOpenedHookImpl extends ProjectOpenedHook + implements Runnable { public static CountDownLatch toOpen = new CountDownLatch(30); public static int opened = 0; @@ -193,7 +204,24 @@ public class ProjectsRootNodeTest extend closed++; } + Project[] arr; + public void run() { + try { + arr = OpenProjects.getDefault().openProjects().get(50, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + fail("Wrong exception"); + } catch (ExecutionException ex) { + fail("Wrong exception"); + } catch (TimeoutException ex) { + // OK + } + } + protected void projectOpened() { + assertFalse("Running", OpenProjects.getDefault().openProjects().isDone()); + // now verify that other threads do not see results from the Future + RequestProcessor.getDefault().post(this).waitFinished(); + assertNull("TimeoutException thrown", arr); if (toWaitOn != null) { try { toWaitOn.await();