[319288] Publishers may either cause lock conflicts or duplicate publishes
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Publisher.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Publisher.java
index 9ce8781..f125d75 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Publisher.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Publisher.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2008 IBM Corporation and others.
+ * Copyright (c) 2008, 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,12 +10,14 @@
  *******************************************************************************/
 package org.eclipse.wst.server.core.internal;
 
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.core.runtime.IConfigurationElement;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.wst.server.core.IModule;
+import org.eclipse.wst.server.core.IServerWorkingCopy;
 import org.eclipse.wst.server.core.TaskModel;
 import org.eclipse.wst.server.core.model.PublisherDelegate;
 /**
@@ -24,6 +26,7 @@
 public class Publisher {
 	private IConfigurationElement element;
 	private PublisherDelegate delegate;
+	private boolean modifyModules = false;
 
 	/**
 	 * Publisher constructor comment.
@@ -78,14 +81,101 @@
 		return delegate;
 	}
 
-	public IStatus execute(int kind, IProgressMonitor monitor, IAdaptable info) throws CoreException {
-		try {
-			Trace.trace(Trace.FINEST, "Task.init " + this);
-			return getDelegate().execute(kind, monitor, info);
-		} catch (Exception e) {
-			Trace.trace(Trace.SEVERE, "Error calling delegate " + toString(), e);
-			return new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, "Error in delegate", e); // TODO
+	/**
+	 * Should the original {@link ISchedulingRule} be changed with the new {@link ISchedulingRule}?
+	 * 
+	 * @param originalRule
+	 *            The original {@link ISchedulingRule}
+	 * @param newRule
+	 *            The new {@link ISchedulingRule}
+	 * @return <code>true</code> if the new scheduling rule should be applied; Otherwise <code>false</code>.
+	 */
+	private boolean changeSchedulingRule(ISchedulingRule originalRule, ISchedulingRule newRule) {
+
+		boolean changeRule = false;
+		if ((originalRule == null) && (newRule == null)) {
+			// no need to change rules if they're both null
+			changeRule = false;
 		}
+		else if((originalRule == null) && (newRule != null)) {
+			// there is currently no rule and a new not-null rule wants to be added 
+			changeRule = true;
+		}
+		else if((originalRule != null) && (newRule == null)) {
+			// there is currently a rule and a new null rule wants to be applied
+			changeRule = true;
+		}
+		else if((originalRule != null) && (newRule != null)) {
+			// there is currently a rule and a new not-null rule wants to be applied.
+			changeRule = !originalRule.equals(newRule);
+		}
+		return changeRule;
+	}
+
+	/**
+	 * rebuild the cache for the modules involved with this task.
+	 */
+	private void rebuildModuleCache() {
+
+		// reset the publishing cache for the modules that are part of this task.
+		Server server = (Server) getDelegate().getTaskModel().getObject(TaskModel.TASK_SERVER);
+		if (server != null) {
+			// make sure the right server is used.
+			if(server.isWorkingCopy()) {
+				IServerWorkingCopy workingCopy = (IServerWorkingCopy)server;
+				server = (Server) workingCopy.getOriginal();
+			}
+			final List moduleList = (List)getDelegate().getTaskModel().getObject(TaskModel.TASK_MODULES);
+			if (moduleList != null) {
+				final Iterator moduleIterator = moduleList.iterator();
+				while (moduleIterator.hasNext()) {
+					IModule[] module = (IModule[]) moduleIterator.next();
+					if (module != null) {
+						Trace.trace(Trace.FINEST, "rebuilding cache for module: " + module[module.length - 1]);
+						server.getServerPublishInfo().rebuildCache(module);
+					}
+				}
+			}
+		}
+	}
+	
+	public IStatus execute(int kind, IProgressMonitor monitor, IAdaptable info) throws CoreException {
+
+		Trace.trace(Trace.FINEST, "Task.init " + this);
+		ISchedulingRule delegatePublisherRule = null;
+		final ISchedulingRule originalPublisherRule = Job.getJobManager().currentRule();
+		IStatus resultStatus = null;
+		boolean changeSchedulingRules = false;
+		try {
+			delegatePublisherRule = getDelegate().getRule();
+			changeSchedulingRules = this.changeSchedulingRule(originalPublisherRule, delegatePublisherRule);
+			Trace.trace(Trace.FINEST, "Change the scheduling rule to execute delegate: " + changeSchedulingRules);
+			if (changeSchedulingRules) {
+				Trace.trace(Trace.FINEST, "Ending the current scheduling rule " + originalPublisherRule);
+				Job.getJobManager().endRule(originalPublisherRule);
+				Trace.trace(Trace.FINEST, "Beginning the new scheduling rule: " + delegatePublisherRule);
+				Job.getJobManager().beginRule(delegatePublisherRule, monitor);
+			}
+			resultStatus = getDelegate().execute(kind, monitor, info);
+			this.modifyModules = getDelegate().isModifyModules();
+			Trace.trace(Trace.FINEST, "The publisher delegate stated that it modified modules: " + this.modifyModules);
+			if(this.modifyModules) {
+				this.rebuildModuleCache();
+			}
+		}
+		catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Error calling delegate " + toString(), e);
+			resultStatus = new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, "Error in delegate", e); // TODO
+		}
+		finally {
+			if (changeSchedulingRules) {
+				Trace.trace(Trace.FINEST, "Reseting the scheduling rules... ending: " + delegatePublisherRule);
+				Job.getJobManager().endRule(delegatePublisherRule);
+				Trace.trace(Trace.FINEST, "Reseting the scheduling rules... beginning: " + originalPublisherRule);
+				Job.getJobManager().beginRule(originalPublisherRule, monitor);
+			}
+		}
+		return resultStatus;
 	}
 
 	public void setTaskModel(TaskModel taskModel) {
@@ -97,6 +187,16 @@
 	}
 
 	/**
+	 * Accessor to find out if this publisher modified any modules that are published on the server.
+	 * 
+	 * @return <code>true</code> if the publisher modified the contents of any modules that are published on the server.
+	 */
+	public boolean isModifyModules() {
+
+		return this.modifyModules;
+	}
+	
+	/**
 	 * Return a string representation of this object.
 	 * 
 	 * @return a string
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ServerPublishInfo.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ServerPublishInfo.java
index 9210003..eadd3c8 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ServerPublishInfo.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/ServerPublishInfo.java
@@ -540,4 +540,21 @@
 			}
 		}
 	}
+
+	/**
+	 * Recreates the cache for the specified {@link IModule}.
+	 * 
+	 * @param module The {@link IModule}
+	 */
+	public void rebuildCache(IModule[] module) {
+
+		synchronized (modulePublishInfo) {
+			final String publishInfoKey = this.getKey(module);
+			ModulePublishInfo mpi = modulePublishInfo.get(publishInfoKey);
+			if(mpi != null) {
+				mpi.startCaching(); // clear out the resource list
+				mpi.fill(module); // rebuild the resource list
+			}
+		}
+	}
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/PublisherDelegate.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/PublisherDelegate.java
index e4b7093..564b322 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/PublisherDelegate.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/PublisherDelegate.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2008 IBM Corporation and others.
+ * Copyright (c) 2008, 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,10 +10,9 @@
  *******************************************************************************/
 package org.eclipse.wst.server.core.model;
 
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.wst.server.core.TaskModel;
 /**
  * An operation that will be executed during publishing. 
@@ -91,4 +90,28 @@
 	 * @throws CoreException if there was an error while executing the task
 	 */
 	public abstract IStatus execute(int kind, IProgressMonitor monitor, IAdaptable info) throws CoreException;
+
+	/**
+	 * Returns the scheduling rule that is required for executing the publisher delegate. The default is the current
+	 * rule defined on the publishing job.
+	 * 
+	 * @return A {@link ISchedulingRule} for the job that defines how this publisher can execute in the publishing job.
+	 *         A <code>null</code> value may be returned if the publishing job does not have any rule defined.
+	 * @since 3.2
+	 */
+	public ISchedulingRule getRule() {
+
+		return Job.getJobManager().currentRule();
+	}
+
+	/**
+	 * Accessor to find out if this publisher delegate modified any modules that are published on the server.
+	 * 
+	 * @return <code>true</code> if the publisher modified the contents of any modules that are published on the server.
+	 * @since 3.2
+	 */
+	public boolean isModifyModules() {
+
+		return false;
+	}
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/ServerBehaviourDelegate.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/ServerBehaviourDelegate.java
index ca46929..dad47aa 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/ServerBehaviourDelegate.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/model/ServerBehaviourDelegate.java
@@ -863,6 +863,33 @@
 			multi.addAll(tempMulti);
 	}*/
 
+	private List<Integer> computeDelta(final List<IModule[]> moduleList) {
+
+		final List<Integer> deltaKindList = new ArrayList<Integer>();
+		final Iterator<IModule[]> iterator = moduleList.iterator();
+		while (iterator.hasNext()) {
+			IModule[] module = iterator.next();
+			if (hasBeenPublished(module)) {
+				IModule m = module[module.length - 1];
+				if ((m.getProject() != null && !m.getProject().isAccessible())
+						|| getPublishedResourceDelta(module).length == 0) {
+					deltaKindList.add(new Integer(ServerBehaviourDelegate.NO_CHANGE));
+				}
+				else {
+					deltaKindList.add(new Integer(ServerBehaviourDelegate.CHANGED));
+				}
+			}
+			else {
+				deltaKindList.add(new Integer(ServerBehaviourDelegate.ADDED));
+			}
+		}
+		this.addRemovedModules(moduleList, null);
+		while (deltaKindList.size() < moduleList.size()) {
+			deltaKindList.add(new Integer(ServerBehaviourDelegate.REMOVED));
+		}
+		return deltaKindList;
+	}
+	
 	/**
 	 * Publish to the server.
 	 * 
@@ -878,25 +905,7 @@
 			return new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, Messages.errorPublishNoRuntime, null);
 		
 		final List<IModule[]> moduleList = getAllModules();
-		final List<Integer> deltaKindList = new ArrayList<Integer>();
-		
-		Iterator iterator = moduleList.iterator();
-		while (iterator.hasNext()) {
-			IModule[] module = (IModule[]) iterator.next();
-			if (hasBeenPublished(module)) {
-				IModule m = module[module.length - 1];
-				if ((m.getProject() != null && !m.getProject().isAccessible())
-						|| getPublishedResourceDelta(module).length == 0)
-					deltaKindList.add(new Integer(ServerBehaviourDelegate.NO_CHANGE));
-				else
-					deltaKindList.add(new Integer(ServerBehaviourDelegate.CHANGED));
-			} else
-				deltaKindList.add(new Integer(ServerBehaviourDelegate.ADDED));
-		}
-		
-		addRemovedModules(moduleList, null);
-		while (deltaKindList.size() < moduleList.size())
-			deltaKindList.add(new Integer(ServerBehaviourDelegate.REMOVED));
+		List<Integer> deltaKindList = this.computeDelta(moduleList);
 		
 		PublishOperation[] tasks = getTasks(kind, moduleList, deltaKindList);
 		int size = 2000 + 3500 * moduleList.size() + 500 * tasks.length;
@@ -1144,18 +1153,22 @@
 	}
 
 	/**
-	 * Execute publishers.
+	 * Execute publishers. If a publisher modified the contents of the module (which is determined by the
+	 * {@link PublisherDelegate}) then the delta list is rebuild.
 	 * 
-	 * @param kind the publish kind
-	 * @param modules the list of modules
-	 * @param deltaKinds the list of delta kind that maps to the list of modules
-	 * @param monitor a progress monitor, or <code>null</code> if progress
-	 *    reporting and cancellation are not desired
-	 * @param info the IAdaptable (or <code>null</code>) provided by the
-	 *    caller in order to supply UI information for prompting the
-	 *    user if necessary. When this parameter is not <code>null</code>,
-	 *    it should minimally contain an adapter for the
-	 *    org.eclipse.swt.widgets.Shell.class
+	 * @param kind
+	 *            the publish kind
+	 * @param modules
+	 *            the list of modules. The contents of this {@link List} may change if the publisher modifies code.
+	 * @param deltaKinds
+	 *            the list of delta kind that maps to the list of modules. The contents of this {@link List} may change
+	 *            if the publisher modifies code.
+	 * @param monitor
+	 *            a progress monitor, or <code>null</code> if progress reporting and cancellation are not desired
+	 * @param info
+	 *            the IAdaptable (or <code>null</code>) provided by the caller in order to supply UI information for
+	 *            prompting the user if necessary. When this parameter is not <code>null</code>, it should minimally
+	 *            contain an adapter for the org.eclipse.swt.widgets.Shell.class
 	 * @throws CoreException
 	 * @since 1.1
 	 */
@@ -1176,12 +1189,17 @@
 			taskModel.putObject(TaskModel.TASK_DELTA_KINDS, deltaKinds);
 		}
 		
+		boolean publisherModifiedCode = false;
 		for (int i = 0; i < size; i++) {
 			Publisher pub = publishers[i];
 			monitor.subTask(NLS.bind(Messages.taskPerforming, pub.getName()));
 			try {
 				pub.setTaskModel(taskModel);
 				IStatus pubStatus = pub.execute(kind, ProgressUtil.getSubMonitorFor(monitor, 500), info);
+				if(!publisherModifiedCode) {
+					// If a publisher has modified modules then there is no reason to keep checking other publishers.
+					publisherModifiedCode = pub.isModifyModules();
+				}
 				multi.add(pubStatus);
 			} catch (CoreException ce) {
 				Trace.trace(Trace.SEVERE, "Publisher failed", ce);
@@ -1192,6 +1210,10 @@
 			if (monitor.isCanceled())
 				return multi;
 		}
+		if (publisherModifiedCode) {
+			// re-create the delta list as at least one publisher has changed the contents of the published modules.
+			deltaKinds = this.computeDelta(modules);
+		}
 		return multi;
 	}