[146924] component caching for expensive lookups
diff --git a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/ComponentCore.java b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/ComponentCore.java
index 7bf4904..6f5b682 100644
--- a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/ComponentCore.java
+++ b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/ComponentCore.java
@@ -23,7 +23,7 @@
 import org.eclipse.wst.common.componentcore.internal.resources.VirtualFile;
 import org.eclipse.wst.common.componentcore.internal.resources.VirtualReference;
 import org.eclipse.wst.common.componentcore.internal.resources.VirtualResource;
-import org.eclipse.wst.common.componentcore.internal.util.ComponentImplRegistryReader;
+import org.eclipse.wst.common.componentcore.internal.util.ComponentImplManager;
 import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
 import org.eclipse.wst.common.componentcore.resources.IVirtualContainer;
 import org.eclipse.wst.common.componentcore.resources.IVirtualFile;
@@ -61,7 +61,7 @@
 	public static IVirtualComponent createComponent(IProject aProject) {
 		if (!ModuleCoreNature.isFlexibleProject(aProject))
 			return null;
-		return ComponentImplRegistryReader.instance().createComponent(aProject);
+		return ComponentImplManager.instance().createComponent(aProject);
 	}
 
 	/**
@@ -91,7 +91,7 @@
 	 * @see IVirtualContainer#create(int, IProgressMonitor)
 	 */
 	public static IVirtualComponent createArchiveComponent(IProject aProject, String aComponentName) {
-		return ComponentImplRegistryReader.instance().createArchiveComponent(aProject, aComponentName);
+		return ComponentImplManager.instance().createArchiveComponent(aProject, aComponentName);
 	}
 
 	/**
@@ -109,7 +109,7 @@
 	 * @see IVirtualResource#createLink(IPath, int, IProgressMonitor)
 	 */
 	public static IVirtualFolder createFolder(IProject aProject, IPath aRuntimePath) {
-		return ComponentImplRegistryReader.instance().createFolder(aProject, aRuntimePath);
+		return ComponentImplManager.instance().createFolder(aProject, aRuntimePath);
 	}
 
 	/**
diff --git a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/resources/ResourceTimestampMappings.java b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/resources/ResourceTimestampMappings.java
new file mode 100644
index 0000000..6182b44
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/resources/ResourceTimestampMappings.java
@@ -0,0 +1,277 @@
+/***************************************************************************************************
+ * Copyright (c) 2003, 2004 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.common.componentcore.internal.resources;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+
+/**
+ * Maps resources to cached data and remembers when the data was cached based on a timestamp
+ * signature of a given resource.
+ * 
+ */
+public class ResourceTimestampMappings {
+
+	private static final IPath COMPONENT_MANIFEST_PATH = new Path(".settings/org.eclipse.wst.common.component");
+	private static final IPath FACET_CONFIG_PATH = new Path(".settings/org.eclipse.wst.common.project.facet.core.xml");
+
+	private static final Object NO_DATA = new Object();
+	private static final Object ERROR = new Object();
+
+	private final Map timestamps = new HashMap();
+	private final Map data = new HashMap();
+
+
+	/**
+	 * Record a timestamp signature for the given resource.
+	 * <p>
+	 * Clients may use {@link #hasChanged(IResource)} to determine if the resource has changed since
+	 * it was recorded.
+	 * </p>
+	 * 
+	 * @param resource
+	 *            The resource that was processed.
+	 * @return True if the recording was successfull. The recording may fail if the resource does
+	 *         not exist.
+	 * 
+	 * @see #hasChanged(IResource)
+	 */
+	public synchronized boolean mark(IResource resource) {
+		return mark(resource, NO_DATA);
+	}
+
+	/**
+	 * Cache the data that was determined when processing the resource. The a timestamp signature
+	 * will be recorded for the resource as well.
+	 * 
+	 * <p>
+	 * Clients may use {@link #hasChanged(IResource)} to determine if the cached data should be
+	 * replaced.
+	 * </p>
+	 * 
+	 * @param resource
+	 *            The resource that was processed.
+	 * @param cachedData
+	 *            The data that was determined when the Resource was processed.
+	 * 
+	 * @return True if the recording was successfull. The recording may fail if the resource does
+	 *         not exist.
+	 * 
+	 * 
+	 * @see #hasChanged(IResource)
+	 * @see #hasCacheData(IResource)
+	 */
+	public synchronized boolean mark(IResource resource, Object cachedData) {
+
+		if (resource.getModificationStamp() != IResource.NULL_STAMP) {
+			if (timestamps.containsKey(resource)) {
+				TimestampSignature signature = (TimestampSignature) timestamps.get(resource);
+				signature.update(resource);
+			} else {
+				timestamps.put(resource, createTimestampSignature(resource));
+			}
+			data.put(resource, cachedData);
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Note that an error occurred when processing this resource.
+	 * 
+	 * @param resource
+	 *            The resource that had some sort of error while processing.
+	 * @return True if the error was recorded. The error may not be recorded if the resource does
+	 *         not exist.
+	 * @see #hasCacheError(IResource)
+	 */
+	public synchronized boolean markError(IResource resource) {
+
+		if (resource.getModificationStamp() != IResource.NULL_STAMP) {
+			if (timestamps.containsKey(resource)) {
+				TimestampSignature signature = (TimestampSignature) timestamps.get(resource);
+				signature.update(resource);
+			} else {
+				timestamps.put(resource, createTimestampSignature(resource));
+			}
+			data.put(resource, ERROR);
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * 
+	 * @param resource
+	 *            The resource that was or is about to be processed.
+	 * @return True if the given resource has changed in a noticeable way since it was marked.
+	 * 
+	 * @see #mark(IResource)
+	 * @see #mark(IResource, Object)
+	 * @see #markError(IResource)
+	 */
+	public boolean hasChanged(IResource resource) {
+		TimestampSignature signature = (TimestampSignature) timestamps.get(resource);
+		return signature == null || signature.hasChanged(resource);
+	}
+
+	/**
+	 * 
+	 * @param resource
+	 *            The resource that was or is about to be processed.
+	 * @return True if there is any data cached for the given resource.
+	 * 
+	 * @see #mark(IResource)
+	 * @see #mark(IResource, Object)
+	 * @see #markError(IResource)
+	 */
+	public boolean hasCacheData(IResource resource) {
+		Object datum = data.get(resource);
+		return datum != null && datum != NO_DATA;
+
+	}
+
+
+	/**
+	 * 
+	 * @param resource
+	 *            The resource that was or is about to be processed.
+	 * @return True if there is any data cached for the given resource.
+	 * 
+	 * @see #mark(IResource)
+	 * @see #mark(IResource, Object)
+	 * @see #markError(IResource)
+	 */
+	public boolean hasCacheError(IResource resource) {
+		return data.get(resource) == ERROR;
+	}
+
+	public Object getData(IResource resource) {
+		Object datum = data.get(resource);
+		if (datum != NO_DATA)
+			return datum;
+		return null;
+	}
+
+
+	private TimestampSignature createTimestampSignature(IResource resource) {
+		switch (resource.getType()) {
+			case IResource.PROJECT :
+				return new ProjectTimestamp((IProject) resource);
+			default :
+				return new SimpleResourceTimestamp(resource);
+		}
+	}
+
+	/**
+	 * Provides a point in time signature of a Resource to determine whether that resource has
+	 * changed in a meaningful way since the time this signature was created or last updated.
+	 * 
+	 */
+	public interface TimestampSignature {
+
+		/**
+		 * 
+		 * @param resource
+		 *            A resource related to this signature
+		 * @return True if the current resource is different from this signature in a meaningful way
+		 */
+		boolean hasChanged(IResource resource);
+
+		/**
+		 * 
+		 * @param resource
+		 *            Update the signature details to the given resource's signature.
+		 */
+		void update(IResource resource);
+
+	}
+
+	/**
+	 * Provides a signature based on the modificationStamp of a resource.
+	 */
+	public class SimpleResourceTimestamp implements TimestampSignature {
+		private long timestamp = 0;
+
+		public SimpleResourceTimestamp(IResource resource) {
+			update(resource);
+		}
+
+		public boolean hasChanged(IResource resource) {
+			return timestamp != resource.getModificationStamp();
+		}
+
+		public void update(IResource resource) {
+			timestamp = resource.getModificationStamp();
+		}
+	}
+
+	/**
+	 * Provides a signature for a project based on the modificationStamp of the (1) project, (2) the
+	 * component manifest, and (3) the facet configuration
+	 */
+	public class ProjectTimestamp implements TimestampSignature {
+
+		private long projectTimestamp = 0;
+		private long componentManifestTimestamp = 0;
+		private long facetConfigTimestamp = 0;
+
+		public ProjectTimestamp(IProject project) {
+			update(project);
+		}
+
+		public boolean hasChanged(IResource resource) {
+			if (resource.getType() == IResource.PROJECT) {
+				IProject project = (IProject) resource;
+				if (projectTimestamp != project.getModificationStamp())
+					return true;
+
+				IFile file = project.getFile(COMPONENT_MANIFEST_PATH);
+				if (!file.exists() || componentManifestTimestamp != file.getModificationStamp())
+					return true;
+
+				file = project.getFile(FACET_CONFIG_PATH);
+				if (!file.exists() || facetConfigTimestamp != file.getModificationStamp())
+					return true;
+
+				return false;
+			}
+			return true;
+
+		}
+
+		public void update(IResource resource) {
+
+			if (resource instanceof IProject) {
+
+				IProject project = (IProject) resource;
+
+				projectTimestamp = project.getModificationStamp();
+
+				IFile file = project.getFile(COMPONENT_MANIFEST_PATH);
+				componentManifestTimestamp = file.getModificationStamp();
+
+				file = project.getFile(FACET_CONFIG_PATH);
+				facetConfigTimestamp = file.getModificationStamp();
+
+			} else {
+				projectTimestamp = componentManifestTimestamp = facetConfigTimestamp = IResource.NULL_STAMP;
+			}
+
+		}
+	}
+
+}
diff --git a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/resources/VirtualResource.java b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/resources/VirtualResource.java
index 9088003..c5ec204 100644
--- a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/resources/VirtualResource.java
+++ b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/resources/VirtualResource.java
@@ -45,6 +45,8 @@
 	private static final String EMPTY_STRING = ""; //$NON-NLS-1$
 	private IVirtualComponent component;
 	private String resourceType;
+	
+	private final ResourceTimestampMappings mapping = new ResourceTimestampMappings();
 
 
 	protected VirtualResource(IProject aComponentProject, IPath aRuntimePath) {
@@ -110,55 +112,37 @@
 	}
 
 	public IPath[] getProjectRelativePaths() {
-		StructureEdit moduleCore = null;
-		try {
-			moduleCore = StructureEdit.getStructureEditForRead(getProject());
-			if (moduleCore !=null) {
-				WorkbenchComponent aComponent = moduleCore.getComponent();
-				if (aComponent != null) {
-					ResourceTreeRoot root = ResourceTreeRoot.getDeployResourceTreeRoot(aComponent);
-					// still need some sort of loop here to search subpieces of the runtime path.
-					ComponentResource[] componentResources = null;
-	
-					if (root != null) {
-						IPath[] estimatedPaths = null;
-						IPath searchPath = null;
-						do {
-							searchPath = (searchPath == null) ? getRuntimePath() : searchPath.removeLastSegments(1);
-							componentResources = root.findModuleResources(searchPath, ResourceTreeNode.CREATE_NONE);
-							estimatedPaths = findBestMatches(componentResources);
-						} while (estimatedPaths.length==0 && canSearchContinue(componentResources, searchPath));
-						if (estimatedPaths==null || estimatedPaths.length==0)
-							return new IPath[] {getRuntimePath()};
-						return estimatedPaths;
-					}
-				}
+ 
+		WorkbenchComponent aComponent = getReadOnlyComponent();
+		if (aComponent != null) {
+			ResourceTreeRoot root = ResourceTreeRoot.getDeployResourceTreeRoot(aComponent);
+			// still need some sort of loop here to search subpieces of the runtime path.
+			ComponentResource[] componentResources = null;
+
+			if (root != null) {
+				IPath[] estimatedPaths = null;
+				IPath searchPath = null;
+				do {
+					searchPath = (searchPath == null) ? getRuntimePath() : searchPath.removeLastSegments(1);
+					componentResources = root.findModuleResources(searchPath, ResourceTreeNode.CREATE_NONE);
+					estimatedPaths = findBestMatches(componentResources);
+				} while (estimatedPaths.length==0 && canSearchContinue(componentResources, searchPath));
+				if (estimatedPaths==null || estimatedPaths.length==0)
+					return new IPath[] {getRuntimePath()};
+				return estimatedPaths;
 			}
-		} finally {
-			if (moduleCore != null) {
-				moduleCore.dispose();
-			}
-		}
+		} 
+ 
 		return new IPath[] {getRuntimePath()};
 	}
 
-	public IPath getProjectRelativePath() {
-		StructureEdit moduleCore = null;
-		if (getRuntimePath().equals(new Path("/"))) {
-			try {
-				moduleCore = StructureEdit.getStructureEditForRead(getProject());
-				if (moduleCore != null) {
-					WorkbenchComponent aComponent = moduleCore.getComponent();
-					if (aComponent != null) {
-						if (((WorkbenchComponentImpl) aComponent).getDefaultSourceRoot() != null)
-							return ((WorkbenchComponentImpl) aComponent).getDefaultSourceRoot();
-					}
-				}
-			} finally {
-				if (moduleCore != null) {
-					moduleCore.dispose();
-				}
-			}
+	public IPath getProjectRelativePath() { 
+		if (getRuntimePath().equals(new Path("/"))) { 
+			WorkbenchComponent aComponent = getReadOnlyComponent();
+			if (aComponent != null) {
+				if (((WorkbenchComponentImpl) aComponent).getDefaultSourceRoot() != null)
+					return ((WorkbenchComponentImpl) aComponent).getDefaultSourceRoot();
+			} 
 		}
 		return getProjectRelativePaths()[0];
 	}
@@ -257,39 +241,25 @@
 	}
 
 	public void setResourceType(String aResourceType) {
-		resourceType = aResourceType;
-		StructureEdit moduleCore = null;
-		try {
-			moduleCore = StructureEdit.getStructureEditForRead(getProject());
-			WorkbenchComponent aComponent = moduleCore.getComponent();
-			ComponentResource[] resources = aComponent.findResourcesByRuntimePath(getRuntimePath());
-			for (int i = 0; i < resources.length; i++) {
-				resources[i].setResourceType(aResourceType);
-			}
-		} finally {
-			if (moduleCore != null) {
-				moduleCore.dispose();
-			}
+		resourceType = aResourceType; 
+		WorkbenchComponent aComponent = getReadOnlyComponent();
+		ComponentResource[] resources = aComponent.findResourcesByRuntimePath(getRuntimePath());
+		for (int i = 0; i < resources.length; i++) {
+			resources[i].setResourceType(aResourceType);
 		}
+		 
 	}
 
 	// TODO Fetch the resource type from the model.
 	public String getResourceType() {
-		if (null == resourceType) {
-			StructureEdit moduleCore = null;
-			try {
-				moduleCore = StructureEdit.getStructureEditForRead(getProject());
-				WorkbenchComponent aComponent = moduleCore.getComponent();
-				ComponentResource[] resources = aComponent.findResourcesByRuntimePath(getRuntimePath());
-				for (int i = 0; i < resources.length; i++) {
-					resourceType = resources[i].getResourceType();
-					return resourceType;
-				}
-			} finally {
-				if (moduleCore != null) {
-					moduleCore.dispose();
-				}
+		if (null == resourceType) { 
+			WorkbenchComponent aComponent = getReadOnlyComponent();
+			ComponentResource[] resources = aComponent.findResourcesByRuntimePath(getRuntimePath());
+			for (int i = 0; i < resources.length; i++) {
+				resourceType = resources[i].getResourceType();
+				return resourceType;
 			}
+			 
 		}
 		resourceType = ""; //$NON-NLS-1$
 		return resourceType;
@@ -335,4 +305,24 @@
 			}
 		}
 	}
+	
+	protected final WorkbenchComponent getReadOnlyComponent() {
+		if(!mapping.hasChanged(getProject()) && mapping.hasCacheData(getProject()))
+			return (WorkbenchComponent) mapping.getData(getProject());
+		
+		StructureEdit moduleCore = null;
+		WorkbenchComponent component = null;
+		try {
+			moduleCore = StructureEdit.getStructureEditForRead(getProject());
+			component = moduleCore.getComponent();
+			mapping.mark(getProject(), component);
+		} finally {
+			if (moduleCore != null) { 
+				moduleCore.dispose();
+			}
+		}
+		return component;
+		
+			
+	}
 }
diff --git a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentImplRegistryReader.java b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentImplManager.java
similarity index 67%
rename from plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentImplRegistryReader.java
rename to plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentImplManager.java
index 4f92654..8e6efc9 100644
--- a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentImplRegistryReader.java
+++ b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentImplManager.java
@@ -22,6 +22,7 @@
 import org.eclipse.core.runtime.SafeRunner;
 import org.eclipse.jem.util.RegistryReader;
 import org.eclipse.wst.common.componentcore.internal.ModulecorePlugin;
+import org.eclipse.wst.common.componentcore.internal.resources.ResourceTimestampMappings;
 import org.eclipse.wst.common.componentcore.internal.resources.VirtualArchiveComponent;
 import org.eclipse.wst.common.componentcore.internal.resources.VirtualComponent;
 import org.eclipse.wst.common.componentcore.internal.resources.VirtualFolder;
@@ -31,29 +32,30 @@
 import org.eclipse.wst.common.project.facet.core.IProjectFacet;
 import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
 
-public class ComponentImplRegistryReader extends RegistryReader {
+public class ComponentImplManager  {
 
 	private static final String COMPONENT_IMPL_EXTENSION_POINT = "componentimpl"; //$NON-NLS-1$
-	private static final String COMPONENT_IMPL = "componentimpl"; //$NON-NLS-1$
-	private static final String TYPE = "typeID"; //$NON-NLS-1$
-	private static final String CLASS = "class"; //$NON-NLS-1$
+	private static final String TAG_COMPONENT_IMPL = "componentimpl"; //$NON-NLS-1$
+	private static final String ATT_TYPE = "typeID"; //$NON-NLS-1$
+	private static final String ATT_CLASS = "class"; //$NON-NLS-1$
 
-	private static final ComponentImplRegistryReader instance = new ComponentImplRegistryReader();
+	private static final ComponentImplManager instance = new ComponentImplManager();
+	private static final Object LOAD_FAILED = new Object();
 
-	private final Map/* <String, ComponentImplDescriptor> */descriptors = new Hashtable();
+	private final Map/* <String, ComponentImplDescriptor> */ descriptors = new Hashtable();
 
-	private final Map/* <ComponentImplDescriptor, IComponentImplFactory> */instances = new Hashtable();
+	private final Map/* <ComponentImplDescriptor, IComponentImplFactory> */ instances = new Hashtable();
+	private final ResourceTimestampMappings factoryMap = new ResourceTimestampMappings();
 
 	/**
 	 * @return Returns the instance.
 	 */
-	public static ComponentImplRegistryReader instance() {
+	public static ComponentImplManager instance() {
 		/* already initialized and registry read by the time the class initializes */
 		return instance;
 	}
 
-	public ComponentImplRegistryReader() {
-		super(ModulecorePlugin.PLUGIN_ID, COMPONENT_IMPL_EXTENSION_POINT);
+	public ComponentImplManager() {
 		SafeRunner.run(new ISafeRunnable() {
 
 			public void handleException(Throwable exception) {
@@ -61,34 +63,12 @@
 			}
 
 			public void run() throws Exception {
-				readRegistry();
+				new ComponentImplRegistryReader().readRegistry();
 			}
 
 		});
 	}
 
-	/**
-	 * @see org.eclipse.wst.common.frameworks.internal.RegistryReader#readElement(org.eclipse.core.runtime.IConfigurationElement)
-	 */
-	public boolean readElement(IConfigurationElement element) {
-		if (COMPONENT_IMPL.equals(element.getName())) {
-
-			/*
-			 * Because the only instance of this type is created from a static singleton field, and
-			 * the registry is initialized in the constructor of this type, other threads cannot
-			 * compete with readElement() for access to <i>descriptors</i>
-			 */
-			String type = element.getAttribute(TYPE);
-			if (type != null)
-				descriptors.put(element.getAttribute(TYPE), new ComponentImplDescriptor(element));
-			else
-				ModulecorePlugin.logError(0, "No type attribute is specified for " + //$NON-NLS-1$
-							ModulecorePlugin.PLUGIN_ID + "." + COMPONENT_IMPL_EXTENSION_POINT + //$NON-NLS-1$ 
-							" extension in " + element.getDeclaringExtension().getNamespaceIdentifier(), null); //$NON-NLS-1$
-			return true;
-		}
-		return false;
-	}
 
 	private IComponentImplFactory getComponentImplFactory(String typeID) {
 
@@ -110,10 +90,20 @@
 		}
 		return factory;
 	}
-	
-	// TODO Don't like this because it's going to cycle every project facet for each project
-	protected IComponentImplFactory findFactoryForProject(IProject project){
+	 
+	private IComponentImplFactory findFactoryForProject(IProject project){
 		try {
+			IComponentImplFactory factory = null;		
+			
+			if( !factoryMap.hasChanged(project) ) {				
+
+				if( factoryMap.hasCacheError(project))
+					return null;
+				
+				if( factoryMap.hasCacheData(project)) 
+					return (IComponentImplFactory) factoryMap.getData(project);
+			} 
+			
 			IFacetedProject facetedProject = ProjectFacetsManager.create(project);
 			if (facetedProject == null) return null;
 			Iterator keys = descriptors.keySet().iterator();
@@ -122,8 +112,9 @@
 				try {
 					IProjectFacet projectFacet = ProjectFacetsManager.getProjectFacet(typeID);
 					if (projectFacet != null && facetedProject.hasProjectFacet(projectFacet)){
-						IComponentImplFactory factory = getComponentImplFactory(typeID);
+						factory = getComponentImplFactory(typeID);
 						if(null != factory){
+							factoryMap.mark(project, factory);
 							return factory;
 						}
 					}
@@ -131,11 +122,14 @@
 					continue;
 				}
 			}
+			
 		} catch (Exception e) {
-			// Just return null
+			ModulecorePlugin.logError(0, "Returning null factory for project: " + project, e); //$NON-NLS-1$
+			factoryMap.markError(project);
 		}
 		return null;
 	}
+	 
 	
 	
 	public IVirtualFolder createFolder(IProject aProject, IPath aRuntimePath){
@@ -181,7 +175,7 @@
 
 		public ComponentImplDescriptor(IConfigurationElement configElement) {
 			element = configElement;
-			type = element.getAttribute(TYPE);
+			type = element.getAttribute(ATT_TYPE);
 		}
 
 		/**
@@ -202,7 +196,7 @@
 				}
 
 				public void run() throws Exception {
-					factory[0] = (IComponentImplFactory) element.createExecutableExtension(CLASS);
+					factory[0] = (IComponentImplFactory) element.createExecutableExtension(ATT_CLASS);
 				}
 
 			});
@@ -219,5 +213,35 @@
 		}
 
 	}
+	
+	private class ComponentImplRegistryReader extends RegistryReader {
+
+		public ComponentImplRegistryReader() {
+			super(ModulecorePlugin.PLUGIN_ID, COMPONENT_IMPL_EXTENSION_POINT);
+		} 
+
+		/**
+		 * @see org.eclipse.wst.common.frameworks.internal.RegistryReader#readElement(org.eclipse.core.runtime.IConfigurationElement)
+		 */
+		public boolean readElement(IConfigurationElement element) {
+			if (TAG_COMPONENT_IMPL.equals(element.getName())) {
+
+				/*
+				 * Because the only instance of this type is created from a static singleton field, and
+				 * the registry is initialized in the constructor of this type, other threads cannot
+				 * compete with readElement() for access to <i>descriptors</i>
+				 */
+				String type = element.getAttribute(ATT_TYPE);
+				if (type != null)
+					descriptors.put(element.getAttribute(ATT_TYPE), new ComponentImplDescriptor(element));
+				else
+					ModulecorePlugin.logError(0, "No type attribute is specified for " + //$NON-NLS-1$
+								ModulecorePlugin.PLUGIN_ID + "." + COMPONENT_IMPL_EXTENSION_POINT + //$NON-NLS-1$ 
+								" extension in " + element.getDeclaringExtension().getNamespaceIdentifier(), null); //$NON-NLS-1$
+				return true;
+			}
+			return false;
+		}
+	}
 
 }
diff --git a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentUtilities.java b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentUtilities.java
index e20ac2c..ddd0e6e 100644
--- a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentUtilities.java
+++ b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/util/ComponentUtilities.java
@@ -38,6 +38,7 @@
 import org.eclipse.wst.common.componentcore.internal.operation.CreateReferenceComponentsOp;
 import org.eclipse.wst.common.componentcore.internal.operation.RemoveReferenceComponentOperation;
 import org.eclipse.wst.common.componentcore.internal.operation.RemoveReferenceComponentsDataModelProvider;
+import org.eclipse.wst.common.componentcore.internal.resources.ResourceTimestampMappings;
 import org.eclipse.wst.common.componentcore.internal.resources.VirtualComponent;
 import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
 import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;
@@ -48,6 +49,8 @@
 import org.eclipse.wst.common.internal.emfworkbench.WorkbenchResourceHelper;
 
 public class ComponentUtilities {
+	
+	private static final ResourceTimestampMappings ContextRootMapping = new ResourceTimestampMappings();
 
 	/**
 	 * Ensure the container is not read-only.
@@ -257,14 +260,24 @@
 	 * 
 	 * @return String value of the context root for runtime of the associated module
 	 */
-	public static String getServerContextRoot(IProject project) {
+	public static String getServerContextRoot(IProject project) {		
+		
+		if(!ContextRootMapping.hasChanged(project)) {
+			if(ContextRootMapping.hasCacheData(project))
+				return (String) ContextRootMapping.getData(project);
+			else if(ContextRootMapping.hasCacheError(project))
+				return null;
+			
+		}
 		
 		StructureEdit moduleCore = null;
 		WorkbenchComponent wbComponent = null;
 		try {
 			moduleCore = StructureEdit.getStructureEditForRead(project);
-			if (moduleCore == null || moduleCore.getComponent() == null)
+			if (moduleCore == null || moduleCore.getComponent() == null) {
+				ContextRootMapping.markError(project);
 				return null;
+			}
 			wbComponent = moduleCore.getComponent();
 		} finally {
 			if (moduleCore != null) {
@@ -275,9 +288,11 @@
 		for (int i = 0; i < existingProps.size(); i++) {
 			Property prop = (Property) existingProps.get(i);
 			if(prop.getName().equals(IModuleConstants.CONTEXTROOT)){
+				ContextRootMapping.mark(project, prop.getValue());
 				return prop.getValue();
 			}
-		}			
+		}		
+		ContextRootMapping.markError(project);
 		// If all else fails...
 		return null;
 	}