Fix for PMC approved bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=203145
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/ITestTracker.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/ITestTracker.java
new file mode 100644
index 0000000..a3369aa
--- /dev/null
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/ITestTracker.java
@@ -0,0 +1,40 @@
+package org.eclipse.jst.jsf.common.internal;
+
+/**
+ * An injection interface that allows classes to selectively report test progress.
+ * 
+ * @author cbateman
+ *
+ */
+public interface ITestTracker 
+{
+    /**
+     * Event types
+     *
+     */
+    public enum Event
+    {
+        /**
+         * Signals that tracking should begin on the eventLabel
+         * The seqId must be repeated on the STOP_TRACKING event
+         * for the same event.
+         */
+        START_TRACKING,
+        /**
+         * Signals that tracking should stop on the named event
+         * for the seqId that was passed first in the START_TRACKING.
+         * 
+         */
+        STOP_TRACKING
+    }
+    
+    /**
+     * Fires the event of type event, a unique instance tracking seqId
+     * and a label called eventLabel.
+     * 
+     * @param event
+     * @param seqId
+     * @param eventLabel
+     */
+    void fireEvent(Event event, long seqId, String eventLabel);
+}
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/IResourceLifecycleListener.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/IResourceLifecycleListener.java
index 16a3d44..a3ab30f 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/IResourceLifecycleListener.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/IResourceLifecycleListener.java
@@ -22,6 +22,36 @@
      */
     public static class EventResult
     {
+        private static EventResult DEFAULT;
+
+        /**
+         * @return an event result with defaults initialized
+         */
+        public static EventResult getDefaultEventResult()
+        {
+            if (DEFAULT == null)
+            {
+                DEFAULT = new EventResult();
+            }
+            return DEFAULT;
+        }
+
+        private static EventResult DISPOSE_AFTER_EVENT;
+
+        /**
+         * @return an event result with default except dispose after
+         * is set
+         */
+        public static EventResult getDisposeAfterEventResult()
+        {
+            if (DISPOSE_AFTER_EVENT == null)
+            {
+                DISPOSE_AFTER_EVENT = new EventResult();
+                DISPOSE_AFTER_EVENT.setDisposeAfterEvent(true);
+            }
+            return DISPOSE_AFTER_EVENT;
+        }
+
         /**
          * set to true if after the current event is finished firing, the source
          * should be disposed. If self-disposal is not applicable, the flag is ignored
@@ -39,7 +69,7 @@
         /**
          * @param disposeAfterEvent
          */
-        public void setDisposeAfterEvent(boolean disposeAfterEvent) {
+        protected void setDisposeAfterEvent(boolean disposeAfterEvent) {
             _disposeAfterEvent = disposeAfterEvent;
         }
     }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/LifecycleListener.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/LifecycleListener.java
index 2cb476b..6e99b52 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/LifecycleListener.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/LifecycleListener.java
@@ -8,13 +8,12 @@
 import org.eclipse.core.resources.IResourceChangeEvent;
 import org.eclipse.core.resources.IResourceChangeListener;
 import org.eclipse.core.resources.IResourceDelta;
-import org.eclipse.core.resources.IResourceDeltaVisitor;
 import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.jst.jsf.common.JSFCommonPlugin;
+import org.eclipse.jst.jsf.common.internal.ITestTracker;
+import org.eclipse.jst.jsf.common.internal.ITestTracker.Event;
+import org.eclipse.jst.jsf.common.internal.resource.IResourceLifecycleListener.EventResult;
 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.common.internal.resource.IResourceLifecycleListener.EventResult;
 
 /**
  * Listens to resource changes and fires lifecycle events
@@ -22,11 +21,27 @@
  * @author cbateman
  *
  */
-public class LifecycleListener implements IResourceChangeListener, IResourceDeltaVisitor
+public class LifecycleListener implements IResourceChangeListener
 {
-    private final IResource                                 _res;
+    private final static boolean                            ENABLE_TEST_TRACKING = false;
+    private static long                                     _seqId;
+    
+    private final List<IResource>                           _resources;
     private final List<IResourceLifecycleListener>          _listeners;
     private boolean                                         _isDisposed = false;
+    private ITestTracker                                    _testTracker; // == null; initialized by setter injection
+
+    /**
+     * Initialize an inactive lifecycle listener.  A workspace listener will not
+     * be installed by this constructor.  The object created using this constructor
+     * will not fire any events until addResource is called at least once to add
+     * a target resource
+     */
+    public LifecycleListener()
+    {
+        _resources = new ArrayList<IResource>();
+        _listeners = new ArrayList<IResourceLifecycleListener>(1);
+    }
 
     /**
      * Create a new lifecycle listener for the res
@@ -34,13 +49,72 @@
      */
     public LifecycleListener(IResource res)
     {
-        _res = res;
-        _listeners = new ArrayList<IResourceLifecycleListener>(1);
+        this();
+        _resources.add(res);
+        ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
+    }
+
+    /**
+     * @param resources
+     */
+    public LifecycleListener(List<IResource> resources)
+    {
+        this();
+        _resources.addAll(resources);
         ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
     }
     
 
     /**
+     * @param testTracker
+     */
+    public final void setTestTracker(ITestTracker testTracker)
+    {
+        _testTracker = testTracker;
+    }
+
+    /**
+     * @param res
+     */
+    public void addResource(final IResource res)
+    {
+        synchronized(_resources)
+        {
+            int preSize = _resources.size();
+            if (!_resources.contains(res))
+            {
+                _resources.add(res);
+            }
+            
+            // if the size of the array was 0
+            // and is now greater, make sure the listener is added
+            if (preSize == 0
+                    && _resources.size() > 0)
+            {
+                ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
+            }
+        }
+    }
+
+    /**
+     * @param res
+     */
+    public void removeResource(final IResource res)
+    {
+        synchronized(_resources)
+        {
+            _resources.remove(res);
+            
+            // if there are no longer target resources,
+            // remove the workspace listener
+            if (_resources.size() == 0)
+            {
+                ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+            }
+        }
+    }
+
+    /**
      * Release the resource change listener
      */
     public void dispose()
@@ -82,7 +156,7 @@
         {
             throw new IllegalStateException();
         }
-        
+
         synchronized(_listeners)
         {
             if (!_listeners.contains(listener))
@@ -116,16 +190,32 @@
 
     public void resourceChanged(IResourceChangeEvent event) 
     {
+        long seqId = _seqId++;
+        
+        if (ENABLE_TEST_TRACKING && _testTracker != null)
+        {
+            _testTracker.fireEvent(Event.START_TRACKING, seqId, "trackMethod_resourceChanged");
+        }
+
         assert(!isDisposed());
 
         switch(event.getType())
         {
             case IResourceChangeEvent.PRE_CLOSE:
             {
-                IProject proj = (IProject) event.getResource();
-                if (proj == _res || proj == _res.getProject())
+                final IProject proj = (IProject) event.getResource();
+
+                synchronized(_resources)
                 {
-                    fireLifecycleEvent(new ResourceLifecycleEvent(_res, EventType.RESOURCE_INACCESSIBLE, ReasonType.RESOURCE_PROJECT_CLOSED));
+                    final List<IResource> resources = copyResourceList();
+                    for (final IResource res : resources)
+                    {
+                        if (proj == res || proj == res.getProject())
+                        {
+                            fireLifecycleEvent(
+                                    new ResourceLifecycleEvent(res, EventType.RESOURCE_INACCESSIBLE, ReasonType.RESOURCE_PROJECT_CLOSED));
+                        }
+                    }
                 }
             }
             break;
@@ -134,90 +224,142 @@
             {
                 IProject proj = (IProject) event.getResource();
 
-                // if the resource being tracked is the resource being deleted,
-                // then fire a resource delete event
-                if (proj == _res)
+                synchronized(_resources)
                 {
-                    fireLifecycleEvent(new ResourceLifecycleEvent(_res, EventType.RESOURCE_INACCESSIBLE, ReasonType.RESOURCE_DELETED));
-                }
-                // if the resource being tracked is a resource in the project being
-                // deleted, then fire a project deleted event
-                else if (proj == _res.getProject())
-                {
-                    fireLifecycleEvent(new ResourceLifecycleEvent(_res, EventType.RESOURCE_INACCESSIBLE, ReasonType.RESOURCE_PROJECT_DELETED));
+                    final List<IResource> resources = copyResourceList();
+                    for (final IResource res : resources)
+                    {
+                        // if the resource being tracked is the resource being deleted,
+                        // then fire a resource delete event
+                        if (proj == res)
+                        {
+                            fireLifecycleEvent(new ResourceLifecycleEvent(res, EventType.RESOURCE_INACCESSIBLE, ReasonType.RESOURCE_DELETED));
+                        }
+                        // if the resource being tracked is a resource in the project being
+                        // deleted, then fire a project deleted event
+                        else if (proj == res.getProject())
+                        {
+                            fireLifecycleEvent(
+                                new ResourceLifecycleEvent(res, EventType.RESOURCE_INACCESSIBLE, ReasonType.RESOURCE_PROJECT_DELETED));
+                        }
+                    }
                 }
             }
             break;
 
             case IResourceChangeEvent.POST_CHANGE:
             {
-                // only bother continuing if the resource we are tracking
-                // is not a project since post-change events on projects 
-                // that we care about won't occur
-                if (_res.getType() != IResource.PROJECT)
+                synchronized(_resources)
                 {
-                    IResourceDelta delta = event.getDelta();
-                    // only care about post change events to resources
-                    // that we are tracking
-                    delta = delta.findMember(_res.getFullPath());
-
-                    try
+                    final List<IResource> resources = copyResourceList();
+                    for (final IResource res : resources)
                     {
-                        if (delta != null)
+                        // only bother continuing if the resource we are tracking
+                        // is not a project since post-change events on projects 
+                        // that we care about won't occur
+                        if (res.getType() != IResource.PROJECT)
                         {
-                            delta.accept(this);
+                            IResourceDelta delta = event.getDelta();
+                            
+//                            long seqId2 = _seqId++;
+//                            if (ENABLE_TEST_TRACKING && _testTracker != null)
+//                            {
+//                                _testTracker.fireEvent(Event.START_TRACKING, seqId2, "testFindMember");
+//                            }
+                            // only care about post change events to resources
+                            // that we are tracking
+                            delta = delta.findMember(res.getFullPath());
+
+                            if (delta != null)
+                            {
+                                visit(delta);
+                            }
+
+//                            if (ENABLE_TEST_TRACKING && _testTracker != null)
+//                            {
+//                                _testTracker.fireEvent(Event.STOP_TRACKING, seqId2, "testFindMember");
+//                            }
                         }
                     }
-                    catch (CoreException ce)
-                    {
-                        // can't do anything but log
-                        JSFCommonPlugin.log(new Throwable(ce));
-                    }
                 }
             }
             break;
+
+            default:
+            // do nothing
             // we only handle these three
         }
+
+        if (ENABLE_TEST_TRACKING && _testTracker != null)
+        {
+            _testTracker.fireEvent(Event.STOP_TRACKING, seqId, "trackMethod_resourceChanged");
+        }
+    }
+
+    private List<IResource> copyResourceList()
+    {
+        synchronized(_resources)
+        {
+            List<IResource>  resList = new ArrayList<IResource>(_resources.size());
+            resList.addAll(_resources);
+            return resList;
+        }
     }
 
     private void fireLifecycleEvent(ResourceLifecycleEvent event)
     {
+       List<IResourceLifecycleListener>                copyListeners
+           = new ArrayList(_listeners.size());
+
+        // copy the listeners to avoid concurrent modification problems
+        // if a listeners removes itself due to an event
         synchronized(_listeners)
         {
-            boolean  disposeAfter = false;
+            copyListeners.addAll(_listeners);
+        }
 
-            for (final IResourceLifecycleListener listener : _listeners)
-            {
-               EventResult result = listener.acceptEvent(event);
-               disposeAfter |= result.getDisposeAfterEvent();
-            }
+        boolean  disposeAfter = false;
 
-            if (disposeAfter)
-            {
-                dispose();
-            }
+        for (final IResourceLifecycleListener listener : copyListeners)
+        {
+           EventResult result = listener.acceptEvent(event);
+           disposeAfter |= result.getDisposeAfterEvent();
+        }
+
+        if (disposeAfter)
+        {
+            dispose();
         }
     }
 
-    public boolean visit(IResourceDelta delta) throws CoreException 
+    private void visit(IResourceDelta delta) 
     {
         assert(!isDisposed());
 
+        final IResource res = delta.getResource();
+
         switch (delta.getKind())
         {
-            case IResourceDelta.REMOVED:
+            case IResourceDelta.CHANGED:
             {
-                if (_res.equals(delta.getResource()))
+                // the contents of the file have changed
+                if ((delta.getFlags() & IResourceDelta.CONTENT) != 0)
                 {
                     fireLifecycleEvent(
                         new ResourceLifecycleEvent
-                            (_res, EventType.RESOURCE_INACCESSIBLE, ReasonType.RESOURCE_DELETED));
-                    // TODO: return false to stop child visits?
+                            (res, EventType.RESOURCE_CHANGED
+                                    , ReasonType.RESOURCE_CHANGED_CONTENTS));
                 }
             }
+            break;
+            case IResourceDelta.REMOVED:
+            {
+                fireLifecycleEvent(
+                    new ResourceLifecycleEvent
+                        (res, EventType.RESOURCE_INACCESSIBLE
+                                    , ReasonType.RESOURCE_DELETED));
+            }
+            break;
         }
-
-        // keep going on children
-        return true;
     }
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/ResourceLifecycleEvent.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/ResourceLifecycleEvent.java
index b4e6fd1..81f6ff6 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/ResourceLifecycleEvent.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/resource/ResourceLifecycleEvent.java
@@ -18,7 +18,13 @@
          * Indicates that the resource is no longer accessible (as testable with
          * IResource.isAccessible).  The reasonType will indicate why.
          */
-        RESOURCE_INACCESSIBLE;
+        RESOURCE_INACCESSIBLE,
+        
+        /**
+         * Indicates that the resource being tracked has changed in some
+         * way, use ReasonType to determine specifics
+         */
+        RESOURCE_CHANGED;
     }
     
     /**
@@ -43,9 +49,13 @@
         /**
          * The resource's project was closed.  This event is pre-change
          */
-        RESOURCE_PROJECT_CLOSED
+        RESOURCE_PROJECT_CLOSED,
+        /**
+         * Occurs when the contents of a non-project resource has changed 
+         */
+        RESOURCE_CHANGED_CONTENTS
     }
-    
+
     private final IResource   _affectedResource;
     private final EventType   _eventType;
     private final ReasonType  _reasonType;
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/jsp/JSPModelProcessor.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/jsp/JSPModelProcessor.java
index 8799331..45d3407 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/jsp/JSPModelProcessor.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/jsp/JSPModelProcessor.java
@@ -18,7 +18,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
 
 import org.eclipse.core.resources.IFile;
@@ -28,10 +27,11 @@
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.jst.jsf.common.JSFCommonPlugin;
-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.common.metadata.Trait;
 import org.eclipse.jst.jsf.common.metadata.internal.TraitValueHelper;
 import org.eclipse.jst.jsf.common.metadata.query.ITaglibDomainMetaDataModelContext;
@@ -50,8 +50,6 @@
 import org.eclipse.jst.jsf.designtime.context.DTFacesContext;
 import org.eclipse.jst.jsp.core.internal.domdocument.DOMModelForJSP;
 import org.eclipse.wst.sse.core.StructuredModelManager;
-import org.eclipse.wst.sse.core.internal.model.ModelLifecycleEvent;
-import org.eclipse.wst.sse.core.internal.provisional.IModelLifecycleListener;
 import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
 import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
 import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
@@ -70,98 +68,100 @@
 public class JSPModelProcessor
 {
     private final static Map<IFile, JSPModelProcessor>  RESOURCE_MAP = 
-    	new HashMap<IFile, JSPModelProcessor>();
+        new HashMap<IFile, JSPModelProcessor>();
     private final static java.util.concurrent.locks.Lock CRITICAL_SECTION =
-    	new  ReentrantLock();
-    
+        new  ReentrantLock();
+    private static LifecycleListener  LIFECYCLE_LISTENER;
+
     /**
      * @param file The file to get the model processor for  
      * @return the processor for a particular model, creating it if it does not
      *         already exist
      * @throws CoreException if an attempt to get the model associated with file
      *         fails due to reasons other than I/O problems
-     * @throws IOException if an attempt to get the model associated with file
-     *         fails due to I/O problems
      */
-    public static JSPModelProcessor get(IFile file) throws CoreException, IOException
+    public static JSPModelProcessor get(IFile file) throws CoreException
     {
-		CRITICAL_SECTION.lock();
-		try
-    	{
-			if (!file.isAccessible())
-			{
-				throw new CoreException(new Status(IStatus.ERROR, JSFCorePlugin.PLUGIN_ID, "File must be accessible"));
-			}
-			
-	        JSPModelProcessor processor = RESOURCE_MAP.get(file);
-	
-	        if (processor == null)
-	        {
-	            processor = new JSPModelProcessor(file);
-	            RESOURCE_MAP.put(file, processor);
-	            processor._refCount.set(1);
-	        }
-	        else
-	        {
-	        	// TODO: should lock refCount separately since this 
-	        	// static method does not have exclusive access
-	        	processor._refCount.incrementAndGet();
-	        }
-	        return processor;
-    	}
-    	finally
-    	{
-    		CRITICAL_SECTION.unlock();
-    	}
+        CRITICAL_SECTION.lock();
+        try
+        {
+            if (!file.isAccessible())
+            {
+                throw new CoreException(new Status(IStatus.ERROR, JSFCorePlugin.PLUGIN_ID, "File must be accessible"));
+            }
+
+            JSPModelProcessor processor = RESOURCE_MAP.get(file);
+
+            if (processor == null)
+            {
+                if (LIFECYCLE_LISTENER == null)
+                {
+                    LIFECYCLE_LISTENER = new LifecycleListener(file);
+                }
+                else
+                {
+                    LIFECYCLE_LISTENER.addResource(file);
+                }
+
+                processor = new JSPModelProcessor(file,LIFECYCLE_LISTENER);
+                RESOURCE_MAP.put(file, processor);
+            }
+
+            return processor;
+        }
+        finally
+        {
+            CRITICAL_SECTION.unlock();
+        }
     }
 
     /**
      * Disposes of the JSPModelProcessor associated with model
      * @param file the model processor to be disposed
      */
-    public static void dispose(IFile file)
+    private static void dispose(IFile file)
     {
-    	CRITICAL_SECTION.lock();
+        CRITICAL_SECTION.lock();
         try
         {
-        	JSPModelProcessor processor = RESOURCE_MAP.get(file);
-        	
-        	if (processor != null)
-        	{
-	            int refCount = processor._refCount.decrementAndGet();
-	
-	            // if either the ref count drops below zero 
-	            // or the file is no longer accessible, the dispose
-	            if (refCount < 1 
-	            		|| !file.isAccessible())
-	            {
-	                RESOURCE_MAP.remove(file);
-	                
-	                if (!processor.isDisposed())
-	                {
-	                	processor.dispose();
-	                }
-	            }
-        	}
+            JSPModelProcessor processor = RESOURCE_MAP.get(file);
+
+            if (processor != null)
+            {
+                RESOURCE_MAP.remove(file);
+                
+                if (!processor.isDisposed())
+                {
+                    processor.dispose();
+                    LIFECYCLE_LISTENER.removeResource(file);
+                }
+                
+            }
+
+            if (RESOURCE_MAP.size() == 0)
+            {
+                // if we no longer have any resources being tracked,
+                // then dispose the lifecycle listener
+                LIFECYCLE_LISTENER.dispose();
+                LIFECYCLE_LISTENER = null;
+            }
         }
         finally
         {
-        	CRITICAL_SECTION.unlock();
+            CRITICAL_SECTION.unlock();
         }
     }
 
     private final IFile             _file;
-    private final DOMModelForJSP    _model;
-    private final ModelListener     _modelListener;
-    private final LifecycleListener _resourceListener;
+    private LifecycleListener       _lifecycleListener;
+    private IResourceLifecycleListener  _resListener;
     private boolean                 _isDisposed;
     private Map<Object, ISymbol>    _requestMap;
     private Map<Object, ISymbol>    _sessionMap;
     private Map<Object, ISymbol>    _applicationMap;
     private Map<Object, ISymbol>    _noneMap;
     private long                    _lastModificationStamp;
-    private AtomicInteger           _refCount = new AtomicInteger(0);
-    
+
     // used to avoid infinite recursion in refresh.  Must never be null
     private final CountingMutex     _lastModificationStampMonitor = new CountingMutex();
 
@@ -170,32 +170,48 @@
      * 
      * @param model
      */
-    private JSPModelProcessor(final IFile  file) throws CoreException, IOException
+    private JSPModelProcessor(final IFile  file, final LifecycleListener lifecycleListener)
     {
-        _model = getModelForFile(file);
-        _modelListener = new ModelListener();
-        _model.addModelLifecycleListener(_modelListener);
+        //_model = getModelForFile(file);
+        //_modelListener = new ModelListener();
+        //_model.addModelLifecycleListener(_modelListener);
         _file = file;
-        _resourceListener = new LifecycleListener(file);
-        _resourceListener.addListener(new IResourceLifecycleListener()
+        _lifecycleListener = lifecycleListener;
+        _resListener = new IResourceLifecycleListener()
         {
             public EventResult acceptEvent(ResourceLifecycleEvent event)
             {
-                EventResult result = new EventResult();
+                final EventResult result = EventResult.getDefaultEventResult();
+
+                // not interested
+                if (!_file.equals(event.getAffectedResource()))
+                {
+                    return result; 
+                }
 
                 if (event.getEventType() == EventType.RESOURCE_INACCESSIBLE)
                 {
                     dispose(_file);
-                    result.setDisposeAfterEvent(true);
+                }
+                else if (event.getEventType() == EventType.RESOURCE_CHANGED)
+                {
+                    // if the file has changed contents on disk, then
+                    // invoke an unforced refresh of the JSP file
+                    if (event.getReasonType() == ReasonType.RESOURCE_CHANGED_CONTENTS)
+                    {
+                        refresh(false);
+                    }
                 }
 
                 return result;
             }
-        });
+        };
+
+        lifecycleListener.addListener(_resListener);
+        
         // a negative value guarantees that refresh(false) will
         // force a refresh on the first run
         _lastModificationStamp = -1;
-        
     }
 
     private DOMModelForJSP getModelForFile(final IFile file)
@@ -229,12 +245,11 @@
     {
         if (!_isDisposed)
         {
-        	_model.releaseFromRead();
-            _model.removeModelLifecycleListener(_modelListener);
-            
             // ensure the resource listener is disposed
-            _resourceListener.dispose();
-            
+            _lifecycleListener.removeListener(_resListener);
+            _resListener = null;
+            _lifecycleListener = null;
+
             if (_requestMap != null)
             {
                 _requestMap.clear();
@@ -259,7 +274,6 @@
                 _noneMap = null;
             }
 
-            _refCount.set(0);
             // mark as disposed
             _isDisposed = true;
         }
@@ -275,13 +289,15 @@
     }
 
     /**
-     * Mainly for test and diagnostic purposes.
-     * 
-     * @return the current number of undisposed references to this model processor
+     * If isModelDirty() returns true, then it means that a call
+     * to refresh(false) will trigger a reprocess of the underlying document.
+     *
+     * @return true if the underlying JSP model is considered to be dirty
      */
-    public int getRefCount()
+    public boolean isModelDirty()
     {
-    	return _refCount.get();
+        final long currentModificationStamp = _file.getModificationStamp();
+        return _lastModificationStamp != currentModificationStamp;
     }
     
     /**
@@ -289,14 +305,13 @@
      * @param forceRefresh -- if true, always refreshes, if false,
      * then it only refreshes if the file's modification has changed
      * since the last refresh
+     * @throws IllegalStateException if isDisposed() == true
      */
     public void refresh(final boolean forceRefresh)
     {
         if (isDisposed())
         {
-            // TODO: should we throw exception?  return false?
-            JSFCorePlugin.log(IStatus.WARNING, "Attempt to refresh a disposed processor", new Throwable("Exception is only to get a stack trace, not an error"));
-            return;
+            throw new IllegalStateException("Processor is disposed for file: "+_file.toString());
         }
 
         synchronized(_lastModificationStampMonitor)
@@ -309,23 +324,27 @@
                 return;
             }
 
+            DOMModelForJSP  model = null;
             try
             {
                 _lastModificationStampMonitor.setSignalled(true);
                 
-                long currentModificationStamp;
-                
-                currentModificationStamp = _file.getModificationStamp();
-    
+
                 // only refresh if forced or if the underlying file has changed
                 // since the last run
                 if (forceRefresh
-                        || _lastModificationStamp != currentModificationStamp)
+                        || isModelDirty())
                 {
-                    refreshInternal();
+                    model = getModelForFile(_file);
+                    refreshInternal(model);
                     _lastModificationStamp = _file.getModificationStamp();
                 }
             }
+            catch (CoreException e) {
+               JSFCorePlugin.log(new RuntimeException(e), "Error refreshing internal model");
+            } catch (IOException e) {
+                JSFCorePlugin.log(new RuntimeException(e), "Error refreshing internal model");
+            }
             // make sure that we unsignal the monitor before releasing the
             // mutex
             finally
@@ -335,23 +354,24 @@
         }
     }
     
-    private void refreshInternal()
+    private void refreshInternal(DOMModelForJSP model)
     {
         final IStructuredDocumentContext context = 
-            IStructuredDocumentContextFactory.INSTANCE.getContext(_model.getStructuredDocument(), -1);
+            IStructuredDocumentContextFactory.INSTANCE.getContext(model.getStructuredDocument(), -1);
         final ITaglibContextResolver taglibResolver =
             IStructuredDocumentContextResolverFactory.INSTANCE.getTaglibContextResolver(context);
-        IDOMDocument document = _model.getDocument();
+        IDOMDocument document = model.getDocument();
         getApplicationMap().clear();
         getRequestMap().clear();
         getSessionMap().clear();
         //long curTime = System.currentTimeMillis();
-        recurseChildNodes(document.getChildNodes(), taglibResolver);
+        recurseChildNodes(model, document.getChildNodes(), taglibResolver);
         //long netTime = System.currentTimeMillis() - curTime;
         //System.out.println("Net time to recurse document: "+netTime);
     }
-   
-    private void recurseChildNodes(final NodeList nodes, 
+
+    private void recurseChildNodes(final DOMModelForJSP model,
+                                   final NodeList nodes, 
                                     final ITaglibContextResolver taglibResolver)
     {
         for (int i = 0; i < nodes.getLength(); i++)
@@ -359,12 +379,12 @@
             final Node child = nodes.item(i);
             
             // process attributes at this node before recursing
-            processAttributes(child, taglibResolver);
-            recurseChildNodes(child.getChildNodes(), taglibResolver);
+            processAttributes(model, child, taglibResolver);
+            recurseChildNodes(model, child.getChildNodes(), taglibResolver);
         }
     }
-    
-    private void processAttributes(final Node node, 
+
+    private void processAttributes(final DOMModelForJSP model, final Node node, 
                                     final ITaglibContextResolver taglibResolver)
     {
         if (taglibResolver.hasTag(node))
@@ -377,13 +397,13 @@
             {
                 final Node attribute = node.getAttributes().item(i);
 
-                processSymbolContrib(uri, elementName, attribute);
+                processSymbolContrib(model, uri, elementName, attribute);
                 processSetsLocale(uri, elementName, attribute);
             }
         }
     }
 
-    private void processSymbolContrib(final String uri, final String elementName, Node attribute)
+    private void processSymbolContrib(final DOMModelForJSP model, final String uri, final String elementName, Node attribute)
     {
         final SymbolContribAggregator  aggregator =
             SymbolContribAggregator.
@@ -403,7 +423,7 @@
                     factory.create(symbolName, 
                                   ISymbolConstants.SYMBOL_SCOPE_REQUEST, //TODO:
                                   IStructuredDocumentContextFactory.INSTANCE.
-                                      getContext(_model.getStructuredDocument(), 
+                                      getContext(model.getStructuredDocument(), 
                                                  attribute),
                                   problems);
 
@@ -434,7 +454,7 @@
         {
             DesignTimeApplicationManager  dtAppMgr =
                 DesignTimeApplicationManager.getInstance(_file.getProject());
-            
+
             DTFacesContext facesContext = dtAppMgr.getFacesContext(_file);
             
             if (facesContext != null)
@@ -457,14 +477,14 @@
         {
             return Collections.unmodifiableMap(map);
         }
-        
+
         return Collections.EMPTY_MAP;
     }
-    
+
     private void updateMap(ISymbol symbol, String  scopeName)
     {
         final Map<Object, ISymbol> map = getMapForScopeInternal(scopeName);
-        
+
         if (map != null)
         {
             map.put(symbol.getName(), symbol);
@@ -540,32 +560,6 @@
     }
 
     /**
-     * Listens to the JSP model and reacts to changes
-     * @author cbateman
-     *
-     */
-    private class ModelListener implements IModelLifecycleListener
-    {
-        public void processPostModelEvent(ModelLifecycleEvent event)
-        {
-            // TODO: figure this event structure out seems like it is possibly
-            // broken...
-            if (((event.getType() & ModelLifecycleEvent.MODEL_DIRTY_STATE) != 0
-                    && !_model.isDirty()) // if the dirty state changed as now not dirty, then we have a save
-                )//|| (event.getType() & ModelLifecycleEvent.MODEL_REINITIALIZED) != 0)
-            {
-                // refresh if modified on disk
-                refresh(false);
-            }
-        }
-
-        public void processPreModelEvent(ModelLifecycleEvent arg0) {
-            // do nothing
-        }
-    }
-    
-    
-    /**
      * Aggregates the sets-locale meta-data
      * 
      * @author cbateman
@@ -577,8 +571,8 @@
         static LocaleSetAggregator create(IProject project, 
                                               final String uri, 
                                               final String elementName, final String attributeName)
-        {            
-        	final ITaglibDomainMetaDataModelContext mdContext = TaglibDomainMetaDataQueryHelper.createMetaDataModelContext(project, uri);
+        {
+            final ITaglibDomainMetaDataModelContext mdContext = TaglibDomainMetaDataQueryHelper.createMetaDataModelContext(project, uri);
             Trait trait = TaglibDomainMetaDataQueryHelper.getTrait(mdContext, elementName+"/"+attributeName, SETS_LOCALE); //$NON-NLS-1$
 
             if (TraitValueHelper.getValueAsBoolean(trait))
@@ -609,12 +603,12 @@
          * @return a new instance only if attributeName is a symbol contributor
          */
         static SymbolContribAggregator create(final IProject project, 
-        									  final String uri, 
+                                              final String uri, 
                                               final String elementName, 
                                               final String attributeName)
         {
-        	final String entityKey = elementName+"/"+attributeName; //$NON-NLS-1$
-        	final ITaglibDomainMetaDataModelContext mdContext = TaglibDomainMetaDataQueryHelper.createMetaDataModelContext(project, uri);
+            final String entityKey = elementName+"/"+attributeName; //$NON-NLS-1$
+            final ITaglibDomainMetaDataModelContext mdContext = TaglibDomainMetaDataQueryHelper.createMetaDataModelContext(project, uri);
             Trait trait = TaglibDomainMetaDataQueryHelper.getTrait(mdContext, entityKey, CONTRIBUTES_VALUE_BINDING);
 
             boolean contribsValueBindings = TraitValueHelper.getValueAsBoolean(trait);
@@ -629,8 +623,8 @@
 
                 if (scope != null && !scope.equals("")) //$NON-NLS-1$
                 {
-                	trait = TaglibDomainMetaDataQueryHelper.getTrait(mdContext, entityKey, VALUE_BINDING_SYMBOL_FACTORY);
-                	symbolFactory = TraitValueHelper.getValueAsString(trait);                      
+                    trait = TaglibDomainMetaDataQueryHelper.getTrait(mdContext, entityKey, VALUE_BINDING_SYMBOL_FACTORY);
+                    symbolFactory = TraitValueHelper.getValueAsString(trait);                      
                 }
 
                 return new SymbolContribAggregator(scope, symbolFactory);
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/jsp/StartupHandler.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/jsp/StartupHandler.java
index 05c442e..b3b4377 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/jsp/StartupHandler.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/jsp/StartupHandler.java
@@ -39,8 +39,8 @@
 public class StartupHandler implements IStartup 
 {
     private final JSPEditorListener    _partListener = new JSPEditorListener();
-    
-	public void earlyStartup() 
+
+    public void earlyStartup() 
     {
         PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
         {
@@ -67,73 +67,69 @@
                     }
                     windows[i].getPartService().addPartListener(_partListener);
                 }
-                
+
                 // TODO: register with all windows?
                 PlatformUI.getWorkbench().addWindowListener(new IWindowListener()
                 {
-        
                     public void windowActivated(IWorkbenchWindow window) {
                         // do nothing
                     }
-        
+
                     public void windowDeactivated(IWorkbenchWindow window) {
                         // do nothing
                     }
-        
+
                     public void windowClosed(IWorkbenchWindow window) {
                         window.getPartService().removePartListener(_partListener);
                     }
-        
+
                     public void windowOpened(IWorkbenchWindow window) {
                         window.getPartService().addPartListener(_partListener);
                     }
-                });    
+                });
             }
         });
-	}
+    }
 
-	private static class JSPEditorListener implements IPartListener2
-	{
-		private JSPModelProcessor 		_processor;
-		
-		public void partActivated(IWorkbenchPartReference partRef) {
-			// do nothing
-		}
+    private static class JSPEditorListener implements IPartListener2
+    {
+        private JSPModelProcessor         _processor;
 
-		public void partBroughtToTop(IWorkbenchPartReference partRef) {
-			// do nothing
-		}
+        public void partActivated(IWorkbenchPartReference partRef) {
+            // do nothing
+        }
+
+        public void partBroughtToTop(IWorkbenchPartReference partRef) {
+            // do nothing
+        }
 
         public void partClosed(IWorkbenchPartReference partRef) {
-            if (partRef instanceof IEditorReference)
-            {
-                releaseJSPModelListener((IEditorReference) partRef);
-            }
-		}
+            // do nothing
+        }
 
-		public void partDeactivated(IWorkbenchPartReference partRef) {
-			// do nothing
-		}
+        public void partDeactivated(IWorkbenchPartReference partRef) {
+            // do nothing
+        }
 
-		public void partOpened(IWorkbenchPartReference partRef) {
+        public void partOpened(IWorkbenchPartReference partRef) {
             if (isValidJSPEditor(partRef))
             {
                 setJSPModelListener((IEditorReference)partRef);
             }
-		}
+        }
 
-		public void partHidden(IWorkbenchPartReference partRef) {
-			// do nothing
-		}
+        public void partHidden(IWorkbenchPartReference partRef) {
+            // do nothing
+        }
 
-		public void partVisible(IWorkbenchPartReference partRef) {
-			// do nothing
-		}
+        public void partVisible(IWorkbenchPartReference partRef) {
+            // do nothing
+        }
 
-		public void partInputChanged(IWorkbenchPartReference partRef) {
-			// do nothing
-		}
-       
+        public void partInputChanged(IWorkbenchPartReference partRef) {
+            // do nothing
+        }
+
         private boolean isJSPEditor(IEditorReference editorRef)
         {
             IFile file = getIFile(editorRef);
@@ -145,7 +141,7 @@
 
             return false;
         }
-        
+
         /**
          * @param editorRef
          * @return true if the editor is editing the JSP content type and
@@ -159,18 +155,17 @@
                     JSFAppConfigUtils.isValidJSFProject(file.getProject()) &&
                         isJSPEditor(editorRef);
         }
-        
-        
+
         boolean isValidJSPEditor(IWorkbenchPartReference partRef)
         {
             if (partRef instanceof IEditorReference)
             {
                 return isValidJSPEditor((IEditorReference)partRef);
             }
-            
+
             return false;
         }
-        
+
         void setJSPModelListener(IEditorReference editorRef)
         {
             IFile file = getIFile(editorRef);
@@ -189,17 +184,7 @@
                 }
             }
         }
-        
-        void releaseJSPModelListener(IEditorReference editorRef)
-        {
-        	IFile file = getIFile(editorRef);
-        	
-            if (file != null)
-            {
-                JSPModelProcessor.dispose(file);
-            }
-        }
-        
+
         IFile getIFile(IEditorReference editorRef)
         {
             try
@@ -216,8 +201,8 @@
             {
                 JSFCorePlugin.getDefault().getLog().log(new Status(IStatus.ERROR, JSFCorePlugin.PLUGIN_ID, 0, "Error acquiring editor input",excp)); //$NON-NLS-1$
             }
-            
+
             return null;
         }
-	}
+    }
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/JSPTagVariableSymbolSourceProvider.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/JSPTagVariableSymbolSourceProvider.java
index 994dc7d..ca109f7 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/JSPTagVariableSymbolSourceProvider.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/JSPTagVariableSymbolSourceProvider.java
@@ -86,13 +86,6 @@
                 JSFCorePlugin.getDefault().getLog().log(new Status(IStatus.ERROR, JSFCorePlugin.PLUGIN_ID, 0, "Error acquiring model processor",e)); //$NON-NLS-1$
                 // fall-through to empty symbol array
             }
-            finally
-            {
-            	if (modelProcessor != null)
-            	{
-            		JSPModelProcessor.dispose(fileContext);
-            	}
-            }
         }
         
         return ISymbol.EMPTY_SYMBOL_ARRAY;
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/ResourceBundleMapSource.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/ResourceBundleMapSource.java
index 2e454c0..4e7d66c 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/ResourceBundleMapSource.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/ResourceBundleMapSource.java
@@ -77,7 +77,7 @@
                     {
                         public EventResult acceptEvent(ResourceLifecycleEvent event) 
                         {
-                            EventResult result = new EventResult();
+                            EventResult result = EventResult.getDefaultEventResult();
                             
                             if (event.getEventType() == EventType.RESOURCE_INACCESSIBLE)
                             {
@@ -92,7 +92,7 @@
                                 {
                                     JSFCorePlugin.log("Error clearing bundle file cache", ce); //$NON-NLS-1$
                                 }
-                                result.setDisposeAfterEvent(true);
+                                result = EventResult.getDisposeAfterEventResult();
                             }
                             
                             return result;