[323079]  [index] Need way to wait for JSP index to be consistent
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java
index 2b4fab5..ed2b955 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java
@@ -10,9 +10,13 @@
  *******************************************************************************/
 package org.eclipse.jst.jsp.core.internal;
 
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
 import org.eclipse.core.resources.ISaveContext;
 import org.eclipse.core.resources.ISaveParticipant;
 import org.eclipse.core.resources.ISavedState;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
@@ -24,7 +28,6 @@
 import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController;
 import org.eclipse.jst.jsp.core.internal.contentproperties.JSPFContentPropertiesManager;
 import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache;
-import org.eclipse.jst.jsp.core.internal.java.JSPTranslatorPersister;
 import org.eclipse.jst.jsp.core.internal.java.search.JSPIndexManager;
 import org.eclipse.jst.jsp.core.internal.taglib.TaglibHelperManager;
 import org.eclipse.jst.jsp.core.taglib.TaglibIndex;
@@ -34,19 +37,22 @@
  * The main plugin class to be used in the desktop.
  */
 public class JSPCorePlugin extends Plugin {
-	// The shared instance.
+	/** singleton instance of the plugin */
 	private static JSPCorePlugin plugin;
-	
-	/** Save participant for this plugin */
-	ISaveParticipant fSaveParticipant;
 
 	/**
+	 * <p>Job used to finish tasks needed to start up the plugin but that did not have
+	 * to block the plugin start up process.</p>
+	 */
+	private Job fPluginInitializerJob;
+	
+	/**
 	 * The constructor.
 	 */
 	public JSPCorePlugin() {
 		super();
 		plugin = this;
-		fSaveParticipant = new SaveParticipant();
+		this.fPluginInitializerJob = new PluginInitializerJob();
 	}
 
 	/**
@@ -73,37 +79,9 @@
 
 		// listen for classpath changes
 		JavaCore.addElementChangedListener(TaglibHelperManager.getInstance());
-
-		//restore save state and process any events that happened before plugin loaded
-		if (JSPTranslatorPersister.ACTIVATED) {
-			Job persister = new Job(JSPCoreMessages.Initializing) {
-				protected IStatus run(IProgressMonitor monitor) {
-					ISavedState savedState = null;
-					try {
-						savedState = ResourcesPlugin.getWorkspace().addSaveParticipant(plugin.getBundle().getSymbolicName(), fSaveParticipant);
-					}
-					catch (CoreException e) {
-						Logger.logException("Could not load previous save state", e);
-					}
-					if (savedState != null) {
-						try {
-							Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
-						}
-						finally {
-							savedState.processResourceChangeEvents(JSPTranslatorPersister.getDefault());
-						}
-					}
-					return Status.OK_STATUS;
-				}
-			};
-			persister.setUser(false);
-			persister.schedule(2000);
-			// set up persister to listen to resource change events
-			ResourcesPlugin.getWorkspace().addResourceChangeListener(JSPTranslatorPersister.getDefault());
-		}
 		
-		//init the JSP index
-		JSPIndexManager.getInstance().initialize();
+		//schedule delayed initialization
+		this.fPluginInitializerJob.schedule(2000);
 
 		// listen for resource changes to update content properties keys
 		JSPFContentPropertiesManager.startup();
@@ -119,22 +97,14 @@
 	public void stop(BundleContext context) throws Exception {
 		DeploymentDescriptorPropertyCache.stop();
 
-		/*
-		 * stop listening for resource changes to update content properties
-		 * keys
-		 */
+		// stop listening for resource changes to update content properties keys
 		JSPFContentPropertiesManager.shutdown();
 
 		//remove the plugin save participant
 		ResourcesPlugin.getWorkspace().removeSaveParticipant(plugin.getBundle().getSymbolicName());
 		
-		//remove the translator persister
-		if(JSPTranslatorPersister.ACTIVATED) {
-			ResourcesPlugin.getWorkspace().removeResourceChangeListener(JSPTranslatorPersister.getDefault());
-		}
-		
 		// stop any indexing
-		JSPIndexManager.getInstance().shutdown();
+		JSPIndexManager.getDefault().stop();
 
 		// stop listening for classpath changes
 		JavaCore.removeElementChangedListener(TaglibHelperManager.getInstance());
@@ -147,6 +117,79 @@
 	}
 	
 	/**
+	 * <p>A {@link Job} used to perform delayed initialization for the plugin</p>
+	 */
+	private static class PluginInitializerJob extends Job {
+		/**
+		 * <p>Default constructor to set up this {@link Job} as a
+		 * long running system {@link Job}</p>
+		 */
+		protected PluginInitializerJob() {
+			super(JSPCoreMessages.Initializing);
+			
+			this.setUser(false);
+			this.setSystem(true);
+			this.setPriority(Job.LONG);
+		}
+		
+		/**
+		 * <p>Perform delayed initialization for the plugin</p>
+		 * 
+		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+		 */
+		protected IStatus run(IProgressMonitor monitor) {
+			IStatus status = Status.OK_STATUS;
+			final IWorkspace workspace = ResourcesPlugin.getWorkspace();
+			try {
+				/*
+				 * Restore save state and process any events that happened before
+				 * plug-in loaded. Don't do it immediately since adding the save
+				 * participant requires a lock on the workspace to compute the
+				 * accumulated deltas, and if the tree is not already locked it
+				 * becomes a blocking call.
+				 */
+				workspace.run(new IWorkspaceRunnable() {
+					public void run(final IProgressMonitor worspaceMonitor) throws CoreException {
+						ISavedState savedState = null;
+						
+						try {
+							//add the save participant for this bundle
+							savedState = ResourcesPlugin.getWorkspace().addSaveParticipant(
+									JSPCorePlugin.plugin.getBundle().getSymbolicName(), new SaveParticipant());
+						} catch (CoreException e) {
+							Logger.logException("JSP Core Plugin failed at loading previously saved state." + //$NON-NLS-1$
+									" All componenets dependent on this state will start as if first workspace load.", e); //$NON-NLS-1$
+						}
+						
+						//if there is a saved state start up using that, else start up cold
+						if(savedState != null) {
+							try {
+								Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
+							} finally {
+								savedState.processResourceChangeEvents(new IResourceChangeListener() {
+									/**
+									 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
+									 */
+									public void resourceChanged(IResourceChangeEvent event) {
+										JSPIndexManager.getDefault().start(event.getDelta(), worspaceMonitor);
+									}
+								});
+							}
+						} else {
+							JSPIndexManager.getDefault().start(null, worspaceMonitor);
+						}
+					}
+				}, monitor);
+			} catch(CoreException e) {
+				status = e.getStatus();
+			}
+			
+			return status;
+		}
+		
+	}
+	
+	/**
 	 * Used so that all of the IResourceChangeEvents that occurred before
 	 * this plugin loaded can be processed.
 	 */
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java
index c7b1154..ac7475e 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java
@@ -115,7 +115,7 @@
 		fDocumentIsDirty = true;
 	}
 
-	public void release() {
+	public synchronized void release() {
 
 		if (fJspDocument != null)
 			fJspDocument.removeDocumentListener(this);
@@ -129,6 +129,7 @@
 				System.out.println("JSPTranslationAdapter releasing:" + fJSPTranslation); //$NON-NLS-1$
 
 			fJSPTranslation.release();
+			fJSPTranslation = null;
 		}
 	}
 
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
index a37b57e..bd40057 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
@@ -3181,9 +3181,12 @@
 
 	private IPath getModelPath() {
 		IPath path = null;
-		ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(getStructuredDocument());
-		if (buffer != null) {
-			path = buffer.getLocation();
+		IStructuredDocument structuredDocument = getStructuredDocument();
+		if (structuredDocument != null) {
+			ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(structuredDocument);
+			if (buffer != null) {
+				path = buffer.getLocation();
+			}
 		}
 		return path;
 	}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java
index 5cafc54..d41723b 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java
@@ -17,31 +17,18 @@
 import java.io.InvalidClassException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
-import java.util.LinkedList;
+import java.io.UnsupportedEncodingException;
 import java.util.zip.CRC32;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IResource;
-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.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.ISafeRunnable;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.core.runtime.SafeRunner;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.content.IContentType;
-import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
 import org.eclipse.jst.jsp.core.internal.JSPCorePlugin;
 import org.eclipse.jst.jsp.core.internal.Logger;
+import org.eclipse.jst.jsp.core.internal.java.search.JSPIndexManager;
 import org.eclipse.jst.jsp.core.internal.modelhandler.ModelHandlerForJSP;
-import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
 import org.eclipse.wst.sse.core.StructuredModelManager;
 import org.eclipse.wst.sse.core.internal.FileBufferModelManager;
 import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
@@ -49,32 +36,19 @@
 import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
 
 /**
- * <p>This {@link IResourceChangeListener} is used to keep the {@link JSPTranslator}s for JSP
- * resources persisted to disk.  It can also be used to get persisted translators</p>
- * <p>This class should be registered as an {@link IResourceChangeListener} on the Workspace
- * as well as processing resource change events from a saved state, for example use see below.<p>
- * <p><b>Plugin Activation:</b>
- * <pre>
- * try {
- *   ISavedState savedState = ResourcesPlugin.getWorkspace().addSaveParticipant(
- *     plugin.getBundle().getSymbolicName(), this.fSaveParticipant);
- *   if (savedState != null) {
- *     savedState.processResourceChangeEvents(JSPTranslatorPersistor.getDefault());
- *   }
- * } catch(CoreException e) {}
- * ResourcesPlugin.getWorkspace().addResourceChangeListener(JSPTranslatorPersistor.getDefault());
- * </pre>
- * <b>Plugin Deactivation:</b>
- * <pre>
- * ResourcesPlugin.getWorkspace().removeSaveParticipant(plugin.getBundle().getSymbolicName());
- * ResourcesPlugin.getWorkspace().removeResourceChangeListener(JSPTranslatorPersistor.getDefault());
- * </pre></p>
+ * <p>This is a static class used to persist JSP translations and retrieve the persisted
+ * translations.</p>
+ * 
+ * <p>It is not actually in charge of finding files to persist, rather it provides API
+ * for some other mechanism that tracks JSP files to call into to persist the translations.</p>
  * 
  * <p>This class can be deactivated through the <code>persistJSPTranslations</code> system property,
  * a value of <code>true</code> means the persister is activated (which is the default), value of
  * <code>false</code> means the persister is not activated.</p>
+ * 
+ * @see JSPIndexManager
  */
-public class JSPTranslatorPersister implements IResourceChangeListener {
+public class JSPTranslatorPersister{
 	/**
 	 * <code>true</code> if the persister is activated, <code>false</code>
 	 * otherwise.  This is determined by checking the system property
@@ -90,63 +64,13 @@
 	/** used to calculate persisted translator file names */
 	private static final CRC32 CHECKSUM_CALC = new CRC32();
 	
-	/** singleton instance of the {@link JSPTranslatorPersister} */
-	private static final JSPTranslatorPersister INSTANCE = new JSPTranslatorPersister();
+	/** lock to use while using the checksum */
+	private static final Object CHECKSUM_CALC_LOCK = new Object();
 	
 	/**
-	 * Used to handle resource change events
-	 * @see #resourceChanged(IResourceChangeEvent)
-	 */
-	private IResourceDeltaVisitor fResourceDeltaVisitor;
-	
-	/** {@link Job} that actually does all the persisting */
-	protected PersisterJob fPersisterJob;
-	
-	/**
-	 * <p>Private singleton default constructor</p>
+	 * <p>Private constructor to prevent creating an instance of this class</p>
 	 */
 	private JSPTranslatorPersister() {
-		this.fResourceDeltaVisitor = new JSPResourceVisitor();
-		this.fPersisterJob = new PersisterJob();
-	}
-	
-	/**
-	 * <p><b>NOTE: </b><i>This can possible return <code>null</code></i></p>
-	 * 
-	 * @return Singleton instance of the {@link JSPTranslatorPersister} if
-	 * {@link #ACTIVATED} is <code>true</code>, <code>null</code> otherwise.
-	 */
-	public static JSPTranslatorPersister getDefault() {
-		return ACTIVATED ? INSTANCE : null;
-	}
-	
-	/**
-	 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
-	 */
-	public void resourceChanged(IResourceChangeEvent event) {
-		switch(event.getType()) {
-			case IResourceChangeEvent.PRE_CLOSE:
-			case IResourceChangeEvent.PRE_DELETE:
-				//pre-close or pre-delete stop the persister job so it does not interfere
-				this.fPersisterJob.stop();
-				break;
-			case IResourceChangeEvent.POST_CHANGE:
-				//post change start up the persister job and process the delta
-				this.fPersisterJob.start();
-				
-				// only analyze the full (starting at root) delta hierarchy
-				IResourceDelta delta = event.getDelta();
-				if (delta != null && delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$
-					try {
-						//use visitor to visit all children
-						delta.accept(this.fResourceDeltaVisitor, false);
-					} catch (CoreException e) {
-						Logger.logException("Processing resource change event delta failed, " +
-								"persisted JSPTranslators may not have been updated.", e);
-					}
-				}
-				break;
-		}
 	}
 	
 	/**
@@ -210,6 +134,45 @@
 	}
 	
 	/**
+	 * @param resource JSP resource who's translation should be persisted
+	 */
+	public static void persistTranslation(IResource resource) {
+		if(ACTIVATED) {
+			IPath path = resource.getFullPath();
+			String filePath = getPersistedTranslatorFilePath(path.toString());
+			IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
+	
+			JSPTranslator translator = getJSPTranslator(file);
+			if(translator != null) {
+				persistTranslator(translator, filePath);
+			}
+		}
+	}
+	
+	/**
+	 * @param resource JSP resource who's translation should no longer be persisted
+	 */
+	public static void removePersistedTranslation(IResource resource) {
+		if(ACTIVATED) {
+			File file = getPersistedFile(resource.getFullPath());
+			file.delete();
+		}
+	}
+	
+	/**
+	 * @param resource JSP resource that has moved and thus its persisted translation should be updated
+	 * @param fromPath Path the JSP resource moved from
+	 */
+	public static void movePersistedTranslation(IResource resource, IPath fromPath) {
+		if(ACTIVATED) {
+			File from = getPersistedFile(fromPath);
+			File to = getPersistedFile(resource.getFullPath());
+	
+			from.renameTo(to);
+		}
+	}
+	
+	/**
 	 * <p>Given the path to a JSP file determines the path to its persisted {@link JSPTranslator}</p>
 	 * 
 	 * @param jspFilePath {@link IPath} to JSP file for which the path to its persisted {@link JSPTranslator}
@@ -218,10 +181,18 @@
 	 * @return OS file path to the persisted {@link JSPTranslator} associated with the JSP file at
 	 * <code>jspFilePath</code>
 	 */
-	protected static String getPersistedTranslatorFilePath(String jspFilePath) {
-		CHECKSUM_CALC.reset();
-		CHECKSUM_CALC.update(jspFilePath.getBytes());
-		String persistedTranslatorFileName = Long.toString(CHECKSUM_CALC.getValue()) + ".translator"; //$NON-NLS-1$
+	private static String getPersistedTranslatorFilePath(String jspFilePath) {
+		String persistedTranslatorFileName = "error.translator"; //$NON-NLS-1$
+		synchronized(CHECKSUM_CALC_LOCK){
+			try {
+				CHECKSUM_CALC.reset();
+				CHECKSUM_CALC.update(jspFilePath.getBytes("utf16")); //$NON-NLS-1$
+				persistedTranslatorFileName = Long.toString(CHECKSUM_CALC.getValue()) + ".translator"; //$NON-NLS-1$
+			} catch (UnsupportedEncodingException e) {
+				Logger.logException("Could not get utf16 encoded bytes to create checksum to store persisted file.", e); //$NON-NLS-1$
+			}
+		}
+		
 		IPath location = PERSIST_LOCATION;
 		
 		// ensure the folder exists on disk
@@ -240,290 +211,69 @@
 	}
 	
 	/**
-	 * @see JSPResourceVisitor#visit(IResourceDelta)
+	 * <p>Gets the associated {@link JSPTranslator} for a specific JSP file.</p>
+	 * <p><b>NOTE: </b><i>This does not get the persisted translator but rather the
+	 * associated translator in memory</i></p>
+	 * 
+	 * @param jspFile {@link IFile} to the JSP file that the associated {@link JSPTranslator}
+	 * is needed for
+	 * @return {@link JSPTranslator} associated with the given <code>jspFilePath</code>, or
+	 * <code>null</code> if none can be found.
 	 */
-	private class JSPResourceVisitor implements IResourceDeltaVisitor {
-		/**
-		 * <p>Default constructor</p>
-		 */
-		protected JSPResourceVisitor() {
-		}
-		
-		/**
-		 * <p>For each {@link IResourceDelta} determine if its a JSP resource and if it is
-		 * add the appropriate action to the {@link PersisterJob} so as not to hold up
-		 * the {@link IResourceDelta}</p>
-		 * 
-		 * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
-		 */
-		public boolean visit(IResourceDelta delta) throws CoreException {
-			if(isJSPResource(delta.getResource())) {
-				switch (delta.getKind()) {
-					case IResourceDelta.CHANGED :
-					case IResourceDelta.ADDED : {
-						/* if a move, then move the persisted translation
-						 * else create a new persisted translation, if its a change then
-						 *   the old persisted translation will be overwritten */
-						if((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
-							final File from = getPersistedFile(delta.getMovedFromPath());
-							final File to = getPersistedFile(delta.getFullPath());
-							//add the move action to the persister job
-							JSPTranslatorPersister.this.fPersisterJob.addAction(new ISafeRunnable() {
-								public void run() throws Exception {
-									renamePersistedTranslator(from, to);
-								}
-
-								public void handleException(Throwable exception) {}
-							});
-						} else {
-							final String filePath = getPersistedTranslatorFilePath(delta.getFullPath().toPortableString());
-							final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(delta.getFullPath());
-							//add the add action to the persister job
-							JSPTranslatorPersister.this.fPersisterJob.addAction(new ISafeRunnable() {
-								public void run() throws Exception {
-									JSPTranslator translator = getJSPTranslator(file);
-									if(translator != null) {
-										persistTranslator(translator, filePath);
-									}
-								}
-
-								public void handleException(Throwable exception) {}
-							});
-						}
-						
-						break;
-					}
-					case IResourceDelta.REMOVED : {
-						/* only remove if its not a move,
-						 * if it is a move the added file delta event will move translation */
-						if((delta.getFlags() & IResourceDelta.MOVED_TO) == 0) {
-							final File file = getPersistedFile(delta.getFullPath());
-							//add the delete action to the persister job
-							JSPTranslatorPersister.this.fPersisterJob.addAction(new ISafeRunnable() {
-								public void run() throws Exception {
-									deletePersistedTranslator(file);
-								}
-
-								public void handleException(Throwable exception) {}
-							});
-						}
-						break;
-					}
+	private static JSPTranslator getJSPTranslator(IFile jspFile) {
+		IStructuredModel model = null;
+		JSPTranslator translator = null;
+		try {
+			model = StructuredModelManager.getModelManager().getModelForRead(jspFile);
+			if(model instanceof IDOMModel) {
+				IDOMDocument doc = ((IDOMModel)model).getDocument();
+				ModelHandlerForJSP.ensureTranslationAdapterFactory(model);
+				JSPTranslationAdapter adapter = (JSPTranslationAdapter)doc.getAdapterFor(IJSPTranslation.class);
+				
+				//don't want to persist a translator that has not already been requested
+				if(adapter != null && adapter.hasTranslation()) {
+					translator = adapter.getJSPTranslation().getTranslator();
 				}
 			}
-			
-			//visit children deltas
-			return true;
-		}
-		
-		/**
-		 * <p>Determines if an {@link IResource} is a JSP resource</p>
-		 * 
-		 * @param resource determine if this {@link IResource} is a JSP resource
-		 * @return <code>true</code> if <code>resource</code> is a JSP resource,
-		 * <code>false</code> otherwise.
-		 */
-		private boolean isJSPResource(IResource resource) {
-			boolean isJSP = false;
-			
-			//general rule for getting files in the workspace
-			if(resource.getFullPath().segmentCount() >= 2) {
-				IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(resource.getFullPath());
-				if(file.getType() == IResource.FILE) {
-					//get JSP content type each time because there is a possibility it could change
-					IContentType contentTypeJSP = Platform.getContentTypeManager().getContentType(
-							ContentTypeIdForJSP.ContentTypeID_JSP);
-					
-					isJSP = contentTypeJSP.isAssociatedWith(file.getName());
-				}
-			}
-			
-			return isJSP;
-		}
-		
-		/**
-		 * <p>Gets the associated {@link JSPTranslator} for a specific JSP file.</p>
-		 * <p><b>NOTE: </b><i>This does not get the persisted translator but rather the
-		 * associated translator in memory</i></p>
-		 * 
-		 * @param jspFile {@link IFile} to the JSP file that the associated {@link JSPTranslator}
-		 * is needed for
-		 * @return {@link JSPTranslator} associated with the given <code>jspFilePath</code>, or
-		 * <code>null</code> if none can be found.
-		 */
-		protected JSPTranslator getJSPTranslator(IFile jspFile) {
-			IStructuredModel model = null;
-			JSPTranslator translator = null;
-			try {
-				model = StructuredModelManager.getModelManager().getModelForRead(jspFile);
-				if(model instanceof IDOMModel) {
-					IDOMDocument doc = ((IDOMModel)model).getDocument();
-					ModelHandlerForJSP.ensureTranslationAdapterFactory(model);
-					JSPTranslationAdapter adapter = (JSPTranslationAdapter)doc.getAdapterFor(IJSPTranslation.class);
-					
-					//don't want to persist a translator that has not already been requested
-					if(adapter != null && adapter.hasTranslation()) {
-						translator = adapter.getJSPTranslation().getTranslator();
-					}
-				}
-			} catch (IOException e) {
-				Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$
-						" because could not read model for same.", e); //$NON-NLS-1$
-			} catch (CoreException e) {
-				Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$
-						" because could not read model for same.", e); //$NON-NLS-1$
-			} finally {
-				if(model != null) {
-					model.releaseFromRead();
-				}
-			}
-			
-			return translator;
-		}
-		
-		/**
-		 * <p>Persists a {@link JSPTranslator} to disk for a specific JSP file</p>
-		 * 
-		 * @param translator {@link JSPTranslator} to persist to disk
-		 * @param jspFilePath {@link IPath} to the JSP file the given <code>translator</code> is for
-		 */
-		protected void persistTranslator(JSPTranslator translator, String filePath) {
-			try {
-				FileOutputStream fos = new FileOutputStream(filePath);
-				ObjectOutputStream out = new ObjectOutputStream(fos);
-				out.writeObject(translator);
-				out.close();
-			} catch (IOException e) {
-				Logger.logException("Was unable to externalize JSPTranslator " + translator + //$NON-NLS-1$
-						" to " + filePath, e); //$NON-NLS-1$
+		} catch (IOException e) {
+			Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$
+					" because could not read model for same.", e); //$NON-NLS-1$
+		} catch (CoreException e) {
+			Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$
+					" because could not read model for same.", e); //$NON-NLS-1$
+		} finally {
+			if(model != null) {
+				model.releaseFromRead();
 			}
 		}
 		
-		/**
-		 * <p>Deletes a persisted translation for a JSP file that has been deleted</p>
-		 * 
-		 * @param jspFilePath {@link IPath} to the JSP file that has been deleted
-		 */
-		protected void deletePersistedTranslator(File file) {
-			file.delete();
-		}
-		
-		/**
-		 * <p>Renames a persisted translation for a JSP file that has moved</p>
-		 * 
-		 * @param jspPrevFilePath {@link IPath} to the previous location of JSP file</p>
-		 * @param jspNewFilePath {@link IPath} to new location of JSP file</p>
-		 */
-		protected void renamePersistedTranslator(File from, File to) {
-			//do the move
-			from.renameTo(to);
-		}
-
-		private File getPersistedFile(IPath path) {
-			return new File(getPersistedTranslatorFilePath(path.toPortableString()));
-		}
+		return translator;
 	}
 	
 	/**
-	 * <p>{@link Job} responsible for reacting to {@link IResourceDelta} visited
-	 * by {@link JSPResourceVisitor}.  This way the actions that need to be taken
-	 * in reaction to the delta to not hold up the {@link IResourceChangeListener}
-	 * or the {@link IResourceDelta}.</p>
-	 *
+	 * <p>Persists a {@link JSPTranslator} to disk for a specific JSP file</p>
+	 * 
+	 * @param translator {@link JSPTranslator} to persist to disk
+	 * @param jspFilePath {@link IPath} to the JSP file the given <code>translator</code> is for
 	 */
-	private class PersisterJob extends Job {
-		/** Length to delay when scheduling job */
-		private static final int DELAY = 500;
-		
-		/** 
-		 *  <code>{@link LinkedList}&lt{@link ISafeRunnable}&gt</code>
-		 * <p>The persister actions that have been queued up by the {@link JSPResourceVisitor}</p>
-		 */
-		private LinkedList fActions;
-		
-		/** Whether this job has been stopped or not */
-		private boolean fIsStopped;
-		
-		/**
-		 * <p>Sets job up as a system job</p>
-		 */
-		protected PersisterJob() {
-			super(JSPCoreMessages.Persisting_JSP_Translations);
-			this.setUser(false);
-			this.setSystem(true);
-			this.setPriority(Job.LONG);
-			
-			this.fActions = new LinkedList();
-			this.fIsStopped = false;
+	private static void persistTranslator(JSPTranslator translator, String filePath) {
+		try {
+			FileOutputStream fos = new FileOutputStream(filePath);
+			ObjectOutputStream out = new ObjectOutputStream(fos);
+			out.writeObject(translator);
+			out.close();
+		} catch (IOException e) {
+			Logger.logException("Was unable to externalize JSPTranslator " + translator + //$NON-NLS-1$
+					" to " + filePath, e); //$NON-NLS-1$
 		}
-		
-		/**
-		 * <p>Starts this job.  This has no effect if the job is already started.</p>
-		 * <p>This should be used in place of {@link Job#schedule()} to reset state
-		 * caused by calling {@link #stop()}</p>
-		 * 
-		 * @see #stop()
-		 */
-		protected synchronized void start() {
-			this.fIsStopped = false;
-			
-			//get the job running again depending on its current state
-			if(this.getState() == Job.SLEEPING) {
-				this.wakeUp(DELAY);
-			} else {
-				this.schedule(DELAY);
-			}
-		}
-		
-		/**
-		 * <p>Stops this job, even if it is running</p>
-		 * <p>This should be used in place of {@link Job#sleep()} because {@link Job#sleep()}
-		 * will not stop a job that is already running but calling this will stop this job
-		 * even if it is running. {@link #start()} must be used to start this job again</p>
-		 * 
-		 * @see #start()
-		 */
-		protected synchronized void stop() {
-			//sleep the job if it is waiting to run
-			this.sleep();
-			
-			//if job is already running will force it to stop
-			this.fIsStopped = true;
-		}
-		
-		/**
-		 * @param action {@link ISafeRunnable} containing a persister action to take
-		 * based on an {@link IResourceDelta} processed by {@link JSPResourceVisitor}
-		 */
-		protected void addAction(ISafeRunnable action) {
-			//add the action
-			synchronized (this.fActions) {
-				this.fActions.addLast(action);
-			}
-			
-			//if job has not been manually stopped, then start it
-			if(!this.fIsStopped) {
-				this.start();
-			}
-		}
+	}
 
-		/**
-		 * <p>Process the actions until there are none left</p>
-		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
-		 */
-		protected IStatus run(IProgressMonitor monitor) {
-			/*  run so long as job has not been stopped,
-			 *monitor canceled, and have actions to process */
-			while(!this.fIsStopped && !monitor.isCanceled() && !this.fActions.isEmpty()) {
-				ISafeRunnable action;
-				synchronized (this.fActions) {
-					action = (ISafeRunnable)this.fActions.removeFirst();
-				}
-				
-				SafeRunner.run(action);
-			}
-			
-			return Status.OK_STATUS;
-		}
+	/**
+	 * @param path {@link IPath} to the JSP file that the persisted translator is needed for
+	 * @return The persisted translator {@link File} for the JSP file at the given path
+	 * or <code>null</code> if no persisted translator exists for the JSP file at the given path
+	 */
+	private static File getPersistedFile(IPath path) {
+		return new File(getPersistedTranslatorFilePath(path.toString()));
 	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java
index c4a43ef..5313b20 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * Copyright (c) 2004, 2010 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,30 +10,15 @@
  *******************************************************************************/
 package org.eclipse.jst.jsp.core.internal.java.search;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.IResourceProxy;
-import org.eclipse.core.resources.IResourceProxyVisitor;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.content.IContentType;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
-import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
 
 /**
- * Re-indexes the entire workspace.
- * Ensures the JSP Index is in a stable state before performing a search.
- * (like after a crash or if previous indexing was canceled)
- * 
- * @author pavery
+ * @deprecated - no longer used
  */
 public class IndexWorkspaceJob extends Job {
 
@@ -44,64 +29,6 @@
 		DEBUG= value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$
 	}
 	
-	/**
-	 * Visitor that retrieves jsp project paths for all jsp files in the workspace,
-	 * and adds the files to be indexed as they are encountered
-	 */
-	private class JSPFileVisitor implements IResourceProxyVisitor {
-	    private List files = new ArrayList(); 
-		
-		// monitor from the Job
-		IProgressMonitor fInnerMonitor = null;
-		public JSPFileVisitor(IProgressMonitor monitor) {
-			this.fInnerMonitor = monitor;
-		}
-		
-		public boolean visit(IResourceProxy proxy) throws CoreException {
-			
-			// check job canceled
-			if (this.fInnerMonitor != null && this.fInnerMonitor.isCanceled()) {
-				setCanceledState();
-				return false;
-			}
-			
-			// check search support canceled
-			if(JSPSearchSupport.getInstance().isCanceled()) {
-				setCanceledState();
-				return false;
-			}
-			
-			if (proxy.getType() == IResource.FILE) {
-				
-				// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3553
-				// check this before description
-				// check name before actually getting the file (less work)
-				if(getJspContentType().isAssociatedWith(proxy.getName())) {
-					IFile file = (IFile) proxy.requestResource();
-					if(file.exists()) {
-						
-						if(DEBUG)
-							System.out.println("(+) IndexWorkspaceJob adding file: " + file.getName()); //$NON-NLS-1$
-						// this call will check the ContentTypeDescription, so don't need to do it here.
-						//JSPSearchSupport.getInstance().addJspFile(file);
-						this.files.add(file);
-						this.fInnerMonitor.subTask(proxy.getName());
-						
-						// don't search deeper for files
-						return false;
-					}
-				}
-			}
-			return true;
-		}
-		
-		public final IFile[] getFiles() {
-		    return (IFile[])this.files.toArray(new IFile[this.files.size()]);
-		}
-	}
-	
-	private IContentType fContentTypeJSP = null;
-	
 	public IndexWorkspaceJob() {
 		// pa_TODO may want to say something like "Rebuilding JSP Index" to be more
 		// descriptive instead of "Updating JSP Index" since they are 2 different things
@@ -110,54 +37,13 @@
 		setSystem(true);
 	}
 
-	IContentType getJspContentType() {
-		if(this.fContentTypeJSP == null)
-			this.fContentTypeJSP = Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP);
-		return this.fContentTypeJSP;
-	}
-	
 	/**
 	 * @see org eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor) 
 	 * for similar method
 	 */
 	protected IStatus run(IProgressMonitor monitor) {
-		
-		IStatus status = Status.OK_STATUS;
-		Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
-		
-		if(monitor.isCanceled()) {
-			setCanceledState();
-			return Status.CANCEL_STATUS;
-		}
-		
-		if(DEBUG)
-			System.out.println(" ^ IndexWorkspaceJob started: "); //$NON-NLS-1$
-		
-		long start = System.currentTimeMillis();
-		
-		try {
-		    JSPFileVisitor visitor = new JSPFileVisitor(monitor);
-		    // collect all jsp files
-			ResourcesPlugin.getWorkspace().getRoot().accept(visitor, IResource.DEPTH_INFINITE);
-			// request indexing
-			// this is pretty much like faking an entire workspace resource delta
-			JSPIndexManager.getInstance().indexFiles(visitor.getFiles());
-		}
-		catch (CoreException e) {
-			if(DEBUG)
-				e.printStackTrace();
-		}
-		finally {
-			monitor.done();
-		}
-		long finish = System.currentTimeMillis();
-		if(DEBUG)
-			System.out.println(" ^ IndexWorkspaceJob finished\n   total time running: " + (finish - start)); //$NON-NLS-1$
-		
-		return status;
+		return Status.OK_STATUS;
 	}
 	
-	void setCanceledState() {
-		JSPIndexManager.getInstance().setIndexState(JSPIndexManager.S_CANCELED);
-	}
+	void setCanceledState() {}
 }
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java
index a6e8d55..be734a4 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java
@@ -7,725 +7,158 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     
  *******************************************************************************/
 package org.eclipse.jst.jsp.core.internal.java.search;
 
 import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
-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.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
-import org.eclipse.core.runtime.Plugin;
-import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.content.IContentType;
-import org.eclipse.core.runtime.jobs.IJobChangeEvent;
-import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.core.runtime.jobs.JobChangeAdapter;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.internal.core.JavaModelManager;
-import org.eclipse.jdt.internal.core.index.Index;
-import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
 import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
 import org.eclipse.jst.jsp.core.internal.JSPCorePlugin;
 import org.eclipse.jst.jsp.core.internal.Logger;
+import org.eclipse.jst.jsp.core.internal.java.JSPTranslatorPersister;
 import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
-import org.eclipse.osgi.util.NLS;
-import org.osgi.framework.Bundle;
+import org.eclipse.wst.sse.core.indexing.AbstractIndexManager;
 
 /**
- * Responsible for keeping the JSP index up to date.
+ * <p>Index manger used to update the JDT index with the Java translations
+ * of JSPs.</p>
  * 
- * @author pavery
+ * <p>Also keeps JSP persistence up to date</p>
+ * 
+ * <p>Any action that needs the JDT index to have all of the latest JSP changes processed
+ * should wait for this manger to report that it is consistent,
+ * {@link #waitForConsistent(org.eclipse.core.runtime.IProgressMonitor)}.  Such actions
+ * include but are not limited to searching and refactoring JSPs.</p>
  */
-public class JSPIndexManager {
-
-	// for debugging
-	// TODO move this to Logger, as we have in SSE
-	static final boolean DEBUG;
-	static {
-		String value = Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/jspindexmanager"); //$NON-NLS-1$
-		DEBUG = value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$
+public class JSPIndexManager extends AbstractIndexManager {
+	/** the singleton instance of the {@link JSPIndexManager} */
+	private static JSPIndexManager INSTANCE;
+	
+	/** the JSP {@link IContentType} */
+	private static final IContentType JSP_CONTENT_TYPE =
+		Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP);
+	
+	/** the location to store state */
+	private IPath fWorkingLocation;
+	
+	/**
+	 * <p>Private singleton constructor</p>
+	 */
+	private JSPIndexManager() {
+		super(JSPCoreMessages.JSPIndexManager_0, JSPCoreMessages.JSPIndexManager_0,
+				JSPCoreMessages.Initializing, JSPCoreMessages.JSPIndexManager_2);
+	}
+	
+	/**
+	 * @return the singleton instance of the {@link JSPIndexManager}
+	 */
+	public static JSPIndexManager getDefault() {
+		return INSTANCE != null ? INSTANCE : (INSTANCE = new JSPIndexManager());
+	}
+	
+	/**
+	 * @deprecated - use {@link #getDefault()} 
+	 **/
+	public synchronized static JSPIndexManager getInstance() {
+		return getDefault();
+		
+	}
+	/**
+	 * @see indexer.internal.indexing.AbstractIndexManager#isResourceToIndex(int, java.lang.String)
+	 */
+	protected boolean isResourceToIndex(int type, IPath path) {
+		String name = path.lastSegment();
+		return 
+			type == IResource.PROJECT || 
+			(type == IResource.FOLDER && !name.equals("bin") && !name.startsWith(".")) || //$NON-NLS-1$ //$NON-NLS-2$
+			JSP_CONTENT_TYPE.isAssociatedWith(path.lastSegment());
 	}
 
-	private static final String PKEY_INDEX_STATE = "jspIndexState"; //$NON-NLS-1$
-
-	private IndexWorkspaceJob indexingJob = new IndexWorkspaceJob();
-
-
-
-	// TODO: consider enumeration for these int constants
-	// set to S_UPDATING once a resource change comes in
-	// set to S_STABLE if:
-	// - we know we aren't interested in the resource change
-	// - or the ProcessFilesJob completes
-	// set to S_CANCELED if an indexing job is canceled
-	// set to S_REBUILDING if re-indexing the entire workspace
-
-	// the int '0' is reserved for the default value if a preference is not
-	// there
-	/** index is reliable to use */
-	public static final int S_STABLE = 1;
-	/** index is being updated (from a resource delta) */
-	public static final int S_UPDATING = 2;
-	/** entire index is being rebuilt */
-	public static final int S_REBUILDING = 3;
 	/**
-	 * indexing job was canceled in the middle of it, index needs to be
-	 * rebuilt
+	 * @see indexer.internal.indexing.AbstractIndexManager#getWorkingLocation()
 	 */
-	public static final int S_CANCELED = 4;
+	protected IPath getWorkingLocation() {
+		if(this.fWorkingLocation == null) {
+			//create path to working area
+    		IPath workingLocation =
+    			JSPCorePlugin.getDefault().getStateLocation().append("jspsearch"); //$NON-NLS-1$
 
-	/** symbolic name for OSGI framework */
-	private final String OSGI_FRAMEWORK_ID = "org.eclipse.osgi"; //$NON-NLS-1$
+            // ensure that it exists on disk
+            File folder = new File(workingLocation.toOSString());
+    		if (!folder.isDirectory()) {
+    			try {
+    				folder.mkdir();
+    			}
+    			catch (SecurityException e) {
+    				Logger.logException(this.getName() +
+    						": Error while creating state location: " + folder + //$NON-NLS-1$
+    						" This renders the index manager irrevocably broken for this workspace session", //$NON-NLS-1$
+    						e);
+    			}
+    		}
+    		
+    		this.fWorkingLocation = workingLocation;
+    	}
+    	
+        return this.fWorkingLocation;
+	}
 
 	/**
-	 * Collects JSP files from a resource delta.
+	 * @see indexer.internal.indexing.AbstractIndexManager#performAction(byte, byte, org.eclipse.core.resources.IResource, org.eclipse.core.runtime.IPath)
 	 */
-	private class JSPResourceVisitor implements IResourceDeltaVisitor {
-		// using hash map ensures only one of each file
-		// must be reset before every use
-		private HashMap jspFiles = null;
-
-		public JSPResourceVisitor() {
-			this.jspFiles = new HashMap();
-		}
-
-		public boolean visit(IResourceDelta delta) throws CoreException {
-
-			// in case JSP search was canceled (eg. when closing the editor)
-			if (JSPSearchSupport.getInstance().isCanceled() || frameworkIsShuttingDown()) {
-				setCanceledState();
-				return false;
+	protected void performAction(byte source, byte action, IResource resource,
+			IPath movePath) {
+		
+		//inform the persister of the action unless it come from a full workspace scan
+		if(JSPTranslatorPersister.ACTIVATED && source != AbstractIndexManager.SOURCE_WORKSPACE_SCAN) {
+			switch(action) {
+				case AbstractIndexManager.ACTION_ADD: {
+					JSPTranslatorPersister.persistTranslation(resource);
+					break;
+				}
+				case AbstractIndexManager.ACTION_REMOVE: {
+					JSPTranslatorPersister.removePersistedTranslation(resource);
+					break;
+				}
+				case AbstractIndexManager.ACTION_ADD_MOVE_FROM: {
+					JSPTranslatorPersister.movePersistedTranslation(resource, movePath);
+					break;
+				}
+				case AbstractIndexManager.ACTION_REMOVE_MOVE_TO: {
+					//do nothing, taken care of by AbstractIndexManager.ACTION_ADD_MOVE_FROM
+					break;
+				}
 			}
-
+		}
+		
+		//add any new JSP files to the JDT index using the JSPSearchSupport
+		if(action == AbstractIndexManager.ACTION_ADD ||
+				action == AbstractIndexManager.ACTION_ADD_MOVE_FROM) {
+		
+			IFile file = (IFile)resource; //this assumption can be made because of #isResourceToIndex
+			JSPSearchSupport ss = JSPSearchSupport.getInstance();
 			try {
-				if (!isHiddenResource(delta.getFullPath())) {
-
-					int kind = delta.getKind();
-					boolean added = (kind & IResourceDelta.ADDED) == IResourceDelta.ADDED;
-					boolean isInterestingChange = false;
-					if ((kind & IResourceDelta.CHANGED) == IResourceDelta.CHANGED) {
-						int flags = delta.getFlags();
-						// ignore things like marker changes
-						isInterestingChange = (flags & IResourceDelta.CONTENT) == IResourceDelta.CONTENT || (flags & IResourceDelta.REPLACED) == IResourceDelta.REPLACED;
-					}
-					boolean removed = (kind & IResourceDelta.REMOVED) == IResourceDelta.REMOVED;
-					if (added || isInterestingChange) {
-
-						visitAdded(delta);
-					}
-					else if (removed) {
-						visitRemoved(delta);
+				IProject project = file.getProject();
+				if (project != null) {
+					IJavaProject jproject = JavaCore.create(project);
+					if (jproject.exists()) {
+						ss.addJspFile(file);
 					}
 				}
 			}
 			catch (Exception e) {
-				// need to set state here somehow, and reindex
-				// otherwise index will be unreliable
-				if (DEBUG)
-					Logger.logException("Delta analysis may not be complete", e); //$NON-NLS-1$
-			}
-			// if the delta has children, continue to add/remove files
-			return true;
-		}
-
-		private void visitRemoved(IResourceDelta delta) {
-			// handle cleanup
-			if (delta.getResource() != null) {
-				IResource r = delta.getResource();
-				if (r.getType() == IResource.FOLDER && r.exists()) {
-					deleteIndex((IFile) r);
-				}
-			}
-		}
-
-		private void visitAdded(IResourceDelta delta) {
-			// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3553
-			// quick check if it's even JSP related to improve
-			// performance
-			// checking name from the delta before getting
-			// resource because it's lighter
-			String filename = delta.getFullPath().lastSegment();
-			if (filename != null && getJspContentType().isAssociatedWith(filename)) {
-				IResource r = delta.getResource();
-				if (r != null && r.exists() && r.getType() == IResource.FILE) {
-					this.jspFiles.put(r.getFullPath(), r);
-				}
-			}
-		}
-
-		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=93463
-		private boolean isHiddenResource(IPath p) {
-			String[] segments = p.segments();
-			for (int i = 0; i < segments.length; i++) {
-				if (segments[i].startsWith(".")) //$NON-NLS-1$
-					return true;
-			}
-			return false;
-		}
-
-		private void deleteIndex(IFile folder) {
-			// cleanup index
-			IndexManager im = JavaModelManager.getIndexManager();
-			IPath folderPath = folder.getFullPath();
-			IPath indexLocation = JSPSearchSupport.getInstance().computeIndexLocation(folderPath);
-			im.removeIndex(indexLocation);
-			// im.indexLocations.removeKey(folderPath);
-			// im.indexLocations.removeValue(indexLocation);
-			File f = indexLocation.toFile();
-			f.delete();
-		}
-
-		public IFile[] getFiles() {
-			return (IFile[]) this.jspFiles.values().toArray(new IFile[this.jspFiles.size()]);
-		}
-
-		public void reset() {
-			this.jspFiles.clear();
-		}
-	}
-
-	// end class JSPResourceVisitor
-
-	/**
-	 * schedules JSP files for indexing by Java core
-	 */
-	private class ProcessFilesJob extends Job {
-		List fileList = null;
-		// keep track of how many files we've indexed
-		int lastFileCursor = 0;
-
-		ProcessFilesJob(String taskName) {
-			super(taskName);
-			fileList = new ArrayList();
-		}
-
-		synchronized void process(IFile[] files) {
-			for (int i = 0; i < files.length; i++) {
-				fileList.add(files[i]);
-			}
-			if (DEBUG) {
-				System.out.println("JSPIndexManager queuing " + files.length + " files"); //$NON-NLS-2$ //$NON-NLS-1$
-			}
-		}
-
-		synchronized IFile[] getFiles() {
-			return (IFile[]) fileList.toArray(new IFile[fileList.size()]);
-		}
-
-		synchronized void clearFiles() {
-			fileList.clear();
-			lastFileCursor = 0;
-			//System.out.println("cleared files");
-		}
-
-		protected IStatus run(IProgressMonitor monitor) {
-			// System.out.println("indexer monitor" + monitor);
-			if (isCanceled(monitor) || frameworkIsShuttingDown()) {
-				setCanceledState();
-				return Status.CANCEL_STATUS;
-			}
-
-			Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
-			long start = System.currentTimeMillis();
-
-			try {
-				IFile[] filesToBeProcessed = getFiles();
-
-				if (DEBUG) {
-					System.out.println("JSPIndexManager indexing " + filesToBeProcessed.length + " files"); //$NON-NLS-2$ //$NON-NLS-1$
-				}
-				// API indicates that monitor is never null
-				monitor.beginTask("", filesToBeProcessed.length); //$NON-NLS-1$
-				JSPSearchSupport ss = JSPSearchSupport.getInstance();
-				String processingNFiles = ""; //$NON-NLS-1$
-
-
-				for (;lastFileCursor < filesToBeProcessed.length; lastFileCursor++) {
-
-					if (isCanceled(monitor) || frameworkIsShuttingDown()) {
-						setCanceledState();
-						return Status.CANCEL_STATUS;
-					}
-					IFile file = filesToBeProcessed[lastFileCursor];
-					try {
-						IProject project = file.getProject();
-						if (project != null) {
-							IJavaProject jproject = JavaCore.create(project);
-							if (jproject.exists()) {
-								ss.addJspFile(file);
-								if (DEBUG) {
-									System.out.println("JSPIndexManager Job added file: " + file.getName()); //$NON-NLS-1$
-								}
-							}
-							// JSP Indexer processing n files
-							processingNFiles = NLS.bind(JSPCoreMessages.JSPIndexManager_2, new String[]{Integer.toString((filesToBeProcessed.length - lastFileCursor))});
-							monitor.subTask(processingNFiles + " - " + file.getName()); //$NON-NLS-1$
-							monitor.worked(1);
-						}
-					}
-					catch (Exception e) {
-						// RATLC00284776
-						// ISSUE: we probably shouldn't be catching EVERY
-						// exception, but
-						// the framework only allows to return IStatus in
-						// order to communicate
-						// that something went wrong, which means the loop
-						// won't complete, and we would hit the same problem
-						// the next time.
-						// 
-						// a possible solution is to keep track of the
-						// exceptions logged
-						// and only log a certain amt of the same one,
-						// otherwise skip it.
-						if (!frameworkIsShuttingDown()) {
-							String filename = file != null ? file.getFullPath().toString() : ""; //$NON-NLS-1$
-							Logger.logException("JSPIndexer problem indexing:" + filename, e); //$NON-NLS-1$
-						}
-					}
-				} // end for
-			}
-			finally {
-				// just in case something didn't follow API (monitor is null)
-				if (monitor != null)
-					monitor.done();
-			}
-
-			// successfully finished, clear files list
-			clearFiles();
-			
-			long finish = System.currentTimeMillis();
-			long diff = finish - start;
-			if (DEBUG) {
-				fTotalTime += diff;
-				System.out.println("============================================================================"); //$NON-NLS-1$
-				System.out.println("this time: " + diff + " cumulative time for resource changed: " + fTotalTime); //$NON-NLS-1$ //$NON-NLS-2$
-				System.out.println("============================================================================"); //$NON-NLS-1$
-			}
-			return Status.OK_STATUS;
-		}
-
-		private boolean isCanceled(IProgressMonitor runMonitor) {
-
-			boolean canceled = false;
-			// check specific monitor passed into run method (the progress
-			// group in this case)
-			// check main search support canceled
-			if (runMonitor != null && runMonitor.isCanceled())
-				canceled = true;
-			else if (JSPSearchSupport.getInstance().isCanceled()) {
-				canceled = true;
-				if (runMonitor != null) {
-					runMonitor.setCanceled(true);
-				}
-			}
-			return canceled;
-		}
-		
-	}
-
-	// end class ProcessFilesJob
-
-	private static JSPIndexManager fSingleton = null;
-	private boolean initialized;
-	private boolean initializing = true;
-
-	private IndexJobCoordinator indexJobCoordinator;
-	private IResourceChangeListener jspResourceChangeListener;
-
-	private JSPResourceVisitor fVisitor = null;
-	private IContentType fContentTypeJSP = null;
-
-	static long fTotalTime = 0;
-
-	// Job for processing resource delta
-	private ProcessFilesJob processFilesJob = null;
-
-	private JSPIndexManager() {
-		processFilesJob = new ProcessFilesJob(JSPCoreMessages.JSPIndexManager_0);
-		// only show in verbose mode
-		processFilesJob.setSystem(true);
-		processFilesJob.setPriority(Job.LONG);
-		processFilesJob.addJobChangeListener(new JobChangeAdapter() {
-			public void done(IJobChangeEvent event) {
-				super.done(event);
-				setStableState();
-			}
-		});
-	}
-
-	public synchronized static JSPIndexManager getInstance() {
-
-		if (fSingleton == null)
-			fSingleton = new JSPIndexManager();
-		return fSingleton;
-	}
-
-	public void initialize() {
-
-		JSPIndexManager singleInstance = getInstance();
-
-
-		if (!singleInstance.initialized) {
-			singleInstance.initialized = true;
-			singleInstance.initializing = true;
-
-			singleInstance.indexJobCoordinator = new IndexJobCoordinator();
-			singleInstance.jspResourceChangeListener = new JSPResourceChangeListener();
-
-			// added as JobChange listener so JSPIndexManager can be smarter
-			// about when it runs
-			Platform.getJobManager().addJobChangeListener(singleInstance.indexJobCoordinator);
-
-			// add JSPIndexManager to keep JSP Index up to date
-			// listening for IResourceChangeEvent.PRE_DELETE and
-			// IResourceChangeEvent.POST_CHANGE
-			ResourcesPlugin.getWorkspace().addResourceChangeListener(jspResourceChangeListener, IResourceChangeEvent.POST_CHANGE);
-
-			// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5091
-			// makes sure IndexManager is aware of our indexes
-			saveIndexes();
-			singleInstance.initializing = false;
-		}
-
-	}
-	
-	synchronized void setIndexState(int state) {
-		if (DEBUG) {
-			System.out.println("JSPIndexManager setting index state to: " + state2String(state)); //$NON-NLS-1$
-		}
-		Plugin jspModelPlugin = JSPCorePlugin.getDefault();
-		jspModelPlugin.getPluginPreferences().setValue(PKEY_INDEX_STATE, state);
-		jspModelPlugin.savePluginPreferences();
-
-	}
-
-	private String state2String(int state) {
-		String s = "UNKNOWN"; //$NON-NLS-1$
-		switch (state) {
-			case (S_STABLE) :
-				s = "S_STABLE"; //$NON-NLS-1$
-				break;
-			case (S_UPDATING) :
-				s = "S_UPDATING"; //$NON-NLS-1$
-				break;
-			case (S_CANCELED) :
-				s = "S_CANCELED"; //$NON-NLS-1$
-				break;
-			case (S_REBUILDING) :
-				s = "S_REBUILDING"; //$NON-NLS-1$
-				break;
-		}
-		return s;
-	}
-
-	int getIndexState() {
-		return JSPCorePlugin.getDefault().getPluginPreferences().getInt(PKEY_INDEX_STATE);
-	}
-
-	void setUpdatingState() {
-		//if (getIndexState() != S_CANCELED)
-		setIndexState(S_UPDATING);
-	}
-
-	void setCanceledState() {
-		setIndexState(JSPIndexManager.S_CANCELED);
-	}
-
-	void setStableState() {
-		//if (getIndexState() != S_CANCELED)
-		setIndexState(S_STABLE);
-	}
-
-	void setRebuildingState() {
-		setIndexState(S_REBUILDING);
-	}
-
-	synchronized void rebuildIndexIfNeeded() {
-		if (getIndexState() != S_STABLE) {
-			rebuildIndex();
-		}
-	}
-
-	void rebuildIndex() {
-
-		if (DEBUG)
-			System.out.println("*** JSP Index unstable, requesting re-indexing"); //$NON-NLS-1$
-
-		getIndexingJob().addJobChangeListener(new JobChangeAdapter() {
-			public void aboutToRun(IJobChangeEvent event) {
-				super.aboutToRun(event);
-				setRebuildingState();
-			}
-
-			public void done(IJobChangeEvent event) {
-				super.done(event);
-				setStableState();
-				getIndexingJob().removeJobChangeListener(this);
-			}
-		});
-		// we're about to reindex everything anyway
-		getProcessFilesJob().clearFiles();
-		getIndexingJob().schedule();
-
-	}
-
-	/**
-	 * Creates and schedules a Job to process collected files. All JSP
-	 * indexing should be done through this method or processFiles(IFile file)
-	 * 
-	 * @param files
-	 */
-	final void indexFiles(IFile[] files) {
-		// don't use this rule
-		// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4931
-		// processFiles.setRule(new IndexFileRule());
-		processFilesJob.process(files);
-	}
-
-
-	/**
-	 * Package protected for access by inner Job class in resourceChanged(...)
-	 * 
-	 * @return
-	 */
-	JSPResourceVisitor getVisitor() {
-
-		if (this.fVisitor == null) {
-			this.fVisitor = new JSPResourceVisitor();
-		}
-		return this.fVisitor;
-	}
-
-	// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5091
-	// makes sure IndexManager is aware of our indexes
-	void saveIndexes() {
-		IndexManager indexManager = JavaModelManager.getIndexManager();
-		IPath jspModelWorkingLocation = JSPSearchSupport.getInstance().getModelJspPluginWorkingLocation();
-
-		File folder = new File(jspModelWorkingLocation.toOSString());
-		String[] files = folder.list();
-		String locay = ""; //$NON-NLS-1$
-		Index index = null;
-		try {
-			for (int i = 0; i < files.length; i++) {
-				if (files[i].toLowerCase().endsWith(".index")) { //$NON-NLS-1$
-					locay = jspModelWorkingLocation.toString() + "/" + files[i]; //$NON-NLS-1$
-					// reuse index file
-					index = new Index(locay, "Index for " + locay, true); //$NON-NLS-1$
-					indexManager.saveIndex(index);
-				}
-			}
-		}
-		catch (Exception e) {
-			// we should be shutting down, want to shut down quietly
-			if (DEBUG)
-				e.printStackTrace();
-		}
-	}
-
-	IContentType getJspContentType() {
-		if (this.fContentTypeJSP == null)
-			this.fContentTypeJSP = Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP);
-		return this.fContentTypeJSP;
-	}
-
-	/**
-	 * A check to see if the OSGI framework is shutting down.
-	 * 
-	 * @return true if the System Bundle is stopped (ie. the framework is
-	 *         shutting down)
-	 */
-	boolean frameworkIsShuttingDown() {
-		// in the Framework class there's a note:
-		// set the state of the System Bundle to STOPPING.
-		// this must be done first according to section 4.19.2 from the OSGi
-		// R3 spec.
-		boolean shuttingDown = Platform.getBundle(OSGI_FRAMEWORK_ID).getState() == Bundle.STOPPING;
-		if (DEBUG && shuttingDown) {
-			System.out.println("JSPIndexManager: system is shutting down!"); //$NON-NLS-1$
-		}
-		return shuttingDown;
-	}
-
-
-	public void shutdown() {
-
-		// stop listening
-		ResourcesPlugin.getWorkspace().removeResourceChangeListener(jspResourceChangeListener);
-
-
-		// stop any searching
-		JSPSearchSupport.getInstance().setCanceled(true);
-
-		// stop listening to jobs
-		Platform.getJobManager().removeJobChangeListener(indexJobCoordinator);
-
-
-		int maxwait = 5000;
-		if (processFilesJob != null) {
-			processFilesJob.cancel();
-		}
-		// attempt to make sure this indexing job is litterally
-		// done before continuing, since we are shutting down
-		waitTillNotRunning(maxwait, processFilesJob);
-
-		if (indexingJob != null) {
-			indexingJob.cancel();
-		}
-		waitTillNotRunning(maxwait, processFilesJob);
-	}
-
-	private void waitTillNotRunning(int maxSeconds, Job job) {
-		int pauseTime = 10;
-		int maxtries = maxSeconds / pauseTime;
-		int count = 0;
-		while (count++ < maxtries && job.getState() == Job.RUNNING) {
-			try {
-				Thread.sleep(pauseTime);
-				// System.out.println("count: " + count + " max: " +
-				// maxtries);
-			}
-			catch (InterruptedException e) {
-				Logger.logException(e);
+				String filename = file != null ? file.getFullPath().toString() : ""; //$NON-NLS-1$
+				Logger.logException("JPSIndexManger: problem indexing:" + filename, e); //$NON-NLS-1$
 			}
 		}
 	}
-
-	private class IndexJobCoordinator extends JobChangeAdapter {
-		
-		public void aboutToRun(IJobChangeEvent event) {
-			Job jobToCoordinate = event.getJob();
-			if (isJobToAvoid(jobToCoordinate)) {
-				// job will be rescheduled when the job we
-				// are avoiding (eg. build) is done
-				getProcessFilesJob().cancel();
-				//System.out.println("cancel:" + jobToCoordinate.getName());
-			}
-		}
-
-		public void done(IJobChangeEvent event) {
-
-			Job jobToCoordinate = event.getJob();
-			if (isJobToAvoid(jobToCoordinate)) {
-				if (getProcessFilesJob().getFiles().length > 0) {
-					getProcessFilesJob().schedule(500);
-					//System.out.println("schedule:" + jobToCoordinate.getName());
-				}
-					
-
-			}
-		}
-
-		private boolean isJobToAvoid(Job jobToCoordinate) {
-			boolean result = false;
-			if (jobToCoordinate.belongsTo(ResourcesPlugin.FAMILY_AUTO_BUILD) || jobToCoordinate.belongsTo(ResourcesPlugin.FAMILY_MANUAL_BUILD) || jobToCoordinate.belongsTo(ResourcesPlugin.FAMILY_AUTO_REFRESH)) {
-				result = true;
-			}
-			return result;
-
-		}
-
-	}
-
-	private class JSPResourceChangeListener implements IResourceChangeListener {
-
-
-		/**
-		 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
-		 */
-		public void resourceChanged(IResourceChangeEvent event) {
-
-			if (isInitializing())
-				return;
-
-			// ignore resource changes if already rebuilding
-			if (getIndexState() == S_REBUILDING)
-				return;
-			// previously canceled, needs entire index rebuild
-			if (getIndexState() == S_CANCELED) {
-				// rebuildIndex();
-				// just resume indexing
-				getProcessFilesJob().schedule(500);
-				//System.out.println("schedule: resource changed, previously canceled");
-				return;
-			}
-
-			IResourceDelta delta = event.getDelta();
-			if (delta != null) {
-				// only care about adds or changes right now...
-				int kind = delta.getKind();
-				boolean added = (kind & IResourceDelta.ADDED) == IResourceDelta.ADDED;
-				boolean changed = (kind & IResourceDelta.CHANGED) == IResourceDelta.CHANGED;
-				if (added || changed) {
-
-					// only analyze the full (starting at root) delta
-					// hierarchy
-					if (delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$
-						try {
-							JSPResourceVisitor v = getVisitor();
-							// clear from last run
-							v.reset();
-							// count files, possibly do this in a job too...
-							// don't include PHANTOM resources
-							delta.accept(v, false);
-
-							// process files from this delta
-							IFile[] files = v.getFiles();
-							if (files.length > 0) {
-								/*
-								 * Job change listener should set back to
-								 * stable when finished
-								 */
-								setUpdatingState();
-								// processFiles(files);
-								indexFiles(files);
-							}
-						}
-						catch (CoreException e) {
-							// need to set state here somehow, and reindex
-							// otherwise index will be unreliable
-							if (DEBUG)
-								Logger.logException(e);
-						}
-						catch (Exception e) {
-							// need to set state here somehow, and reindex
-							// otherwise index will be unreliable
-							if (DEBUG)
-								Logger.logException(e);
-						}
-					}
-				}
-
-			}
-		}
-
-	}
-
-	IndexWorkspaceJob getIndexingJob() {
-		return indexingJob;
-	}
-
-	ProcessFilesJob getProcessFilesJob() {
-		return processFilesJob;
-	}
-
-	boolean isInitializing() {
-		return initializing;
-	}
-
 }
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java
index ed3ef8b..f17e430 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2007 IBM Corporation and others.
+ * Copyright (c) 2004, 2010 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -33,7 +33,6 @@
 import org.eclipse.jdt.core.search.SearchEngine;
 import org.eclipse.jdt.core.search.SearchPattern;
 import org.eclipse.jdt.core.search.SearchRequestor;
-import org.eclipse.jdt.internal.core.JavaModelManager;
 import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
 import org.eclipse.jst.jsp.core.internal.JSPCorePlugin;
 import org.eclipse.jst.jsp.core.internal.Logger;
@@ -315,10 +314,67 @@
      * @param requestor
      *            passed in to accept search matches (and do "something" with
      *            them)
+     * 
+     * @deprecated use {@link #search(String, IJavaSearchScope, int, int, int, boolean, SearchRequestor, IProgressMonitor)}
      */
     public void search(String searchText, IJavaSearchScope scope, int searchFor, int limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor) {
+    	this.search(searchText, scope, searchFor, limitTo, matchMode, isCaseSensitive,
+    			requestor, new NullProgressMonitor());
+    }
 
-        JSPIndexManager.getInstance().rebuildIndexIfNeeded();
+    /**
+     * Search for an IJavaElement, constrained by the given parameters. Runs in
+     * a background Job (results may still come in after this method call)
+     * 
+     * @param element
+     * @param scope
+     * @param requestor
+     * 
+     * @deprecated use {@link #search(IJavaElement, IJavaSearchScope, SearchRequestor, IProgressMonitor)}
+     */
+    public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) {
+    	this.search(element, scope, requestor, new NullProgressMonitor());
+    }
+
+    /**
+     * Search for an IJavaElement, constrained by the given parameters. Runs in
+     * an IWorkspace runnable (results will be reported by the end of this
+     * method)
+     * 
+     * @param element
+     * @param scope
+     * @param requestor
+     */
+    public void searchRunnable(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) {
+    	this.searchRunnable(element, scope, requestor, new NullProgressMonitor());
+    }
+    
+    /**
+     * Perform a java search w/ the given parameters. Runs in a background Job
+     * (results may still come in after this method call)
+     * 
+     * @param searchText
+     *            the string of text to search on
+     * @param searchFor
+     *            IJavaSearchConstants.TYPE, METHOD, FIELD, PACKAGE, etc...
+     * @param limitTo
+     *            IJavaSearchConstants.DECLARATIONS,
+     *            IJavaSearchConstants.REFERENCES,
+     *            IJavaSearchConstants.IMPLEMENTORS, or
+     *            IJavaSearchConstants.ALL_OCCURRENCES
+     * @param matchMode
+     *            allow * wildcards or not
+     * @param isCaseSensitive
+     * @param requestor
+     *            passed in to accept search matches (and do "something" with
+     *            them)
+     */
+    public void search(String searchText, IJavaSearchScope scope, int searchFor, int
+    		limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor,
+    		IProgressMonitor monitor) {
+
+    	//wait for the index
+		JSPIndexManager.getDefault().waitForConsistent(monitor);
 
         SearchJob job = new SearchJob(searchText, scope, searchFor, limitTo, matchMode, isCaseSensitive, requestor);
         setCanceled(false);
@@ -338,10 +394,12 @@
      * @param scope
      * @param requestor
      */
-    public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) {
+    public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor,
+    		IProgressMonitor monitor) {
 
-        JSPIndexManager.getInstance().rebuildIndexIfNeeded();
-
+    	//wait for the index
+		JSPIndexManager.getDefault().waitForConsistent(monitor);
+    	
         SearchJob job = new SearchJob(element, scope, requestor);
         setCanceled(false);
         job.setUser(true);
@@ -359,9 +417,11 @@
      * @param scope
      * @param requestor
      */
-    public void searchRunnable(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) {
-
-        JSPIndexManager.getInstance().rebuildIndexIfNeeded();
+    public void searchRunnable(IJavaElement element, IJavaSearchScope scope,
+    		SearchRequestor requestor, IProgressMonitor monitor) {
+    	
+    	//wait for the index
+		JSPIndexManager.getDefault().waitForConsistent(monitor);
 
         SearchRunnable searchRunnable = new SearchRunnable(element, scope, requestor);
         try {
@@ -467,11 +527,6 @@
             // this is the only difference from
             // IndexManager#computeIndexLocation(...)
             indexLocation = getModelJspPluginWorkingLocation().append(fileName);
-
-            // pa_TODO need to add to java path too, so JDT search support knows
-            // there should be a non internal way to do this.
-            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=77564
-            JavaModelManager.getIndexManager().indexLocations.put(containerPath, indexLocation);
         //}
         return indexLocation;
     }
@@ -483,7 +538,7 @@
             return this.fJspPluginLocation;
 
         // Append the folder name "jspsearch" to keep the state location area cleaner
-        IPath stateLocation = JSPCorePlugin.getDefault().getStateLocation().append("jspsearch");
+        IPath stateLocation = JSPCorePlugin.getDefault().getStateLocation().append("jspsearch"); //$NON-NLS-1$
 
         // pa_TODO workaround for
         // https://bugs.eclipse.org/bugs/show_bug.cgi?id=62267
diff --git a/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java
index 399f27a..1cd9d31 100644
--- a/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java
+++ b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * Copyright (c) 2004, 2010 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -59,7 +59,7 @@
 				SearchRequestor jspRequestor = new JSPSearchRequestor(requestor);
 				
 				// pa_TODO need to adapt JavaSearchScope to a JSPSearchScope
-				JSPSearchSupport.getInstance().search(element, new JSPSearchScope(), jspRequestor);
+				JSPSearchSupport.getInstance().search(element, new JSPSearchScope(), jspRequestor, monitor);
 				
 			}
 			else if(querySpecification instanceof PatternQuerySpecification) {
@@ -79,7 +79,8 @@
 														patternQuery.getLimitTo(), 
 														SearchPattern.R_PATTERN_MATCH, 
 														false, 
-														jspRequestor);
+														jspRequestor,
+														monitor);
 			}
 		}
 	}
diff --git a/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF
index 1676e52..95d82ca 100644
--- a/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF
@@ -7,6 +7,7 @@
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
 Export-Package: org.eclipse.wst.sse.core,
+ org.eclipse.wst.sse.core.indexing,
  org.eclipse.wst.sse.core.internal;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui",
  org.eclipse.wst.sse.core.internal.cleanup;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui",
  org.eclipse.wst.sse.core.internal.document;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui",
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java
new file mode 100644
index 0000000..0adc70b
--- /dev/null
+++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java
@@ -0,0 +1,1706 @@
+/*******************************************************************************
+ * Copyright (c) 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     
+ *******************************************************************************/
+package org.eclipse.wst.sse.core.indexing;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+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.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
+import org.eclipse.core.resources.ISaveParticipant;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.sse.core.internal.Logger;
+
+/**
+ * <p>
+ * A generic class for implementing a resource index manager. It is important
+ * to note that this only provides the framework for managing an index, not
+ * actually indexing. The subtle difference is that the manager is in charge
+ * of paying attention to all of the resource actions that take place in the
+ * workspace and filtering those actions down to simple actions that need to
+ * be performed on whatever index this manager is managing.
+ * </p>
+ * 
+ * <p>
+ * The manager does its very best to make sure the index is always consistent,
+ * even if resource events take place when the manager is not running. In the
+ * event that the manager determines it has missed, lost, or corrupted any
+ * resource change events that have occurred before, during, or after its
+ * activation or deactivation then the manager will inspect the entire
+ * workspace to insure the index it is managing is consistent.
+ * </p>
+ * 
+ * <p>
+ * This class and its contents are <b>PROVISIONAL</b>. They can and very likely will
+ * change prior to finalization in the next main release.
+ * </p>
+ * 
+ */
+public abstract class AbstractIndexManager {
+	
+	/** Used to encode path bytes in case they contain double byte characters */
+	private static final String ENCODING_UTF16 = "utf16"; //$NON-NLS-1$
+	
+	/** Default time to wait for other tasks to finish */
+	private static final int WAIT_TIME = 300;
+	
+	/**
+	 * <p>Used to report progress on jobs where the total work to complete is unknown.
+	 * The created effect is a progress bar that moves but that will never complete.</p>
+	 */
+	private static final int UNKNOWN_WORK = 100;
+	
+	/** The amount of events to batch up before sending them off to the processing job*/
+	private static final int BATCH_UP_AMOUNT = 100;
+	
+	/** If this file exists then a full workspace re-processing is needed */ 
+	private static final String RE_PROCESS_FILE_NAME = ".re-process"; //$NON-NLS-1$
+	
+	/** Common error message to log */
+	private static final String LOG_ERROR_INDEX_INVALID =
+		"Index may become invalid, incomplete, or enter some other inconsistent state."; //$NON-NLS-1$
+	
+	/** State: manager is stopped */
+	private static final byte STATE_DISABLED = 0;
+	
+	/** State: manager is running */
+	private static final byte STATE_ENABLED = 1;
+	
+	/** Action: add to index */
+	protected static final byte ACTION_ADD = 0;
+	
+	/** Action: remove from index */
+	protected static final byte ACTION_REMOVE = 1;
+	
+	/** Action: add to index caused by move operation */
+	protected static final byte ACTION_ADD_MOVE_FROM = 2;
+	
+	/** Action: remove from index caused by move operation */
+	protected static final byte ACTION_REMOVE_MOVE_TO = 3;
+	
+	/** Source: action originated from resource change event */
+	protected static final byte SOURCE_RESOURCE_CHANGE = 0;
+	
+	/** Source: action originated from workspace scan */
+	protected static final byte SOURCE_WORKSPACE_SCAN = 1;
+	
+	/** Source: action originated from saved state */
+	protected static final byte SOURCE_SAVED_STATE = 2;
+	
+	/** Source: preserved resources to index */
+	protected static final byte SOURCE_PRESERVED_RESOURCES_TO_INDEX = 3;
+	
+	/** the name of this index manager */
+	private String fName;
+	
+	/** Message to use when manager is running */
+	private String fMessage_Running;
+	
+	/** Message to use when manager is initializing */
+	private String fMessage_Initializing;
+	
+	/**
+	 * <p>Message to use when manager is processing files.
+	 * Should contain one parameter to be filled in with the number of files to process.</p>
+	 */
+	private String fMessage_ProcessingFiles;
+	
+	/** {@link IResourceChangeListener} to listen for file changes */
+	private ResourceChangeListener fResourceChangeListener;
+	
+	/** The {@link Job} that does all of the indexing */
+	private ResourceEventProcessingJob fResourceEventProcessingJob;
+	
+	/** A {@link Job} to search the workspace for all files */
+	private Job fWorkspaceVisitorJob;
+	
+	/**
+	 * <p>Current state of the manager</p>
+	 * 
+	 * @see #STATE_DISABLED
+	 * @see #STATE_ENABLED
+	 */
+	private volatile byte fState;
+	
+	/** used to prevent manager from starting and stopping at the same time */
+	private Object fStartStopLock = new Object();
+	
+	/** <code>true</code> if the manager is currently starting, <code>false</code> otherwise */
+	private boolean fStarting;
+
+	/**
+	 * <p>Creates the manager with a given name.</p>
+	 * 
+	 * @param name This will be pre-pended to progress reporting messages and thus should
+	 * be translated
+	 * 
+	 * @param messageRunning Message to use when manager is running. Should be user readable.
+	 * @param messagegInitializing Message to use when manager is initializing. Should be user readable.
+	 * @param messageProcessingFiles Message to use when manager is processing files.
+	 * Should contain one parameter to be filled in with the number of files to process.
+	 * Should be user readable.
+	 */
+	
+	protected AbstractIndexManager(String name, String messageRunning,
+			String messagegInitializing, String messageProcessingFiles) {
+		
+		this.fMessage_Running = messageRunning;
+		this.fMessage_ProcessingFiles = messageProcessingFiles;
+		this.fMessage_Initializing = messagegInitializing;
+		
+		this.fName = name;
+		this.fState = STATE_DISABLED;
+		this.fResourceChangeListener = new ResourceChangeListener();
+		this.fResourceEventProcessingJob = new ResourceEventProcessingJob();
+		this.fStarting = false;
+	}
+	
+	/**
+	 * <p>Starts up the {@link AbstractIndexManager}.  If a {@link IResourceDelta}
+	 * is provided then it is assumed that all other files in the workspace
+	 * have already been index and thus only those in the provided
+	 * {@link IResourceDelta} will be processed.  Else if the provided
+	 * {@link IResourceDelta} is <code>null</code> it is assumed no files
+	 * have been indexed yet so the entire workspace will be searched for
+	 * files to be indexed.</p>
+	 * 
+	 * <p>If {@link IResourceDelta} is provided this will block until that delta
+	 * has finished processing.  If no {@link IResourceDelta} provided then a
+	 * separate job will be created to process the entire workspace and this method
+	 * will return without waiting for that job to complete</p>
+	 * 
+	 * <p>Will block until {@link #stop()} has finished running if it is
+	 * currently running</p>
+	 * 
+	 * @param savedStateDelta the delta from a saved state, if <code>null</code>
+	 * then the entire workspace will be searched for files to index, else
+	 * only files in this {@link IResourceDelta} will be indexed
+	 * @param monitor This action can not be canceled but this monitor will be used
+	 * to report progress
+	 */
+	public final void start(IResourceDelta savedStateDelta, IProgressMonitor monitor) {
+		SubMonitor progress = SubMonitor.convert(monitor);
+		synchronized (this.fStartStopLock) {
+			this.fStarting = true;
+			
+			if(this.fState == STATE_DISABLED) {
+				//report status
+				progress.beginTask(this.fName, 2);
+				
+				//start listening for resource change events
+				this.fResourceChangeListener.start();
+				
+				//check to see if a full re-index is required
+				boolean forcedFullReIndexNeeded = this.isForcedFullReIndexNeeded();
+				
+				/* start the indexing job only loading preserved state if not doing full index
+				 * if failed loading preserved state then force full re-index
+				 */
+				forcedFullReIndexNeeded = !this.fResourceEventProcessingJob.start(!forcedFullReIndexNeeded,
+						progress.newChild(1));
+				progress.setWorkRemaining(1);
+				
+				//don't bother processing saved delta if forced full re-index is needed
+				boolean stillNeedFullReIndex = forcedFullReIndexNeeded;
+				if(!forcedFullReIndexNeeded) {
+					//if there is a delta attempt to process it
+					if(savedStateDelta != null) {
+						stillNeedFullReIndex = false;
+						try {
+							//deal with reporting progress
+							SubMonitor savedStateProgress = progress.newChild(1, SubMonitor.SUPPRESS_NONE);
+							
+							//process delta
+							ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(savedStateProgress,
+									AbstractIndexManager.SOURCE_SAVED_STATE);
+							savedStateDelta.accept(visitor);
+							
+							//process any remaining batched up resources to index
+							visitor.processBatchedResourceEvents();
+						} catch (CoreException e) {
+							stillNeedFullReIndex = true;
+							Logger.logException(this.fName + ": Could not process saved state. " + //$NON-NLS-1$
+									"Forced to do a full workspace re-index.", e); //$NON-NLS-1$
+						}
+					}
+				}
+				progress.worked(1);
+				
+				//if need to process the entire workspace do so in another job
+				if(stillNeedFullReIndex){
+					this.fWorkspaceVisitorJob = new WorkspaceVisitorJob();
+					this.fWorkspaceVisitorJob.schedule();
+				}
+				
+				//update state
+				this.fState = STATE_ENABLED;
+			}
+			this.fStarting = false;
+		}
+	}
+	
+	/**
+	 * <p>Safely shuts down the manager.</p>
+	 * 
+	 * <p>This will block until the {@link #start(IResourceDelta, IProgressMonitor)} has
+	 * finished (if running).  Also until the current resource event has finished being
+	 * processed.  Finally it will block until the events still to be processed by
+	 * the processing job have been preserved to be processed on the next call to
+	 * {@link #start(IResourceDelta, IProgressMonitor)}.</p>
+	 * 
+	 * <p>If at any point during this shut down processes something goes wrong the
+	 * manager will be sure that on the next call to {@link #start(IResourceDelta, IProgressMonitor)}
+	 * the entire workspace will be re-processed.</p>
+	 * 
+	 * @throws InterruptedException
+	 */
+	public final void stop() throws InterruptedException {
+		synchronized (this.fStartStopLock) {
+			if(this.fState != STATE_DISABLED) {
+				
+				//stop listening for events, and wait for the current event to finish
+				this.fResourceChangeListener.stop();
+				
+				// if currently visiting entire workspace, give up and try again next load
+				boolean forceFullReIndexNextStart = false;
+				if(this.fWorkspaceVisitorJob != null) {
+					if (this.fWorkspaceVisitorJob.getState() != Job.NONE) {
+						this.fWorkspaceVisitorJob.cancel();
+						
+						this.forceFullReIndexNextStart();
+						forceFullReIndexNextStart = true;
+					}
+				}
+				
+				//stop the indexing job, only preserve if not already forcing a re-index
+				forceFullReIndexNextStart = !this.fResourceEventProcessingJob.stop(!forceFullReIndexNextStart);
+				
+				//if preserving failed, then force re-index
+				if(forceFullReIndexNextStart) {
+					this.forceFullReIndexNextStart();
+				}
+				
+				//update status
+				this.fState = STATE_DISABLED;
+			}
+		}
+	}
+	
+	/**
+	 * @return the name of this indexer
+	 */
+	protected String getName() {
+		return this.fName;
+	}
+	
+	/**
+	 * <p>Should be called by a client of the index this manager manages before the index
+	 * is accessed, assuming the client wants an index consistent with the latest
+	 * resource changes.</p>
+	 * 
+	 * <p>The supplied monitor will be used to supply user readable progress as the manager
+	 * insures the index has been given all the latest resource events.  This monitor
+	 * may be canceled, but if it is the state of the index is not guaranteed to be
+	 * consistent with the latest resource change events.</p>
+	 * 
+	 * @param monitor Used to report user readable progress as the manager insures the
+	 * index is consistent with the latest resource events.  This monitor can be canceled
+	 * to stop waiting for consistency but then no guaranty is made about the consistency
+	 * of the index in relation to unprocessed resource changes
+	 * 
+	 * @return <code>true</code> if the wait finished successfully and the manager is consistent,
+	 * <code>false</code> otherwise, either an error occurred while waiting for the manager
+	 * or the monitor was canceled
+	 * 
+	 * @throws InterruptedException This can happen when waiting for other jobs
+	 */
+	public final boolean waitForConsistent(IProgressMonitor monitor) {
+		boolean success = true;
+		boolean interupted = false;
+		SubMonitor progress = SubMonitor.convert(monitor);
+		
+		//set up the progress of waiting
+		int remainingWork = 4;
+		progress.beginTask(this.fMessage_Running,remainingWork);
+		
+		//wait for start up
+		if(this.fStarting && !monitor.isCanceled()) {
+			SubMonitor startingProgress = progress.newChild(1);
+			startingProgress.subTask(this.fMessage_Initializing);
+			while(this.fStarting && !monitor.isCanceled()) {
+				//this creates a never ending progress that still moves forward
+				startingProgress.setWorkRemaining(UNKNOWN_WORK);
+				startingProgress.newChild(1).worked(1);
+				try {
+					Thread.sleep(WAIT_TIME);
+				} catch (InterruptedException e) {
+					interupted = true;
+				}
+			}
+		}
+		progress.setWorkRemaining(--remainingWork);
+		
+		//wait for workspace visiting job
+		if(this.fWorkspaceVisitorJob != null && this.fWorkspaceVisitorJob.getState() != Job.NONE && !monitor.isCanceled()) {
+			SubMonitor workspaceVisitorProgress = progress.newChild(1);
+			workspaceVisitorProgress.subTask(this.fMessage_Initializing);
+			while(this.fWorkspaceVisitorJob.getState() != Job.NONE && !monitor.isCanceled()) {
+				//this creates a never ending progress that still moves forward
+				workspaceVisitorProgress.setWorkRemaining(UNKNOWN_WORK);
+				workspaceVisitorProgress.newChild(1).worked(1);
+				try {
+					Thread.sleep(WAIT_TIME);
+				} catch (InterruptedException e) {
+					interupted = true;
+				}
+			}
+		}
+		progress.setWorkRemaining(--remainingWork);
+		
+		//wait for the current resource event
+		if(this.fResourceChangeListener.isProcessingEvents() && !monitor.isCanceled()) {
+			SubMonitor workspaceVisitorProgress = progress.newChild(1);
+			workspaceVisitorProgress.subTask(this.fMessage_Running);
+			while(this.fResourceChangeListener.isProcessingEvents() && !monitor.isCanceled()) {
+				workspaceVisitorProgress.setWorkRemaining(UNKNOWN_WORK);
+				workspaceVisitorProgress.newChild(1).worked(1);
+				try {
+					this.fResourceChangeListener.waitForCurrentEvent(WAIT_TIME);
+				} catch (InterruptedException e) {
+					interupted = true;
+				}
+			}
+		}
+		progress.setWorkRemaining(--remainingWork);
+		
+		//wait for all files to be indexed
+		if(this.fResourceEventProcessingJob.getNumResourceEventsToProcess() != 0 && !monitor.isCanceled()) {
+			SubMonitor indexingProgress = progress.newChild(1);
+			int prevNumResrouces;
+			int numResources = this.fResourceEventProcessingJob.getNumResourceEventsToProcess();
+			while(numResources != 0 && !monitor.isCanceled()) {
+				//update the progress indicator
+				indexingProgress.subTask(NLS.bind(this.fMessage_ProcessingFiles, "" + numResources)); //$NON-NLS-1$
+				indexingProgress.setWorkRemaining(numResources);
+				prevNumResrouces = numResources;
+				numResources = this.fResourceEventProcessingJob.getNumResourceEventsToProcess();
+				int numProcessed = prevNumResrouces - numResources;
+				indexingProgress.worked(numProcessed > 0 ? numProcessed : 0);
+				
+				//give the index some time to do some indexing
+				try {
+					this.fResourceEventProcessingJob.waitForConsistant(WAIT_TIME);
+				} catch (InterruptedException e) {
+					interupted = true;
+				}
+			}
+		}
+		progress.setWorkRemaining(--remainingWork);
+		
+		if(monitor.isCanceled()) {
+			success = false;
+		}
+		
+		//reset the interrupted flag if we were interrupted
+		if(interupted) {
+			Thread.currentThread().interrupt();
+		}
+		
+		return success;
+	}
+	
+	/**
+	 * <p>Called for each {@link IResource} given in a resource delta.  If the resource
+	 * type is a file then used to determine if that file should be processed by the manager,
+	 * if the resource type is a project or directory then it is used to determine if the children
+	 * of the project or directory should be processed looking for file resources.</p>
+	 * 
+	 * <p><b>NOTE:</b> Even if <code>true</code> is returned for a directory resource that
+	 * only means the children of the directory should be inspected for possible files to index.
+	 * Directories themselves can not be managed in order to add to an index.</p>
+	 * 
+	 * @param type the {@link IResource#getType()} result of the resource to possibly index
+	 * @param path the full {@link IPath} to the resource to possibly index
+	 * @return <code>true</code> If the resource with the given <code>type</code> and
+	 * <code>path</code> should either itself be indexed, or its children should be indexed,
+	 * <code>false</code> if neither the described resource or any children should be indexed
+	 */
+	protected abstract boolean isResourceToIndex(int type, IPath path);
+	
+	/**
+	 * <p>Called for each {@link ResourceEvent} gathered by the various sources and processed
+	 * by the {@link ResourceEventProcessingJob}.  The implementation of this method
+	 * should use the given information to update the index this manager is managing.</p>
+	 * 
+	 * @param source The source that reported this resource event
+	 * @param action The action to be taken on the given <code>resource</code>
+	 * @param resource The index should perform the given <code>action</code> on this
+	 * resource
+	 * @param movePath If the given <code>action</code> is {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM}
+	 * or {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this field will not be
+	 * null and reports the path the given <code>resource</code> was either moved from or
+	 * moved to respectively.
+	 * 
+	 * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+	 * @see AbstractIndexManager#SOURCE_SAVED_STATE
+	 * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN
+	 * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX
+	 * 
+	 * @see AbstractIndexManager#ACTION_ADD
+	 * @see AbstractIndexManager#ACTION_REMOVE
+	 * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
+	 * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
+	 */
+	protected abstract void performAction(byte source, byte action, IResource resource,
+			IPath movePath);
+	
+	/**
+	 * <p>Gets the working location of the manager. This is where any relevant
+	 * state can be persisted.</p>
+	 * 
+	 * @return the working location of the manager
+	 */
+    protected abstract IPath getWorkingLocation();
+	
+	/**
+	 * <p>Next time the manager starts up force a full workspace index</p>
+	 */
+	private void forceFullReIndexNextStart() {
+		IPath reIndexPath =
+			AbstractIndexManager.this.getWorkingLocation().append(RE_PROCESS_FILE_NAME);
+		File file = new File(reIndexPath.toOSString());
+		try {
+			file.createNewFile();
+		} catch (IOException e) {
+			Logger.logException(this.fName + ": Could not create file to tell manager to" + //$NON-NLS-1$
+					" do a full re-index on next load. " + //$NON-NLS-1$
+					AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e);
+		}
+	}
+	
+	/**
+	 * @return <code>true</code> if a full workspace index is needed as dictated by
+	 * a previous call to {@link #forceFullReIndexNextStart()}, <code>false</code>
+	 * otherwise
+	 */
+	private boolean isForcedFullReIndexNeeded() {
+		boolean forcedFullReIndexNeeded = false;
+		IPath reIndexPath =
+			AbstractIndexManager.this.getWorkingLocation().append(RE_PROCESS_FILE_NAME);
+		File file = new File(reIndexPath.toOSString());
+		if(file.exists()) {
+			file.delete();
+			forcedFullReIndexNeeded = true;
+		}
+		
+		return forcedFullReIndexNeeded;
+	}
+	
+	/**
+	 * <p>A system {@link Job} used to visit all of the files in the workspace
+	 * looking for files to index.</p>
+	 * 
+	 * <p>This should only have to be done once per workspace on the first load,
+	 * but if it fails or a SavedState can not be retrieved on a subsequent
+	 * workspace load then this will have to be done again.</p>
+	 */
+	private class WorkspaceVisitorJob extends Job {
+		/**
+		 * <p>Default constructor that sets up this job as a system job</p>
+		 */
+		protected WorkspaceVisitorJob() {
+			super(AbstractIndexManager.this.fMessage_Initializing);
+			
+			this.setUser(false);
+			this.setSystem(true);
+			this.setPriority(Job.LONG);
+		}
+		
+		/**
+		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+		 */
+		protected IStatus run(IProgressMonitor monitor) {
+			try {
+				//update status
+				monitor.beginTask(AbstractIndexManager.this.fMessage_Initializing,
+						IProgressMonitor.UNKNOWN);
+				
+				//visit the workspace
+				WorkspaceVisitor visitor = new WorkspaceVisitor(monitor);
+				ResourcesPlugin.getWorkspace().getRoot().accept(visitor, IResource.NONE);
+				
+				//process any remaining batched up resources to index
+				visitor.processBatchedResourceEvents();
+			} catch(CoreException e) {
+				Logger.logException(AbstractIndexManager.this.fName +
+						": Failed visiting entire workspace for initial index. " + //$NON-NLS-1$
+						AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e);
+			}
+			
+			IStatus status;
+			if(monitor.isCanceled()) {
+				status = Status.CANCEL_STATUS;
+			} else {
+				status = Status.OK_STATUS;
+			}
+			
+			return status;
+		}
+		
+		/**
+		 * <p>An {@link IResourceProxyVisitor} used to visit all of the files in the
+		 * workspace looking for files to add to the index.</p>
+		 * 
+		 * <p><b>NOTE: </b>After this visitor is used {@link WorkspaceVisitor#processBatchedResourceEvents()
+		 * must be called to flush out the last of the {@link ResourceEvent}s produced
+		 * by this visitor.</p>
+		 */
+		private class WorkspaceVisitor implements IResourceProxyVisitor {
+			/** {@link IProgressMonitor} used to report status and check for cancellation */
+			private SubMonitor fProgress;
+			
+			/**
+			 * {@link Map}&lt{@link IResource}, {@link ResourceEvent}&gt
+			 * <p>Map of resources events created and batched up by this visitor.
+			 * These events are periodical be sent off to the
+			 * {@link ResourceEventProcessingJob} but need to be sent off
+			 * one final time after this visitor finishes it work.</p>
+			 * 
+			 * @see #processBatchedResourceEvents()
+			 */
+			private Map fBatchedResourceEvents;
+			
+			/**
+			 * <p>Default constructor</p>
+			 * @param monitor used to report status and allow this visitor to be canceled
+			 */
+			protected WorkspaceVisitor(IProgressMonitor monitor) {
+				this.fProgress = SubMonitor.convert(monitor);
+				this.fBatchedResourceEvents = new LinkedHashMap(BATCH_UP_AMOUNT);
+			}
+
+			/**
+			 * <p>As long as the monitor is not canceled visit each file in the workspace
+			 * that should be visited.</p>
+			 * 
+			 * @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.core.resources.IResourceProxy)
+			 * @see AbstractIndexManager#shouldVisit(String)
+			 */
+			public boolean visit(IResourceProxy proxy) throws CoreException {
+				this.fProgress.subTask(proxy.getName());
+				
+				boolean visitChildren = false;
+				
+				/* if not canceled or a hidden resource then process file
+				 * else don't visit children
+				 */
+				if(!this.fProgress.isCanceled()) {
+					/* if root node always visit children
+					 * else ask manager implementation if resource and its children should be visited 
+					 */
+					IPath path = proxy.requestFullPath();
+					if(path.toString().equals("/")) { //$NON-NLS-1$
+						visitChildren = true;
+					} else if(isResourceToIndex(proxy.getType(), path)) {
+						if(proxy.getType() == IResource.FILE) {
+							//add the file to be indexed
+							IFile file = (IFile) proxy.requestResource();
+							if(file.exists()) {
+								this.fBatchedResourceEvents.put(file, new ResourceEvent(
+										AbstractIndexManager.SOURCE_WORKSPACE_SCAN,
+										AbstractIndexManager.ACTION_ADD,
+										null));
+							}
+						}
+						visitChildren = true;
+					}
+					visitChildren = false;
+				} else {
+					visitChildren = false;
+				}
+				
+				//batch up resource changes before sending them out
+				if(this.fBatchedResourceEvents.size() >= BATCH_UP_AMOUNT) {
+					this.processBatchedResourceEvents();
+				}
+				
+				return visitChildren;
+			}
+			
+			/**
+			 * <p>Sends any batched up resource events created by this visitor to the
+			 * {@link ResourceEventProcessingJob}.<p>
+			 * 
+			 * <p><b>NOTE:</b> This will be called every so often as the visitor is
+			 * visiting resources but needs to be called a final time by the user of
+			 * this visitor to be sure the final events are sent off</p>
+			 */
+			protected void processBatchedResourceEvents() {
+				AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents(
+						this.fBatchedResourceEvents);
+				this.fBatchedResourceEvents.clear();
+			}
+		}
+	}
+	
+	/**
+	 * <p>Used to listen to resource change events in the workspace.  These events
+	 * are batched up and then passed onto the {@link ResourceEventProcessingJob}.</p>
+	 */
+	private class ResourceChangeListener implements IResourceChangeListener {
+		/**
+		 * <p>The number of events currently being processed by this listener.</p>
+		 * <p>Use the {@link #fEventsBeingProcessedLock} when reading or writing this field</p>
+		 * 
+		 * @see #fEventsBeingProcessedLock
+		 */
+		private volatile int fEventsBeingProcessed;
+		
+		/**
+		 * Lock to use when reading or writing {@link #fEventsBeingProcessed}
+		 * 
+		 * @see #fEventsBeingProcessed
+		 */
+		private final Object fEventsBeingProcessedLock = new Object();
+		
+		/**
+		 * <p>Current state of this listener</p>
+		 * 
+		 * @see AbstractIndexManager#STATE_DISABLED
+		 * @see AbstractIndexManager#STATE_ENABLED
+		 */
+		private volatile byte fState;
+		
+		/**
+		 * <p>Default constructor</p>
+		 */
+		protected ResourceChangeListener() {
+			this.fState = STATE_DISABLED;
+			this.fEventsBeingProcessed = 0;
+		}
+		
+		/**
+		 * <p>Start listening for resource change events</p>
+		 */
+		protected void start() {
+			this.fState = STATE_ENABLED;
+			ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
+		}
+		
+		/**
+		 * <p>Stop listening for resource change events and if already processing
+		 * an event then wait for that processing to finish</p>
+		 * 
+		 * @throws InterruptedException waiting for a current event to finish processing
+		 * could be interrupted
+		 */
+		protected void stop() throws InterruptedException {
+			ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+			
+			//wait indefinitely for current event to finish processing
+			this.waitForCurrentEvent(0);
+			
+			this.fState = STATE_DISABLED;
+		}
+		
+		/**
+		 * <p>Blocks until either the current resource event has been processed or
+		 * until the given timeout has passed.</p>
+		 * 
+		 * @param timeout block until either this timeout elapses (0 means never to timeout)
+		 * or the current resource change event finishes being processed
+		 * 
+		 * @throws InterruptedException This can happen when waiting for a lock
+		 */
+		protected void waitForCurrentEvent(int timeout) throws InterruptedException {
+			synchronized (this.fEventsBeingProcessedLock) {
+				if(this.fEventsBeingProcessed != 0) {
+					this.fEventsBeingProcessedLock.wait(timeout);
+				}
+			}
+		}
+		
+		/**
+		 * @return <code>true</code> if this listener is currently processing any
+		 * events, <code>false</code> otherwise.
+		 */
+		protected boolean isProcessingEvents() {
+			return this.fEventsBeingProcessed != 0;
+		}
+		
+		/**
+		 * <p>Process a resource change event.  If it is a pre-close or pre-delete then
+		 * the {@link ResourceEventProcessingJob} is paused so it does not try to
+		 * process resources that are about to be deleted.  The {@link ResourceDeltaVisitor}
+		 * is used to actually process the event.</p>
+		 * 
+		 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
+		 * @see ResourceDeltaVisitor
+		 */
+		public void resourceChanged(IResourceChangeEvent event) {
+			try {
+				//update the number of events being processed
+				synchronized (this.fEventsBeingProcessedLock) {
+					++this.fEventsBeingProcessed;
+				}
+				
+				if(this.fState == STATE_ENABLED) {
+					switch(event.getType()) {
+						case IResourceChangeEvent.PRE_CLOSE:
+						case IResourceChangeEvent.PRE_DELETE:{
+							//pre-close or pre-delete pause the persister job so it does not interfere
+							AbstractIndexManager.this.fResourceEventProcessingJob.pause();
+							break;
+						}
+						case IResourceChangeEvent.POST_BUILD:
+						case IResourceChangeEvent.POST_CHANGE: {
+							//post change start up the indexer job and process the delta
+							AbstractIndexManager.this.fResourceEventProcessingJob.unPause();
+							
+							// only analyze the full (starting at root) delta hierarchy
+							IResourceDelta delta = event.getDelta();
+							if (delta != null && delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$
+								try {
+									//use visitor to visit all children
+									ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(
+											AbstractIndexManager.SOURCE_RESOURCE_CHANGE);
+									delta.accept(visitor, false);
+									
+									//process any remaining batched up resources to index
+									visitor.processBatchedResourceEvents();
+								} catch (CoreException e) {
+									Logger.logException(AbstractIndexManager.this.fName +
+											": Failed visiting resrouce change delta. " + //$NON-NLS-1$
+											AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e);
+								}
+							}
+							break;
+						}
+					}
+				} else {
+					Logger.log(Logger.ERROR, "A resource change event came in after " + //$NON-NLS-1$
+							AbstractIndexManager.this.fName + " shut down. This should never " + //$NON-NLS-1$
+							"ever happen, but if it does the index may now be inconsistant."); //$NON-NLS-1$
+				}
+			} finally {
+				//no matter how we exit be sure to update the number of events being processed
+				synchronized (this.fEventsBeingProcessedLock) {
+					--this.fEventsBeingProcessed;
+					
+					//if currently not events being processed, then notify
+					if(this.fEventsBeingProcessed == 0) {
+						this.fEventsBeingProcessedLock.notifyAll();
+					}
+				}
+			}
+		}
+	}
+	
+	/**
+	 * <p>Used to visit {@link IResourceDelta}s from both the {@link IResourceChangeListener}
+	 * and from a {@link ISaveParticipant} given to {@link AbstractIndexManager#start(IResourceDelta, IProgressMonitor)}.
+	 * The resource events are batched into groups of {@link AbstractIndexManager#BATCH_UP_AMOUNT}
+	 * before being passed onto the {@link ResourceEventProcessingJob}.</p>
+	 * 
+	 * <p><b>NOTE 1: </b> This class is intended for one time use, thus a new instance should
+	 * be instantiated each time this visitor is needed to process a new {@link IResourceDelta}.</p>
+	 * 
+	 * <p><b>NOTE 2: </b> Be sure to call {@link ResourceDeltaVisitor#processBatchedResourceEvents()}
+	 * after using this visitor to be sure any remaining events get passed onto the
+	 * {@link ResourceEventProcessingJob}.</p>
+	 * 
+	 * @see ResourceDeltaVisitor#processBatchedResourceEvents()
+	 */
+	private class ResourceDeltaVisitor implements IResourceDeltaVisitor {
+		/** {@link IProgressMonitor} used to report status */
+		private SubMonitor fProgress;
+		
+		/**
+		 * <p>The source that should be used when sending resource events to the
+		 * {@link ResourceEventProcessingJob}.</p>
+		 * 
+		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
+		 * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+		 */
+		private byte fSource;
+		
+		/**
+		 * <p>Due to the nature of a visitor it has no way of knowing the total amount
+		 * of work it has to do but it can start to predict it based on the number of
+		 * children of each event it processes and whether it plans on visiting those
+		 * children</p>
+		 */
+		private int fPredictedWorkRemaining;
+		
+		/**
+		 * {@link Map}&lt{@link IResource}, {@link ResourceEvent}&gt
+		 * <p>Map of resources events created and batched up by this visitor.
+		 * These events are periodical be sent off to the
+		 * {@link ResourceEventProcessingJob} but need to be sent off
+		 * one final time after this visitor finishes it work.</p>
+		 * 
+		 * @see #processBatchedResourceEvents()
+		 */
+		private Map fBatchedResourceEvents;
+		
+		/**
+		 * <p>Creates a visitor that will create resource events based on the resources
+		 * it visits and using the given source as the source of the events.</p>
+		 * 
+		 * @param source The source of the events that should be used when creating
+		 * resource events from visited resources
+		 * 
+		 * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
+		 */
+		protected ResourceDeltaVisitor(byte source) {
+			this(SubMonitor.convert(null), source);
+		}
+		
+		/**
+		 * <p>Creates a visitor that will create resource events based on the resources
+		 * it visits and using the given source as the source of the events and
+		 * report its status to the given progress as best it can as it visits
+		 * resources.</p>
+		 * 
+		 * <p><b>NOTE:</b> While the {@link SubMonitor} is provided to report status the
+		 * visitor will not honor any cancellation requests.</p>
+		 * 
+		 * @param progress Used to report status. This visitor can <b>not</b> be
+		 * canceled
+		 * @param source The source of the events that should be used when creating
+		 * resource events from visited resources
+		 * 
+		 * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
+		 */
+		protected ResourceDeltaVisitor(SubMonitor progress, byte source) {
+			this.fProgress = progress;
+			this.fSource = source;
+			this.fBatchedResourceEvents = new LinkedHashMap(BATCH_UP_AMOUNT);
+			this.fPredictedWorkRemaining = 1;
+		}
+		
+		/**
+		 * <p>Transforms each {@link IResourceDelta} into a {@link ResourceEvent}.
+		 * Batches up these {@link ResourceEvent}s and then passes them onto the
+		 * {@link ResourceEventProcessingJob}.</p>
+		 * 
+		 * <p><b>NOTE 1: </b> Be sure to call {@link ResourceDeltaVisitor#processBatchedResourceEvents()}
+		 * after using this visitor to be sure any remaining events get passed onto the
+		 * {@link ResourceEventProcessingJob}.</p>
+		 * 
+		 * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
+		 * @see #processBatchedResourceEvents()
+		 */
+		public boolean visit(IResourceDelta delta) throws CoreException {
+			//report status
+			this.fProgress.subTask(
+					NLS.bind(AbstractIndexManager.this.fMessage_ProcessingFiles,
+							"" + fPredictedWorkRemaining, delta.getFullPath().toString())); //$NON-NLS-1$
+			
+			//process delta if resource not hidden
+			boolean visitChildren = false;
+			
+			/* if root node always visit its children
+			 * else ask manager implementation if resource and its children should be visited 
+			 */
+			if(delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$
+				visitChildren = true;
+			} else {
+				IResource resource = delta.getResource();
+	
+				//check if should index resource or its children
+				if(isResourceToIndex(resource.getType(), resource.getFullPath())) {
+					if(resource.getType() == IResource.FILE) {
+					
+						switch (delta.getKind()) {
+							case IResourceDelta.CHANGED : {
+								/* ignore any change that is not a CONTENT, REPLACED, TYPE,
+								 * or MOVE_FROM change
+								 */
+								if( !((delta.getFlags() & IResourceDelta.CONTENT) != 0) &&
+										!((delta.getFlags() & IResourceDelta.REPLACED) != 0) &&
+										!((delta.getFlags() & IResourceDelta.TYPE) != 0) &&
+										!(((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0))) {
+									
+									break;
+								}
+							}
+							//$FALL-THROUGH$ it is intended that sometimes a change will fall through to add
+							case IResourceDelta.ADDED : {
+								if((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
+									//create add move from action
+									this.fBatchedResourceEvents.put(resource, new ResourceEvent(
+											this.fSource,
+											AbstractIndexManager.ACTION_ADD_MOVE_FROM,
+											delta.getMovedFromPath()));
+		
+								} else {
+									//create add action
+									this.fBatchedResourceEvents.put(resource, new ResourceEvent(
+											this.fSource,
+											AbstractIndexManager.ACTION_ADD,
+											null));
+								}
+								
+								break;
+							}
+							case IResourceDelta.REMOVED : {
+								if((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
+									//create remove move to action
+									this.fBatchedResourceEvents.put(resource, new ResourceEvent(
+											this.fSource,
+											AbstractIndexManager.ACTION_REMOVE_MOVE_TO,
+											delta.getMovedToPath()));
+								} else {
+									//create remove action
+									this.fBatchedResourceEvents.put(resource, new ResourceEvent(
+											this.fSource,
+											AbstractIndexManager.ACTION_REMOVE,
+											null));
+								}
+								break;
+							}
+						}
+					}//end is file
+					
+					visitChildren = true;
+				} else {
+					visitChildren = false;
+				}
+					
+				
+				
+				//deal with trying to report progress
+				if(visitChildren) {
+					this.fPredictedWorkRemaining += delta.getAffectedChildren().length;
+				}
+				this.fProgress.setWorkRemaining(this.fPredictedWorkRemaining);
+				this.fProgress.worked(1);
+				--this.fPredictedWorkRemaining;
+				
+				//batch up resource changes before sending them out
+				if(this.fBatchedResourceEvents.size() >= BATCH_UP_AMOUNT) {
+					this.processBatchedResourceEvents();
+				}
+			}
+			
+			return visitChildren;
+		}
+		
+		/**
+		 * <p>Sends any batched up resource events created by this visitor to the
+		 * {@link ResourceEventProcessingJob}.<p>
+		 * 
+		 * <p><b>NOTE:</b> This will be called every so often as the visitor is
+		 * visiting resources but needs to be called a final time by the user of
+		 * this visitor to be sure the final events are sent off</p>
+		 */
+		protected void processBatchedResourceEvents() {
+			AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents(
+					this.fBatchedResourceEvents);
+			this.fBatchedResourceEvents.clear();
+		}
+	}
+	
+	/**
+	 * <p>Collects {@link ResourceEvent}s from the different sources and then processes
+	 * each one by calling {@link AbstractIndexManager#performAction(byte, byte, IResource, IPath)}
+	 * for each {@link ResourceEvent}.</p>
+	 * 
+	 * @see AbstractIndexManager#performAction(byte, byte, IResource, IPath)
+	 */
+	private class ResourceEventProcessingJob extends Job {
+		/** Length to delay when scheduling job */
+		private static final int DELAY = 500;
+		
+		/**
+		 * <p>Name of the file where resource events still to index
+		 * will be preserved for the next start up.</p>
+		 */
+		private static final String PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME = ".preservedResourceEvents";  //$NON-NLS-1$
+		
+		/**
+		 * <p>This needs to be updated if {@link #preserveReceivedResourceEvents()} ever
+		 * changes how it persists resource events so that {@link #loadPreservedReceivedResourceEvents(SubMonitor)}
+		 * knows when opening a file that the format is of the current version and if not
+		 * knows it does not know how to read the older version.</p>
+		 * 
+		 * @see #preserveReceivedResourceEvents()
+		 * @see #loadPreservedReceivedResourceEvents(SubMonitor)
+		 */
+		private static final long serialVersionUID = 1L;
+		
+		/** Whether this job has been paused or not */
+		private volatile boolean fIsPaused;
+		
+		/**
+		 * {@link Map}&lt{@link IResource}, {@link ResourceEvent}&gt
+		 * <p>The list of resources events to be processed</p>
+		 */
+		private Map fResourceEvents;
+		
+		/** Lock used when accessing {@link #fBatchedResourceEvents} */
+		private final Object fResourceEventsLock = new Object();
+		
+		/**
+		 * Locked used for allowing other jobs to wait on this job.  This job
+		 * will notify those waiting on this lock whenever it is done processing
+		 * all resource events it currently knows about.
+		 * 
+		 * @see #waitForConsistant(int)
+		 */
+		private final Object fToNotifyLock = new Object();
+		
+		/**
+		 * <p>Sets up this job as a long running system job</p>
+		 */
+		protected ResourceEventProcessingJob() {
+			super(AbstractIndexManager.this.fMessage_Running);
+			
+			//set this up as a long running system job
+			this.setUser(false);
+			this.setSystem(true);
+			this.setPriority(Job.LONG);
+			
+			this.fIsPaused = false;
+			this.fResourceEvents = new LinkedHashMap();
+		}
+		
+		/**
+		 * <p>Loads any preserved {@link ResourceEvent}s from the last time {@link #stop(boolean)}
+		 * was invoked and schedules the job to be run</p>
+		 * 
+		 * <p><b>NOTE: </b>Should be used instead of of calling {@link Job#schedule()}
+		 * because this method also takes care of loading preserved state.</p>
+		 * 
+		 * @param loadPreservedResourceEvents <code>true</code> if should load any
+		 * preserved {@link ResourceEvent}s from the last time {@link #stop(boolean)}
+		 * was invoked
+		 * 
+		 * @return <code>true</code> if either <code>loadPreservedResourceEvents</code>
+		 * was false or there was success in loading the preserved {@link ResourceEvent}s.
+		 * If <code>false</code> then some {@link ResourceEvent}s may have been loosed
+		 * and to insure index consistency with the workspace a full workspace  re-index
+		 * is needed.
+		 * 
+		 * @see #stop(boolean)
+		 */
+		protected synchronized boolean start(boolean loadPreservedResourceEvents, 
+				SubMonitor progress) {
+			
+			boolean successLoadingPreserved = true;
+			
+			//attempt to load preserved resource events if requested
+			if(!loadPreservedResourceEvents) {
+				File preservedResourceEventsFile = this.getPreservedResourceEventsFile();
+				preservedResourceEventsFile.delete();
+			} else {
+				successLoadingPreserved = this.loadPreservedReceivedResourceEvents(progress);
+			}
+			
+			//start up the job
+			this.schedule();
+			
+			return successLoadingPreserved;
+		}
+		
+		/**
+		 * <p>Immediately stops the job and preserves any {@link ResourceEvent}s in the queue
+		 * to be processed by not yet processed if requested</p>
+		 * 
+		 * @param preserveResourceEvents <code>true</code> to preserve any {@link ResourceEvent}s
+		 * in the queue yet to be processed, <code>false</code> otherwise
+		 * 
+		 * @return <code>true</code> if either <code>preserveResourceEvents</code> is 
+		 * <code>false</code> or if there was success in preserving the {@link ResourceEvent}s
+		 * yet to be processed.  If <code>false</code> then the preserving failed and a
+		 * full workspace re-processing is needed the next time the manager is started
+		 * 
+		 * @throws InterruptedException This could happen when trying to cancel or join 
+		 * the job in progress, but it really shouldn't
+		 * 
+		 * @see #start(boolean, SubMonitor)
+		 */
+		protected synchronized boolean stop(boolean preserveResourceEvents) throws InterruptedException {
+			//this will not block indefinitely because it is known this job can be canceled
+			this.cancel();
+			this.join();
+			
+			//preserve if requested, else be sure no preserve file is left over for next start
+			boolean success = true;
+			if(preserveResourceEvents && this.hasResourceEventsToProcess()) {
+				success = this.preserveReceivedResourceEvents();
+			} else {
+				this.getPreservedResourceEventsFile().delete();
+			}
+			
+			return success;
+		}
+		
+		/**
+		 * @return <code>true</code> if job is currently running or paused
+		 * 
+		 * @see #pause()
+		 * @see #unPause()
+		 */
+		protected synchronized boolean isProcessing() {
+			return this.getState() != Job.NONE || this.fIsPaused;
+		}
+		
+		/**
+		 * <p>Un-pauses this job.  This has no effect if the job is already running.</p>
+		 * <p>This should be used in place of {@link Job#schedule()} to reset state
+		 * caused by calling {@link #pause()}</p>
+		 * 
+		 * @see #pause()
+		 */
+		protected synchronized void unPause() {
+			this.fIsPaused = false;
+			
+			//get the job running again depending on its current state
+			if(this.getState() == Job.SLEEPING) {
+				this.wakeUp(DELAY);
+			} else {
+				this.schedule(DELAY);
+			}
+		}
+		
+		/**
+		 * <p>Pauses this job, even if it is running</p>
+		 * <p>This should be used in place of {@link Job#sleep()} because {@link Job#sleep()}
+		 * will not pause a job that is already running but calling this will pause this job
+		 * even if it is running. {@link #unPause()} must be used to start this job again</p>
+		 * 
+		 * @see #unPause()
+		 */
+		protected synchronized void pause() {
+			//if job is already running this will force it to pause
+			this.fIsPaused = true;
+			
+			//this only works if the job is not running
+			this.sleep();
+		}
+		
+		/**
+		 * <p>Adds a batch of {@link ResourceEvent}s to the queue of events to be processed.
+		 * Will also un-pause the job if it is not already running</p>
+		 * 
+		 * @param resourceEvents {@link Map}&lt{@link IResource}, {@link ResourceEvent}&gt
+		 * A batch of {@link ResourceEvent}s to be processed
+		 * 
+		 * @see #addResourceEvent(ResourceEvent)
+		 * @see #unPause()
+		 */
+		protected void addResourceEvents(Map resourceEvents) {
+			Iterator iter = resourceEvents.keySet().iterator();
+			while(iter.hasNext()) {
+				IResource resource = (IResource)iter.next();
+				ResourceEvent resourceEvent = (ResourceEvent)resourceEvents.get(resource);
+				addResourceEvent(resource, resourceEvent);
+			}
+			
+			//un-pause the processor if it is not already running
+			if(!isProcessing()) {
+				this.unPause();
+			}
+		}
+		
+		/**
+		 * <p>Gets the number of {@link ResourceEvent}s left to process by this job. This
+		 * count is only valid for the exact moment it is returned because events are
+		 * constantly being added and removed from the queue of events to process</p>
+		 * 
+		 * @return the number of {@link ResourceEvent}s left to process
+		 */
+		protected int getNumResourceEventsToProcess() {
+			return this.fResourceEvents.size();
+		}
+		
+		/**
+		 * <p>Blocks until either the given timeout elapses (0 means never to timeout), or
+		 * there are currently no {@link ResourceEvent}s to process or being processed
+		 * by this job</p>
+		 * 
+		 * @param timeout block until either this timeout elapses (0 means never to timeout)
+		 * or there are currently no {@link ResourceEvent}s to process or being processed
+		 * by this job
+		 * 
+		 * @throws InterruptedException This can happen when waiting for a lock
+		 */
+		protected void waitForConsistant(int timeout) throws InterruptedException {
+			if(hasResourceEventsToProcess() || isProcessing()) {
+				synchronized (this.fToNotifyLock) {
+					this.fToNotifyLock.wait(timeout);
+				}
+			}
+		}
+		
+		/**
+		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+		 */
+		protected IStatus run(IProgressMonitor monitor) {
+			try {
+				//report status
+				SubMonitor progress = SubMonitor.convert(monitor);
+				
+				while(!this.fIsPaused && !monitor.isCanceled() && this.hasResourceEventsToProcess()) {
+					//report status
+					progress.setTaskName(NLS.bind(AbstractIndexManager.this.fMessage_ProcessingFiles,
+									"" + getNumResourceEventsToProcess())); //$NON-NLS-1$
+					progress.setWorkRemaining(getNumResourceEventsToProcess());
+					
+					//get the next event to process
+					ResourceEvent resourceEvent = null;
+					IResource resource = null;
+					synchronized (this.fResourceEventsLock) {
+						resource = (IResource) this.fResourceEvents.keySet().iterator().next();
+						resourceEvent = (ResourceEvent)this.fResourceEvents.remove(resource);
+					}
+					
+					//report status
+					monitor.subTask(resource.getName());
+					
+					//perform action safely
+					final byte source = resourceEvent.fSource;
+					final byte action = resourceEvent.fAction;
+					final IResource finResource = resource;
+					final IPath movePath = resourceEvent.fMovePath;
+					SafeRunner.run(new ISafeRunnable() {
+						public void run() throws Exception {
+							AbstractIndexManager.this.performAction(source, action,
+									finResource, movePath);
+						}
+	
+						public void handleException(Throwable e) {
+							Logger.logException("Error while performing an update to the index. " + //$NON-NLS-1$
+									AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e);
+						}
+					});
+					
+					//report progress
+					progress.worked(1);
+					
+					//avoid dead locks
+					Job.getJobManager().currentJob().yieldRule(monitor);
+				}
+				
+				//done work
+				monitor.done();
+			} finally {
+				//want to be sure we notify no matter how we exit
+				this.notifyIfConsistant();
+			}
+			
+			/* if canceled then return CANCEL,
+			 * else if done or paused return OK
+			 */
+			IStatus exitStatus;
+			if(monitor.isCanceled()) {
+				exitStatus = Status.CANCEL_STATUS;
+			} else {
+				exitStatus = Status.OK_STATUS;
+			}
+			
+			return exitStatus;
+		}
+		
+		/**
+		 * <p>If resource not already scheduled to be processed, schedule it
+		 * else if resource already scheduled to be processed, update the action only if
+		 * the new action comes from a resource change event.</p>
+		 * 
+		 * <p>Ignore other sources for updating existing resource events because all other
+		 * sources are "start-up" type sources and thus only {@link ResourceEvent} with a
+		 * source of a resource change event trump existing events.</p>
+		 * 
+		 * @param resourceEvent {@link ResourceEvent} to be processed by this job
+		 */
+		private void addResourceEvent(IResource resource, ResourceEvent resourceEvent) {
+			
+			synchronized (this.fResourceEventsLock) {
+				/* if resource not already scheduled to be processed, schedule it
+				 * else if resource already scheduled to be processed, update the action only if
+				 * 		the new action comes from a resource change event
+				 */
+				if(!this.fResourceEvents.containsKey(resource)) {
+					this.fResourceEvents.put(resource, resourceEvent);
+				} else if(resourceEvent.fSource == AbstractIndexManager.SOURCE_RESOURCE_CHANGE) {
+					((ResourceEvent)this.fResourceEvents.get(resource)).fAction = resourceEvent.fAction;
+				} else {
+					//Purposely ignoring all other resource events
+				}
+			}
+		}
+		
+		/**
+		 * @return <code>true</code> if there are any resources to process,
+		 * <code>false</code> otherwise
+		 */
+		private boolean hasResourceEventsToProcess() {
+			return !this.fResourceEvents.isEmpty();
+		}
+		
+		/**
+		 * <p>Preserves all of the resource events that have been received by this
+		 * manager but not yet processed</p>
+		 * 
+		 * <p>If this operation was successful then the next time the manager starts
+		 * it can load these events and process them.  If it was not successful then a
+		 * full re-processing of the entire workspace will need to take place to be
+		 * sure the index is consistent.</p>
+		 * 
+		 * <p><b>NOTE:</b> If this method changes how it preserves these events then
+		 * {@link #serialVersionUID} will need to be incremented so that the manager
+		 * does not attempt to load an old version of the file that may exist in a users
+		 * workspace.  Also {@link #loadPreservedReceivedResourceEvents(SubMonitor)} will
+		 * have to be updated to load the new file structure.</p>
+		 * 
+		 * @return <code>true</code> if successfully preserved the resource events that
+		 * have been received by not yet processed, <code>false</code> otherwise
+		 * 
+		 * @see #serialVersionUID
+		 * @see #loadPreservedReceivedResourceEvents(SubMonitor)
+		 */
+		private boolean preserveReceivedResourceEvents() {
+			File preservedResourceEventsFile = this.getPreservedResourceEventsFile();
+			boolean success = true;
+			
+			synchronized (this.fResourceEventsLock) {
+				DataOutputStream dos = null;
+				try {
+					//if file already exists delete it
+					if(preservedResourceEventsFile.exists()) {
+						preservedResourceEventsFile.delete();
+						preservedResourceEventsFile.createNewFile();
+					}
+					
+					//create output objects
+					FileOutputStream fos = new FileOutputStream(preservedResourceEventsFile);
+					BufferedOutputStream bos = new BufferedOutputStream(fos);
+					dos = new DataOutputStream(bos);
+					
+					//write serial version
+					dos.writeLong(serialVersionUID);
+					
+					//write size
+					dos.writeInt(this.getNumResourceEventsToProcess());
+					
+					//write out all the information needed to restore the resource events to process
+					Iterator iter = this.fResourceEvents.keySet().iterator();
+					while(iter.hasNext()) {
+						IResource resource = (IResource)iter.next();
+						ResourceEvent resourceEvent = (ResourceEvent)this.fResourceEvents.get(resource);
+						
+						if(resourceEvent.fSource != AbstractIndexManager.SOURCE_WORKSPACE_SCAN) {
+							//write out information
+							dos.writeByte(resourceEvent.fAction);
+							dos.writeByte(resource.getType());
+							byte[] pathBytes = resource.getFullPath().toString().getBytes(ENCODING_UTF16);
+							dos.write(pathBytes);
+							dos.writeByte('\0');
+							if(resourceEvent.fMovePath != null) {
+								dos.writeBytes(resourceEvent.fMovePath.toPortableString());
+							}
+							dos.writeByte('\0');
+						}
+					}
+					
+					this.fResourceEvents.clear();
+					
+					dos.flush();
+				} catch (FileNotFoundException e) {
+					Logger.logException(AbstractIndexManager.this.fName +
+							": Exception while opening file to preserve resrouces to index.", //$NON-NLS-1$
+							e);
+					success = false;
+				} catch (IOException e) {
+					Logger.logException(AbstractIndexManager.this.fName +
+							": Exception while writing to file to preserve resrouces to index.", //$NON-NLS-1$
+							e);
+					success = false;
+				} finally {
+					//be sure to close output
+					if(dos != null) {
+						try {
+							dos.close();
+						} catch (IOException e) {
+							Logger.logException(AbstractIndexManager.this.fName +
+									": Exception while closing file with preserved resources to index.", //$NON-NLS-1$
+									e);
+							success = false;
+						}
+					}
+				}
+				
+				//if failed, for consistency must do a full re-process next workspace load
+				if(!success) {
+					preservedResourceEventsFile.delete();
+				}
+			}
+			
+			return success;
+		}
+		
+		/**
+		 * <p>Loads the received resource events that were preserved during the manager's
+		 * last shut down so they can be processed now</p>
+		 * 
+		 * <p>If this operation is not successful then a full re-processing of the
+		 * entire workspace is needed to be sure the index is consistent.</p>
+		 * 
+		 * @param progress used to report status of loading the preserved received resource events
+		 * @return <code>true</code> if the loading of the preserved received resource events
+		 * was successful, <code>false</code> otherwise.
+		 * 
+		 * @see #serialVersionUID
+		 * @see #preserveReceivedResourceEvents()
+		 */
+		private boolean loadPreservedReceivedResourceEvents(SubMonitor progress) {
+			progress.subTask(AbstractIndexManager.this.fMessage_Initializing);
+			
+			boolean success = true;
+			File preservedResourceEventsFile = this.getPreservedResourceEventsFile();
+			
+			if(preservedResourceEventsFile.exists()) {
+				Map preservedResourceEvents = null;
+				
+				DataInputStream dis = null;
+				try {
+					FileInputStream fis = new FileInputStream(preservedResourceEventsFile);
+					BufferedInputStream bis = new BufferedInputStream(fis);
+					dis = new DataInputStream(bis);
+					
+					//check serial version first
+					long preservedSerialVersionUID = dis.readLong();
+					if(preservedSerialVersionUID == serialVersionUID) {
+						
+						//read each record
+						int numberOfRecords = dis.readInt();
+						preservedResourceEvents = new LinkedHashMap(numberOfRecords);
+						progress.setWorkRemaining(numberOfRecords);
+						for(int i = 0; i < numberOfRecords; ++i) {
+							//action is first byte
+							byte action = dis.readByte();
+							
+							//file type is the next byte 
+							byte fileType = dis.readByte();
+							
+							//resource location are the next bytes
+							ByteArrayOutputStream resourceLocoationBOS = new ByteArrayOutputStream();
+							byte b = dis.readByte();
+							while(b != '\0') {
+								resourceLocoationBOS.write(b);
+								b = dis.readByte();
+							}
+							
+							//move path are the next bytes
+							ByteArrayOutputStream movePathBOS = new ByteArrayOutputStream();
+							b = dis.readByte();
+							while(b != '\0') {
+								movePathBOS.write(b);
+								b = dis.readByte();
+							}
+							
+							//get the resource
+							IResource resource = null;
+							IPath resourcePath = new Path(new String(resourceLocoationBOS.toByteArray(), ENCODING_UTF16));
+							if(fileType == IResource.FILE) {
+								resource =
+									ResourcesPlugin.getWorkspace().getRoot().getFile(resourcePath);
+							} else {
+								resource =
+									ResourcesPlugin.getWorkspace().getRoot().getFolder(resourcePath);
+							}
+							
+							//get the move path
+							IPath movePath = null;
+							if(movePathBOS.size() != 0) {
+								movePath = new Path(new String(movePathBOS.toByteArray(), ENCODING_UTF16));
+							}
+							
+							//add the object to the list of of preserved resources
+							preservedResourceEvents.put(resource, new ResourceEvent(
+									AbstractIndexManager.SOURCE_PRESERVED_RESOURCES_TO_INDEX,
+									action, movePath));
+							
+							progress.worked(1);
+						}	
+					} else {
+						success = false;
+					}
+				} catch (FileNotFoundException e) {
+					Logger.logException(AbstractIndexManager.this.fName +
+							": Exception while opening file to read preserved resources to index.", //$NON-NLS-1$
+							e);
+					success = false;
+				} catch (IOException e) {
+					Logger.logException(AbstractIndexManager.this.fName +
+							": Exception while reading from file of preserved resources to index.", //$NON-NLS-1$
+							e);
+					success = false;
+				} finally {
+					if(dis != null) {
+						try {
+							dis.close();
+						} catch (IOException e) {
+							Logger.logException(AbstractIndexManager.this.fName +
+									": Exception while closing file of preserved resources" + //$NON-NLS-1$
+									" to index that was just read.  This should have no" + //$NON-NLS-1$
+									" effect on the consistency of the index.", //$NON-NLS-1$
+									e);
+						}
+					}
+				}
+				
+				//if success loading preserved then add to master list
+				if(success) {
+					synchronized (this.fResourceEventsLock) {
+						Iterator iter = preservedResourceEvents.keySet().iterator();
+						while(iter.hasNext()) {
+							IResource resource = (IResource)iter.next();
+							ResourceEvent event = (ResourceEvent)preservedResourceEvents.get(resource);
+							this.fResourceEvents.put(resource, event);
+						}
+					}
+				} else {
+					//failed reading file, so delete it
+					preservedResourceEventsFile.delete();
+				}
+			}
+			
+			return success;
+		}
+		
+		/**
+		 * @return {@link File} that contains any resource events received but not processed
+		 * by this manager the last time it shutdown. This file may or may not actually
+		 * exist.
+		 * 
+		 * @see #preserveReceivedResourceEvents()
+		 * @see #loadPreservedReceivedResourceEvents(SubMonitor)
+		 */
+		private File getPreservedResourceEventsFile() {
+			IPath preservedResroucesToIndexPath =
+				AbstractIndexManager.this.getWorkingLocation().append(
+						PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME);
+			return new File(preservedResroucesToIndexPath.toOSString());
+		}
+		
+		/**
+		 * <p>If all resource events have been processed 
+		 */
+		private void notifyIfConsistant() {
+			if(!this.hasResourceEventsToProcess()) {
+				synchronized (this.fToNotifyLock) {
+					this.fToNotifyLock.notifyAll();
+				}
+			}
+		}
+	}
+	
+	/**
+	 * <p>Represents a resource that was discovered by this manager.  Contains
+	 * all the information this manager and the index needs to know about this
+	 * resource.  Such has how the manager was notified about this resource and
+	 * the type of action occurring on the resource.</p>
+	 */
+	private static class ResourceEvent {
+		/**
+		 * <p>The source of this resource event</p>
+		 * 
+		 * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
+		 * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN
+		 * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX
+		 */
+		protected byte fSource;
+		
+		/**
+		 * <p>The action that the index should take with this resource</p>
+		 *
+		 * @see AbstractIndexManager#ACTION_ADD
+		 * @see AbstractIndexManager#ACTION_REMOVE
+		 * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
+		 * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
+		 */
+		protected byte fAction;
+		
+		/**
+		 * 
+		 * <p>If the {@link #fAction} is {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} or
+		 * {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this field will have the
+		 * path the resource was moved from or moved to. Else this field will be <code>null</code></p>
+		 * 
+		 * <p><b>NOTE: </b>Maybe <code>null</code>.</p>
+		 * 
+		 * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
+		 * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
+		 */
+		protected IPath fMovePath;
+		
+		/**
+		 * <p>Creates a resource event that the index needs to react to in some way</p>
+		 * 
+		 * @param source source that the manager used to learn of this resource
+		 * @param action action the index should take on this resource
+		 * @param resource resource that the index should know about
+		 * @param movePath if action is {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} or
+		 * {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this should be the path the
+		 * resource was moved from or moved to respectively, else should be <code>null</code>
+		 * 
+		 * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
+		 * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN
+		 * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX
+		 * 
+		 * @see AbstractIndexManager#ACTION_ADD
+		 * @see AbstractIndexManager#ACTION_REMOVE
+		 * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
+		 * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
+		 */
+		protected ResourceEvent(byte source, byte action, IPath movePath) {
+			this.fSource = source;
+			this.fAction = action;
+			this.fMovePath = movePath;
+		}
+	}
+}
\ No newline at end of file