[154359] API improvements to ModuleFactory
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/ServerUtil.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/ServerUtil.java
index d532266..b75b321 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/ServerUtil.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/ServerUtil.java
@@ -36,6 +36,7 @@
 	 * Constant identifying the job family identifier for server operations.
 	 * 
 	 * @see IJobManager#join(Object, IProgressMonitor)
+	 * @since 2.0
 	 */
 	public static final Object SERVER_JOB_FAMILY = ServerPlugin.PLUGIN_ID;
 
@@ -51,6 +52,10 @@
 	 * is contained with the project, this method will return an arbitrary module
 	 * unless the module factory defines an ordering. If there might be multiple
 	 * modules in a project, users should typically use getModules(IProject) instead.
+	 * <p>
+	 * This method may trigger bundle loading and is not suitable for
+	 * short/UI operations.
+	 * </p>
 	 * 
 	 * @param project a project
 	 * @return a module that is contained with the project, or null if no
@@ -61,19 +66,19 @@
 		if (project == null)
 			throw new IllegalArgumentException();
 		
-		IModule[] modules = getModules();
-		if (modules != null) {
-			int size = modules.length;
-			for (int i = 0; i < size; i++) {
-				if (modules[i] != null && project.equals(modules[i].getProject()))
-					return modules[i];
-			}
-		}
+		IModule[] modules = getModules(project);
+		if (modules != null && modules.length > 0)
+			return modules[0];
+		
 		return null;
 	}
 
 	/**
 	 * Returns the modules contained within the given project.
+	 * <p>
+	 * This method may trigger bundle loading and is not suitable for
+	 * short/UI operations.
+	 * </p>
 	 * 
 	 * @param project a project
 	 * @return a possibly-empty array of modules
@@ -83,22 +88,39 @@
 		if (project == null)
 			throw new IllegalArgumentException();
 		
-		IModule[] modules = getModules();
-		List list = new ArrayList();
-		if (modules != null) {
-			int size = modules.length;
+		// use a set for better contains() performance
+		Set set = new HashSet();
+		
+		ModuleFactory[] factories = ServerPlugin.getModuleFactories();
+		if (factories != null) {
+			int size = factories.length;
 			for (int i = 0; i < size; i++) {
-				if (modules[i] != null && project.equals(modules[i].getProject()))
-					list.add(modules[i]);
+				IModule[] modules = factories[i].getModules(project, null);
+				if (modules != null) {
+					int size2 = modules.length;
+					for (int j = 0; j < size2; j++) {
+						if (!set.contains(modules[j])) {
+							if (isSupportedModule(factories[i].getModuleTypes(), modules[j].getModuleType()))
+								set.add(modules[j]);
+							else
+								Trace.trace(Trace.WARNING, "Invalid module returned from factory, ignored: " + modules[j]);
+						}
+					}
+				}
 			}
 		}
-		IModule[] modules2 = new IModule[list.size()];
-		list.toArray(modules2);
-		return modules2;
+		IModule[] modules = new IModule[set.size()];
+		set.toArray(modules);
+		return modules;
 	}
 
 	/**
-	 * Returns a module from the given moduleId. The moduleId must not be null.
+	 * Returns the module with the given moduleId, if one exists. The moduleId
+	 * must not be null.
+	 * <p>
+	 * This method may trigger bundle loading and is not suitable for
+	 * short/UI operations.
+	 * </p>
 	 * 
 	 * @param moduleId a module id
 	 * @return the module, or <code>null</code> if the module could not be found
@@ -115,30 +137,33 @@
 		ModuleFactory moduleFactory = ServerPlugin.findModuleFactory(factoryId);
 		if (moduleFactory == null)
 			return null;
-
+		
 		String moduleSubId = moduleId.substring(index+1);
-		IModule module = moduleFactory.getModule(moduleSubId);
-		if (module != null)
-			return module;
-		return null;
+		return moduleFactory.findModule(moduleSubId, null);
 	}
 
 	/**
 	 * Return all the available modules from all factories whose
 	 * type matches the given module types.
+	 * <p>
+	 * This method may trigger bundle loading and is not suitable for
+	 * short/UI operations. It also performs a search of all available
+	 * modules of the given types, and due to performance reasons should
+	 * not be used unless absolutely required.
+	 * </p>
 	 * 
 	 * @param moduleTypes an array of module types
 	 * @return a possibly empty array of modules
 	 */
 	public static IModule[] getModules(IModuleType[] moduleTypes) {
 		List list = new ArrayList();
-
+		
 		ModuleFactory[] factories = ServerPlugin.getModuleFactories();
 		if (factories != null) {
 			int size = factories.length;
 			for (int i = 0; i < size; i++) {
 				if (isSupportedModule(factories[i].getModuleTypes(), moduleTypes)) {
-					IModule[] modules = factories[i].getModules();
+					IModule[] modules = factories[i].getModules(null);
 					if (modules != null) {
 						int size2 = modules.length;
 						for (int j = 0; j < size2; j++)
@@ -155,6 +180,12 @@
 	/**
 	 * Return all the available modules from all factories whose
 	 * type matches the given module type id.
+	 * <p>
+	 * This method may trigger bundle loading and is not suitable for
+	 * short/UI operations. It also performs a search of all available
+	 * modules of this type, and due to performance reasons should not
+	 * be used unless absolutely required.
+	 * </p>
 	 * 
 	 * @param type a module type
 	 * @return a possibly empty array of modules
@@ -167,7 +198,7 @@
 			int size = factories.length;
 			for (int i = 0; i < size; i++) {
 				if (isSupportedModule(factories[i].getModuleTypes(), type, null)) {
-					IModule[] modules = factories[i].getModules();
+					IModule[] modules = factories[i].getModules(null);
 					if (modules != null) {
 						int size2 = modules.length;
 						for (int j = 0; j < size2; j++)
@@ -284,38 +315,6 @@
 	}
 
 	/**
-	 * Return all the available modules from all factories.
-	 * 
-	 * @return a possibly empty array of modules
-	 */
-	private static IModule[] getModules() {
-		// use a set for better contains() performance
-		Set set = new HashSet();
-		
-		ModuleFactory[] factories = ServerPlugin.getModuleFactories();
-		if (factories != null) {
-			int size = factories.length;
-			for (int i = 0; i < size; i++) {
-				IModule[] modules = factories[i].getModules();
-				if (modules != null) {
-					int size2 = modules.length;
-					for (int j = 0; j < size2; j++) {
-						if (!set.contains(modules[j])) {
-							if (isSupportedModule(factories[i].getModuleTypes(), modules[j].getModuleType()))
-								set.add(modules[j]);
-							else
-								Trace.trace(Trace.WARNING, "Invalid module returned from factory, ignored: " + modules[j]);
-						}
-					}
-				}
-			}
-		}
-		IModule[] modules = new IModule[set.size()];
-		set.toArray(modules);
-		return modules;
-	}
-
-	/**
 	 * Adds or removes modules from a server. Will search for the first parent module
 	 * of each module and add it to the server instead. This method will handle multiple
 	 * modules having the same parent (the parent will only be added once), but may not
@@ -823,6 +822,7 @@
 	 * 
 	 * @param server a server
 	 * @return a scheduling rule for this server
+	 * @since 2.0
 	 */
 	public static ISchedulingRule getServerSchedulingRule(IServer server) {
 		return new ServerSchedulingRule(server);
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Module.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Module.java
index 7ee216f..da6c864 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Module.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Module.java
@@ -57,7 +57,7 @@
 	/**
 	 * Returns the id of this module.
 	 *
-	 * @return java.lang.String
+	 * @return the id
 	 */
 	public String getId() {
 		return id2;
@@ -66,7 +66,7 @@
 	/**
 	 * Returns the internal (partial) id of this module.
 	 *
-	 * @return the id
+	 * @return the partial id
 	 */
 	public String getInternalId() {
 		return id;
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ModuleFactory.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ModuleFactory.java
index 3b0530e..8043f0c 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ModuleFactory.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ModuleFactory.java
@@ -10,9 +10,9 @@
  *******************************************************************************/
 package org.eclipse.wst.server.core.internal;
 
-import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IProgressMonitor;
 
@@ -27,8 +27,6 @@
 	private IConfigurationElement element;
 	public ModuleFactoryDelegate delegate;
 	private List moduleTypes;
-	
-	private List modules;
 
 	/**
 	 * ModuleFactory constructor comment.
@@ -48,7 +46,7 @@
 	public String getId() {
 		return element.getAttribute("id");
 	}
-	
+
 	/**
 	 * Returns the index (ordering) of this task.
 	 *
@@ -61,7 +59,7 @@
 			return -1;
 		}
 	}
-	
+
 	/**
 	 * Return the supported module types.
 	 * 
@@ -70,12 +68,12 @@
 	public IModuleType[] getModuleTypes() {
 		if (moduleTypes == null)
 			moduleTypes = ServerPlugin.getModuleTypes(element.getChildren("moduleType"));
-
+		
 		IModuleType[] mt = new IModuleType[moduleTypes.size()];
 		moduleTypes.toArray(mt);
 		return mt;
 	}
-	
+
 	/**
 	 * Returns true if this modules factory produces project modules.
 	 *
@@ -93,9 +91,7 @@
 			try {
 				long time = System.currentTimeMillis();
 				delegate = (ModuleFactoryDelegate) element.createExecutableExtension("class");
-				//delegate.initialize(this);
 				InternalInitializer.initializeModuleFactoryDelegate(delegate, this, monitor);
-				//ResourceManager.getInstance().addModuleFactoryListener(delegate);
 				Trace.trace(Trace.PERFORMANCE, "ModuleFactory.getDelegate(): <" + (System.currentTimeMillis() - time) + "> " + getId());
 			} catch (Throwable t) {
 				Trace.trace(Trace.SEVERE, "Could not create delegate " + toString() + ": " + t.getMessage());
@@ -105,54 +101,39 @@
 	}
 
 	/*
-	 * @see
+	 * @see ModuleFactoryDelegate#getModules()
 	 */
-	public IModule getModule(String id) {
-		IModule[] modules2 = getModules();
-		if (modules2 != null) {
-			int size = modules2.length;
-			for (int i = 0; i < size; i++) {
-				String id2 = modules2[i].getId();
-				int index = id2.indexOf(":");
-				if (index >= 0)
-					id2 = id2.substring(index+1);
-				
-				if (id.equals(id2))
-					return modules2[i];
-			}
+	public IModule[] getModules(IProgressMonitor monitor) {
+		try {
+			return getDelegate(monitor).getModules();
+		} catch (Throwable t) {
+			Trace.trace(Trace.SEVERE, "Error calling delegate " + toString(), t);
+			return new IModule[0];
 		}
-		return null;
-	}
-
-	public void clearModuleCache() {
-		modules = null;
 	}
 
 	/*
-	 * @see
+	 * @see ModuleFactoryDelegate#getModules(IProject)
 	 */
-	public IModule[] getModules() {
-		//Trace.trace(Trace.FINER, "getModules() > " + this);
-		if (modules == null) {
-			try {
-				modules = new ArrayList();
-				IModule[] modules2 = getDelegate(null).getModules();
-				if (modules2 != null) {
-					int size = modules2.length;
-					for (int i = 0; i < size; i++)
-						modules.add(modules2[i]);
-				}
-			} catch (Throwable t) {
-				Trace.trace(Trace.SEVERE, "Error calling delegate " + toString() + ": " + t.getMessage());
-				return null;
-			}
+	public IModule[] getModules(IProject project, IProgressMonitor monitor) {
+		try {
+			return getDelegate(monitor).getModules(project);
+		} catch (Throwable t) {
+			Trace.trace(Trace.SEVERE, "Error calling delegate " + toString(), t);
+			return new IModule[0];
 		}
-		
-		//Trace.trace(Trace.FINER, "getModules() < " + this);
-		
-		IModule[] m = new IModule[modules.size()];
-		modules.toArray(m);
-		return m;
+	}
+
+	/*
+	 * @see ModuleFactoryDelegate#findModule(String)
+	 */
+	public IModule findModule(String id, IProgressMonitor monitor) {
+		try {
+			return getDelegate(monitor).findModule(id);
+		} catch (Throwable t) {
+			Trace.trace(Trace.SEVERE, "Error calling delegate " + toString(), t);
+			return null;
+		}
 	}
 
 	/**
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ResourceManager.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ResourceManager.java
index e554eaa..fc2c734 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ResourceManager.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ResourceManager.java
@@ -720,14 +720,6 @@
 		IServer[] servers2 = new IServer[servers.size()];
 		servers.toArray(servers2);
 		
-		Arrays.sort(servers2, new Comparator() {
-			public int compare(Object o1, Object o2) {
-				IServer a = (IServer) o1;
-				IServer b = (IServer) o2;
-				return a.getName().compareToIgnoreCase(b.getName());
-			}
-		});
-		
 		return servers2;
 	}
 
@@ -976,18 +968,19 @@
 		// process module changes
 		ProjectModuleFactoryDelegate.handleGlobalProjectChange(project, delta);
 		
-		final IModule module = ServerUtil.getModule(project);
-		if (module == null)
+		IModule[] modules = ServerUtil.getModules(project);
+		if (modules == null)
 			return;
 		
 		Trace.trace(Trace.FINEST, "- publishHandleProjectChange");
 		
 		IServer[] servers2 = getServers();
-		if (servers2 != null) {
-			int size = servers2.length;
-			for (int i = 0; i < size; i++) {
-			if (servers2[i].getAdapter(ServerDelegate.class) != null)
-				((Server) servers2[i]).handleModuleProjectChange(module);
+		int size = modules.length;
+		int size2 = servers2.length;
+		for (int i = 0; i < size; i++) {
+			for (int j = 0; j < size2; j++) {
+				if (servers2[j].getAdapter(ServerDelegate.class) != null)
+					((Server) servers2[j]).handleModuleProjectChange(modules[i]);
 			}
 		}
 		Trace.trace(Trace.FINEST, "< publishHandleProjectChange");
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Server.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Server.java
index 3fe2642..c8c4671 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Server.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Server.java
@@ -533,7 +533,7 @@
 		Trace.trace(Trace.FINEST, "> handleDeployableProjectChange() " + this + " " + module);
 		
 		// check for duplicate jobs already waiting and don't create a new one
-		Job[] jobs = Job.getJobManager().find(ServerPlugin.PLUGIN_ID);
+		Job[] jobs = Job.getJobManager().find(ServerUtil.SERVER_JOB_FAMILY);
 		if (jobs != null) {
 			int size = jobs.length;
 			for (int i = 0; i < size; i++) {
@@ -1671,7 +1671,7 @@
 		synchronousStop(true);
 		synchronousStart(mode2, monitor);
 	}
-	
+
 	/*
 	 * @see IServer#restart(String, IOperationListener)
 	 */
@@ -1681,9 +1681,9 @@
 		
 		if (getServerState() == STATE_STOPPED)
 			return;
-	
+		
 		Trace.trace(Trace.FINEST, "Restarting server: " + getName());
-	
+		
 		try {
 			final IOperationListener listener2 = listener;
 			IServerListener curListener = new IServerListener() {
@@ -2186,8 +2186,7 @@
 						moduleType = new ModuleType(moduleTypeId, moduleTypeVersion);
 					module = new DeletedModule(moduleId, name, moduleType);
 				}
-				if (module != null)
-					modules.add(module);
+				modules.add(module);
 			}
 		}
 		
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/ModuleFactoryDelegate.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/ModuleFactoryDelegate.java
index 461d813..bd6c363 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/ModuleFactoryDelegate.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/ModuleFactoryDelegate.java
@@ -10,6 +10,9 @@
  *******************************************************************************/
 package org.eclipse.wst.server.core.model;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.wst.server.core.IModule;
@@ -39,7 +42,7 @@
  */
 public abstract class ModuleFactoryDelegate {
 	private ModuleFactory factory;
-	
+
 	/**
 	 * Delegates must have a public 0-arg constructor.
 	 */
@@ -89,19 +92,22 @@
 
 	/**
 	 * Clears the cache of modules returned by getModules(). Delegates can call this
-	 * method if they know that the results of the last call to getModules() is
-	 * invalid and should be refreshed.
+	 * method if they know that the results of getModules() is invalid and should be
+	 * refreshed.
 	 * 
+	 * @deprecated This method is implementation specific and never called by the
+	 *    framework. It shouldn't be part of the public API, but subclasses are still
+	 *    welcome to provide their own method to clear the cache.
 	 * @see #getModules()
 	 */
 	public void clearModuleCache() {
-		factory.clearModuleCache();
+		// ignore
 	}
 
 	/**
 	 * Creates a module instance with the given static information. This method is used
 	 * by module factory delegates to create module instances.
-	 *  
+	 * 
 	 * @param id the module id
 	 * @param name the module name
 	 * @param type the module type id
@@ -136,4 +142,77 @@
 	 * @return a possibly-empty array of modules {@link IModule}
 	 */
 	public abstract IModule[] getModules();
+
+	/**
+	 * Return all modules created by this factory that are contained within the
+	 * given project. Subclasses should override this method if they do not need
+	 * to filter through the entire project list.
+	 * <p>
+	 * This method is normally called by the web server core framework.
+	 * Clients (other than the delegate) should never call this method.
+	 * </p>
+	 * <p>
+	 * A new array is returned on each call, so clients may store or modify the result.
+	 * </p>
+	 * 
+	 * @param project a project
+	 * @return a possibly-empty array of modules {@link IModule}
+	 * @since 2.0
+	 */
+	public IModule[] getModules(IProject project) {
+		IModule[] modules = getModules();
+		if (project != null && modules != null) {
+			List list = new ArrayList(modules.length);
+			int size = modules.length;
+			for (int i = 0; i < size; i++) {
+				if (project.equals(modules[i].getProject()))
+					list.add(modules[i]);
+			}
+			
+			IModule[] m = new IModule[list.size()];
+			list.toArray(m);
+			return m;
+		}
+		
+		return new IModule[0];
+	}
+
+	/**
+	 * Returns the module created by this factory that has the given id, or
+	 * <code>null</code> if there is no module with the given id. The id must
+	 * not be null.
+	 * <p>
+	 * Subclasses should override this method if they do not need to search
+	 * through the entire project list.
+	 * </p>
+	 * <p>
+	 * This method is normally called by the web server core framework.
+	 * Clients (other than the delegate) should never call this method.
+	 * </p>
+	 * 
+	 * @param id a module id
+	 * @return the module with the given id, or <code>null</code> if no module
+	 *    could be found {@link IModule}
+	 * @since 2.0
+	 */
+	public IModule findModule(String id) {
+		if (id == null)
+			return null;
+		
+		IModule[] modules = getModules();
+		if (id != null && modules != null) {
+			int size = modules.length;
+			for (int i = 0; i < size; i++) {
+				String id2 = modules[i].getId();
+				int index = id2.indexOf(":");
+				if (index >= 0)
+					id2 = id2.substring(index+1);
+				
+				if (id.equals(id2))
+					return modules[i];
+			}
+		}
+		
+		return null;
+	}
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/ProjectModuleFactoryDelegate.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/ProjectModuleFactoryDelegate.java
index a177b84..0402ec9 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/ProjectModuleFactoryDelegate.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/ProjectModuleFactoryDelegate.java
@@ -27,45 +27,73 @@
  * @since 1.0
  */
 public abstract class ProjectModuleFactoryDelegate extends ModuleFactoryDelegate {
-	private static List factories = new ArrayList();
-
-	// list of IModules
-	private List modules;
+	// map of IProject to IModule[]
+	private Map modules = new HashMap();
 
 	/**
 	 * Construct a new ProjectModuleFactoryDelegate.
 	 */
 	public ProjectModuleFactoryDelegate() {
 		super();
-		
-		factories.add(this);
 	}
 
 	/**
-	 * Cache any preexisting modules.
+	 * Cache modules that exist in the given project.
+	 * 
+	 * @param project a project to cache
+	 * @since 2.0
 	 */
-	private final void cacheModules() {
-		if (modules != null)
-			return;
+	private final IModule[] cacheModules(IProject project) {
+		if (project == null || !project.isAccessible())
+			return null;
+		
+		IModule[] m = null;
+		try {
+			m = (IModule[]) modules.get(project);
+			if (m != null)
+				return m;
+		} catch (Exception e) {
+			// ignore
+		}
 		
 		try {
-			clearCache();
-			IProject[] projects2 = getWorkspaceRoot().getProjects();
-			int size = projects2.length;
-			modules = new ArrayList(size);
+			m = createModules(project);
+			if (m != null) {
+				modules.put(project, m);
+				return m;
+			}
+		} catch (Throwable t) {
+			Trace.trace(Trace.SEVERE, "Error creating module", t);
+		}
+		return new IModule[0];
+	}
+
+	/**
+	 * Cache all existing modules.
+	 */
+	private final void cacheModules() {
+		try {
+			IProject[] projects = getWorkspaceRoot().getProjects();
+			int size = projects.length;
 			for (int i = 0; i < size; i++) {
-				//Trace.trace("caching: " + this + " " + projects[i] + " " + isValidModule(projects[i]));
-				if (projects2[i].isAccessible()) {
+				if (projects[i].isAccessible()) {
+					boolean cache = true;
 					try {
-						IModule[] modules2 = createModules(projects2[i]);
-						if (modules2 != null) {
-							int size2 = modules2.length;
-							for (int j = 0; j < size2; j++)
-								if (modules2[j] != null)
-									modules.add(modules2[j]);
+						Object o = modules.get(projects[i]);
+						if (o != null)
+							cache = false;
+					} catch (Exception e) {
+						// ignore
+					}
+					
+					if (cache) {
+						try {
+							IModule[] modules2 = createModules(projects[i]);
+							if (modules2 != null)
+								modules.put(projects[i], modules2);
+						} catch (Throwable t) {
+							Trace.trace(Trace.SEVERE, "Error creating module for " + projects[i].getName(), t);
 						}
-					} catch (Throwable t) {
-						Trace.trace(Trace.SEVERE, "Error creating module", t);
 					}
 				}
 			}
@@ -75,7 +103,7 @@
 	}
 
 	/**
-	 * Return the workspace root.
+	 * Returns the workspace root.
 	 * 
 	 * @return the workspace root
 	 */
@@ -83,16 +111,21 @@
 		return ResourcesPlugin.getWorkspace().getRoot();
 	}
 
-	/**
-	 * Return the modules provided by this factory.
-	 * 
-	 * @return a possibly-empty array of modules
+	/*
+	 * @see ModuleFactoryDelegate#getModules()
 	 */
 	public final IModule[] getModules() {
 		cacheModules();
 		
-		IModule[] modules2 = new IModule[modules.size()];
-		modules.toArray(modules2);
+		List list = new ArrayList();
+		Iterator iter = modules.values().iterator();
+		while (iter.hasNext()) {
+			IModule[] m = (IModule[]) iter.next();
+			list.addAll(Arrays.asList(m));
+		}
+		
+		IModule[] modules2 = new IModule[list.size()];
+		list.toArray(modules2);
 		return modules2;
 	}
 
@@ -102,15 +135,15 @@
 	 * @param project a project
 	 * @param delta a resource delta
 	 */
-	public final static void handleGlobalProjectChange(final IProject project, IResourceDelta delta) {
-		ModuleFactory[] factories2 = ServerPlugin.getModuleFactories();
-		int size = factories2.length;
+	public final static void handleGlobalProjectChange(IProject project, IResourceDelta delta) {
+		ModuleFactory[] factories = ServerPlugin.getModuleFactories();
+		int size = factories.length;
 		for (int i = 0; i < size; i++) {
-			if (factories2[i].delegate != null && factories2[i].delegate instanceof ProjectModuleFactoryDelegate) {
-				ProjectModuleFactoryDelegate pmfd = (ProjectModuleFactoryDelegate) factories2[i].delegate;
+			if (factories[i].delegate != null && factories[i].delegate instanceof ProjectModuleFactoryDelegate) {
+				ProjectModuleFactoryDelegate pmfd = (ProjectModuleFactoryDelegate) factories[i].delegate;
 				if (pmfd.deltaAffectsModules(delta)) {
-					pmfd.modules = null;
-					factories2[i].clearModuleCache();
+					pmfd.clearCache(project);
+					pmfd.clearCache();
 				}
 			}
 		}
@@ -159,13 +192,23 @@
 	}
 
 	/**
-	 * Clear and cached metadata.
+	 * Clear cached metadata.
+	 * 
+	 * @deprecated use {@link #clearCache(IProject)} instead
 	 */
 	protected void clearCache() {
 		// ignore
 	}
 
 	/**
+	 * Clear cached metadata.
+	 * @since 2.0
+	 */
+	protected void clearCache(IProject project) {
+		modules.put(project, null);
+	}
+
+	/**
 	 * Creates the module for a given project.
 	 * 
 	 * @param project a project to create modules for
@@ -202,4 +245,39 @@
 	protected IPath[] getListenerPaths() {
 		return null;
 	}
+
+	/*
+	 * @see ModuleFactoryDelegate#getModules(IProject)
+	 * @since 2.0
+	 */
+	public IModule[] getModules(IProject project) {
+		return cacheModules(project);
+	}
+
+	/*
+	 * @see ModuleFactoryDelegate#findModule(String)
+	 * @since 2.0
+	 */
+	public IModule findModule(String id) {
+		// first assume that the id is a project name
+		IProject project = getWorkspaceRoot().getProject(id);
+		if (project != null) {
+			IModule[] m = cacheModules(project);
+			if (m != null) {
+				int size = m.length;
+				for (int i = 0; i < size; i++) {
+					String id2 = m[i].getId();
+					int index = id2.indexOf(":");
+					if (index >= 0)
+						id2 = id2.substring(index+1);
+					
+					if (id.equals(id2))
+						return m[i];
+				}
+			}
+		}
+		
+		// otherwise default to searching all modules
+		return super.findModule(id);
+	}
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/SocketUtil.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/SocketUtil.java
index 0f043d1..5ab8920 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/SocketUtil.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/SocketUtil.java
@@ -32,12 +32,12 @@
 public class SocketUtil {
 	private static final Random rand = new Random(System.currentTimeMillis());
 
+	protected static final Object lock = new Object();
+
 	private static List localHostCache;
 
 	private static List addressCache;
 
-	protected static Object lock = new Object();
-
 	/**
 	 * Static utility class - cannot create an instance.
 	 */