Fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=203145.   Changes the way the JSPModelProcessor works so that it triggers off of IResource changes rather than IStructuredModel events.  This has the benefit that the IStructuredModel doesn't need to be held open for long periods and can quickly release resources.  LifecycleListener has been changed to support listening for events on multiple resources so that only one listener is needed for all resources watched by JSPModelProcessors.
diff --git a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/LifecycleStressTest.java b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/LifecycleStressTest.java
new file mode 100644
index 0000000..d8f0f4d
--- /dev/null
+++ b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/LifecycleStressTest.java
@@ -0,0 +1,192 @@
+package org.eclipse.jst.jsf.core.tests.resource;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.jst.jsf.common.internal.ITestTracker;
+import org.eclipse.jst.jsf.common.internal.resource.LifecycleListener;
+import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent.EventType;
+import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent.ReasonType;
+import org.eclipse.jst.jsf.core.tests.TestsPlugin;
+import org.eclipse.jst.jsf.test.util.JSFTestUtil;
+import org.eclipse.jst.jsf.test.util.PerfTracker;
+import org.eclipse.jst.jsf.test.util.WebProjectTestEnvironment;
+
+public class LifecycleStressTest extends TestCase
+{
+    private final static int NUM_PROJECTS = 50;
+    private final static int NUM_FILES = 100;
+    
+    private Map<IProject, List<IFile>>      _resources;
+    private LifecycleListener               _listener;
+
+    @Override
+    protected void setUp() throws Exception 
+    {
+        super.setUp();
+
+        JSFTestUtil.setValidationEnabled(false);
+
+        _resources = new HashMap<IProject, List<IFile>>();
+        _listener = new LifecycleListener();
+
+        for (int i = 0; i < NUM_PROJECTS; i++)
+        {
+            final WebProjectTestEnvironment webProjectTestEnv = 
+                new WebProjectTestEnvironment(getProjectName(getName(),i));
+            webProjectTestEnv.createProject(false);
+            assertNotNull(webProjectTestEnv);
+            assertNotNull(webProjectTestEnv.getTestProject());
+            assertTrue(webProjectTestEnv.getTestProject().isAccessible());
+
+            List<IFile> list = new ArrayList<IFile>();
+
+            for (int j = 0; j < NUM_FILES; j++)
+            {
+                IFile file = (IFile) webProjectTestEnv.loadResourceInWebRoot
+                    (TestsPlugin.getDefault().getBundle()
+                     , "/testfiles/jsps/testdata1.jsp.data", "/testdata_"+j+".jsp");
+
+                list.add(file);
+                _listener.addResource(file);
+            }
+            _resources.put(webProjectTestEnv.getTestProject(), list);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception 
+    {
+        for (final IProject project : _resources.keySet())
+        {
+            project.delete(true, null);
+        }
+
+        _resources.clear();
+    }
+
+    public void testChanges() throws Exception
+    {
+        final int NUM_ITERATIONS = 1000;
+        final int[] projectIdx = new int[NUM_ITERATIONS];
+        final int[] fileIdx = new int[NUM_ITERATIONS];
+
+        final Random random = new Random();
+
+        for (int i = 0; i < NUM_ITERATIONS; i++)
+        {
+            projectIdx[i] = random.nextInt(NUM_PROJECTS);
+            fileIdx[i] = random.nextInt(NUM_FILES);
+        }
+
+        traceIntArray(System.out, "Project sequence: ", projectIdx);
+        traceIntArray(System.out, "File sequence: ", fileIdx);
+
+        LifecycleTestTracker tracker = new LifecycleTestTracker(NUM_ITERATIONS);
+        _listener.setTestTracker(tracker);
+
+        for (int i = 0; i < NUM_ITERATIONS; i++)
+        {
+            final IProject project = 
+                ResourcesPlugin.getWorkspace().getRoot().getProject(getProjectName(getName(),projectIdx[i]));
+            List<IFile> fileList = _resources.get(project);
+            MockListener listener = new MockListener();
+            _listener.addListener(listener);
+
+            // simulates a content change
+            fileList.get(fileIdx[i]).touch(null);
+
+            listener.assertAcceptedEvent(fileList.get(fileIdx[i]), EventType.RESOURCE_CHANGED, ReasonType.RESOURCE_CHANGED_CONTENTS);
+            _listener.removeListener(listener);
+        }
+
+        tracker.report(System.out);
+    }
+
+    private String getProjectName(final String testName, final int idx)
+    {
+        return getClass().getName()+"_"+testName+idx;
+    }
+
+    private void traceIntArray(final PrintStream stream, final String prefix, final int[] intArray)
+    {
+        stream.print(prefix);
+
+        for (int i = 0; i < intArray.length-1; i++)
+        {
+            stream.print(intArray[i]+",");
+        }
+
+        stream.print(intArray[intArray.length-1]);
+        stream.print("\n");
+    }
+
+    private static class LifecycleTestTracker implements ITestTracker
+    {
+        private final PerfTracker  _resourceChangedTimes;
+//        private final PerfTracker  _findMemberTimes;
+
+        private long                _lastResourceChangedId ;
+        //private long                _lastFindMemberChangedId;
+        private long               _curResourceChangedTime;
+        //private long               _curFindMemberChangedTime;
+
+        public LifecycleTestTracker(int numberOfDataPoints)
+        {
+            _resourceChangedTimes = 
+                new PerfTracker("ResourceChanged", numberOfDataPoints+100);
+//            _findMemberTimes = 
+//                new PerfTracker("findMember", numberOfDataPoints*NUM_FILES*NUM_PROJECTS+100);
+        }
+
+        public void fireEvent(final Event event, final long seqId, final String eventLabel) 
+        {
+            // get the current as quickly as possible
+            long curTime = System.nanoTime();
+
+            switch(event)
+            {
+                case START_TRACKING:
+                    if("trackMethod_resourceChanged".equals(eventLabel))
+                    {
+                        _curResourceChangedTime = curTime;
+                        _lastResourceChangedId = seqId;
+                    }
+                    else if ("testFindMember".equals(eventLabel))
+                    {
+//                        _curFindMemberChangedTime = curTime;
+//                        _lastFindMemberChangedId = seqId;
+                    }
+                break;
+                
+                case STOP_TRACKING:
+                    if("trackMethod_resourceChanged".equals(eventLabel))
+                    {
+                        assertEquals(_lastResourceChangedId, seqId);
+                        _resourceChangedTimes.recordTime(curTime - _curResourceChangedTime);
+                    }
+                    else if ("testFindMember".equals(eventLabel))
+                    {
+                        //assertEquals(_lastFindMemberChangedId, seqId);
+                        //_findMemberTimes.recordTime(curTime - _curFindMemberChangedTime);
+                    }
+                break;
+            }
+        }
+        
+        public void report(PrintStream stream)
+        {
+            _resourceChangedTimes.printReport(System.out);
+            //_findMemberTimes.printReport(System.out);
+        }
+    }
+}
diff --git a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/MockListener.java b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/MockListener.java
new file mode 100644
index 0000000..24e8a91
--- /dev/null
+++ b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/MockListener.java
@@ -0,0 +1,75 @@
+/**
+ * 
+ */
+package org.eclipse.jst.jsf.core.tests.resource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jst.jsf.common.internal.resource.IResourceLifecycleListener;
+import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent;
+import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent.EventType;
+import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent.ReasonType;
+
+class MockListener implements IResourceLifecycleListener
+{
+    private List<ResourceLifecycleEvent>   _acceptedEvents = 
+        new ArrayList<ResourceLifecycleEvent>();
+
+    public EventResult acceptEvent(ResourceLifecycleEvent event) 
+    {
+        _acceptedEvents.add(event);
+        return new EventResult();
+    }
+
+    public void assertAcceptedResourceInaccessible(final IResource res, final ReasonType  reason)
+    {
+        assertAcceptedEvent(res, EventType.RESOURCE_INACCESSIBLE, reason);
+    }
+
+    public void assertNoAcceptedResourceInaccessible(final IResource res, final ReasonType reason)
+    {
+        assertNoAcceptedEvent(res, EventType.RESOURCE_INACCESSIBLE, reason);
+    }
+
+    public void assertNoAcceptedEvent(final IResource res, final EventType eventType, final ReasonType reason)
+    {
+        for (ResourceLifecycleEvent event : _acceptedEvents)
+        {
+            if (event.getEventType() == eventType
+                    && event.getAffectedResource().equals(res))
+            {
+                if (reason == event.getReasonType())
+                {
+                    TestLifecycleListener.fail("Expected not to find RESOURCE_INACCESSIBLE event for resource: "+res.toString());                    }
+            }
+        }
+    }
+
+    public void assertAcceptedEvent(final IResource res, final EventType eventType, final ReasonType reason)
+    {
+        for (ResourceLifecycleEvent event : _acceptedEvents)
+        {
+            if (event.getEventType() == eventType
+                    && event.getReasonType() == reason
+                    && event.getAffectedResource().equals(res))
+            {
+                if (reason == event.getReasonType())
+                {
+                    return;
+                }
+                else
+                {
+                    // this output is diagnostic and doesn't necessarily
+                    // indicate a problem
+                    System.out.printf("Expected event found with different result: %s instead of %s", event.getReasonType().toString(), reason.toString());
+                }
+            }
+        }
+
+        // if we get to here then we have failed to find the expected
+        // event
+        TestLifecycleListener.fail("Expected to find" + eventType + " event, reason "+reason+" for resource: "+res.toString());
+    }
+}
\ No newline at end of file
diff --git a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/TestLifecycleListener.java b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/TestLifecycleListener.java
index aa1fdae..eab061a 100644
--- a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/TestLifecycleListener.java
+++ b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/resource/TestLifecycleListener.java
@@ -7,9 +7,9 @@
 
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
-import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent;
 import org.eclipse.jst.jsf.common.internal.resource.IResourceLifecycleListener;
 import org.eclipse.jst.jsf.common.internal.resource.LifecycleListener;
+import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent;
 import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent.EventType;
 import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent.ReasonType;
 import org.eclipse.jst.jsf.core.tests.TestsPlugin;
@@ -19,8 +19,9 @@
 public class TestLifecycleListener extends TestCase 
 {
     private WebProjectTestEnvironment _webProjectTestEnv;
-    private IResource                 _res;
-    
+    private IResource                 _res1;
+    private IResource                 _res2;
+
     @Override
     protected void setUp() throws Exception 
     {
@@ -35,8 +36,10 @@
         assertNotNull(_webProjectTestEnv.getTestProject());
         assertTrue(_webProjectTestEnv.getTestProject().isAccessible());
 
-        _res = _webProjectTestEnv.loadResourceInWebRoot(TestsPlugin.getDefault().getBundle()
+        _res1 = _webProjectTestEnv.loadResourceInWebRoot(TestsPlugin.getDefault().getBundle()
                 , "/testfiles/jsps/testdata1.jsp.data", "/testdata1.jsp");
+        _res2 = _webProjectTestEnv.loadResourceInWebRoot(TestsPlugin.getDefault().getBundle()
+                , "/testfiles/jsps/testdata1.jsp.data", "/testdata2.jsp");
     }
 
     private void testInaccessibleCondition(IResource res, Runnable runnable, ReasonType reason)
@@ -55,10 +58,37 @@
         testListener.removeListener(mockListener);
         testListener.dispose();
     }
+    
+    private void testInaccessibleConditionMultiple(List<IResource> resources, Runnable runnable, List<ReasonType> reasons)
+    {
+        assertEquals(resources.size(), reasons.size());
+
+        for (IResource res : resources)
+        {
+            assertTrue(res.isAccessible());
+        }
+
+        LifecycleListener testListener = new LifecycleListener(resources);
+        MockListener mockListener = new MockListener();
+        testListener.addListener(mockListener);
+
+        runnable.run();
+
+        for (int i = 0; i < resources.size(); i++)
+        {
+            final IResource res = resources.get(i);
+            final ReasonType reason = reasons.get(i);
+            mockListener.assertAcceptedResourceInaccessible(res,reason);
+            assertFalse(res.isAccessible());
+        }
+
+        testListener.removeListener(mockListener);
+        testListener.dispose();
+    }
 
     public void testProjectClosedForProject() throws Exception
     {
-        Runnable runnable = new Runnable()
+        final Runnable runnable = new Runnable()
         {
             public void run() 
             {
@@ -76,7 +106,7 @@
 
     public void testProjectClosedForContainedResource() throws Exception
     {
-        Runnable runnable = new Runnable()
+        final Runnable runnable = new Runnable()
         {
             public void run()
             {
@@ -88,12 +118,12 @@
                 }
             }
         };
-        testInaccessibleCondition(_res, runnable,  ReasonType.RESOURCE_PROJECT_CLOSED);
+        testInaccessibleCondition(_res1, runnable,  ReasonType.RESOURCE_PROJECT_CLOSED);
     }
 
     public void testProjectDeletedForProject() throws Exception
     {
-        Runnable runnable = new Runnable()
+        final Runnable runnable = new Runnable()
         {
             public void run()
             {
@@ -110,45 +140,194 @@
 
     public void testProjectDeletedForContainedResource() throws Exception
     {
-        Runnable runnable = new Runnable()
+        final Runnable runnable = new Runnable()
         {
             public void run()
             {
                 // now delete the project
-                try {
+                try 
+                {
                     _webProjectTestEnv.getTestProject().delete(true, null);
-                } catch (CoreException e) {
+                } catch (CoreException e) 
+                {
                     throw new RuntimeException(e);
                 }
             }
         };
-        testInaccessibleCondition(_res, runnable,  ReasonType.RESOURCE_PROJECT_DELETED);
+        testInaccessibleCondition(_res1, runnable,  ReasonType.RESOURCE_PROJECT_DELETED);
     }
 
     public void testResourceDeleted() throws Exception
     {
-        Runnable runnable = new Runnable()
+        final Runnable runnable = new Runnable()
         {
             public void run()
             {
                 // now delete the resource
                 try {
-                    _res.delete(true, null);
+                    _res1.delete(true, null);
                 } catch (CoreException e) {
                     throw new RuntimeException(e);
                 }
             }
         };
-        testInaccessibleCondition(_res, runnable,  ReasonType.RESOURCE_DELETED);
+        testInaccessibleCondition(_res1, runnable,  ReasonType.RESOURCE_DELETED);
     }
 
+    public void testMultipleResourcesDeleted() throws Exception
+    {
+        final Runnable runnable = new Runnable()
+        {
+            public void run()
+            {
+                // now delete the resource
+                try {
+                    _res1.delete(true, null);
+                    _res2.delete(true, null);
+                } catch (CoreException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+        
+        List<IResource> resources = new ArrayList<IResource>();
+        resources.add(_res1);
+        resources.add(_res2);
+        List<ReasonType> reasons = new ArrayList<ReasonType>(); 
+        reasons.add(ReasonType.RESOURCE_DELETED);
+        reasons.add(ReasonType.RESOURCE_DELETED);
+        testInaccessibleConditionMultiple(resources, runnable, reasons);
+    }
+
+    public void testMultipleResourcesProjectClosed() throws Exception
+    {
+        final Runnable runnable = new Runnable()
+        {
+            public void run()
+            {
+                // now delete the resource
+                try {
+                   _res1.getProject().close(null);
+                } catch (CoreException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+        
+        List<IResource> resources = new ArrayList<IResource>();
+        resources.add(_res1);
+        resources.add(_res2);
+        List<ReasonType> reasons = new ArrayList<ReasonType>(); 
+        reasons.add(ReasonType.RESOURCE_PROJECT_CLOSED);
+        reasons.add(ReasonType.RESOURCE_PROJECT_CLOSED);
+        testInaccessibleConditionMultiple(resources, runnable, reasons);
+    }
+
+    public void testAddResource() throws Exception
+    {
+        assertTrue(_res1.isAccessible());
+        assertTrue(_res2.isAccessible());
+
+        LifecycleListener testListener = new LifecycleListener(_res1);
+        MockListener mockListener = new MockListener();
+        testListener.addListener(mockListener);
+        testListener.addResource(_res2);
+
+        _res1.getProject().close(null);
+
+        mockListener.assertAcceptedResourceInaccessible(_res1, ReasonType.RESOURCE_PROJECT_CLOSED);
+        mockListener.assertAcceptedResourceInaccessible(_res2, ReasonType.RESOURCE_PROJECT_CLOSED);
+        assertFalse(_res1.isAccessible());
+        assertFalse(_res2.isAccessible());
+
+        testListener.removeListener(mockListener);
+        testListener.dispose();
+    }
+    
+    public void testAddRemoveResource() throws Exception
+    {
+        assertTrue(_res1.isAccessible());
+        assertTrue(_res2.isAccessible());
+
+        LifecycleListener testListener = new LifecycleListener(_res1);
+        MockListener mockListener = new MockListener();
+        testListener.addListener(mockListener);
+        testListener.addResource(_res2);
+        testListener.removeResource(_res1);
+        
+        _res1.getProject().close(null);
+
+        // we removed res1, so should find an event for it
+        mockListener.assertNoAcceptedResourceInaccessible(_res1, ReasonType.RESOURCE_PROJECT_CLOSED);
+        mockListener.assertAcceptedResourceInaccessible(_res2, ReasonType.RESOURCE_PROJECT_CLOSED);
+        assertFalse(_res1.isAccessible());
+        assertFalse(_res2.isAccessible());
+
+        testListener.removeListener(mockListener);
+        testListener.dispose();
+    }
+
+    public void testRemoveListenerResource() throws Exception
+    {
+        assertTrue(_res1.isAccessible());
+        assertTrue(_res2.isAccessible());
+
+        LifecycleListener testListener = new LifecycleListener(_res1);
+        testListener.addResource(_res2);
+        MockListener mockListener = new MockListener();
+        MockListener mockListener2 = new MockListener();
+        
+        testListener.addListener(mockListener);
+        testListener.addListener(mockListener2);
+        _res1.delete(true, null);
+
+        // both listeners should get the event on res1
+        mockListener.assertAcceptedResourceInaccessible(_res1, ReasonType.RESOURCE_DELETED);
+        mockListener2.assertAcceptedResourceInaccessible(_res1, ReasonType.RESOURCE_DELETED);
+        assertFalse(_res1.isAccessible());
+
+        // remove the listener for mock2
+        testListener.removeListener(mockListener2);
+
+        _res2.delete(true, null);
+
+        // the first mockListener should get it
+        mockListener.assertAcceptedResourceInaccessible(_res2, ReasonType.RESOURCE_DELETED);
+        // the second one was removed before the delete
+        // so it should not have accepted an event
+        mockListener2.assertNoAcceptedResourceInaccessible(_res2, ReasonType.RESOURCE_DELETED);
+        assertFalse(_res2.isAccessible());
+
+        testListener.removeListener(mockListener);
+        testListener.dispose();
+    }
+    
+    public void testResourceChangedEvent() throws Exception
+    {
+        assertTrue(_res1.isAccessible());
+        assertTrue(_res2.isAccessible());
+
+        LifecycleListener testListener = new LifecycleListener(_res1);
+        testListener.addResource(_res2);
+        MockListener mockListener = new MockListener();
+        testListener.addListener(mockListener);
+        
+        // simulate a content change
+        _res1.touch(null);
+
+        mockListener.assertAcceptedEvent(_res1, EventType.RESOURCE_CHANGED, ReasonType.RESOURCE_CHANGED_CONTENTS);
+
+        testListener.removeListener(mockListener);
+        testListener.dispose();
+    }
+    
     public void testDisposeAfterEvent() throws Exception
     {
-        LifecycleListener testListener = new LifecycleListener(_res);
+        LifecycleListener testListener = new LifecycleListener(_res1);
         MockListenerThatDoesDispose mockListener = new MockListenerThatDoesDispose();
         testListener.addListener(mockListener);
 
-        _res.delete(true, null);
+        _res1.delete(true, null);
         
         assertTrue(testListener.isDisposed());
         
@@ -165,41 +344,6 @@
         assertTrue(caughtAssertion);
     }
 
-    private class MockListener implements IResourceLifecycleListener
-    {
-        private List<ResourceLifecycleEvent>   acceptedEvents = 
-            new ArrayList<ResourceLifecycleEvent>();
-
-        public EventResult acceptEvent(ResourceLifecycleEvent event) 
-        {
-            acceptedEvents.add(event);
-            return new EventResult();
-        }
-
-        public void assertAcceptedResourceInaccessible(final IResource res, final ReasonType  reason)
-        {
-            for (ResourceLifecycleEvent event : acceptedEvents)
-            {
-                if (event.getEventType() == EventType.RESOURCE_INACCESSIBLE
-                        && event.getAffectedResource() == res)
-                {
-                    if (reason == event.getReasonType())
-                    {
-                        return;
-                    }
-                    else
-                    {
-                        System.out.printf("Expected event found with different result: %s instead of %s", event.getReasonType().toString(), reason.toString());
-                    }
-                }
-            }
-
-            // if we get to here then we have failed to find the expected
-            // event
-            fail("Expected to find RESOURCE_INACCESSIBLE event for resource: "+res.toString());
-        }
-    }
-    
     /**
      * A mock object that tests the disposeAfterEvent flag.
      * @author cbateman
@@ -209,8 +353,7 @@
     {
         public EventResult acceptEvent(ResourceLifecycleEvent event) 
         {
-            EventResult result = new EventResult();
-            result.setDisposeAfterEvent(true);
+            EventResult result = EventResult.getDisposeAfterEventResult();
             return result;
         }
     }
diff --git a/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestJSPModelProcessor.java b/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestJSPModelProcessor.java
index 1de6302..34483ce 100644
--- a/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestJSPModelProcessor.java
+++ b/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestJSPModelProcessor.java
@@ -1,12 +1,16 @@
 package org.eclipse.jst.jsf.designtime.tests;
 
 import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeSet;
 
 import junit.framework.TestCase;
 
 import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IResource;
 import org.eclipse.jst.jsf.context.symbol.ISymbol;
 import org.eclipse.jst.jsf.context.symbol.source.ISymbolConstants;
 import org.eclipse.jst.jsf.core.IJSFCoreConstants;
@@ -19,7 +23,10 @@
 
 public class TestJSPModelProcessor extends TestCase 
 {
+    private static final int NUM_JSPS = 25;
     private IFile _testJSP1;
+    private List<IFile> _jsps;
+    
     private JSFFacetedTestEnvironment _jsfFactedTestEnvironment;
 
     @Override
@@ -42,9 +49,15 @@
                 , new ByteArrayInputStream(input.toBytes())
                 , "bundles", "bundle1.properties");
         
-        IResource res = projectTestEnvironment.loadResourceInWebRoot(DesignTimeTestsPlugin.getDefault().getBundle()
+        _testJSP1 = (IFile) projectTestEnvironment.loadResourceInWebRoot(DesignTimeTestsPlugin.getDefault().getBundle()
                 , "/testdata/testdata1.jsp.data", "testdata1.jsp");
-        _testJSP1 = (IFile) res;
+
+        _jsps = new ArrayList<IFile>(NUM_JSPS);
+        for (int i = 0; i < NUM_JSPS; i++)
+        {
+            _jsps.add((IFile) projectTestEnvironment.loadResourceInWebRoot(DesignTimeTestsPlugin.getDefault().getBundle()
+                , "/testdata/testdata1.jsp.data", "testdata_"+i+".jsp"));
+        }
 
         _jsfFactedTestEnvironment = new JSFFacetedTestEnvironment(projectTestEnvironment);
         _jsfFactedTestEnvironment.initialize(IJSFCoreConstants.FACET_VERSION_1_1);    
@@ -54,55 +67,11 @@
     protected void tearDown() throws Exception {
     }
 
-    public void testGetAndDispose() throws Exception
+    public void testGet() throws Exception
     {
         JSPModelProcessor processor = JSPModelProcessor.get(_testJSP1);
         assertNotNull(processor);
-        assertEquals(1, processor.getRefCount());
-        JSPModelProcessor.dispose(_testJSP1);
-        assertEquals(0, processor.getRefCount());
-    }
-
-    public void testGetAndDisposeMultiple() throws Exception
-    {
-        JSPModelProcessor processor = JSPModelProcessor.get(_testJSP1);
-        assertNotNull(processor);
-        assertEquals(1, processor.getRefCount());
-
-        // get again
-        processor = JSPModelProcessor.get(_testJSP1);
-        assertEquals(2, processor.getRefCount());
-        // and again
-        processor = JSPModelProcessor.get(_testJSP1);
-        assertEquals(3, processor.getRefCount());
-        // and dispose once
-        JSPModelProcessor.dispose(_testJSP1);
-        assertEquals(2, processor.getRefCount());
-
-        // and reacquire
-        processor = JSPModelProcessor.get(_testJSP1);
-        assertEquals(3, processor.getRefCount());
-
-        // dispose twice
-        JSPModelProcessor.dispose(_testJSP1);
-        JSPModelProcessor.dispose(_testJSP1);
-        assertEquals(1, processor.getRefCount());
-
-        // one final dispose
-        JSPModelProcessor.dispose(_testJSP1);
-        assertEquals(0, processor.getRefCount());
-
-        // exceed by one
-        JSPModelProcessor.dispose(_testJSP1);
-        assertEquals(0, processor.getRefCount());
-
-        // acquire
-        processor = JSPModelProcessor.get(_testJSP1);
-        assertEquals(1, processor.getRefCount());
-
-        // dispose single reference
-        JSPModelProcessor.dispose(_testJSP1);
-        assertEquals(0, processor.getRefCount());
+        assertFalse(processor.isDisposed());
     }
 
     public void testGetMapForScope() throws Exception
@@ -111,29 +80,21 @@
         JSPModelProcessor processor = JSPModelProcessor.get(_testJSP1);
         assertNotNull(processor);
 
-        try
-        {
-            Map<Object, ISymbol> scopeMap = 
-                processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_REQUEST_STRING);
-            assertTrue(scopeMap.isEmpty());
+         Map<Object, ISymbol> scopeMap = 
+            processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_REQUEST_STRING);
+        assertTrue(scopeMap.isEmpty());
 
-            scopeMap = 
-                processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_SESSION_STRING);
-            assertTrue(scopeMap.isEmpty());
+        scopeMap = 
+            processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_SESSION_STRING);
+        assertTrue(scopeMap.isEmpty());
 
-            scopeMap = 
-                processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_APPLICATION_STRING);
-            assertTrue(scopeMap.isEmpty());
+        scopeMap = 
+            processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_APPLICATION_STRING);
+        assertTrue(scopeMap.isEmpty());
 
-            scopeMap = 
-                processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_NONE_STRING);
-            assertTrue(scopeMap.isEmpty());
-        }
-        finally
-        {
-            JSPModelProcessor.dispose(_testJSP1);
-            assertEquals(0, processor.getRefCount());
-        }
+        scopeMap = 
+            processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_NONE_STRING);
+        assertTrue(scopeMap.isEmpty());
     }
 
     public void testRefreshAndGet() throws Exception
@@ -142,25 +103,17 @@
         JSPModelProcessor processor = JSPModelProcessor.get(_testJSP1);
         assertNotNull(processor);
 
-        try
-        {
-            Map<Object, ISymbol> scopeMap = 
-                processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_REQUEST_STRING);
-            assertTrue(scopeMap.isEmpty());
+        Map<Object, ISymbol> scopeMap = 
+            processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_REQUEST_STRING);
+        assertTrue(scopeMap.isEmpty());
 
-            processor.refresh(false);
+        processor.refresh(false);
 
-            // after refresh we should have a symbol for the loadBundle and the dataTable
-            scopeMap = 
-                processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_REQUEST_STRING);
-            assertFalse(scopeMap.isEmpty());
-            assertEquals(2, scopeMap.size());
-        }
-        finally
-        {
-            JSPModelProcessor.dispose(_testJSP1);
-            assertEquals(0, processor.getRefCount());
-        }
+        // after refresh we should have a symbol for the loadBundle and the dataTable
+        scopeMap = 
+            processor.getMapForScope(ISymbolConstants.SYMBOL_SCOPE_REQUEST_STRING);
+        assertFalse(scopeMap.isEmpty());
+        assertEquals(2, scopeMap.size());
     }
 
     public void testFileDeletion_RegressionBug199480() throws Exception
@@ -172,12 +125,114 @@
         // if we not refreshed yet, then should be no symbols
         JSPModelProcessor processor = JSPModelProcessor.get(_testJSP1);
         assertNotNull(processor);
+        assertFalse(processor.isDisposed());
 
         _testJSP1.delete(true, null);
         // file is deleted, so the processor should dispose itself on the 
         // resource change event
         assertTrue(processor.isDisposed());
-        assertEquals(0, processor.getRefCount());
-        processor.refresh(true);
+    }
+    
+    public void testProjectClosure() throws Exception
+    {
+        // ensure that if the enclosing project of the associated IFile
+        // is closed, then the processor gets disposed
+        JSPModelProcessor processor = JSPModelProcessor.get(_testJSP1);
+        assertNotNull(processor);
+        assertFalse(processor.isDisposed());
+
+        _testJSP1.getProject().close(null);
+
+        // file is deleted, so the processor should dispose itself on the 
+        // resource change event
+        assertTrue(processor.isDisposed());
+    }
+
+    public void testProjectDeletion() throws Exception
+    {
+        // ensure that if the enclosing project of the associated IFile
+        // is deleted, then the processor gets disposed
+        JSPModelProcessor processor = JSPModelProcessor.get(_testJSP1);
+        assertNotNull(processor);
+        assertFalse(processor.isDisposed());
+
+        _testJSP1.getProject().delete(true,null);
+
+        // file is deleted, so the processor should dispose itself on the 
+        // resource change event
+        assertTrue(processor.isDisposed());
+    }
+    
+    public void testChangeRefresh() throws Exception
+    {
+        // random order of access to the jsps, but always the same between runs
+        final int order[] = new int[] {6,19,10,16,14,4,13,11,24,2,3,23,20,15,17,9,1,5,22,12,21,8,18,0,7};
+        assertEquals(NUM_JSPS, order.length);
+        
+        for (int i = 0; i < order.length; i++)
+        {
+            final IFile file = _jsps.get(order[i]);
+            JSPModelProcessor processor = JSPModelProcessor.get(file);
+            // the processor model should start out dirty since it won't
+            // get refreshed unless the resource detects a change or if
+            // it is explicitly refreshed
+            assertTrue(processor.isModelDirty()); 
+
+            // this should trigger a change event and update the model
+            file.touch(null);
+            
+            assertFalse(processor.isModelDirty());
+
+            // now delete the file and ensure the processor is disposed
+            file.delete(true, null);
+
+            assertTrue(processor.isDisposed());
+        }
+    }
+
+    public void testExplicitRefresh() throws Exception
+    {
+        // random order of access to the jsps, but always the same between runs
+        final int order[] = new int[] {6,19,10,16,14,4,13,11,24,2,3,23,20,15,17,9,1,5,22,12,21,8,18,0,7};
+        assertEquals(NUM_JSPS, order.length);
+        
+        for (int i = 0; i < order.length; i++)
+        {
+            final IFile file = _jsps.get(order[i]);
+            JSPModelProcessor processor = JSPModelProcessor.get(file);
+            // the processor model should start out dirty since it won't
+            // get refreshed unless the resource detects a change or if
+            // it is explicitly refreshed
+            assertTrue(processor.isModelDirty()); 
+
+            // since the model is dirty this should trigger a refresh
+            processor.refresh(false);
+            
+            assertFalse(processor.isModelDirty());
+
+            // now delete the file and ensure the processor is disposed
+            file.delete(true, null);
+
+            assertTrue(processor.isDisposed());
+        }
+    }
+    
+    public static void main(String[] args)
+    {
+       Set<Integer> set = new TreeSet<Integer>();
+       
+       Random random = new Random();
+       
+       while(set.size() < NUM_JSPS)
+       {
+           Integer value = Integer.valueOf(Math.abs(random.nextInt()) % NUM_JSPS);
+           
+           if (!set.contains(value))
+           {
+               System.out.printf("%d,", value);
+               set.add(value);
+           }
+       }
     }
 }
+
diff --git a/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestResourceBundleMapSource.java b/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestResourceBundleMapSource.java
index 78af824..c3f9ef0 100644
--- a/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestResourceBundleMapSource.java
+++ b/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestResourceBundleMapSource.java
@@ -102,22 +102,15 @@
      * what's expected
      */
     @SuppressWarnings("unchecked")
-    public void testSanity()
+    public void testSanity() throws Exception
     {
-        try
-        {
-            Map  map = ResourceBundleMapSourceFactory.getResourceBundleMapSource(_project1, "bundles.bundle1");
-            assertNotNull(map);
-            assertEquals(map.size(), 3);
+        Map  map = ResourceBundleMapSourceFactory.getResourceBundleMapSource(_project1, "bundles.bundle1");
+        assertNotNull(map);
+        assertEquals(map.size(), 3);
 
-            map = ResourceBundleMapSourceFactory.getResourceBundleMapSource(_project2, "bundles.bundle1");
-            assertNotNull(map);
-            assertEquals(map.size(), 3);
-        }
-        catch (Exception e)
-        {
-            fail(e.getLocalizedMessage());
-        }
+        map = ResourceBundleMapSourceFactory.getResourceBundleMapSource(_project2, "bundles.bundle1");
+        assertNotNull(map);
+        assertEquals(map.size(), 3);
     }
 
     @Override
@@ -133,44 +126,30 @@
      * Verify the expected contents of bundle1 in project1
      */
     @SuppressWarnings("unchecked")
-    public void testContentsProject1Bundle1()
+    public void testContentsProject1Bundle1()  throws Exception
     {
-        try
-        {
-            Map  map = ResourceBundleMapSourceFactory.getResourceBundleMapSource(_project1, "bundles.bundle1");
-            assertTrue(map.containsKey("prop1"));
-            assertEquals("blah", map.get("prop1"));
-            assertTrue(map.containsKey("one.dot"));
-            assertEquals("blah1", map.get("one.dot"));
-            assertTrue(map.containsKey("two.dot.property"));
-            assertEquals("blah3", map.get("two.dot.property"));
-        }
-        catch (Exception e)
-        {
-            fail(e.getLocalizedMessage());
-        }
+        Map  map = ResourceBundleMapSourceFactory.getResourceBundleMapSource(_project1, "bundles.bundle1");
+        assertTrue(map.containsKey("prop1"));
+        assertEquals("blah", map.get("prop1"));
+        assertTrue(map.containsKey("one.dot"));
+        assertEquals("blah1", map.get("one.dot"));
+        assertTrue(map.containsKey("two.dot.property"));
+        assertEquals("blah3", map.get("two.dot.property"));
     }
 
     /**
      * Verify the expected contents of bundle1 in project2
      */
     @SuppressWarnings("unchecked")
-    public void testContentsProject2Bundle1()
+    public void testContentsProject2Bundle1() throws Exception
     {
-        try
-        {
-            Map  map = ResourceBundleMapSourceFactory.getResourceBundleMapSource(_project2, "bundles.bundle1");
-            assertTrue(map.containsKey("x_prop1"));
-            assertEquals("x_blah", map.get("x_prop1"));
-            assertTrue(map.containsKey("x_one.dot"));
-            assertEquals("x_blah1", map.get("x_one.dot"));
-            assertTrue(map.containsKey("x_two.dot.property"));
-            assertEquals("x_blah3", map.get("x_two.dot.property"));
-        }
-        catch (Exception e)
-        {
-            fail(e.getLocalizedMessage());
-        }
+        Map  map = ResourceBundleMapSourceFactory.getResourceBundleMapSource(_project2, "bundles.bundle1");
+        assertTrue(map.containsKey("x_prop1"));
+        assertEquals("x_blah", map.get("x_prop1"));
+        assertTrue(map.containsKey("x_one.dot"));
+        assertEquals("x_blah1", map.get("x_one.dot"));
+        assertTrue(map.containsKey("x_two.dot.property"));
+        assertEquals("x_blah3", map.get("x_two.dot.property"));
     }
 
     /**
@@ -211,8 +190,6 @@
         assertNull(map.get("two.dot.property"));
     }
 
-    
-    
     /**
      * Regression test of https://bugs.eclipse.org/bugs/show_bug.cgi?id=196452.
      * 
diff --git a/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestStartupHandler.java b/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestStartupHandler.java
index 99c40fc..d526916 100644
--- a/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestStartupHandler.java
+++ b/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestStartupHandler.java
@@ -32,10 +32,10 @@
 {
 	private IFile _testJSP1;
 	private WebProjectTestEnvironment _projectTestEnvironment;
-	private JSFFacetedTestEnvironment _jsfFactedTestEnvironment;
+    private JSFFacetedTestEnvironment _jsfFactedTestEnvironment;
 
-	protected void setUp() throws Exception 
-	{
+    protected void setUp() throws Exception 
+    {
         super.setUp();
         JSFTestUtil.setValidationEnabled(false);
         JSFTestUtil.setInternetProxyPreferences(true, "www-proxy.us.oracle.com","80");
@@ -45,120 +45,122 @@
         _projectTestEnvironment.createProject(false);
 
         JDTTestEnvironment jdtTestEnvironment = 
-        	new JDTTestEnvironment(_projectTestEnvironment);
+            new JDTTestEnvironment(_projectTestEnvironment);
 
         final TestFileResource input = new TestFileResource();
         input.load(DesignTimeTestsPlugin.getDefault().getBundle(), 
-        		"/testdata/bundle1.resources.data");
+                "/testdata/bundle1.resources.data");
         jdtTestEnvironment.addResourceFile("src"
-        		, new ByteArrayInputStream(input.toBytes())
-        		, "bundles", "bundle1.properties");
+                , new ByteArrayInputStream(input.toBytes())
+                , "bundles", "bundle1.properties");
         
         IResource res = _projectTestEnvironment.loadResourceInWebRoot(DesignTimeTestsPlugin.getDefault().getBundle()
-        		, "/testdata/testdata1.jsp.data", "testdata1.jsp");
+                , "/testdata/testdata1.jsp.data", "testdata1.jsp");
         _testJSP1 = (IFile) res;
 
         _jsfFactedTestEnvironment = new JSFFacetedTestEnvironment(_projectTestEnvironment);
-        _jsfFactedTestEnvironment.initialize(IJSFCoreConstants.FACET_VERSION_1_1);	
+        _jsfFactedTestEnvironment.initialize(IJSFCoreConstants.FACET_VERSION_1_1);    
     }
 
-	protected void tearDown() throws Exception 
-	{
-		super.tearDown();
-	}
+    protected void tearDown() throws Exception 
+    {
+        super.tearDown();
+    }
 
-	public void testLaunchEditor() throws Exception
-	{
-		final IWorkbenchPage  curPage = 
-			PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
-		final IEditorPart editor =
-			IDE.openEditor
-				(curPage
-				, _testJSP1);
-		assertNotNull(editor);
-		final IEditorPart foundEditor = ResourceUtil.findEditor(curPage, _testJSP1);
-		assertEquals(editor, foundEditor);
-		
-		JSPModelProcessor  processor = JSPModelProcessor.get(_testJSP1);
-		// should have a second reference due to the open editor
-		assertEquals(2, processor.getRefCount());
-		
-		curPage.closeEditor(foundEditor, false);
-		
-		// closing the editor part should decrement the refcount
-		assertEquals(1, processor.getRefCount());
-		
-		JSPModelProcessor.dispose(_testJSP1);
-		assertEquals(0, processor.getRefCount());
-	}
-	
-	/**
-	 * Ensure that if an editor with a JSPModelProcessor attached is closed
-	 * through the action of closing the enclosing IProject, then StartupHandler
-	 * detects this and correctly disposes the model processor.
-	 * 
-	 * @throws Exception
-	 */
-	public void testBug196760() throws Exception
-	{
-		final IWorkbenchPage  curPage = 
-			PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
-		final IEditorPart editor =	IDE.openEditor(curPage, _testJSP1);
-		assertNotNull(editor);
-		final IEditorPart foundEditor = ResourceUtil.findEditor(curPage, _testJSP1);
-		assertEquals(editor, foundEditor);
+    public void testLaunchEditor() throws Exception
+    {
+        final IWorkbenchPage  curPage = 
+            PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+        final IEditorPart editor =
+            IDE.openEditor
+                (curPage
+                , _testJSP1);
+        assertNotNull(editor);
+        final IEditorPart foundEditor = ResourceUtil.findEditor(curPage, _testJSP1);
+        assertEquals(editor, foundEditor);
+        
+        JSPModelProcessor  processor = JSPModelProcessor.get(_testJSP1);
+        // should have a second reference due to the open editor
+        assertNotNull(processor);
+        assertFalse(processor.isDisposed());
+        curPage.closeEditor(foundEditor, false);
 
-		final JSPModelProcessor  processor = JSPModelProcessor.get(_testJSP1);
-		// should have a second reference due to the open editor
-		assertEquals(2, processor.getRefCount());
+        // closing the editor part should have no effect on the processor
+        // being disposed
+        assertFalse(processor.isDisposed());
+        JSPModelProcessor notDuplicate = JSPModelProcessor.get(_testJSP1);
+        assertEquals(processor, notDuplicate);
+    }
 
-		// close project with with user action
-		final CloseResourceAction action =
-			new CloseResourceAction(
-					PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell())
-			{
-				public void superRun()
-				{
-					super.run();
-					
-					int numTries = 0;
-					
-					while(_projectTestEnvironment.getTestProject().isOpen())
-					{
-						try {
-							numTries++;
-							Thread.sleep(1000);
-						} catch (InterruptedException e) {
-							// fall through
-						}
+    /**
+     * Ensure that if an editor with a JSPModelProcessor attached is closed
+     * through the action of closing the enclosing IProject, then StartupHandler
+     * detects this and correctly disposes the model processor.
+     * 
+     * @throws Exception
+     */
+    public void testBug196760() throws Exception
+    {
+        final IWorkbenchPage  curPage = 
+            PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+        final IEditorPart editor =    IDE.openEditor(curPage, _testJSP1);
+        assertNotNull(editor);
+        final IEditorPart foundEditor = ResourceUtil.findEditor(curPage, _testJSP1);
+        assertEquals(editor, foundEditor);
 
-						if (numTries > 10)
-						{
-							throw new RuntimeException("Number of tries exceeeded 10");
-						}
-					}
-				}
+        final JSPModelProcessor  processor = JSPModelProcessor.get(_testJSP1);
+        assertNotNull(processor);
+        assertFalse(processor.isDisposed());
 
-				@Override
-				public void run() 
-				{
-					PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
-					{
-						public void run() 
-						{
-							superRun();
-						}
-					});
-				}
-			};
+        // close project with with user action
+        final CloseResourceAction action =
+            new CloseResourceAction(
+                    PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell())
+            {
+                public void superRun()
+                {
+                    super.run();
+                    
+                    int numTries = 0;
+                    
+                    while(_projectTestEnvironment.getTestProject().isOpen())
+                    {
+                        try {
+                            numTries++;
+                            Thread.sleep(1000);
+                        } catch (InterruptedException e) {
+                            // fall through
+                        }
 
-		action.selectionChanged(new StructuredSelection(_projectTestEnvironment.getTestProject()));
-		action.run();
+                        if (numTries > 10)
+                        {
+                            throw new RuntimeException("Number of tries exceeeded 10");
+                        }
+                    }
+                }
 
-		// if the editor is closed due a project close, ensure that the processor is properly
-		// disposed
-		assertFalse(_projectTestEnvironment.getTestProject().isOpen());
-		JSPModelProcessor.dispose(_testJSP1);
-		assertEquals(0, processor.getRefCount());
-	}
+                @Override
+                public void run() 
+                {
+                    PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+                    {
+                        public void run() 
+                        {
+                            superRun();
+                        }
+                    });
+                }
+            };
+
+        action.selectionChanged(new StructuredSelection(_projectTestEnvironment.getTestProject()));
+        action.run();
+
+        // if the editor is closed due a project close, ensure that the processor is properly
+        // disposed
+        assertFalse(_projectTestEnvironment.getTestProject().isOpen());
+        
+        // assert that the processor is disposed because its parent project
+        // has been closed
+        assertTrue(processor.isDisposed());
+    }
 }
diff --git a/jsf/tests/org.eclipse.jst.jsf.test.util/src/org/eclipse/jst/jsf/test/util/PerfTracker.java b/jsf/tests/org.eclipse.jst.jsf.test.util/src/org/eclipse/jst/jsf/test/util/PerfTracker.java
new file mode 100644
index 0000000..f8300f9
--- /dev/null
+++ b/jsf/tests/org.eclipse.jst.jsf.test.util/src/org/eclipse/jst/jsf/test/util/PerfTracker.java
@@ -0,0 +1,74 @@
+/**
+ * 
+ */
+package org.eclipse.jst.jsf.test.util;
+
+import java.io.PrintStream;
+import java.text.MessageFormat;
+
+public class PerfTracker {
+    private long _max = Long.MIN_VALUE; // ensure any value compared to to this
+                                        // will be bigger
+    private long _maxIdx = 0;
+    private long _min = Long.MAX_VALUE; // ensure any value compared to this
+                                        // will be smaller
+    private long _minIdx = 0;
+    private long _runningTotal = 0;
+    private final long[] _times;
+    private int _numTimesRecorded = 0;
+
+    private final String _name;
+
+    public PerfTracker(final String name, final int numOfRuns) {
+        _times = new long[numOfRuns];
+        _name = name;
+    }
+
+    public void recordTime(long time) {
+        _max = Math.max(_max, time);
+        _maxIdx = _max == time ? _numTimesRecorded : _maxIdx;
+
+        _min = Math.min(_min, time);
+        _minIdx = _min == time ? _numTimesRecorded : _minIdx;
+
+        _runningTotal += time;
+
+        _times[_numTimesRecorded++] = time;
+    }
+
+    @SuppressWarnings("boxing")
+    public void printReport(PrintStream outStream) {
+        outStream
+                .println("===================================================");
+        outStream.println("Report for performance test: " + _name);
+        outStream.println("Number of iterations: " + _numTimesRecorded);
+        outStream
+                .println("===================================================");
+        outStream.println(MessageFormat.format("Max: {0}, Max Index: {1}",
+                new Object[] { _max, _maxIdx }));
+        outStream.println(MessageFormat.format("Min: {0}, Min Index: {1}",
+                new Object[] { _min, _minIdx }));
+        outStream.println(MessageFormat.format(
+                "Avg: {0}, StdDev: {1}, StdDev Ignore Max/Min: {2}",
+                new Object[] { average(), calculateStdDev(false),
+                        calculateStdDev(true) }));
+        outStream
+                .println("===================================================");
+        outStream.println("");
+    }
+
+    private double average() {
+        return _runningTotal / _numTimesRecorded;
+    }
+
+    private double calculateStdDev(boolean ignoreMaxMin) {
+        double total = 0;
+        final double avg = average();
+        for (int i = 0; i < _numTimesRecorded; i++) {
+            if (!ignoreMaxMin || ((i != _maxIdx) && (i != _minIdx))) {
+                total += Math.pow((_times[i] - avg), 2) / _numTimesRecorded;
+            }
+        }
+        return Math.sqrt(total);
+    }
+}
\ No newline at end of file
diff --git a/jsf/tests/org.eclipse.jst.jsf.validation.el.tests/src/org/eclipse/jst/jsf/validation/el/tests/perf/StressTest.java b/jsf/tests/org.eclipse.jst.jsf.validation.el.tests/src/org/eclipse/jst/jsf/validation/el/tests/perf/StressTest.java
index 6efd5e1..1d13284 100644
--- a/jsf/tests/org.eclipse.jst.jsf.validation.el.tests/src/org/eclipse/jst/jsf/validation/el/tests/perf/StressTest.java
+++ b/jsf/tests/org.eclipse.jst.jsf.validation.el.tests/src/org/eclipse/jst/jsf/validation/el/tests/perf/StressTest.java
@@ -1,7 +1,5 @@
 package org.eclipse.jst.jsf.validation.el.tests.perf;
 
-import java.io.PrintStream;
-import java.text.MessageFormat;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.jst.jsf.context.symbol.ISymbol;
@@ -10,6 +8,7 @@
 import org.eclipse.jst.jsf.designtime.context.DTFacesContext;
 import org.eclipse.jst.jsf.designtime.el.AbstractDTPropertyResolver;
 import org.eclipse.jst.jsf.designtime.el.AbstractDTVariableResolver;
+import org.eclipse.jst.jsf.test.util.PerfTracker;
 import org.eclipse.jst.jsf.validation.el.tests.base.JSPTestCase;
 import org.eclipse.jst.jsf.validation.el.tests.base.SingleJSPTestCase;
 import org.eclipse.jst.jsf.validation.internal.el.ELExpressionValidator;
@@ -118,7 +117,8 @@
         final int      elOffset = 819;
         assertEquals("myBean.stringProperty", getELText(_structuredDocument,elOffset));
 
-        final PerfTracker perfTracker = new PerfTracker("Stress Simple Bean Property Validation", numTimes);
+        final PerfTracker perfTracker = 
+            new PerfTracker("Stress Simple Bean Property Validation", numTimes);
 
         // resolve the same variable 100K times
         for (int x = 0; x < numTimes; x++)
@@ -135,70 +135,4 @@
 
         perfTracker.printReport(System.out);
     }
-    
-   private static class PerfTracker
-   {
-       private long           _max = Long.MIN_VALUE;  // ensure any value compared to to this will be bigger
-       private long           _maxIdx = 0;
-       private long           _min = Long.MAX_VALUE;  // ensure any value compared to this will be smaller
-       private long           _minIdx = 0;
-       private long           _runningTotal = 0;
-       private final long[]   _times;
-       private int            _numTimesRecorded = 0;
-
-       private final String         _name;
-       
-       public PerfTracker(final String name, final int numOfRuns)
-       {
-           _times = new long[numOfRuns];
-           _name = name;
-       }
-
-       public void recordTime(long time)
-       {
-           _max = Math.max(_max, time);
-           _maxIdx = _max == time ? _numTimesRecorded : _maxIdx;
-           
-           _min = Math.min(_min, time);
-           _minIdx = _min == time ? _numTimesRecorded : _minIdx;
-           
-           _runningTotal += time;
-
-           _times[_numTimesRecorded] = time;
-           _numTimesRecorded++;
-       }
-       @SuppressWarnings("boxing")
-    public void printReport(PrintStream outStream)
-       {
-           outStream.println("===================================================");
-           outStream.println("Report for performance test: "+_name);
-           outStream.println("Number of iterations: "+_numTimesRecorded);
-           outStream.println("===================================================");
-           outStream.println(MessageFormat.format("Max: {0}, Max Index: {1}", new Object[] {_max, _maxIdx}));
-           outStream.println(MessageFormat.format("Min: {0}, Min Index: {1}", new Object[] {_min, _minIdx}));
-           outStream.println(MessageFormat.format("Avg: {0}, StdDev: {1}, StdDev Ignore Max/Min: {2}", new Object[]{average(), calculateStdDev(false), calculateStdDev(true)}));
-           outStream.println("===================================================");
-           outStream.println("");
-       }
-       
-       private double   average()
-       {
-           return _runningTotal/_numTimesRecorded;
-       }
-       
-       private double calculateStdDev(boolean ignoreMaxMin)
-       {
-           double total = 0;
-           final double avg = average();
-           for (int i = 0; i < _numTimesRecorded; i++)
-           {
-               if (!ignoreMaxMin 
-                      || ((i != _maxIdx) && (i != _minIdx)))
-               {
-                   total += Math.pow((_times[i] - avg), 2) / _numTimesRecorded;
-               }
-           }
-           return Math.sqrt(total);
-       }
-   }
 }