This commit was manufactured by cvs2svn to create tag 'R3_0_2'.
diff --git a/features/org.eclipse.jst.server_adapters.feature/feature.xml b/features/org.eclipse.jst.server_adapters.feature/feature.xml
index c24a357..d36572c 100644
--- a/features/org.eclipse.jst.server_adapters.feature/feature.xml
+++ b/features/org.eclipse.jst.server_adapters.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jst.server_adapters.feature"
       label="%featureName"
-      version="3.0.0.qualifier"
+      version="3.0.2.qualifier"
       provider-name="%providerName">
 
    <description>
diff --git a/features/org.eclipse.jst.server_adapters.sdk.feature/feature.xml b/features/org.eclipse.jst.server_adapters.sdk.feature/feature.xml
index e39f7ab..ee61073 100644
--- a/features/org.eclipse.jst.server_adapters.sdk.feature/feature.xml
+++ b/features/org.eclipse.jst.server_adapters.sdk.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jst.server_adapters.sdk.feature"
       label="%featureName"
-      version="3.0.0.qualifier"
+      version="3.0.2.qualifier"
       provider-name="%providerName">
 
    <description>
@@ -28,5 +28,5 @@
    <includes
          id="org.eclipse.jst.server_adapters.feature.source"
          version="0.0.0"/>
- 
+
 </feature>
diff --git a/features/org.eclipse.jst.server_core.feature/feature.xml b/features/org.eclipse.jst.server_core.feature/feature.xml
index e7323d6..a1526ea 100644
--- a/features/org.eclipse.jst.server_core.feature/feature.xml
+++ b/features/org.eclipse.jst.server_core.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jst.server_core.feature"
       label="%featureName"
-      version="3.0.0.qualifier"
+      version="3.0.1.qualifier"
       provider-name="%providerName">
 
    <description>
diff --git a/features/org.eclipse.wst.server_ui.feature/feature.xml b/features/org.eclipse.wst.server_ui.feature/feature.xml
index ce2c6b6..61072a0 100644
--- a/features/org.eclipse.wst.server_ui.feature/feature.xml
+++ b/features/org.eclipse.wst.server_ui.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.wst.server_ui.feature"
       label="%featureName"
-      version="3.0.1.qualifier"
+      version="3.0.2.qualifier"
       provider-name="%providerName">
 
    <description>
diff --git a/plugins/org.eclipse.jst.server.core/META-INF/MANIFEST.MF b/plugins/org.eclipse.jst.server.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..d51ba9e
--- /dev/null
+++ b/plugins/org.eclipse.jst.server.core/META-INF/MANIFEST.MF
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jst.server.core; singleton:=true
+Bundle-Version: 1.1.1.qualifier
+Bundle-Activator: org.eclipse.jst.server.core.internal.JavaServerPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.jst.server.core,
+ org.eclipse.jst.server.core.internal;x-friends:="org.eclipse.jst.server.ui",
+ org.eclipse.jst.server.core.internal.cactus;x-friends:="org.eclipse.jst.server.ui"
+Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.core.resources;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.core.expressions;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.debug.core;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.jdt.core;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.jdt.launching;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.wst.server.core;bundle-version="[1.0.304,2.0.0)",
+ org.eclipse.wst.common.project.facet.core;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.jst.common.project.facet.core;bundle-version="[1.1.0,2.0.0)"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/internal/Trace.java b/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/internal/Trace.java
new file mode 100644
index 0000000..fd470af
--- /dev/null
+++ b/plugins/org.eclipse.jst.server.core/src/org/eclipse/jst/server/core/internal/Trace.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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.jst.server.core.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+/**
+ * Helper class to route trace output.
+ */
+public class Trace {
+	/**
+	 * Config tracing
+	 */
+	public static final byte CONFIG = 0;
+	/**
+	 * Warning tracing
+	 */
+	public static final byte WARNING = 1;
+	/**
+	 * Severe tracing
+	 */
+	public static final byte SEVERE = 2;
+	/**
+	 * Finest tracing
+	 */
+	public static final byte FINEST = 3;
+
+	public static final byte PUBLISHING = 4;
+
+	private static Set<String> logged = new HashSet<String>();
+
+	/**
+	 * Trace constructor comment.
+	 */
+	private Trace() {
+		super();
+	}
+
+	/**
+	 * Trace the given text.
+	 *
+	 * @param level trace level
+	 * @param s String
+	 */
+	public static void trace(byte level, String s) {
+		Trace.trace(level, s, null);
+	}
+
+	/**
+	 * Trace the given message and exception.
+	 *
+	 * @param level trace level
+	 * @param s String
+	 * @param t Throwable
+	 */
+	public static void trace(byte level, String s, Throwable t) {
+		if (s == null)
+			return;
+		
+		if (level == SEVERE) {
+			if (!logged.contains(s)) {
+				JavaServerPlugin.getInstance().getLog().log(new Status(IStatus.ERROR, JavaServerPlugin.PLUGIN_ID, s, t));
+				logged.add(s);
+			}
+		}
+		
+		if (!JavaServerPlugin.getInstance().isDebugging())
+			return;
+		
+		System.out.println(JavaServerPlugin.PLUGIN_ID + " " + s);
+		if (t != null)
+			t.printStackTrace();
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.jst.server.preview.adapter/META-INF/MANIFEST.MF b/plugins/org.eclipse.jst.server.preview.adapter/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5eccac1
--- /dev/null
+++ b/plugins/org.eclipse.jst.server.preview.adapter/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jst.server.preview.adapter;singleton:=true
+Bundle-Version: 1.0.101.qualifier
+Bundle-Activator: org.eclipse.jst.server.preview.adapter.internal.PreviewPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.jst.server.preview.adapter.internal;x-friends:="org.eclipse.jst.server.ui"
+Require-Bundle: org.eclipse.jdt.core;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.jdt.launching;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.wst.server.core;bundle-version="[1.0.103,2.0.0)",
+ org.eclipse.jst.server.core;bundle-version="[1.0.204,2.0.0)",
+ org.eclipse.debug.ui;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.wst.server.ui;bundle-version="[1.0.103,2.0.0)",
+ org.eclipse.wst.common.project.facet.ui;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.jst.common.project.facet.core;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.jdt.debug.ui;bundle-version="[3.2.100,4.0.0)"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/plugins/org.eclipse.jst.server.preview.adapter/src/org/eclipse/jst/server/preview/adapter/internal/core/PreviewServerBehaviour.java b/plugins/org.eclipse.jst.server.preview.adapter/src/org/eclipse/jst/server/preview/adapter/internal/core/PreviewServerBehaviour.java
new file mode 100644
index 0000000..5ef20cb
--- /dev/null
+++ b/plugins/org.eclipse.jst.server.preview.adapter/src/org/eclipse/jst/server/preview/adapter/internal/core/PreviewServerBehaviour.java
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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.jst.server.preview.adapter.internal.core;
+
+import java.io.IOException;
+
+import org.eclipse.core.resources.IContainer;
+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.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.jst.server.core.IWebModule;
+import org.eclipse.jst.server.preview.adapter.internal.IMemento;
+import org.eclipse.jst.server.preview.adapter.internal.Messages;
+import org.eclipse.jst.server.preview.adapter.internal.PreviewPlugin;
+import org.eclipse.jst.server.preview.adapter.internal.ProgressUtil;
+import org.eclipse.jst.server.preview.adapter.internal.Trace;
+import org.eclipse.jst.server.preview.adapter.internal.XMLMemento;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.server.core.IModule;
+import org.eclipse.wst.server.core.IServer;
+import org.eclipse.wst.server.core.ServerPort;
+import org.eclipse.wst.server.core.model.IModuleResource;
+import org.eclipse.wst.server.core.model.IModuleResourceDelta;
+import org.eclipse.wst.server.core.model.ServerBehaviourDelegate;
+import org.eclipse.wst.server.core.util.IStaticWeb;
+import org.eclipse.wst.server.core.util.ProjectModule;
+import org.eclipse.wst.server.core.util.PublishUtil;
+import org.eclipse.wst.server.core.util.SocketUtil;
+/**
+ * Generic Http server.
+ */
+public class PreviewServerBehaviour extends ServerBehaviourDelegate {
+	// the thread used to ping the server to check for startup
+	protected transient PingThread ping = null;
+	protected transient IDebugEventSetListener processListener;
+
+	/**
+	 * PreviewServer.
+	 */
+	public PreviewServerBehaviour() {
+		super();
+	}
+
+	public void initialize(IProgressMonitor monitor) {
+		// do nothing
+	}
+
+	public PreviewRuntime getPreviewRuntime() {
+		if (getServer().getRuntime() == null)
+			return null;
+
+		return (PreviewRuntime) getServer().getRuntime().loadAdapter(PreviewRuntime.class, null);
+	}
+
+	public PreviewServer getPreviewServer() {
+		return (PreviewServer) getServer().getAdapter(PreviewServer.class);
+	}
+
+	/**
+	 * Returns the runtime base path for relative paths in the server
+	 * configuration.
+	 * 
+	 * @return the base path
+	 */
+	public IPath getRuntimeBaseDirectory() {
+		return getServer().getRuntime().getLocation();
+	}
+
+	/**
+	 * Setup for starting the server.
+	 * 
+	 * @param launch ILaunch
+	 * @param launchMode String
+	 * @param monitor IProgressMonitor
+	 * @throws CoreException if anything goes wrong
+	 */
+	protected void setupLaunch(ILaunch launch, String launchMode, IProgressMonitor monitor) throws CoreException {
+		// check that ports are free
+		ServerPort[] ports = getPreviewServer().getServerPorts();
+		int port = ports[0].getPort();
+		
+		if (SocketUtil.isPortInUse(port, 5))
+			throw new CoreException(new Status(IStatus.ERROR, PreviewPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorPortInUse, new String[] {port + "", getServer().getName()}), null));
+		
+		// generate preview config file
+		XMLMemento memento = XMLMemento.createWriteRoot("server");
+		memento.putInteger("port", port);
+		
+		IModule[] modules = getServer().getModules();
+		for (IModule module : modules) {
+			IMemento mod = memento.createChild("module");
+			mod.putString("name", module.getName());
+			String type = module.getModuleType().getId();
+			if ("wst.web".equals(type)) {
+				IStaticWeb staticWeb = (IStaticWeb) module.loadAdapter(IStaticWeb.class, null);
+				if (staticWeb != null)
+					mod.putString("context", staticWeb.getContextRoot());
+				mod.putString("type", "static");
+			} else if ("jst.web".equals(type)) {
+				IWebModule webModule = (IWebModule) module.loadAdapter(IWebModule.class, null);
+				if (webModule != null)
+					mod.putString("context", webModule.getContextRoot());
+				mod.putString("type", "j2ee");
+			}
+			mod.putString("path", getModulePublishDirectory(module).toPortableString());
+		}
+		try {
+			memento.saveToFile(getTempDirectory().append("preview.xml").toOSString());
+		} catch (IOException e) {
+			Trace.trace(Trace.SEVERE, "Could not write preview config", e);
+			throw new CoreException(new Status(IStatus.ERROR, PreviewPlugin.PLUGIN_ID, 0, "Could not write preview configuration", null));
+		}
+		
+		setServerRestartState(false);
+		setServerState(IServer.STATE_STARTING);
+		setMode(launchMode);
+		
+		// ping server to check for startup
+		try {
+			String url = "http://localhost";
+			if (port != 80)
+				url += ":" + port;
+			ping = new PingThread(getServer(), url, this);
+		} catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Can't ping for Tomcat startup.");
+		}
+	}
+
+	protected void addProcessListener(final IProcess newProcess) {
+		if (processListener != null || newProcess == null)
+			return;
+		
+		processListener = new IDebugEventSetListener() {
+			public void handleDebugEvents(DebugEvent[] events) {
+				if (events != null) {
+					for (DebugEvent event : events) {
+						if (newProcess != null && newProcess.equals(event.getSource()) && event.getKind() == DebugEvent.TERMINATE) {
+							stop(true);
+						}
+					}
+				}
+			}
+		};
+		DebugPlugin.getDefault().addDebugEventListener(processListener);
+	}
+
+	protected void setServerStarted() {
+		setServerState(IServer.STATE_STARTED);
+	}
+
+	protected void publishServer(int kind, IProgressMonitor monitor) throws CoreException {
+		monitor = ProgressUtil.getMonitorFor(monitor);
+		monitor.done();
+
+		setServerPublishState(IServer.PUBLISH_STATE_NONE);
+	}
+
+	/*
+	 * Publishes the given module to the server.
+	 */
+	protected void publishModule(int kind, int deltaKind, IModule[] moduleTree, IProgressMonitor monitor) throws CoreException {
+		IModule module = moduleTree[moduleTree.length - 1];
+		if (isSingleRootStructure(module))
+			return;
+		
+		IPath to = getModulePublishDirectory(module);
+		
+		if (kind == IServer.PUBLISH_CLEAN || deltaKind == ServerBehaviourDelegate.REMOVED) {
+			IStatus[] status = PublishUtil.deleteDirectory(to.toFile(), monitor);
+			throwException(status);
+		}
+		
+		IModuleResource[] res = getResources(moduleTree);
+		IStatus[] status = PublishUtil.publishSmart(res, to, monitor);
+		throwException(status);
+		
+		setModulePublishState(moduleTree, IServer.PUBLISH_STATE_NONE);
+	}
+
+	/**
+	 * Utility method to throw a CoreException based on the contents of a list of
+	 * error and warning status.
+	 * 
+	 * @param status a List containing error and warning IStatus
+	 * @throws CoreException
+	 */
+	private static void throwException(IStatus[] status) throws CoreException {
+		if (status == null || status.length == 0)
+			return;
+		
+		if (status.length == 1)
+			throw new CoreException(status[0]);
+		
+		String message = Messages.errorPublish;
+		MultiStatus status2 = new MultiStatus(PreviewPlugin.PLUGIN_ID, 0, status, message, null);
+		throw new CoreException(status2);
+	}
+
+	public void restart(String launchMode) throws CoreException {
+		setServerState(IServer.STATE_STOPPED);
+		setServerState(IServer.STATE_STARTED);
+	}
+
+	/**
+	 * Cleanly shuts down and terminates the server.
+	 * 
+	 * @param force <code>true</code> to kill the server
+	 */
+	public void stop(boolean force) {
+		int state = getServer().getServerState();
+		if (state == IServer.STATE_STOPPED)
+			return;
+		
+		setServerState(IServer.STATE_STOPPING);
+		
+		if (ping != null) {
+			ping.stop();
+			ping = null;
+		}
+		
+		if (processListener != null) {
+			DebugPlugin.getDefault().removeDebugEventListener(processListener);
+			processListener = null;
+		}
+		
+		try {
+			Trace.trace(Trace.FINEST, "Killing the process");
+			ILaunch launch = getServer().getLaunch();
+			if (launch != null)
+				launch.terminate();
+		} catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Error killing the process", e);
+		}
+		
+		setServerState(IServer.STATE_STOPPED);
+	}
+
+	protected IPath getTempDirectory() {
+		return super.getTempDirectory();
+	}
+
+	/**
+	 * Returns <code>true</code> if the module in the workspace has a single
+	 * root structure - i.e. matches the spec disk layout - and <code>false</code>
+	 * otherwise.
+	 * 
+	 * @return <code>true</code> if the module in the workspace has a single
+	 *    root structure - i.e. matches the spec disk layout - and
+	 *    <code>false</code> otherwise
+	 */
+	protected boolean isSingleRootStructure(IModule module) {
+		ProjectModule pm = (ProjectModule) module.loadAdapter(ProjectModule.class, null);
+		if (pm == null)
+			return false;
+		
+		return pm.isSingleRootStructure();
+	}
+
+	/**
+	 * Returns the module's publish path.
+	 * 
+	 * @param module a module
+	 * @return the publish directory for the module
+	 */
+	protected IPath getModulePublishDirectory(IModule module) {
+		if (isSingleRootStructure(module)) {
+			String type = module.getModuleType().getId();
+			if ("wst.web".equals(type)) {
+				IStaticWeb webModule = (IStaticWeb) module.loadAdapter(IStaticWeb.class, null);
+				if (webModule != null) {
+					//IContainer[] moduleFolder = webModule.getResourceFolders();
+					//if (moduleFolder != null && moduleFolder.length > 0)
+					//	return moduleFolder[0].getLocation();							
+				}
+			} else if ("jst.web".equals(type)) {
+				IWebModule webModule = (IWebModule) module.loadAdapter(IWebModule.class, null);
+				if (webModule != null) {
+					IContainer[] moduleFolder = webModule.getResourceFolders();
+					if (moduleFolder != null && moduleFolder.length > 0)
+						return moduleFolder[0].getLocation();							
+				}
+			}
+		}
+		
+		return getTempDirectory().append(module.getName());
+	}
+
+	/**
+	 * Return a string representation of this object.
+	 * 
+	 * @return a string
+	 */
+	public String toString() {
+		return "PreviewServer";
+	}
+
+	protected IModuleResourceDelta[] getPublishedResourceDelta(IModule[] module) {
+		return super.getPublishedResourceDelta(module);
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.core/META-INF/MANIFEST.MF b/plugins/org.eclipse.wst.server.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..48472e4
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.core/META-INF/MANIFEST.MF
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.wst.server.core; singleton:=true
+Bundle-Version: 1.1.1.qualifier
+Bundle-Activator: org.eclipse.wst.server.core.internal.ServerPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.wst.server.core,
+ org.eclipse.wst.server.core.internal;x-friends:="org.eclipse.wst.server.ui",
+ org.eclipse.wst.server.core.internal.facets;x-friends:="org.eclipse.wst.server.ui",
+ org.eclipse.wst.server.core.internal.provisional;x-internal:=true,
+ org.eclipse.wst.server.core.model,
+ org.eclipse.wst.server.core.util
+Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.core.resources;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.core.expressions;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.debug.core;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.update.core;bundle-version="[3.2.0,4.0.0)";resolution:=optional,
+ org.eclipse.wst.common.project.facet.core;bundle-version="[1.1.0,2.0.0)";resolution:=optional
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Trace.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Trace.java
new file mode 100644
index 0000000..ea2fd7f
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/internal/Trace.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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.server.core.internal;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+/**
+ * Helper class to route trace output.
+ */
+public class Trace {
+	public static final byte CONFIG = 0;
+	public static final byte INFO = 1;
+	public static final byte WARNING = 2;
+	public static final byte SEVERE = 3;
+	public static final byte FINER = 4;
+	public static final byte FINEST = 5;
+	
+	public static final byte RESOURCES = 6;
+	public static final byte EXTENSION_POINT = 7;
+	public static final byte LISTENERS = 8;
+	public static final byte RUNTIME_TARGET = 9;
+	public static final byte PERFORMANCE = 10;
+	public static final byte PUBLISHING = 11;
+
+	private static final String[] levelNames = new String[] {
+		"CONFIG   ", "INFO     ", "WARNING  ", "SEVERE   ", "FINER    ", "FINEST   ",
+		"RESOURCES", "EXTENSION", "LISTENERS", "TARGET   ", "PERF     ", "PUBLISH  "};
+
+	private static final SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy HH:mm.ss.SSS");
+
+	private static Set<String> logged = new HashSet<String>();
+
+	/**
+	 * Trace constructor comment.
+	 */
+	private Trace() {
+		super();
+	}
+
+	/**
+	 * Trace the given text.
+	 *
+	 * @param level a trace level
+	 * @param s a message
+	 */
+	public static void trace(int level, String s) {
+		trace(level, s, null);
+	}
+
+	/**
+	 * Trace the given message and exception.
+	 *
+	 * @param level a trace level
+	 * @param s a message
+	 * @param t a throwable
+	 */
+	public static void trace(int level, String s, Throwable t) {
+		if (s == null)
+			return;
+		
+		if (level == SEVERE) {
+			if (!logged.contains(s)) {
+				ServerPlugin.log(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, s, t));
+				logged.add(s);
+			}
+		}
+		
+		if (!ServerPlugin.getInstance().isDebugging())
+			return;
+		
+		StringBuffer sb = new StringBuffer(ServerPlugin.PLUGIN_ID);
+		sb.append(" ");
+		sb.append(levelNames[level]);
+		sb.append(" ");
+		sb.append(sdf.format(new Date()));
+		sb.append(" ");
+		sb.append(s);
+		//Platform.getDebugOption(ServerCore.PLUGIN_ID + "/" + "resources");
+
+		System.out.println(sb.toString());
+		if (t != null)
+			t.printStackTrace();
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/PublishHelper.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/PublishHelper.java
new file mode 100644
index 0000000..3b8d979
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/PublishHelper.java
@@ -0,0 +1,806 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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.server.core.util;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.*;
+import org.eclipse.wst.server.core.internal.Messages;
+import org.eclipse.wst.server.core.internal.ProgressUtil;
+import org.eclipse.wst.server.core.internal.ServerPlugin;
+import org.eclipse.wst.server.core.internal.Trace;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.server.core.model.IModuleFile;
+import org.eclipse.wst.server.core.model.IModuleFolder;
+import org.eclipse.wst.server.core.model.IModuleResource;
+import org.eclipse.wst.server.core.model.IModuleResourceDelta;
+/**
+ * Utility class with an assortment of useful publishing file methods.
+ *
+ * @since 3.0
+ */
+public final class PublishHelper {
+	// size of the buffer
+	private static final int BUFFER = 65536;
+
+	// the buffer
+	private static byte[] buf = new byte[BUFFER];
+
+	private static final IStatus[] EMPTY_STATUS = new IStatus[0];
+
+	private static final File defaultTempDir = ServerPlugin.getInstance().getStateLocation().toFile();
+
+	private static final String TEMPFILE_PREFIX = "tmp";
+
+	private File tempDir;
+
+	/**
+	 * Create a new PublishHelper.
+	 * 
+	 * @param tempDirectory a temporary directory to use during publishing, or <code>null</code>
+	 *    to use the default. If it does not exist, the folder will be created
+	 */
+	public PublishHelper(File tempDirectory) {
+		this.tempDir = tempDirectory;
+		if (tempDir == null)
+			tempDir = defaultTempDir;
+		else if (!tempDir.exists())
+			tempDir.mkdirs();
+	}
+
+	/**
+	 * Copy a file from a to b. Closes the input stream after use.
+	 * 
+	 * @param in an input stream
+	 * @param to a path to copy to. the directory must already exist
+	 * @param ts timestamp
+	 * @throws CoreException if anything goes wrong
+	 */
+	private void copyFile(InputStream in, IPath to, long ts, IModuleFile mf) throws CoreException {
+		OutputStream out = null;
+		
+		File tempFile = null;
+		try {
+			File file = to.toFile();
+			tempFile = File.createTempFile(TEMPFILE_PREFIX, "." + to.getFileExtension(), tempDir);
+			
+			out = new FileOutputStream(tempFile);
+			
+			int avail = in.read(buf);
+			while (avail > 0) {
+				out.write(buf, 0, avail);
+				avail = in.read(buf);
+			}
+			
+			out.close();
+			out = null;
+			
+			moveTempFile(tempFile, file);
+			
+			if (ts != IResource.NULL_STAMP && ts != 0)
+				file.setLastModified(ts);
+		} catch (CoreException e) {
+			throw e;
+		} catch (Exception e) {
+			IPath path = mf.getModuleRelativePath().append(mf.getName());
+			Trace.trace(Trace.SEVERE, "Error copying file: " + path.toOSString() + " to " + to.toOSString(), e);
+			throw new CoreException(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorCopyingFile, path.toOSString(), e.getLocalizedMessage()), null));
+		} finally {
+			if (tempFile != null && tempFile.exists())
+				tempFile.deleteOnExit();
+			try {
+				if (in != null)
+					in.close();
+			} catch (Exception ex) {
+				// ignore
+			}
+			try {
+				if (out != null)
+					out.close();
+			} catch (Exception ex) {
+				// ignore
+			}
+		}
+	}
+
+	/**
+	 * Utility method to recursively delete a directory.
+	 *
+	 * @param dir a directory
+	 * @param monitor a progress monitor, or <code>null</code> if progress
+	 *    reporting and cancellation are not desired
+	 * @return a possibly-empty array of error and warning status
+	 */
+	public static IStatus[] deleteDirectory(File dir, IProgressMonitor monitor) {
+		if (!dir.exists() || !dir.isDirectory())
+			return new IStatus[] { new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorNotADirectory, dir.getAbsolutePath()), null) };
+		
+		List<IStatus> status = new ArrayList<IStatus>(2);
+		
+		try {
+			File[] files = dir.listFiles();
+			int size = files.length;
+			monitor = ProgressUtil.getMonitorFor(monitor);
+			monitor.beginTask(NLS.bind(Messages.deletingTask, new String[] { dir.getAbsolutePath() }), size * 10);
+			
+			// cycle through files
+			boolean deleteCurrent = true;
+			for (int i = 0; i < size; i++) {
+				File current = files[i];
+				if (current.isFile()) {
+					if (!current.delete()) {
+						status.add(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorDeleting, files[i].getAbsolutePath()), null));
+						deleteCurrent = false;
+					}
+					monitor.worked(10);
+				} else if (current.isDirectory()) {
+					monitor.subTask(NLS.bind(Messages.deletingTask, new String[] {current.getAbsolutePath()}));
+					IStatus[] stat = deleteDirectory(current, ProgressUtil.getSubMonitorFor(monitor, 10));
+					if (stat != null && stat.length > 0) {
+						deleteCurrent = false;
+						addArrayToList(status, stat);
+					}
+				}
+			}
+			if (deleteCurrent && !dir.delete())
+				status.add(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorDeleting, dir.getAbsolutePath()), null));
+			monitor.done();
+		} catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Error deleting directory " + dir.getAbsolutePath(), e);
+			status.add(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, e.getLocalizedMessage(), null));
+		}
+		
+		IStatus[] stat = new IStatus[status.size()];
+		status.toArray(stat);
+		return stat;
+	}
+
+	/**
+	 * Smart copy the given module resources to the given path.
+	 * 
+	 * @param resources an array of module resources
+	 * @param path an external path to copy to
+	 * @param monitor a progress monitor, or <code>null</code> if progress
+	 *    reporting and cancellation are not desired
+	 * @return a possibly-empty array of error and warning status 
+	 */
+	public IStatus[] publishSmart(IModuleResource[] resources, IPath path, IProgressMonitor monitor) {
+		return publishSmart(resources, path, null, monitor);
+	}
+
+	/**
+	 * Smart copy the given module resources to the given path.
+	 * 
+	 * @param resources an array of module resources
+	 * @param path an external path to copy to
+	 * @param ignore an array of paths relative to path to ignore, i.e. not delete or copy over
+	 * @param monitor a progress monitor, or <code>null</code> if progress
+	 *    reporting and cancellation are not desired
+	 * @return a possibly-empty array of error and warning status 
+	 */
+	public IStatus[] publishSmart(IModuleResource[] resources, IPath path, IPath[] ignore, IProgressMonitor monitor) {
+		if (resources == null)
+			return EMPTY_STATUS;
+		
+		monitor = ProgressUtil.getMonitorFor(monitor);
+		
+		List<IStatus> status = new ArrayList<IStatus>(2);
+		File toDir = path.toFile();
+		int fromSize = resources.length;
+		String[] fromFileNames = new String[fromSize];
+		for (int i = 0; i < fromSize; i++)
+			fromFileNames[i] = resources[i].getName();
+		List<String> ignoreFileNames = new ArrayList<String>();
+		if (ignore != null) {
+			for (int i = 0; i < ignore.length; i++) {
+				if (ignore[i].segmentCount() == 1) {
+					ignoreFileNames.add(ignore[i].toOSString());
+				}
+			}
+		}
+		
+		//	cache files and file names for performance
+		File[] toFiles = null;
+		String[] toFileNames = null;
+		
+		boolean foundExistingDir = false;
+		if (toDir.exists()) {
+			if (toDir.isDirectory()) {
+				foundExistingDir = true;
+				toFiles = toDir.listFiles();
+				int toSize = toFiles.length;
+				toFileNames = new String[toSize];
+				
+				// check if this exact file exists in the new directory
+				for (int i = 0; i < toSize; i++) {
+					toFileNames[i] = toFiles[i].getName();
+					boolean isDir = toFiles[i].isDirectory();
+					boolean found = false;
+					for (int j = 0; j < fromSize; j++) {
+						if (toFileNames[i].equals(fromFileNames[j]) && isDir == resources[j] instanceof IModuleFolder) {
+							found = true;
+							break;
+						}
+					}
+					
+					// delete file if it can't be found or isn't the correct type
+					if (!found) {
+						boolean delete = true;
+						// if should be preserved, don't delete and don't try to copy
+						for (String preserveFileName : ignoreFileNames) {
+							if (toFileNames[i].equals(preserveFileName)) {
+								delete = false;
+								break;
+							}
+						}
+						if (delete) {
+							if (isDir) {
+								IStatus[] stat = deleteDirectory(toFiles[i], null);
+								addArrayToList(status, stat);
+							} else {
+								if (!toFiles[i].delete())
+									status.add(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorDeleting, toFiles[i].getAbsolutePath()), null));
+							}
+						}
+						toFiles[i] = null;
+						toFileNames[i] = null;
+					}
+				}
+			} else { //if (toDir.isFile())
+				if (!toDir.delete()) {
+					status.add(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorDeleting, toDir.getAbsolutePath()), null));
+					IStatus[] stat = new IStatus[status.size()];
+					status.toArray(stat);
+					return stat;
+				}
+			}
+		}
+		if (!foundExistingDir && !toDir.mkdirs()) {
+			status.add(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorMkdir, toDir.getAbsolutePath()), null));
+			IStatus[] stat = new IStatus[status.size()];
+			status.toArray(stat);
+			return stat;
+		}
+		
+		if (monitor.isCanceled())
+			return new IStatus[] { Status.CANCEL_STATUS };
+		
+		monitor.worked(50);
+		
+		// cycle through files and only copy when it doesn't exist
+		// or is newer
+		if (toFiles == null) {
+			toFiles = toDir.listFiles();
+			if (toFiles == null)
+				toFiles = new File[0];
+		}
+		int toSize = toFiles.length;
+		
+		int dw = 0;
+		if (toSize > 0)
+			dw = 500 / toSize;
+		
+		// cache file names and last modified dates for performance
+		if (toFileNames == null)
+			toFileNames = new String[toSize];
+		long[] toFileMod = new long[toSize];
+		for (int i = 0; i < toSize; i++) {
+			if (toFiles[i] != null) {
+				if (toFileNames[i] != null)
+					toFileNames[i] = toFiles[i].getName();
+				toFileMod[i] = toFiles[i].lastModified();
+			}
+		}
+		
+		for (int i = 0; i < fromSize; i++) {
+			IModuleResource current = resources[i];
+			String name = fromFileNames[i];
+			boolean currentIsDir = current instanceof IModuleFolder;
+			
+			if (!currentIsDir) {
+				// check if this is a new or newer file
+				boolean copy = true;
+				IModuleFile mf = (IModuleFile) current;
+				
+				long mod = -1;
+				IFile file = (IFile) mf.getAdapter(IFile.class);
+				if (file != null) {
+					mod = file.getLocalTimeStamp();
+				} else {
+					File file2 = (File) mf.getAdapter(File.class);
+					mod = file2.lastModified();
+				}
+				
+				for (int j = 0; j < toSize; j++) {
+					if (name.equals(toFileNames[j]) && mod == toFileMod[j]) {
+						copy = false;
+						break;
+					}
+				}
+				
+				if (copy) {
+					try {
+						copyFile(mf, path.append(name));
+					} catch (CoreException ce) {
+						status.add(ce.getStatus());
+					}
+				}
+				monitor.worked(dw);
+			} else { //if (currentIsDir) {
+				IModuleFolder folder = (IModuleFolder) current;
+				IModuleResource[] children = folder.members();
+
+				// build array of ignored Paths that apply to this folder
+				IPath[] ignoreChildren = null;
+				if (ignore != null) {
+					List<IPath> ignoreChildPaths = new ArrayList<IPath>();
+					for (int j = 0; j < ignore.length; j++) {
+						IPath preservePath = ignore[j];
+						if (preservePath.segment(0).equals(name)) {
+							ignoreChildPaths.add(preservePath.removeFirstSegments(1));
+						}
+					}
+					if (ignoreChildPaths.size() > 0)
+						ignoreChildren = ignoreChildPaths.toArray(new Path[ignoreChildPaths.size()]);
+				}
+				monitor.subTask(NLS.bind(Messages.copyingTask, new String[] {name, name}));
+				IStatus[] stat = publishSmart(children, path.append(name), ignoreChildren, ProgressUtil.getSubMonitorFor(monitor, dw));
+				addArrayToList(status, stat);
+			}
+		}
+		if (monitor.isCanceled())
+			return new IStatus[] { Status.CANCEL_STATUS };
+		
+		monitor.worked(500 - dw * toSize);
+		monitor.done();
+		
+		IStatus[] stat = new IStatus[status.size()];
+		status.toArray(stat);
+		return stat;
+	}
+
+	/**
+	 * Handle a delta publish.
+	 * 
+	 * @param delta a module resource delta
+	 * @param path the path to publish to
+	 * @param monitor a progress monitor, or <code>null</code> if progress
+	 *    reporting and cancellation are not desired
+	 * @return a possibly-empty array of error and warning status
+	 */
+	public IStatus[] publishDelta(IModuleResourceDelta[] delta, IPath path, IProgressMonitor monitor) {
+		if (delta == null)
+			return EMPTY_STATUS;
+		
+		monitor = ProgressUtil.getMonitorFor(monitor);
+		
+		List<IStatus> status = new ArrayList<IStatus>(2);
+		int size2 = delta.length;
+		for (int i = 0; i < size2; i++) {
+			IStatus[] stat = publishDelta(delta[i], path, monitor);
+			addArrayToList(status, stat);
+		}
+		
+		IStatus[] stat = new IStatus[status.size()];
+		status.toArray(stat);
+		return stat;
+	}
+
+	/**
+	 * Handle a delta publish.
+	 * 
+	 * @param delta a module resource delta
+	 * @param path the path to publish to
+	 * @param monitor a progress monitor, or <code>null</code> if progress
+	 *    reporting and cancellation are not desired
+	 * @return a possibly-empty array of error and warning status
+	 */
+	public IStatus[] publishDelta(IModuleResourceDelta delta, IPath path, IProgressMonitor monitor) {
+		List<IStatus> status = new ArrayList<IStatus>(2);
+		
+		IModuleResource resource = delta.getModuleResource();
+		int kind2 = delta.getKind();
+		
+		if (resource instanceof IModuleFile) {
+			IModuleFile file = (IModuleFile) resource;
+			try {
+				if (kind2 == IModuleResourceDelta.REMOVED)
+					deleteFile(path, file);
+				else {
+					IPath path2 = path.append(file.getModuleRelativePath()).append(file.getName());
+					File f = path2.toFile().getParentFile();
+					if (!f.exists())
+						f.mkdirs();
+					
+					copyFile(file, path2);
+				}
+			} catch (CoreException ce) {
+				status.add(ce.getStatus());
+			}
+			IStatus[] stat = new IStatus[status.size()];
+			status.toArray(stat);
+			return stat;
+		}
+		
+		if (kind2 == IModuleResourceDelta.ADDED) {
+			IPath path2 = path.append(resource.getModuleRelativePath()).append(resource.getName());
+			File file = path2.toFile();
+			if (!file.exists() && !file.mkdirs()) {
+				status.add(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorMkdir, path2), null));
+				IStatus[] stat = new IStatus[status.size()];
+				status.toArray(stat);
+				return stat;
+			}
+		}
+		
+		IModuleResourceDelta[] childDeltas = delta.getAffectedChildren();
+		int size = childDeltas.length;
+		for (int i = 0; i < size; i++) {
+			IStatus[] stat = publishDelta(childDeltas[i], path, monitor);
+			addArrayToList(status, stat);
+		}
+		
+		if (kind2 == IModuleResourceDelta.REMOVED) {
+			IPath path2 = path.append(resource.getModuleRelativePath()).append(resource.getName());
+			File file = path2.toFile();
+			if (file.exists() && !file.delete()) {
+				status.add(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorDeleting, path2), null));
+			}
+		}
+		
+		IStatus[] stat = new IStatus[status.size()];
+		status.toArray(stat);
+		return stat;
+	}
+
+	private static void deleteFile(IPath path, IModuleFile file) throws CoreException {
+		Trace.trace(Trace.PUBLISHING, "Deleting: " + file.getName() + " from " + path.toString());
+		IPath path2 = path.append(file.getModuleRelativePath()).append(file.getName());
+		if (path2.toFile().exists() && !path2.toFile().delete())
+			throw new CoreException(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorDeleting, path2), null));
+	}
+
+	private void copyFile(IModuleFile mf, IPath path) throws CoreException {
+		Trace.trace(Trace.PUBLISHING, "Copying: " + mf.getName() + " to " + path.toString());
+		
+		IFile file = (IFile) mf.getAdapter(IFile.class);
+		if (file != null)
+			copyFile(file.getContents(), path, file.getLocalTimeStamp(), mf);
+		else {
+			File file2 = (File) mf.getAdapter(File.class);
+			InputStream in = null;
+			try {
+				in = new FileInputStream(file2);
+			} catch (IOException e) {
+				throw new CoreException(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorReading, file2.getAbsolutePath()), e));
+			}
+			copyFile(in, path, file2.lastModified(), mf);
+		}
+	}
+
+	/**
+	 * Publish the given module resources to the given path.
+	 * 
+	 * @param resources an array of module resources
+	 * @param path a path to publish to
+	 * @param monitor a progress monitor, or <code>null</code> if progress
+	 *    reporting and cancellation are not desired
+	 * @return a possibly-empty array of error and warning status
+	 */
+	public IStatus[] publishFull(IModuleResource[] resources, IPath path, IProgressMonitor monitor) {
+		if (resources == null)
+			return EMPTY_STATUS;
+		
+		monitor = ProgressUtil.getMonitorFor(monitor);
+		
+		List<IStatus> status = new ArrayList<IStatus>(2);
+		int size = resources.length;
+		for (int i = 0; i < size; i++) {
+			IStatus[] stat = copy(resources[i], path, monitor);
+			addArrayToList(status, stat);
+		}
+		
+		IStatus[] stat = new IStatus[status.size()];
+		status.toArray(stat);
+		return stat;
+	}
+
+	private IStatus[] copy(IModuleResource resource, IPath path, IProgressMonitor monitor) {
+		String name = resource.getName();
+		Trace.trace(Trace.PUBLISHING, "Copying: " + name + " to " + path.toString());
+		List<IStatus> status = new ArrayList<IStatus>(2);
+		if (resource instanceof IModuleFolder) {
+			IModuleFolder folder = (IModuleFolder) resource;
+			IStatus[] stat = publishFull(folder.members(), path, monitor);
+			addArrayToList(status, stat);
+		} else {
+			IModuleFile mf = (IModuleFile) resource;
+			path = path.append(mf.getModuleRelativePath()).append(name);
+			File f = path.toFile().getParentFile();
+			if (!f.exists())
+				f.mkdirs();
+			try {
+				copyFile(mf, path);
+			} catch (CoreException ce) {
+				status.add(ce.getStatus());
+			}
+		}
+		IStatus[] stat = new IStatus[status.size()];
+		status.toArray(stat);
+		return stat;
+	}
+
+	/**
+	 * Creates a new zip file containing the given module resources. Deletes the existing file
+	 * (and doesn't create a new one) if resources is null or empty.
+	 * 
+	 * @param resources an array of module resources
+	 * @param path the path where the zip file should be created 
+	 * @param monitor a progress monitor, or <code>null</code> if progress
+	 *    reporting and cancellation are not desired
+	 * @return a possibly-empty array of error and warning status
+	 */
+	public IStatus[] publishZip(IModuleResource[] resources, IPath path, IProgressMonitor monitor) {
+		if (resources == null || resources.length == 0) {
+			// should also check if resources consists of all empty directories
+			File file = path.toFile();
+			if (file.exists())
+				file.delete();
+			return EMPTY_STATUS;
+		}
+		
+		monitor = ProgressUtil.getMonitorFor(monitor);
+		
+		File tempFile = null;
+		try {
+			File file = path.toFile();
+			tempFile = File.createTempFile(TEMPFILE_PREFIX, "." + path.getFileExtension(), tempDir);
+			
+			BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(tempFile));
+			ZipOutputStream zout = new ZipOutputStream(bout);
+			addZipEntries(zout, resources);
+			zout.close();
+			
+			moveTempFile(tempFile, file);
+		} catch (CoreException e) {
+			return new IStatus[] { e.getStatus() };
+		} catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Error zipping", e);
+			return new Status[] { new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorCreatingZipFile, path.lastSegment(), e.getLocalizedMessage()), e) };
+		} finally {
+			if (tempFile != null && tempFile.exists())
+				tempFile.deleteOnExit();
+		}
+		return EMPTY_STATUS;
+	}
+
+	private static void addZipEntries(ZipOutputStream zout, IModuleResource[] resources) throws Exception {
+		if (resources == null)
+			return;
+		
+		int size = resources.length;
+		for (int i = 0; i < size; i++) {
+			if (resources[i] instanceof IModuleFolder) {
+				IModuleFolder mf = (IModuleFolder) resources[i];
+				IModuleResource[] res = mf.members();
+				
+				IPath path = mf.getModuleRelativePath().append(mf.getName());
+				String entryPath = path.toPortableString();
+				if (!entryPath.endsWith("/"))
+					entryPath += '/';
+				
+				ZipEntry ze = new ZipEntry(entryPath);
+				
+				long ts = 0;
+				IContainer folder = (IContainer) mf.getAdapter(IContainer.class);
+				if (folder != null)
+					ts = folder.getLocalTimeStamp();
+				
+				if (ts != IResource.NULL_STAMP && ts != 0)
+					ze.setTime(ts);
+				
+				zout.putNextEntry(ze);
+				zout.closeEntry();
+				
+				addZipEntries(zout, res);
+				continue;
+			}
+			
+			IModuleFile mf = (IModuleFile) resources[i];
+			IPath path = mf.getModuleRelativePath().append(mf.getName());
+			
+			ZipEntry ze = new ZipEntry(path.toPortableString());
+			
+			InputStream in = null;
+			long ts = 0;
+			IFile file = (IFile) mf.getAdapter(IFile.class);
+			if (file != null) {
+				ts = file.getLocalTimeStamp();
+				in = file.getContents();
+			} else {
+				File file2 = (File) mf.getAdapter(File.class);
+				ts = file2.lastModified();
+				in = new FileInputStream(file2);
+			}
+			
+			if (ts != IResource.NULL_STAMP && ts != 0)
+				ze.setTime(ts);
+			
+			zout.putNextEntry(ze);
+			
+			try {
+				int n = 0;
+				while (n > -1) {
+					n = in.read(buf);
+					if (n > 0)
+						zout.write(buf, 0, n);
+				}
+			} finally {
+				in.close();
+			}
+			
+			zout.closeEntry();
+		}
+	}
+
+	/**
+	 * Utility method to move a temp file into position by deleting the original and
+	 * swapping in a new copy.
+	 *  
+	 * @param tempFile
+	 * @param file
+	 * @throws CoreException
+	 */
+	private void moveTempFile(File tempFile, File file) throws CoreException {
+		if (file.exists()) {
+			if (!safeDelete(file, 2)) {
+				// attempt to rewrite an existing file with the tempFile contents if
+				// the existing file can't be deleted to permit the move
+				try {
+					InputStream in = new FileInputStream(tempFile);
+					IStatus status = copyFile(in, file.getPath());
+					if (!status.isOK()) {
+						MultiStatus status2 = new MultiStatus(ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorDeleting, file.toString()), null);
+						status2.add(status);
+						throw new CoreException(status2);
+					}
+					return;
+				} catch (FileNotFoundException e) {
+					// shouldn't occur
+				} finally {
+					tempFile.delete();
+				}
+				/*if (!safeDelete(file, 8)) {
+					tempFile.delete();
+					throw new CoreException(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorDeleting, file.toString()), null));
+				}*/
+			}
+		}
+		if (!safeRename(tempFile, file, 10))
+			throw new CoreException(new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorRename, tempFile.toString()), null));
+	}
+
+	/**
+	 * Copy a file from a to b. Closes the input stream after use.
+	 *
+	 * @param in an InputStream
+	 * @param to the file to copy to
+	 * @return a status
+	 */
+	private IStatus copyFile(InputStream in, String to) {
+		OutputStream out = null;
+		
+		try {
+			out = new FileOutputStream(to);
+			
+			int avail = in.read(buf);
+			while (avail > 0) {
+				out.write(buf, 0, avail);
+				avail = in.read(buf);
+			}
+			return Status.OK_STATUS;
+		} catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Error copying file", e);
+			return new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorCopyingFile, new String[] {to, e.getLocalizedMessage()}), e);
+		} finally {
+			try {
+				if (in != null)
+					in.close();
+			} catch (Exception ex) {
+				// ignore
+			}
+			try {
+				if (out != null)
+					out.close();
+			} catch (Exception ex) {
+				// ignore
+			}
+		}
+	}
+
+	/**
+	 * Safe delete. Tries to delete multiple times before giving up.
+	 * 
+	 * @param f
+	 * @return <code>true</code> if it succeeds, <code>false</code> otherwise
+	 */
+	private static boolean safeDelete(File f, int retrys) {
+		int count = 0;
+		while (count < retrys) {
+			if (!f.exists())
+				return true;
+			
+			f.delete();
+			
+			if (!f.exists())
+				return true;
+			
+			count++;
+			// delay if we are going to try again
+			if (count < retrys) {
+				try {
+					Thread.sleep(100);
+				} catch (Exception e) {
+					// ignore
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Safe rename. Will try multiple times before giving up.
+	 * 
+	 * @param from
+	 * @param to
+	 * @param retrys number of times to retry
+	 * @return <code>true</code> if it succeeds, <code>false</code> otherwise
+	 */
+	private static boolean safeRename(File from, File to, int retrys) {
+		// make sure parent dir exists
+		File dir = to.getParentFile();
+		if (dir != null && !dir.exists())
+			dir.mkdirs();
+		
+		int count = 0;
+		while (count < retrys) {
+			if (from.renameTo(to))
+				return true;
+			
+			count++;
+			// delay if we are going to try again
+			if (count < retrys) {
+				try {
+					Thread.sleep(100);
+				} catch (Exception e) {
+					// ignore
+				}
+			}
+		}
+		return false;
+	}
+
+	private static void addArrayToList(List<IStatus> list, IStatus[] a) {
+		if (list == null || a == null || a.length == 0)
+			return;
+		
+		int size = a.length;
+		for (int i = 0; i < size; i++)
+			list.add(a[i]);
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.preview.adapter/META-INF/MANIFEST.MF b/plugins/org.eclipse.wst.server.preview.adapter/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..487f18a
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.preview.adapter/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.wst.server.preview.adapter;singleton:=true
+Bundle-Version: 1.0.101.qualifier
+Bundle-Activator: org.eclipse.wst.server.preview.adapter.internal.core.PreviewPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.wst.server.core;bundle-version="[1.0.204,2.0.0)",
+ org.eclipse.wst.server.ui;bundle-version="[1.0.103,2.0.0)",
+ org.eclipse.debug.ui;bundle-version="[3.3.0,4.0.0)",
+ org.eclipse.wst.common.project.facet.ui;bundle-version="[1.2.0,2.0.0)"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/plugins/org.eclipse.wst.server.preview.adapter/src/org/eclipse/wst/server/preview/adapter/internal/core/PreviewServerBehaviour.java b/plugins/org.eclipse.wst.server.preview.adapter/src/org/eclipse/wst/server/preview/adapter/internal/core/PreviewServerBehaviour.java
new file mode 100644
index 0000000..ea0f5ec
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.preview.adapter/src/org/eclipse/wst/server/preview/adapter/internal/core/PreviewServerBehaviour.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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.server.preview.adapter.internal.core;
+
+import java.io.IOException;
+
+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.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.server.core.IModule;
+import org.eclipse.wst.server.core.IServer;
+import org.eclipse.wst.server.core.ServerPort;
+import org.eclipse.wst.server.core.model.IModuleResource;
+import org.eclipse.wst.server.core.model.IModuleResourceDelta;
+import org.eclipse.wst.server.core.model.ServerBehaviourDelegate;
+import org.eclipse.wst.server.core.util.IStaticWeb;
+import org.eclipse.wst.server.core.util.ProjectModule;
+import org.eclipse.wst.server.core.util.PublishUtil;
+import org.eclipse.wst.server.core.util.SocketUtil;
+/**
+ * Preview server.
+ */
+public class PreviewServerBehaviour extends ServerBehaviourDelegate {
+	// the thread used to ping the server to check for startup
+	protected transient PingThread ping = null;
+	protected transient IDebugEventSetListener processListener;
+
+	/**
+	 * PreviewServer.
+	 */
+	public PreviewServerBehaviour() {
+		super();
+	}
+
+	public void initialize(IProgressMonitor monitor) {
+		// do nothing
+	}
+
+	public PreviewRuntime getPreviewRuntime() {
+		if (getServer().getRuntime() == null)
+			return null;
+
+		return (PreviewRuntime) getServer().getRuntime().loadAdapter(PreviewRuntime.class, null);
+	}
+
+	public PreviewServer getPreviewServer() {
+		return (PreviewServer) getServer().getAdapter(PreviewServer.class);
+	}
+
+	/**
+	 * Returns the runtime base path for relative paths in the server
+	 * configuration.
+	 * 
+	 * @return the base path
+	 */
+	public IPath getRuntimeBaseDirectory() {
+		return getServer().getRuntime().getLocation();
+	}
+
+	/**
+	 * Setup for starting the server.
+	 * 
+	 * @param launch ILaunch
+	 * @param launchMode String
+	 * @param monitor IProgressMonitor
+	 * @throws CoreException if anything goes wrong
+	 */
+	protected void setupLaunch(ILaunch launch, String launchMode, IProgressMonitor monitor) throws CoreException {
+		// check that ports are free
+		ServerPort[] ports = getPreviewServer().getServerPorts();
+		int port = ports[0].getPort();
+		
+		if (SocketUtil.isPortInUse(port, 5))
+			throw new CoreException(new Status(IStatus.ERROR, PreviewPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorPortInUse, new String[] {port + "", getServer().getName()}), null));
+		
+		// generate preview config file
+		XMLMemento memento = XMLMemento.createWriteRoot("server");
+		memento.putInteger("port", port);
+		
+		IModule[] modules = getServer().getModules();
+		for (IModule module : modules) {
+			IMemento mod = memento.createChild("module");
+			mod.putString("name", module.getName());
+			String type = module.getModuleType().getId();
+			if ("wst.web".equals(type)) {
+				IStaticWeb staticWeb = (IStaticWeb) module.loadAdapter(IStaticWeb.class, null);
+				if (staticWeb != null)
+					mod.putString("context", staticWeb.getContextRoot());
+				mod.putString("type", "static");
+			}
+			mod.putString("path", getModulePublishDirectory(module).toPortableString());
+		}
+		try {
+			memento.saveToFile(getTempDirectory().append("preview.xml").toOSString());
+		} catch (IOException e) {
+			Trace.trace(Trace.SEVERE, "Could not write preview config", e);
+			throw new CoreException(new Status(IStatus.ERROR, PreviewPlugin.PLUGIN_ID, 0, "Could not write preview configuration", null));
+		}
+		
+		setServerRestartState(false);
+		setServerState(IServer.STATE_STARTING);
+		setMode(launchMode);
+		
+		// ping server to check for startup
+		try {
+			String url = "http://localhost";
+			if (port != 80)
+				url += ":" + port;
+			ping = new PingThread(getServer(), url, this);
+		} catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Can't ping for Tomcat startup.");
+		}
+	}
+
+	protected void addProcessListener(final IProcess newProcess) {
+		if (processListener != null || newProcess == null)
+			return;
+		
+		processListener = new IDebugEventSetListener() {
+			public void handleDebugEvents(DebugEvent[] events) {
+				if (events != null) {
+					for (DebugEvent event : events) {
+						if (newProcess != null && newProcess.equals(event.getSource()) && event.getKind() == DebugEvent.TERMINATE) {
+							stop(true);
+						}
+					}
+				}
+			}
+		};
+		DebugPlugin.getDefault().addDebugEventListener(processListener);
+	}
+
+	protected void setServerStarted() {
+		setServerState(IServer.STATE_STARTED);
+	}
+
+	protected void publishServer(int kind, IProgressMonitor monitor) throws CoreException {
+		monitor = ProgressUtil.getMonitorFor(monitor);
+		monitor.done();
+
+		setServerPublishState(IServer.PUBLISH_STATE_NONE);
+	}
+
+	/*
+	 * Publishes the given module to the server.
+	 */
+	protected void publishModule(int kind, int deltaKind, IModule[] moduleTree, IProgressMonitor monitor) throws CoreException {
+		IModule module = moduleTree[moduleTree.length - 1];
+		if (isSingleRootStructure(module))
+			return;
+		
+		IPath to = getModulePublishDirectory(module);
+		
+		if (kind == IServer.PUBLISH_CLEAN || deltaKind == ServerBehaviourDelegate.REMOVED) {
+			IStatus[] status = PublishUtil.deleteDirectory(to.toFile(), monitor);
+			throwException(status);
+		}
+		
+		IModuleResource[] res = getResources(moduleTree);
+		IStatus[] status = PublishUtil.publishSmart(res, to, monitor);
+		throwException(status);
+		
+		setModulePublishState(moduleTree, IServer.PUBLISH_STATE_NONE);
+	}
+
+	/**
+	 * Utility method to throw a CoreException based on the contents of a list of
+	 * error and warning status.
+	 * 
+	 * @param status a List containing error and warning IStatus
+	 * @throws CoreException
+	 */
+	private static void throwException(IStatus[] status) throws CoreException {
+		if (status == null || status.length == 0)
+			return;
+		
+		if (status.length == 1)
+			throw new CoreException(status[0]);
+		
+		String message = Messages.errorPublish;
+		MultiStatus status2 = new MultiStatus(PreviewPlugin.PLUGIN_ID, 0, status, message, null);
+		throw new CoreException(status2);
+	}
+
+	public void restart(String launchMode) throws CoreException {
+		setServerState(IServer.STATE_STOPPED);
+		setServerState(IServer.STATE_STARTED);
+	}
+
+	/**
+	 * Cleanly shuts down and terminates the server.
+	 * 
+	 * @param force <code>true</code> to kill the server
+	 */
+	public void stop(boolean force) {
+		int state = getServer().getServerState();
+		if (state == IServer.STATE_STOPPED)
+			return;
+		
+		setServerState(IServer.STATE_STOPPING);
+		
+		if (ping != null) {
+			ping.stop();
+			ping = null;
+		}
+		
+		if (processListener != null) {
+			DebugPlugin.getDefault().removeDebugEventListener(processListener);
+			processListener = null;
+		}
+		
+		try {
+			Trace.trace(Trace.FINEST, "Killing the process");
+			ILaunch launch = getServer().getLaunch();
+			if (launch != null)
+				launch.terminate();
+		} catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Error killing the process", e);
+		}
+		
+		setServerState(IServer.STATE_STOPPED);
+	}
+
+	protected IPath getTempDirectory() {
+		return super.getTempDirectory();
+	}
+
+	/**
+	 * Returns <code>true</code> if the module in the workspace has a single
+	 * root structure - i.e. matches the spec disk layout - and <code>false</code>
+	 * otherwise.
+	 * 
+	 * @return <code>true</code> if the module in the workspace has a single
+	 *    root structure - i.e. matches the spec disk layout - and
+	 *    <code>false</code> otherwise
+	 */
+	protected boolean isSingleRootStructure(IModule module) {
+		ProjectModule pm = (ProjectModule) module.loadAdapter(ProjectModule.class, null);
+		if (pm == null)
+			return false;
+		
+		return pm.isSingleRootStructure();
+	}
+
+	/**
+	 * Returns the module's publish path.
+	 * 
+	 * @param module a module
+	 * @return the publish directory for the module
+	 */
+	protected IPath getModulePublishDirectory(IModule module) {
+		if (isSingleRootStructure(module)) {
+			IStaticWeb webModule = (IStaticWeb) module.loadAdapter(IStaticWeb.class, null);
+			if (webModule != null) {
+				//IContainer[] moduleFolder = webModule.getResourceFolders();
+				//if (moduleFolder != null && moduleFolder.length > 0)
+				//	return moduleFolder[0].getLocation();							
+			}
+		}
+		
+		return getTempDirectory().append(module.getName());
+	}
+
+	/**
+	 * Return a string representation of this object.
+	 * 
+	 * @return java.lang.String
+	 */
+	public String toString() {
+		return "PreviewServer";
+	}
+
+	protected IModuleResourceDelta[] getPublishedResourceDelta(IModule[] module) {
+		return super.getPublishedResourceDelta(module);
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/META-INF/MANIFEST.MF b/plugins/org.eclipse.wst.server.ui/META-INF/MANIFEST.MF
index 8815a61..71f3f04 100644
--- a/plugins/org.eclipse.wst.server.ui/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.wst.server.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.wst.server.ui; singleton:=true
-Bundle-Version: 1.1.1.qualifier
+Bundle-Version: 1.1.2.qualifier
 Bundle-Activator: org.eclipse.wst.server.ui.internal.ServerUIPlugin
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/plugins/org.eclipse.wst.server.ui/serverAdapterSites.xml b/plugins/org.eclipse.wst.server.ui/serverAdapterSites.xml
new file mode 100644
index 0000000..54ec5e0
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverAdapterSites.xml
@@ -0,0 +1,10 @@
+<extensions>
+    <site url="http://www.apache.org/dist/geronimo/eclipse/updates/"/>
+    <site url="http://download.boulder.ibm.com/ibmdl/pub/software/websphere/wasce/updates/"/>
+    <site url="http://www.pramati.com/downloads/eclipse/updates/"/>
+    <site url="http://update.eclipse.org/updates/3.3/"/>
+    <site url="http://download.oracle.com/otn_software/oepe/ganymede/wls-adapter/"/>
+    <site url="https://ajax.dev.java.net/eclipse"/>
+    <site url="https://www.sdn.sap.com/downloads/updates/netweaver/nwds/sapnwserver/"/>
+    <site url="http://www.webtide.com/eclipse"/>
+</extensions>
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.java
index 48a4403..79f2f7a 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.java
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.java
@@ -183,6 +183,7 @@
 	public static String wizModuleRemoveAll;
 	public static String wizModuleRequiredModule;
 	public static String wizModuleRequiredModules;
+	public static String wizModulePublishImmediately;
 	public static String wizTaskTitle;
 	public static String wizTaskDescription;
 	public static String wizErrorInvalidFolder;
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.properties b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.properties
index f768261..08b92ba 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.properties
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.properties
@@ -85,6 +85,7 @@
 wizModuleRemoveAll=<< Re&move All
 wizModuleRequiredModule={0} is required and cannot be removed from the server
 wizModuleRequiredModules=One of the required modules ({0}) must be added to the server
+wizModulePublishImmediately=If server is started, publish changes &immediately
 
 # Wizard info and error messages
 wizErrorInvalidFolder=The folder must be a server project or a folder within a server project
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerPropertyPage.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerPropertyPage.java
new file mode 100644
index 0000000..53a7147
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerPropertyPage.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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.server.ui.internal;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.PropertyPage;
+import org.eclipse.ui.help.IWorkbenchHelpSystem;
+import org.eclipse.wst.server.core.IRuntimeType;
+import org.eclipse.wst.server.core.IServer;
+import org.eclipse.wst.server.core.IServerType;
+import org.eclipse.wst.server.core.internal.Server;
+/**
+ * PropertyPage for servers.
+ */
+public class ServerPropertyPage extends PropertyPage {
+	protected IServer server;
+
+	protected IServer defaultServer;
+
+	/**
+	 * ServerPropertyPage constructor comment.
+	 */
+	public ServerPropertyPage() {
+		super();
+	}
+
+	/**
+	 * Create the body of the page.
+	 *
+	 * @param parent org.eclipse.swt.widgets.Composite
+	 * @return org.eclipse.swt.widgets.Control
+	 */
+	protected Control createContents(Composite parent) {
+		try {
+			IAdaptable element = getElement();
+			server = (IServer) element.getAdapter(IServer.class);
+			
+			Composite composite = new Composite(parent, SWT.NONE);
+			GridLayout layout = new GridLayout();
+			layout.marginHeight = 0;
+			layout.marginWidth = 0;
+			layout.numColumns = 3;
+			composite.setLayout(layout);
+			composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+			
+			IWorkbenchHelpSystem whs = PlatformUI.getWorkbench().getHelpSystem();
+			whs.setHelp(composite, ContextIds.SERVER_PROPERTY_PAGE);			
+			
+			// name
+			Label label = new Label(composite, SWT.NONE);
+			label.setText(Messages.propServerInfoName);
+			
+			label = new Label(composite, SWT.NONE);
+			label.setText(server.getName());
+			GridData data = new GridData(GridData.FILL_HORIZONTAL);
+			data.horizontalSpan = 2;
+			label.setLayoutData(data);
+			
+			// type
+			label = new Label(composite, SWT.NONE);
+			label.setText(Messages.propServerInfoType);
+			
+			IServerType serverType = server.getServerType();
+			label = new Label(composite, SWT.NONE);
+			if (serverType != null)
+				label.setText(serverType.getName());
+			else
+				label.setText(Messages.elementUnknownName);
+			data = new GridData(GridData.FILL_HORIZONTAL);
+			data.horizontalSpan = 2;
+			label.setLayoutData(data);
+			
+			// vendor
+			label = new Label(composite, SWT.NONE);
+			label.setText(Messages.propServerInfoVendor);
+			
+			IRuntimeType runtimeType = null;
+			if (serverType != null)
+				runtimeType = serverType.getRuntimeType();
+			label = new Label(composite, SWT.NONE);
+			if (runtimeType != null)
+				label.setText(runtimeType.getVendor());
+			else
+				label.setText(Messages.elementUnknownName);
+			data = new GridData(GridData.FILL_HORIZONTAL);
+			data.horizontalSpan = 2;
+			label.setLayoutData(data);
+			
+			// location
+			label = new Label(composite, SWT.NONE);
+			label.setText(Messages.switchServerLocation);
+			label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_BEGINNING));
+			
+			final Label serverLocation = new Label(composite, SWT.NONE);
+			final Server svr = (Server) server;
+			if (svr.getFile() != null)
+				serverLocation.setText(svr.getFile().getFullPath().toPortableString());
+			else
+				serverLocation.setText(Messages.switchServerLocationMetadata);
+			
+			serverLocation.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING));
+			
+			Button switchLocation = new Button(composite, SWT.PUSH);
+			switchLocation.setText(Messages.actionSwitchServerLocation);
+			switchLocation.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));
+			switchLocation.setEnabled(!server.isReadOnly());
+			switchLocation.addSelectionListener(new SelectionAdapter() {
+				public void widgetSelected(SelectionEvent e) {
+					try {
+						Server.switchLocation(svr, null);
+					} catch (CoreException ce) {
+						Trace.trace(Trace.SEVERE, "Error switching server location", ce);
+					}
+					if (svr.getFile() != null)
+						serverLocation.setText(svr.getFile().getFullPath().toPortableString());
+					else
+						serverLocation.setText(Messages.switchServerLocationMetadata);
+				}
+			});
+			
+			Dialog.applyDialogFont(composite);
+			
+			return composite;
+		} catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Error creating property page", e);
+			return null;
+		}
+	}
+
+	protected void performDefaults() {
+		super.performDefaults();
+	}
+
+	/**
+	 * @see org.eclipse.jface.preference.PreferencePage#performOk()
+	 */
+	public boolean performOk() {
+		return super.performOk();
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerToolTip.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerToolTip.java
new file mode 100644
index 0000000..83501e1
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerToolTip.java
@@ -0,0 +1,296 @@
+/**********************************************************************
+ * Copyright (c) 2007 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.server.ui.internal;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.internal.text.html.HTML2TextReader;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.window.ToolTip;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.wst.server.core.*;
+import org.eclipse.wst.server.core.internal.Trace;
+import org.eclipse.wst.server.ui.IServerModule;
+import org.eclipse.wst.server.ui.internal.provisional.IServerToolTip;
+
+public class ServerToolTip extends ToolTip {	
+	protected Hashtable<String,ArrayList<IServerToolTip>> toolTipProviders = new Hashtable<String,ArrayList<IServerToolTip>>();	
+	protected static Shell CURRENT_TOOLTIP;
+	protected Label hintLabel;
+	protected Tree tree;
+	protected int x;
+	protected int y;
+
+	public ServerToolTip(final Tree tree) {
+		super(tree);
+		
+		this.tree = tree;
+		
+		tree.addMouseMoveListener(new MouseMoveListener() {
+			public void mouseMove(MouseEvent e) {
+				x = e.x;
+				y = e.y;
+			}
+		});
+		
+		tree.addKeyListener(new KeyListener() {
+			public void keyPressed(KeyEvent  e) {
+				if (e.keyCode == SWT.ESC) {
+					if (CURRENT_TOOLTIP != null) {
+						CURRENT_TOOLTIP.dispose();
+						CURRENT_TOOLTIP = null;
+					}
+					activate();
+				}
+				if (e.keyCode == SWT.F6) {
+					if (CURRENT_TOOLTIP == null) {
+						deactivate();
+						hide();
+						createFocusedTooltip(tree);
+					}
+				}
+			}
+			public void keyReleased(KeyEvent e){
+				// nothing to do 
+			}
+		});
+		
+		loadExtensions();
+	}
+	
+	public void createFocusedTooltip(final Control control) {
+		final Shell stickyTooltip = new Shell(control.getShell(), SWT.ON_TOP | SWT.TOOL
+				| SWT.NO_FOCUS);
+		stickyTooltip.setLayout(new FillLayout());
+		stickyTooltip.setBackground(stickyTooltip.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+		
+		control.getDisplay().asyncExec(new Runnable() {
+			public void run() {
+				Event event = new Event();
+				event.x = x;
+				event.y = y;
+				event.widget = tree;
+				
+				createToolTipContentArea(event, stickyTooltip);
+				stickyTooltip.pack();
+				
+				stickyTooltip.setLocation(stickyTooltip.getDisplay().getCursorLocation());				
+				hintLabel.setText(Messages.toolTipDisableFocus);
+				stickyTooltip.setVisible(true);
+//				Eventually we want to add a listener that checks if
+//              the mouseDown event is occurring outside of the bounds of the tooltip
+//              if it is, then hide the tooltip
+//				addListener(stickyTooltip);
+			}
+		});
+		CURRENT_TOOLTIP = stickyTooltip;
+	}
+
+//  read the createFocusedTooltip method for information on why this is commented out
+//
+//	private void addListener(Control control){
+//		control.addMouseListener(new StickyTipMouseListener());
+//		if (control instanceof Composite){
+//			Control[] childrens = ((Composite)control).getChildren();
+//			for (Control child :childrens){
+//				addListener(child);
+//			}
+//		}
+//		
+//	}
+	
+	
+	@Override
+	protected Object getToolTipArea(Event event) {
+		Object o = tree.getItem(new Point(event.x,event.y));
+		return o;
+	}
+
+	protected final boolean shouldCreateToolTip(Event event) {
+		if (tree.getItem(new Point(event.x, event.y)) == null)
+			return false;
+		return super.shouldCreateToolTip(event);
+	}
+
+	protected Composite createToolTipContentArea(Event event, Composite parent) {
+		Object o = tree.getItem(new Point(event.x, event.y));
+		if (o == null)
+			return null;
+		
+		IServer server = null;
+		IServerModule module = null;
+		if (o instanceof TreeItem) {
+			Object obj = ((TreeItem)o).getData();
+			if (obj instanceof IServer)
+				server = (IServer) obj;
+			if (obj instanceof IServerModule)
+				module = (IServerModule) obj;
+		}
+		
+		FillLayout layout = (FillLayout)parent.getLayout();
+		layout.type = SWT.VERTICAL;
+		parent.setLayout(layout);
+		
+		// set the default text for the tooltip
+		StyledText sText = new StyledText(parent, SWT.NONE);
+		sText.setEditable(false);
+		sText.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+		
+		if (module != null) {
+			IModule[] modules = module.getModule();
+			IModule m = modules[modules.length - 1];
+			sText.setText("<b>" + m.getName() + "</b>");
+			//sText.setText("<b>" + m.getName() + "</b></p>" + m.getModuleType().getName());
+			
+			StyledText sText2 = new StyledText(parent, SWT.NONE);
+			sText2.setEditable(false);
+			sText2.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+			sText2.setText(m.getModuleType().getName());
+		}
+		
+		if (server != null) {
+			sText.setText("<b>" + server.getName() + "</b>");
+			
+			// add adopters content
+			if (server.getServerType() != null) {
+				ArrayList<IServerToolTip> listOfProviders = toolTipProviders.get(server.getServerType().getId());
+				
+				final Composite adoptersComposite = new Composite(parent,SWT.NONE);
+				adoptersComposite.setLayout(new FillLayout());
+				adoptersComposite.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+				
+				if (listOfProviders != null) {
+					for (IServerToolTip tipProvider : listOfProviders) {
+						tipProvider.createContent(adoptersComposite,server);
+					}
+				}
+			}
+		}
+		
+		// add the F3 text
+		hintLabel = new Label(parent,SWT.BORDER);
+		hintLabel.setAlignment(SWT.RIGHT);
+		hintLabel.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+		hintLabel.setText(Messages.toolTipEnableFocus);
+		hintLabel.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
+		
+		final Font font;
+		Display display = parent.getDisplay();
+		FontData[] fd = parent.getFont().getFontData();
+		int size2 = fd.length;
+		for (int i = 0; i < size2; i++)
+			fd[i].setHeight(7);
+		font = new Font(display, fd);
+		parent.addDisposeListener(new DisposeListener() {
+			public void widgetDisposed(DisposeEvent e) {
+				font.dispose();
+			}
+		});
+		hintLabel.setFont(font);
+		
+		parseText(sText.getText(),sText);
+		
+		return parent;
+	}
+
+	protected void parseText(String htmlText,StyledText sText) {	
+		TextPresentation presentation = new TextPresentation();
+		HTML2TextReader reader = new HTML2TextReader(new StringReader(htmlText), presentation);
+		String text;
+		
+		try {
+			text = reader.getString();
+		} catch (IOException e) {
+			text= ""; //$NON-NLS-1$
+		}
+		
+		sText.setText(text);		
+		Iterator iter = presentation.getAllStyleRangeIterator();
+		while (iter.hasNext()) {
+			StyleRange sr = (StyleRange)iter.next();
+			sText.setStyleRange(sr);
+		}
+	}
+
+	private void loadExtensions() {
+		Trace.trace(Trace.EXTENSION_POINT, "->- Loading serverToolTip extension point ->-");
+		
+		// search for extension points 
+		IExtensionRegistry reg = Platform.getExtensionRegistry();
+		IConfigurationElement[] extensions = reg.getConfigurationElementsFor(ServerUIPlugin.PLUGIN_ID + ".serverToolTip");
+		
+		IServerType[] serverTypes = ServerCore.getServerTypes();
+		
+		for (int i=0; i < extensions.length; i++){			
+			IConfigurationElement exElement = extensions[i];
+			
+			// Sort the extensions based on serverType
+			String exServerType = exElement.getAttribute("serverTypes");
+			
+			for (IServerType serverType : serverTypes) {
+				if (exServerType.compareTo("*") == 0 || 
+						exServerType.startsWith(serverType.getId()) == true) {
+					try {
+						IServerToolTip exTooltip = (IServerToolTip) exElement.createExecutableExtension("class");
+						ArrayList<IServerToolTip> listOfProviders = new ArrayList<IServerToolTip>(); 
+						if (toolTipProviders.containsKey(serverType)) {
+							listOfProviders = toolTipProviders.get(serverType);
+						}
+						listOfProviders.add(exTooltip);
+						toolTipProviders.put(serverType.getId(), listOfProviders);
+					} catch (CoreException e) {
+						Trace.trace(Trace.SEVERE, "Tooltip failed to load" + extensions[i].toString(), e);
+					}
+					Trace.trace(Trace.EXTENSION_POINT, "  Loaded serverToolTip: " + extensions[i].getAttribute("id"));
+				}
+			}
+		}
+	}
+
+//  read the createFocusedTooltip method for information on why this is commented out
+//
+//	protected class StickyTipMouseListener implements MouseListener{
+//
+//		public void mouseDoubleClick(MouseEvent e) {
+//			// TODO Auto-generated method stub
+//			
+//		}
+//
+//		public void mouseDown(MouseEvent e) {
+//			//System.out.println("mouseDown");
+//			if (CURRENT_TOOLTIP.getBounds().contains(new Point(e.x,e.y)) == true){
+//				CURRENT_TOOLTIP.setVisible(false);
+//				CURRENT_TOOLTIP.dispose();
+//				activate();
+//				CURRENT_TOOLTIP.removeMouseListener(this);
+//			}
+//		}
+//
+//		public void mouseUp(MouseEvent e) {
+//			// TODO Auto-generated method stub
+//			
+//		}
+//		
+//	}	
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerUIPreferences.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerUIPreferences.java
index a66d68b..e9c6e3a 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerUIPreferences.java
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/ServerUIPreferences.java
@@ -29,6 +29,7 @@
 	private static final String PREF_ENABLE_BREAKPOINTS = "enable-breakpoints";
 	private static final String PREF_RESTART = "restart";
 	private static final String PREF_CREATE_SERVER_WITH_RUNTIME = "create-server";
+	private static final String PREF_PUBLISH_ON_ADD_REMOVE = "publish-on-add-remove";
 
 	public static final byte SAVE_EDITORS_ALWAYS = 2;
 	public static final byte SAVE_EDITORS_NEVER = 0;
@@ -72,6 +73,7 @@
 		preferences.setDefault(PREF_HOST_NAMES, "localhost");
 		preferences.setDefault(PREF_SHOW_ON_ACTIVITY, true);
 		preferences.setDefault(PREF_CREATE_SERVER_WITH_RUNTIME, false);
+		preferences.setDefault(PREF_PUBLISH_ON_ADD_REMOVE, true);
 	}
 
 	/**
@@ -340,4 +342,23 @@
 		preferences.setValue(PREF_CREATE_SERVER_WITH_RUNTIME, b);
 		ServerUIPlugin.getInstance().savePluginPreferences();
 	}
+
+	/**
+	 * Returns the setting for publishing when modules are added or removed.
+	 * 
+	 * @return boolean
+	 */
+	public boolean getPublishOnAddRemoveModule() {
+		return preferences.getBoolean(PREF_PUBLISH_ON_ADD_REMOVE);
+	}
+
+	/**
+	 * Sets the value for publishing when modules are added or removed.
+	 * 
+	 * @param b
+	 */
+	public void setPublishOnAddRemoveModule(boolean b) {
+		preferences.setValue(PREF_PUBLISH_ON_ADD_REMOVE, b);
+		ServerUIPlugin.getInstance().savePluginPreferences();
+	}
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Trace.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Trace.java
new file mode 100644
index 0000000..c8513f7
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Trace.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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.server.ui.internal;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+/**
+ * Helper class to route trace output.
+ */
+public class Trace {
+	public static final byte CONFIG = 0;
+	public static final byte INFO = 1;
+	public static final byte WARNING = 2;
+	public static final byte SEVERE = 3;
+	public static final byte FINEST = 4;
+	public static final byte FINER = 5;
+	public static final byte PERFORMANCE = 6;
+	public static final byte EXTENSION_POINT = 7;
+
+	private static final String[] levelNames = new String[] {
+		"CONFIG ", "INFO   ", "WARNING", "SEVERE ", "FINER  ", "FINEST ", "PERF   ", "EXTENSION"};
+
+	private static final SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy HH:mm.ss.SSS");
+
+	private static Set<String> logged = new HashSet<String>();
+
+	/**
+	 * Trace constructor comment.
+	 */
+	private Trace() {
+		super();
+	}
+
+	/**
+	 * Trace the given text.
+	 *
+	 * @param level a trace level
+	 * @param s a message
+	 */
+	public static void trace(byte level, String s) {
+		trace(level, s, null);
+	}
+
+	/**
+	 * Trace the given message and exception.
+	 *
+	 * @param level a trace level
+	 * @param s a message
+	 * @param t a throwable
+	 */
+	public static void trace(byte level, String s, Throwable t) {
+		if (s == null)
+			return;
+		
+		if (level == SEVERE) {
+			if (!logged.contains(s)) {
+				ServerUIPlugin.getInstance().getLog().log(new Status(IStatus.ERROR, ServerUIPlugin.PLUGIN_ID, s, t));
+				logged.add(s);
+			}
+		}
+		
+		if (!ServerUIPlugin.getInstance().isDebugging())
+			return;
+		
+		StringBuffer sb = new StringBuffer(ServerUIPlugin.PLUGIN_ID);
+		sb.append(" ");
+		sb.append(levelNames[level]);
+		sb.append(" ");
+		sb.append(sdf.format(new Date()));
+		sb.append(" ");
+		sb.append(s);
+		System.out.println(sb.toString());
+		if (t != null)
+			t.printStackTrace();
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/DeleteAction.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/DeleteAction.java
new file mode 100644
index 0000000..43bf11e
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/DeleteAction.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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.server.ui.internal.view.servers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds;
+import org.eclipse.wst.server.core.IServer;
+import org.eclipse.wst.server.core.ServerCore;
+import org.eclipse.wst.server.ui.internal.DeleteServerDialog;
+import org.eclipse.wst.server.ui.internal.Messages;
+import org.eclipse.swt.widgets.Shell;
+/**
+ * Action for deleting server resources.
+ */
+public class DeleteAction extends AbstractServerAction {
+	protected IServer[] servers;
+	protected IFolder[] configs;
+
+	/**
+	 * DeleteAction constructor.
+	 * 
+	 * @param shell a shell
+	 * @param sp a selection provider
+	 */
+	public DeleteAction(Shell shell, ISelectionProvider sp) {
+		super(shell, sp, Messages.actionDelete);
+		ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
+		setImageDescriptor(sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
+		setDisabledImageDescriptor(sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE_DISABLED));
+		setActionDefinitionId(IWorkbenchActionDefinitionIds.DELETE);
+	}
+
+	public boolean accept(IServer server) {
+		servers = new IServer[] { server };
+		List<IFolder> list = new ArrayList<IFolder>();
+		
+		int size = servers.length;
+		for (int i = 0; i < size; i++) {
+			if (servers[i].isReadOnly())
+				return false;
+			
+			if (servers[i].getServerConfiguration() != null)
+				list.add(servers[i].getServerConfiguration());
+		}
+		
+		// remove configurations that are still referenced by other servers
+		IServer[] servers2 = ServerCore.getServers();
+		if (servers2 != null) {
+			int size2 = servers2.length;
+			for (int j = 0; j < size2; j++) {
+				boolean found = false;
+				for (int i = 0; i < size; i++) {
+					if (servers[i].equals(servers2[j]))
+						found = true;
+				}
+				if (!found) {
+					IFolder folder = servers2[j].getServerConfiguration();
+					if (folder != null && list.contains(folder))
+						list.remove(folder);
+				}
+			}
+		}
+		
+		configs = new IFolder[list.size()];
+		list.toArray(configs);
+		return true;
+	}
+
+	public void perform(IServer server) {
+		DeleteServerDialog dsd = new DeleteServerDialog(shell, servers, configs);
+		dsd.open();
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/RemoveModuleAction.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/RemoveModuleAction.java
index b198af0..756ba36 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/RemoveModuleAction.java
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/RemoveModuleAction.java
@@ -16,8 +16,8 @@
 import org.eclipse.wst.server.core.IModule;
 import org.eclipse.wst.server.core.IServer;
 import org.eclipse.wst.server.core.IServerWorkingCopy;
-import org.eclipse.wst.server.core.internal.Server;
 import org.eclipse.wst.server.ui.internal.Messages;
+import org.eclipse.wst.server.ui.internal.ServerUIPlugin;
 import org.eclipse.wst.server.ui.internal.Trace;
 import org.eclipse.swt.widgets.Shell;
 /**
@@ -53,7 +53,8 @@
 				wc.modifyModules(null, new IModule[] { module }, null);
 				server = wc.save(true, null);
 				
-				if (server.getServerState() != IServer.STATE_STOPPED && ((Server)server).getAutoPublishSetting() != Server.AUTO_PUBLISH_DISABLE) {
+				if (server.getServerState() != IServer.STATE_STOPPED &&
+						ServerUIPlugin.getPreferences().getPublishOnAddRemoveModule()) {
 					final IAdaptable info = new IAdaptable() {
 						public Object getAdapter(Class adapter) {
 							if (Shell.class.equals(adapter))
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/ServersView.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/ServersView.java
new file mode 100644
index 0000000..5d754ad
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/view/servers/ServersView.java
@@ -0,0 +1,460 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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.server.ui.internal.view.servers;
+
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.wst.server.core.*;
+import org.eclipse.wst.server.core.internal.Server;
+import org.eclipse.wst.server.core.internal.UpdateServerJob;
+import org.eclipse.wst.server.core.model.ServerDelegate;
+import org.eclipse.wst.server.ui.internal.*;
+import org.eclipse.wst.server.ui.internal.actions.NewServerWizardAction;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.FileTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.*;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.contexts.IContextService;
+import org.eclipse.ui.keys.IBindingService;
+import org.eclipse.ui.part.ResourceTransfer;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.ui.views.navigator.LocalSelectionTransfer;
+/**
+ * A view of servers, their modules, and status.
+ */
+public class ServersView extends ViewPart {
+	private static final String TAG_COLUMN_WIDTH = "columnWidth";
+	private static final String SERVERS_VIEW_CONTEXT = "org.eclipse.ui.serverViewScope";
+
+	protected Action noneAction = new Action(Messages.dialogMonitorNone) {
+		// dummy action
+	};
+
+	protected int[] cols;
+
+	protected Tree treeTable;
+	protected ServerTableViewer tableViewer;
+
+	// actions on a server
+	protected Action[] actions;
+	protected Action actionModifyModules;
+	protected Action openAction, showInConsoleAction, showInDebugAction, propertiesAction, monitorPropertiesAction;
+	protected Action copyAction, pasteAction, deleteAction, renameAction;
+
+	/**
+	 * ServersView constructor comment.
+	 */
+	public ServersView() {
+		super();
+	}
+
+	protected SelectionListener getHeaderListener(final int col) {
+		return new SelectionAdapter() {
+			/**
+			 * Handles the case of user selecting the header area.
+			 */
+			public void widgetSelected(SelectionEvent e) {
+				if (tableViewer == null)
+					return;
+				TreeColumn column = (TreeColumn) e.widget;
+				tableViewer.resortTable(column, col);
+			}
+		};
+	}
+
+	/**
+	 * createPartControl method comment.
+	 * 
+	 * @param parent a parent composite
+	 */
+	public void createPartControl(Composite parent) {
+		treeTable = new Tree(parent, SWT.SINGLE | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL);
+		treeTable.setHeaderVisible(true);
+		treeTable.setLinesVisible(false);
+		treeTable.setLayoutData(new GridData(GridData.FILL_BOTH));
+		treeTable.setFont(parent.getFont());
+		PlatformUI.getWorkbench().getHelpSystem().setHelp(treeTable, ContextIds.VIEW_SERVERS);
+		
+		// add columns
+		TreeColumn column = new TreeColumn(treeTable, SWT.SINGLE);
+		column.setText(Messages.viewServer);
+		column.setWidth(cols[0]);
+		column.addSelectionListener(getHeaderListener(0));
+		treeTable.setSortColumn(column);
+		treeTable.setSortDirection(SWT.UP);
+		
+		TreeColumn column2 = new TreeColumn(treeTable, SWT.SINGLE);
+		column2.setText(Messages.viewState);
+		column2.setWidth(cols[1]);
+		column2.addSelectionListener(getHeaderListener(1));
+		
+		TreeColumn column3 = new TreeColumn(treeTable, SWT.SINGLE);
+		column3.setText(Messages.viewStatus);
+		column3.setWidth(cols[2]);
+		column3.addSelectionListener(getHeaderListener(2));
+		
+		IContextService contextSupport = (IContextService)getSite().getService(IContextService.class);
+		contextSupport.activateContext(SERVERS_VIEW_CONTEXT);
+		
+		deferInitialization();
+	}
+
+	private void deferInitialization() {
+		TreeItem item = new TreeItem(treeTable, SWT.NONE);
+		item.setText(Messages.viewInitializing);
+		
+		tableViewer = new ServerTableViewer(this, treeTable);
+		initializeActions(tableViewer);
+		
+		Job job = new Job(Messages.jobInitializingServersView) {
+			public IStatus run(IProgressMonitor monitor) {
+				IServer[] servers = ServerCore.getServers();
+				int size = servers.length;
+				for (int i = 0; i < size; i++) {
+					((Server)servers[i]).getAllModules().iterator();
+					/*while (iterator.hasNext()) {
+						Module module = (Module) iterator.next();
+						module.g
+					}*/
+				}
+				
+				Display.getDefault().asyncExec(new Runnable() {
+					public void run() {
+						try {
+							deferredInitialize();
+						} catch (Exception e) {
+							// ignore - view has already been closed
+						}
+					}
+				});
+				return Status.OK_STATUS;
+			}
+		};
+		
+		job.setSystem(true);
+		job.setPriority(Job.SHORT);
+		job.schedule();
+	}
+
+	protected void deferredInitialize() {
+		tableViewer.initialize();
+		tableViewer.addOpenListener(new IOpenListener() {
+			public void open(OpenEvent event) {
+				try {
+					IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+					Object data = sel.getFirstElement();
+					if (!(data instanceof IServer))
+						return;
+					IServer server = (IServer) data;
+					ServerUIPlugin.editServer(server);
+				} catch (Exception e) {
+					Trace.trace(Trace.SEVERE, "Could not open server", e);
+				}
+			}
+		});
+		
+		MenuManager menuManager = new MenuManager("#PopupMenu");
+		menuManager.setRemoveAllWhenShown(true);
+		final Shell shell = treeTable.getShell();
+		menuManager.addMenuListener(new IMenuListener() {
+			public void menuAboutToShow(IMenuManager mgr) {
+				fillContextMenu(shell, mgr);
+			}
+		});
+		Menu menu = menuManager.createContextMenu(treeTable);
+		treeTable.setMenu(menu);
+		getSite().registerContextMenu(menuManager, tableViewer);
+		getSite().setSelectionProvider(tableViewer);
+		
+		initDragAndDrop();
+		
+		// init the tooltip
+		ServerToolTip toolTip = new ServerToolTip(treeTable);
+		toolTip.setShift(new Point(10, 3));
+		toolTip.setPopupDelay(400); // in ms
+		toolTip.setHideOnMouseDown(true);
+		toolTip.activate();
+		
+		if (tableViewer.getTree().getItemCount() > 0) {
+			Object obj = tableViewer.getTree().getItem(0).getData();
+			tableViewer.setSelection(new StructuredSelection(obj));
+		}
+		
+		Thread thread = new Thread() {
+			public void run() {
+				try {
+					Thread.sleep(5000);
+				} catch (Exception e) {
+					// ignore
+				}
+				IServer[] servers = ServerCore.getServers();
+				int size = servers.length;
+				for (int i = 0; i < size; i++) {
+					IServer server = servers[i];
+					if (server.getServerType() != null && server.getServerState() == IServer.STATE_UNKNOWN) {
+						UpdateServerJob job = new UpdateServerJob(server);
+						job.schedule();
+					}
+				}
+			}
+		};
+		thread.setDaemon(true);
+		thread.setPriority(Thread.MIN_PRIORITY + 1);
+		thread.start();
+	}
+
+	public void init(IViewSite site, IMemento memento) throws PartInitException {
+		super.init(site, memento);
+		cols = new int[3];
+		for (int i = 0; i < 3; i++) {
+			cols[i] = 200;
+			if (memento != null) {
+				Integer in = memento.getInteger(TAG_COLUMN_WIDTH + i);
+				if (in != null && in.intValue() > 5)
+					cols[i] = in.intValue();
+			}
+		}
+	}
+
+	public void saveState(IMemento memento) {
+		TreeColumn[] tc = treeTable.getColumns();
+		for (int i = 0; i < 3; i++) {
+			int width = tc[i].getWidth();
+			if (width != 0)
+				memento.putInteger(TAG_COLUMN_WIDTH + i, width);
+		}
+	}
+
+	/**
+	 * Initialize actions
+	 * 
+	 * @param provider a selection provider
+	 */
+	public void initializeActions(ISelectionProvider provider) {
+		Shell shell = getSite().getShell();
+		IActionBars actionBars = getViewSite().getActionBars();
+		
+		actions = new Action[6];
+		// create the start actions
+		actions[0] = new StartAction(shell, provider, ILaunchManager.DEBUG_MODE);
+		actionBars.setGlobalActionHandler("org.eclipse.wst.server.debug", actions[0]);
+		actions[1] = new StartAction(shell, provider, ILaunchManager.RUN_MODE);
+		actionBars.setGlobalActionHandler("org.eclipse.wst.server.run", actions[1]);
+		actions[2] = new StartAction(shell, provider, ILaunchManager.PROFILE_MODE);
+		
+		// create the stop action
+		actions[3] = new StopAction(shell, provider);
+		actionBars.setGlobalActionHandler("org.eclipse.wst.server.stop", actions[3]);
+		
+		// create the publish actions
+		actions[4] = new PublishAction(shell, provider);
+		actionBars.setGlobalActionHandler("org.eclipse.wst.server.publish", actions[4]);
+		actions[5] = new PublishCleanAction(shell, provider);
+		
+		// create the open action
+		openAction = new OpenAction(provider);
+		actionBars.setGlobalActionHandler("org.eclipse.ui.navigator.Open", openAction);
+		
+		// create copy, paste, and delete actions
+		pasteAction = new PasteAction(shell, provider, tableViewer.clipboard);
+		copyAction = new CopyAction(provider, tableViewer.clipboard, pasteAction);
+		deleteAction = new DeleteAction(shell, provider);
+		renameAction = new RenameAction(shell, tableViewer, provider);
+		actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), copyAction);
+		actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), pasteAction);
+		actionBars.setGlobalActionHandler(ActionFactory.DELETE.getId(), deleteAction);
+		actionBars.setGlobalActionHandler(ActionFactory.RENAME.getId(), renameAction);
+		
+		// create the other actions
+		actionModifyModules = new ModuleSloshAction(shell, provider);
+		showInConsoleAction = new ShowInConsoleAction(provider);
+		showInDebugAction = new ShowInDebugAction(provider);
+		
+		// create the properties action
+		propertiesAction = new PropertiesAction(shell, provider);
+		actionBars.setGlobalActionHandler(ActionFactory.PROPERTIES.getId(), propertiesAction);
+		monitorPropertiesAction = new PropertiesAction(shell, "org.eclipse.wst.server.ui.properties.monitor", provider);
+		
+		// add toolbar buttons
+		IContributionManager cm = actionBars.getToolBarManager();
+		for (int i = 0; i < actions.length - 1; i++)
+			cm.add(actions[i]);
+		
+		cm.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+	}
+
+	private static void fillNewContextMenu(Shell shell, ISelection selection, IMenuManager menu) {
+		IAction newServerAction = new NewServerWizardAction();
+		newServerAction.setText(Messages.actionNewServer);
+		menu.add(newServerAction);
+	}
+
+	protected void fillContextMenu(Shell shell, IMenuManager menu) {
+		// get selection but avoid no selection or multiple selection
+		IServer server = null;
+		IModule[] module = null;
+		IStructuredSelection selection = (IStructuredSelection) tableViewer.getSelection();
+		if (!selection.isEmpty()) {
+			Iterator iterator = selection.iterator();
+			Object obj = iterator.next();
+			if (obj instanceof IServer)
+				server = (IServer) obj;
+			if (obj instanceof ModuleServer) {
+				ModuleServer ms = (ModuleServer) obj;
+				server = ms.server;
+				module = ms.module;
+			}
+			if (iterator.hasNext()) {
+				server = null;
+				module = null;
+			}
+		}
+		
+		// new action
+		MenuManager newMenu = new MenuManager(Messages.actionNew);
+		fillNewContextMenu(null, selection, newMenu);
+		menu.add(newMenu);
+		
+		// open action
+		if (server != null && module == null) {
+			menu.add(openAction);
+			
+			String text = Messages.actionShowIn;
+			final IWorkbench workbench = PlatformUI.getWorkbench();
+			final IBindingService bindingService = (IBindingService) workbench
+					.getAdapter(IBindingService.class);
+			final TriggerSequence[] activeBindings = bindingService
+					.getActiveBindingsFor("org.eclipse.ui.navigate.showInQuickMenu");
+			if (activeBindings.length > 0) {
+				text += "\t" + activeBindings[0].format();
+			}
+			
+			MenuManager showInMenu = new MenuManager(text);
+			showInMenu.add(showInConsoleAction);
+			showInMenu.add(showInDebugAction);
+			//IActionBars actionBars = getViewSite().getActionBars();
+			//actionBars.setGlobalActionHandler("group.show", showInMenu);
+			menu.add(showInMenu);
+			menu.add(new Separator());
+		} else
+			menu.add(new Separator());
+		
+		if (server != null) {
+			if (module == null) {
+				menu.add(copyAction);
+				menu.add(pasteAction);
+				menu.add(deleteAction);
+				menu.add(renameAction);
+			} else if (module.length == 1)
+				menu.add(new RemoveModuleAction(shell, server, module[0]));
+			menu.add(new Separator());
+		}
+		
+		if (server != null && module == null) {
+			// server actions
+			for (int i = 0; i < actions.length; i++)
+				menu.add(actions[i]);
+			
+			menu.add(new Separator());
+			menu.add(actionModifyModules);
+			
+			// monitor
+			if (server.getServerType() != null) {
+				final MenuManager menuManager = new MenuManager(Messages.actionMonitor);
+				
+				final IServer server2 = server;
+				final Shell shell2 = shell;
+				menuManager.addMenuListener(new IMenuListener() {
+					public void menuAboutToShow(IMenuManager manager) {
+						menuManager.removeAll();
+						if (server2.getAdapter(ServerDelegate.class) != null) {
+							ServerPort[] ports = server2.getServerPorts(null);
+							if (ports != null) {
+								int size = ports.length;
+								for (int i = 0; i < size; i++) {
+									if (!ports[i].isAdvanced())
+										menuManager.add(new MonitorServerPortAction(shell2, server2, ports[i]));
+								}
+							}
+						}
+						
+						if (menuManager.isEmpty())
+							menuManager.add(noneAction);
+						
+						menuManager.add(new Separator());
+						menuManager.add(monitorPropertiesAction);
+					}
+				});
+				
+				// add an initial menu item so that the menu appears correctly
+				noneAction.setEnabled(false);
+				menuManager.add(noneAction);
+				menu.add(menuManager);
+			}
+		}
+		
+		if (server != null && module != null) {
+			menu.add(new Separator());
+			menu.add(new StartModuleAction(server, module));
+			menu.add(new StopModuleAction(server, module));			
+			menu.add(new RestartModuleAction(server, module));
+		}
+		
+		menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+		menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS+"-end"));
+		
+		if (server != null) {
+			menu.add(new Separator());
+			menu.add(propertiesAction);
+		}
+	}
+
+	/**
+	 * 
+	 */
+	public void setFocus() {
+		if (treeTable != null)
+			treeTable.setFocus();
+	}
+
+	/**
+    * Adds drag and drop support to the Servers view.
+    */
+   protected void initDragAndDrop() {
+		int ops = DND.DROP_COPY;
+		Transfer[] transfers = new Transfer[] { LocalSelectionTransfer.getInstance(),
+			ResourceTransfer.getInstance(), FileTransfer.getInstance() };
+		//tableViewer.addDragSupport(ops, transfers, new ServersViewDragAdapter(viewer));
+		tableViewer.addDropSupport(ops | DND.DROP_DEFAULT, transfers, new ServersViewDropAdapter(tableViewer));
+   }
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/AbstractTreeContentProvider.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/AbstractTreeContentProvider.java
new file mode 100644
index 0000000..3e5e096
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/AbstractTreeContentProvider.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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.server.ui.internal.viewers;
+
+import java.util.*;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.wst.server.ui.internal.ServerUIPlugin;
+/**
+ * Runtime type content provider.
+ */
+public abstract class AbstractTreeContentProvider implements ITreeContentProvider {
+	public static final String ROOT = "root";
+
+	protected Object initialSelection;
+
+	public class TreeElement {
+		public String text;
+		public List<Object> contents;
+	}
+
+	protected Object[] elements;
+	protected Map<Object, TreeElement> elementToParentMap = new HashMap<Object, TreeElement>(2);
+	protected Map<String, TreeElement> textMap = new HashMap<String, TreeElement>(2);
+
+	/**
+	 * AbstractTreeContentProvider constructor comment.
+	 */
+	public AbstractTreeContentProvider() {
+		super();
+		
+		fillTree();
+	}
+
+	public AbstractTreeContentProvider(boolean init) {
+		super();
+	}
+
+	protected abstract void fillTree();
+
+	protected void clean() {
+		elements = null;
+		elementToParentMap = new HashMap<Object, TreeElement>(2);
+		textMap = new HashMap<String, TreeElement>(2);
+		
+		initialSelection = null;
+	}
+
+	protected TreeElement getOrCreate(List<TreeElement> list, String text) {
+		try {
+			Object obj = textMap.get(text);
+			if (obj != null)
+				return (TreeElement) obj;
+		} catch (Exception e) {
+			return null;
+		}
+		
+		TreeElement element = new TreeElement();
+		element.text = text;
+		element.contents = new ArrayList<Object>();
+		textMap.put(text, element);
+		list.add(element);
+		return element;
+	}
+	
+	protected TreeElement getOrCreate(List<TreeElement> list, String id, String text) {
+		try {
+			Object obj = textMap.get(id);
+			if (obj != null)
+				return (TreeElement) obj;
+		} catch (Exception e) {
+			return null;
+		}
+		
+		TreeElement element = new TreeElement();
+		element.text = text;
+		element.contents = new ArrayList<Object>();
+		textMap.put(id, element);
+		list.add(element);
+		return element;
+	}
+
+	protected TreeElement getByText(String text) {
+		try {
+			return textMap.get(text);
+		} catch (Exception e) {
+			return null;
+		}
+	}
+	
+	protected TreeElement getParentImpl(Object obj) {
+		try {
+			return elementToParentMap.get(obj);
+		} catch (Exception e) {
+			return null;
+		}
+	}
+
+	/**
+	 * Disposes of this content provider.  
+	 * This is called by the viewer when it is disposed.
+	 */
+	public void dispose() {
+		// do nothing
+	}
+
+	/**
+	 * Returns the elements to display in the viewer 
+	 * when its input is set to the given element. 
+	 * These elements can be presented as rows in a table, items in a list, etc.
+	 * The result is not modified by the viewer.
+	 *
+	 * @param element the input element
+	 * @return the array of elements to display in the viewer
+	 */
+	public Object[] getElements(Object element) {
+		return elements;
+	}
+
+	public Object[] getChildren(Object element) {
+		if (!(element instanceof TreeElement))
+			return null;
+		
+		TreeElement rte = (TreeElement) element;
+		return rte.contents.toArray();
+	}
+
+	public Object getParent(Object element) {
+		return getParentImpl(element);
+	}
+
+	public boolean hasChildren(Object element) {
+		Object[] children = getChildren(element);
+		return children != null && children.length > 0;
+	}
+
+	/**
+	 * Notifies this content provider that the given viewer's input
+	 * has been switched to a different element.
+	 * <p>
+	 * A typical use for this method is registering the content provider as a listener
+	 * to changes on the new input (using model-specific means), and deregistering the viewer 
+	 * from the old input. In response to these change notifications, the content provider
+	 * propagates the changes to the viewer.
+	 * </p>
+	 *
+	 * @param viewer the viewer
+	 * @param oldInput the old input element, or <code>null</code> if the viewer
+	 *   did not previously have an input
+	 * @param newInput the new input element, or <code>null</code> if the viewer
+	 *   does not have an input
+	 */
+	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+		// do nothing
+	}
+
+	private Object[] getAllObjects() {
+		List<Object> list = new ArrayList<Object>();
+		Object[] obj = getElements(null);
+		if (obj != null) {
+			int size = obj.length;
+			for (int i = 0; i < size; i++) {
+				if (!(obj[i] instanceof AbstractTreeContentProvider.TreeElement))
+					list.add(obj[i]);
+				getAllChildren(list, obj[i]);
+			}
+		}
+		return list.toArray();
+	}
+
+	private void getAllChildren(List<Object> list, Object element) {
+		Object[] obj = getChildren(element);
+		if (obj != null) {
+			int size = obj.length;
+			for (int i = 0; i < size; i++) {
+				if (!(obj[i] instanceof AbstractTreeContentProvider.TreeElement))
+					list.add(obj[i]);
+				getAllChildren(list, obj[i]);
+			}
+		}
+	}
+
+	public Object getInitialSelection() {
+		if (initialSelection == null) {
+			InitialSelectionProvider isp = ServerUIPlugin.getInitialSelectionProvider();
+			initialSelection = isp.getInitialSelection(getAllObjects());
+		}
+		return initialSelection;
+	}
+	
+	public Object getInitialSelection(IProject project){
+		if (initialSelection == null) {
+			InitialSelectionProvider isp = ServerUIPlugin.getInitialSelectionProvider();
+			initialSelection = isp.getInitialSelection(getAllObjects(),project);
+		}
+		return initialSelection;
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/InitialSelectionProvider.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/InitialSelectionProvider.java
new file mode 100644
index 0000000..248fad9
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/InitialSelectionProvider.java
@@ -0,0 +1,264 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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.server.ui.internal.viewers;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.wst.common.project.facet.core.IFacetedProject;
+import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
+import org.eclipse.wst.server.core.*;
+import org.eclipse.wst.server.core.internal.ResourceManager;
+import org.eclipse.wst.server.core.internal.facets.FacetUtil;
+import org.eclipse.wst.server.ui.internal.ServerUIPlugin;
+import org.eclipse.wst.server.ui.internal.Trace;
+/**
+ * Class used to sort categories, runtime types, and server types in the
+ * New wizards.
+ */
+public class InitialSelectionProvider extends ViewerSorter {
+
+	public Object getInitialSelection(Object[] obj) {
+		if (obj == null || obj.length == 0)
+			return null;
+		
+		if (obj[0] instanceof IRuntimeType) {
+			int size = obj.length;
+			IRuntimeType[] rt = new IRuntimeType[size];
+			for (int i = 0; i < size; i++)
+				rt[i] = (IRuntimeType) obj[i];
+			return getInitialSelection(rt);
+		}
+		
+		if (obj[0] instanceof IServerType) {
+			int size = obj.length;
+			IServerType[] st = new IServerType[size];
+			for (int i = 0; i < size; i++)
+				st[i] = (IServerType) obj[i];
+			return getInitialSelection(st);
+		}
+		
+		if (obj[0] instanceof IServer) {
+			int size = obj.length;
+			IServer[] st = new IServer[size];
+			for (int i = 0; i < size; i++)
+				st[i] = (IServer) obj[i];
+			return getInitialSelection(st);
+		}
+		
+		return null;
+	}
+
+	/**
+	 * 
+	 * @param serverTypes
+	 * @return the initial selection
+	 */
+	public IServerType getInitialSelection(IServerType[] serverTypes) {
+		if (serverTypes == null)
+			return null;
+		
+		int size = serverTypes.length;
+		for (int i = 0; i < size; i++) {
+			if (hasRuntime(serverTypes[i]))
+				return serverTypes[i];
+		}
+		return getDefaultServerType(serverTypes);
+	}
+
+	/**
+	 * 
+	 * @param servers
+	 * @return the initial selection
+	 */
+	public IServer getInitialSelection(IServer[] servers) {
+		return getInitialSelection(servers,null);
+	}
+	
+	/**
+	 * Allows adopters to provide an initial selection out of a list of items.
+	 * <p>
+	 * The <code>IProject</code> can be null, in cases where a project selection was not available (ie: New Server Wizard)
+	 * </p><p>
+	 * Returning <code>null</code> means no object is applicable to be selected. 
+	 * </p>
+	 * 
+	 * @param servers
+	 * @param project
+	 * @return
+	 */
+	public IServer getInitialSelection(IServer[] servers, IProject project){
+		if (servers == null)
+			return null;
+		
+		IServer rval = servers[0];
+		
+		if (project != null){
+			try{
+				// check for the targeted runtime of the project
+				IFacetedProject facetedProject = ProjectFacetsManager.create(project);
+				if (facetedProject != null){
+					org.eclipse.wst.common.project.facet.core.runtime.IRuntime facetedRuntime = facetedProject.getPrimaryRuntime();
+						if (facetedRuntime != null){
+							IRuntime runtime = FacetUtil.getRuntime(facetedRuntime);
+							IServer server = findServerFromRuntime(runtime.getId());
+							if (server != null){
+								rval = server;
+							}
+						}
+					}
+				}
+				catch (CoreException ce){
+					Trace.trace(Trace.WARNING,"Could not create a faceted project",ce);
+				}
+			}
+		return rval;
+	}
+
+	/**
+	 * 
+	 * @param runtimeTypes
+	 * @return the initial selection
+	 */
+	public IRuntimeType getInitialSelection(IRuntimeType[] runtimeTypes) {
+		if (runtimeTypes == null)
+			return null;
+		
+		int size = runtimeTypes.length;
+		for (int i = 0; i < size; i++) {
+			if (hasRuntime(runtimeTypes[i]))
+				return runtimeTypes[i];
+		}
+		return getDefaultRuntimeType(runtimeTypes);
+	}
+
+	protected boolean hasRuntime(IServerType serverType) {
+		return hasRuntime(serverType.getRuntimeType());
+	}
+
+	protected boolean hasRuntime(IRuntimeType runtimeType) {
+		if (runtimeType == null)
+			return false;
+		IRuntime[] runtimes = ServerUIPlugin.getRuntimes(runtimeType);
+		return runtimes != null && runtimes.length > 0;
+	}
+
+	/**
+	 * Returns a default server type, typically the 'first' one sorted
+	 * alphabetically by name.
+	 * 
+	 * @param serverTypes
+	 * @return the default server type
+	 */
+	protected IServerType getDefaultServerType(IServerType[] serverTypes) {
+		if (serverTypes == null)
+			return null;
+		
+		int size = serverTypes.length;
+		if (size == 1)
+			return serverTypes[0];
+		
+		IServerType first = serverTypes[0];
+		for (int i = 1; i < size; i++) {
+			if (DefaultViewerSorter.compareServerTypes(first, serverTypes[i]) > 0)
+				first = serverTypes[i];
+		}
+		return first;
+	}
+
+	/**
+	 * Returns a default runtime type, typically the 'first' one sorted
+	 * alphabetically by name.
+	 * 
+	 * @param runtimeTypes
+	 * @return the default runtime type
+	 */
+	protected IRuntimeType getDefaultRuntimeType(IRuntimeType[] runtimeTypes) {
+		if (runtimeTypes == null)
+			return null;
+		
+		int size = runtimeTypes.length;
+		if (size == 1)
+			return runtimeTypes[0];
+		
+		IRuntimeType first = runtimeTypes[0];
+		for (int i = 1; i < size; i++) {
+			if (DefaultViewerSorter.compareRuntimeTypes(first, runtimeTypes[i]) > 0)
+				first = runtimeTypes[i];
+		}
+		return first;
+	}
+	
+	/**
+	 * Allows adopters to provide an initial selection out of a list of items.
+	 * <p>
+	 * The <code>IProject</code> can be null, in cases where a project selection was not available (ie: New Server Wizard)
+	 * </p><p>
+	 * Returning <code>null</code> means no object is applicable to be selected. 
+	 * </p>
+	 * 
+	 * @param obj Contains an array of all the possible object to be selected. 
+	 * @param project
+	 * @return The object to be selected from the <code>obj[]</code> 
+	 */
+	public Object getInitialSelection(Object [] obj, IProject project){
+		if (obj == null || obj.length == 0)
+			return null;
+		
+		if (obj[0] instanceof IRuntimeType) {
+			int size = obj.length;
+			IRuntimeType[] rt = new IRuntimeType[size];
+			for (int i = 0; i < size; i++)
+				rt[i] = (IRuntimeType) obj[i];
+			return getInitialSelection(rt);
+		}
+		
+		if (obj[0] instanceof IServerType) {
+			int size = obj.length;
+			IServerType[] st = new IServerType[size];
+			for (int i = 0; i < size; i++)
+				st[i] = (IServerType) obj[i];
+			return getInitialSelection(st);
+		}
+		
+		if (obj[0] instanceof IServer) {
+			int size = obj.length;
+			IServer[] st = new IServer[size];
+			for (int i = 0; i < size; i++)
+				st[i] = (IServer) obj[i];
+			return getInitialSelection(st,project);
+		}
+		
+		return null;		
+	}
+	
+	/**
+	 * Returns the server with the given runtime id, or <code>null</code> 
+	 * if none. This convenience method searches the list of registered servers
+	 * for the matching runtime id. The id may not be null.
+	 * 
+	 * @param runtimeId
+	 * @return
+	 */
+	private static IServer findServerFromRuntime(String runtimeId){
+		if (runtimeId == null)
+			throw new IllegalArgumentException();
+			
+		IServer [] servers = ResourceManager.getInstance().getServers();
+		for (IServer server:servers){
+			if (runtimeId == server.getRuntime().getId()){
+				return server;
+			}
+		}
+		return null;
+	}
+	
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/ServerComposite.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/ServerComposite.java
new file mode 100644
index 0000000..2588b10
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/ServerComposite.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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.server.ui.internal.viewers;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.ui.dialogs.FilteredTree;
+
+import org.eclipse.wst.server.core.IModule;
+import org.eclipse.wst.server.core.IServer;
+import org.eclipse.wst.server.ui.internal.Messages;
+import org.eclipse.wst.server.ui.internal.ServerUIPlugin;
+import org.eclipse.wst.server.ui.internal.view.servers.ServerTableLabelProvider;
+/**
+ * 
+ */
+public class ServerComposite extends AbstractTreeComposite {
+	protected IServer selection;
+	protected ServerSelectionListener listener;
+	protected ServerTreeContentProvider contentProvider;
+	protected boolean initialSelection = true;
+
+	protected IModule module;
+	protected String launchMode;
+	protected boolean includeIncompatibleVersions;
+
+	public interface ServerSelectionListener {
+		public void serverSelected(IServer server);
+	}
+
+	public ServerComposite(Composite parent, ServerSelectionListener listener2, IModule module, String launchMode) {
+		super(parent);
+		this.module = module;
+		this.launchMode = launchMode;
+		
+		this.listener = listener2;
+		
+		Tree tree2 = treeViewer.getTree();
+		TreeColumn column = new TreeColumn(tree2, SWT.SINGLE);
+		column.setText(Messages.viewServer);
+		column.setWidth(325);
+		
+		TreeColumn column2 = new TreeColumn(tree2, SWT.SINGLE);
+		column2.setText(Messages.viewState);
+		column2.setWidth(100);
+		
+		contentProvider = new ServerTreeContentProvider(module, launchMode);
+		treeViewer.setContentProvider(contentProvider);
+		
+		//ILabelProvider labelProvider = new ServerTreeLabelProvider();
+		ILabelProvider labelProvider = new ServerTableLabelProvider();
+		labelProvider.addListener(new ILabelProviderListener() {
+			public void labelProviderChanged(LabelProviderChangedEvent event) {
+				Object[] obj = event.getElements();
+				if (obj == null)
+					treeViewer.refresh(true);
+				else {
+					obj = ServerUIPlugin.adaptLabelChangeObjects(obj);
+					int size = obj.length;
+					for (int i = 0; i < size; i++)
+						treeViewer.refresh(obj[i], true);
+				}
+			}
+		});
+		treeViewer.setLabelProvider(labelProvider);
+		treeViewer.setInput(AbstractTreeContentProvider.ROOT);
+		treeViewer.expandToLevel(1);
+		
+		treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				Object obj = getSelection(event.getSelection());
+				if (obj instanceof IServer) {
+					selection = (IServer) obj;
+					setDescription(selection.getServerType().getRuntimeType().getDescription());
+				} else {
+					selection = null;
+					setDescription("");
+				}
+				listener.serverSelected(selection);
+			}
+		});
+	}
+
+	protected void createTree() {
+		tree = new FilteredTree(this, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION, new ServerPatternFilter());
+	}
+
+	public void setIncludeIncompatibleVersions(boolean b) {
+		includeIncompatibleVersions = b;
+		ISelection sel = treeViewer.getSelection();
+		contentProvider.setIncludeIncompatibleVersions(b);
+		treeViewer.refresh();
+		treeViewer.setSelection(sel, true);
+	}
+
+	public void setVisible(boolean visible) {
+		super.setVisible(visible);
+		if (visible && initialSelection) {
+			initialSelection = false;
+			deferInitialization();
+		}
+	}
+
+	public void refreshAll() {
+		ISelection sel = treeViewer.getSelection();
+		contentProvider = new ServerTreeContentProvider(module, launchMode);
+		contentProvider.setIncludeIncompatibleVersions(includeIncompatibleVersions);
+		treeViewer.setContentProvider(contentProvider);
+		treeViewer.setSelection(sel);
+	}
+
+	protected String getDescriptionLabel() {
+		return null;
+	}
+
+	protected String getTitleLabel() {
+		return Messages.wizNewServerSelectExisting;
+	}
+
+	public IServer getSelectedServer() {
+		return selection;
+	}
+
+	public void setSelection(IServer server) {
+		if (server != null)
+			treeViewer.setSelection(new StructuredSelection(server), true);
+		else
+			treeViewer.setSelection(null);
+	}
+
+	protected void deferInitialization() {
+		Job job = new Job(Messages.jobInitializingServersView) {
+			public IStatus run(IProgressMonitor monitor) {
+				Display.getDefault().asyncExec(new Runnable() {
+					public void run() {
+						try {
+							if (contentProvider.getInitialSelection(module.getProject()) != null)
+								treeViewer.setSelection(new StructuredSelection(contentProvider.getInitialSelection(module.getProject())), true);
+						} catch (Exception e) {
+							// ignore - wizard has already been closed
+						}
+					}
+				});
+				return Status.OK_STATUS;
+			}
+		};
+		
+		job.setSystem(true);
+		job.setPriority(Job.SHORT);
+		job.schedule();
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/ModifyModulesWizard.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/ModifyModulesWizard.java
index 1c6b776..9cdc191 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/ModifyModulesWizard.java
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/ModifyModulesWizard.java
@@ -16,8 +16,8 @@
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.wst.server.core.*;
-import org.eclipse.wst.server.core.internal.Server;
 import org.eclipse.wst.server.ui.internal.Messages;
+import org.eclipse.wst.server.ui.internal.ServerUIPlugin;
 import org.eclipse.wst.server.ui.internal.wizard.fragment.ModifyModulesWizardFragment;
 import org.eclipse.wst.server.ui.wizard.WizardFragment;
 /**
@@ -26,7 +26,7 @@
 public class ModifyModulesWizard extends TaskWizard {
 	static class ModifyModulesWizard2 extends WizardFragment {
 		protected void createChildFragments(List<WizardFragment> list) {
-			list.add(new ModifyModulesWizardFragment());
+			list.add(new ModifyModulesWizardFragment(true));
 			list.add(WizardTaskUtil.SaveServerFragment);
 			
 			list.add(new WizardFragment() {
@@ -34,7 +34,8 @@
 					IServerAttributes svr = (IServerAttributes) getTaskModel().getObject(TaskModel.TASK_SERVER);
 					if (svr instanceof IServer) {
 						IServer server = (IServer) svr;
-						if (server.getServerState() != IServer.STATE_STOPPED && ((Server)server).getAutoPublishSetting() != Server.AUTO_PUBLISH_DISABLE) {
+						if (server.getServerState() != IServer.STATE_STOPPED &&
+								ServerUIPlugin.getPreferences().getPublishOnAddRemoveModule()) {
 							IAdaptable info = null;
 							/*IAdaptable info = new IAdaptable() {
 								public Object getAdapter(Class adapter) {
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/ModifyModulesWizardFragment.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/ModifyModulesWizardFragment.java
index 02b9e09..498555d 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/ModifyModulesWizardFragment.java
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/ModifyModulesWizardFragment.java
@@ -19,6 +19,7 @@
 import org.eclipse.wst.server.core.*;
 import org.eclipse.wst.server.core.internal.IModuleVisitor;
 import org.eclipse.wst.server.core.internal.Server;
+import org.eclipse.wst.server.ui.internal.ServerUIPlugin;
 import org.eclipse.wst.server.ui.internal.Trace;
 import org.eclipse.wst.server.ui.internal.wizard.WizardTaskUtil;
 import org.eclipse.wst.server.ui.internal.wizard.page.ModifyModulesComposite;
@@ -31,11 +32,16 @@
 	protected ModifyModulesComposite comp;
 
 	protected IModule module;
+	protected boolean showPublishOption;
 
 	public ModifyModulesWizardFragment() {
 		// do nothing
 	}
 
+	public ModifyModulesWizardFragment(boolean showPublishOption) {
+		this.showPublishOption = showPublishOption;
+	}
+
 	public ModifyModulesWizardFragment(IModule module) {
 		this.module = module;
 	}
@@ -48,7 +54,7 @@
 	 * @see org.eclipse.wst.server.ui.internal.task.WizardTask#getWizardPage()
 	 */
 	public Composite createComposite(Composite parent, IWizardHandle handle) {
-		comp = new ModifyModulesComposite(parent, handle, module);
+		comp = new ModifyModulesComposite(parent, handle, module, showPublishOption);
 		return comp;
 	}
 
@@ -120,7 +126,10 @@
 	}
 
 	public void performFinish(IProgressMonitor monitor) throws CoreException {
-		if (comp != null)
+		if (comp != null) {
 			WizardTaskUtil.modifyModules(comp.getModulesToAdd(), comp.getModulesToRemove(), getTaskModel(), monitor);
+			if (showPublishOption)
+				ServerUIPlugin.getPreferences().setPublishOnAddRemoveModule(comp.shouldPublishImmediately());
+		}
 	}
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/TasksWizardFragment.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/TasksWizardFragment.java
new file mode 100644
index 0000000..5bc5f47
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/TasksWizardFragment.java
@@ -0,0 +1,305 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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.server.ui.internal.wizard.fragment;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.wst.server.core.*;
+import org.eclipse.wst.server.core.internal.*;
+import org.eclipse.wst.server.core.model.PublishOperation;
+import org.eclipse.wst.server.ui.internal.editor.IOrdered;
+import org.eclipse.wst.server.ui.internal.wizard.WizardTaskUtil;
+import org.eclipse.wst.server.ui.wizard.WizardFragment;
+/**
+ * 
+ */
+public class TasksWizardFragment extends WizardFragment {
+	private static final int TASKS_PER_PAGE = 5;
+
+	public class TaskInfo implements IOrdered {
+		public int kind;
+		public String id;
+		public PublishOperation task2;
+		
+		private static final String DEFAULT = "default:";
+		
+		public boolean isSelected() {
+			if (id == null)
+				return false;
+			
+			if (selectedTaskMap.containsKey(id))
+				return (selectedTaskMap.get(id)).booleanValue();
+			
+			if (selectedTaskMap.containsKey(DEFAULT + id))
+				return (selectedTaskMap.get(DEFAULT + id)).booleanValue();
+
+			return false;
+		}
+		
+		public void setDefaultSelected(boolean sel) {
+			selectedTaskMap.put(DEFAULT + getId(), new Boolean(sel));
+		}
+
+		public boolean getDefaultSelected() {
+			return (selectedTaskMap.get(DEFAULT + id)).booleanValue();
+		}
+
+		public void setSelected(boolean sel) {
+			selectedTaskMap.put(getId(), new Boolean(sel));
+		}
+		
+		public int getOrder() {
+			return task2.getOrder();
+		}
+		
+		protected String getId() {
+			return id;
+		}
+		
+		public boolean equals(Object obj) {
+			if (!(obj instanceof TaskInfo))
+				return false;
+			
+			TaskInfo ti = (TaskInfo) obj;
+			if (kind != ti.kind)
+				return false;
+			
+			if (id == null || !id.equals(ti.id))
+				return false;
+			
+			if (task2 == null && ti.task2 != null)
+				return false;
+			
+			if (task2 != null && ti.task2 == null)
+				return false;
+			
+			try {
+				if (task2 != null && ti.task2 != null) {
+					if (task2.getKind() != ti.task2.getKind())
+						return false;
+					if (task2.getOrder() != ti.task2.getOrder())
+						return false;
+					if (!task2.getLabel().equals(ti.task2.getLabel()))
+						return false;
+					if (!task2.getDescription().equals(ti.task2.getDescription()))
+						return false;
+				}
+			} catch (Exception e) {
+				// ignore
+			}
+			return true;
+		}
+	}
+
+	protected List<TaskInfo> tasks;
+	protected boolean hasOptionalTasks;
+	protected boolean hasPreferredTasks;
+
+	protected Map<String, Boolean> selectedTaskMap = new HashMap<String, Boolean>();
+
+	public TasksWizardFragment() {
+		// do nothing
+	}
+
+	protected void createChildFragments(List<WizardFragment> list) {
+		if (tasks == null || tasks.isEmpty())
+			return;
+		
+		int size = tasks.size();
+		int pages = (size - 1) / TASKS_PER_PAGE + 1;
+		for (int i = 0; i < pages; i++) {
+			SubTasksWizardFragment fragment = new SubTasksWizardFragment();
+			List list2 = tasks.subList(TASKS_PER_PAGE * i, Math.min(size, TASKS_PER_PAGE * (i+1)));
+			fragment.updateTasks(list2);
+			list.add(fragment);
+		}
+	}
+
+	public void enter() {
+		updateTasks();
+	}
+
+	public List getChildFragments() {
+		updateTasks();
+		return super.getChildFragments();
+	}
+
+	public void setTaskModel(TaskModel taskModel) {
+		super.setTaskModel(taskModel);
+		updateTasks();
+	}
+
+	public void updateTasks() {
+		if (getTaskModel() == null) {
+			tasks = null;
+			return;
+		}
+		
+		IServerAttributes server = (IServerAttributes) getTaskModel().getObject(TaskModel.TASK_SERVER);
+		List modules = (List) getTaskModel().getObject(TaskModel.TASK_MODULES);
+		
+		if (server != null && modules == null) {
+			final List<IModule[]> moduleList = new ArrayList<IModule[]>();
+			((Server) server).visit(new IModuleVisitor() {
+				public boolean visit(IModule[] module2) {
+					moduleList.add(module2);
+					return true;
+				}
+			}, null);
+			
+			modules = moduleList;
+		}
+		
+		if (server != null && modules != null) {
+			hasOptionalTasks = false;
+			hasPreferredTasks = false;
+			List<TaskInfo> taskList = new ArrayList<TaskInfo>(5);
+			createTasks(taskList, server, modules);
+			
+			if (tasks == null || !tasks.equals(taskList)) {
+				tasks = taskList;
+				updateChildFragments();
+				
+				boolean b = hasOptionalTasks || hasPreferredTasks;
+				getTaskModel().putObject(WizardTaskUtil.TASK_HAS_TASKS, new Boolean(b));				
+			}
+		}
+	}
+
+	protected void createTasks(List<TaskInfo> taskList, IServerAttributes server, List modules) {
+		if (server == null)
+			return;
+		
+		List<String> enabledTasks = ((Server)server).getEnabledOptionalPublishOperationIds();
+		List<String> disabledTasks = ((Server)server).getDisabledPreferredPublishOperationIds();
+		PublishOperation[] tasks2 = ((Server)server).getAllTasks(modules);
+		for (int j = 0; j < tasks2.length; j++) {
+			int kind = tasks2[j].getKind();
+			String id = ((Server)server).getPublishOperationId(tasks2[j]);
+			if (kind == PublishOperation.OPTIONAL || kind == PublishOperation.PREFERRED)
+				hasOptionalTasks = true;
+			if (kind == PublishOperation.PREFERRED)
+				hasPreferredTasks = true;
+			tasks2[j].setTaskModel(getTaskModel());
+			
+			boolean selected = true;
+			if (kind == PublishOperation.OPTIONAL) {
+				if (!enabledTasks.contains(id))
+					selected = false;
+			} else if (kind == PublishOperation.PREFERRED) {
+				if (disabledTasks.contains(id))
+					selected = false;
+			}
+			taskList.add(getServerTask(server, tasks2[j], selected));
+		}
+	}
+
+	public TaskInfo getServerTask(IServerAttributes server, PublishOperation task2, boolean selected) {
+		TaskInfo sti = new TaskInfo();
+		sti.task2 = task2;
+		sti.kind = task2.getKind();
+		sti.id = ((Server)server).getPublishOperationId(task2);
+		sti.setDefaultSelected(selected);
+		
+		return sti;
+	}
+
+	/**
+	 * @see WizardFragment#performFinish(IProgressMonitor)
+	 */
+	public void performFinish(IProgressMonitor monitor) throws CoreException {
+		if (!hasOptionalTasks)
+			return;
+		
+		if (tasks == null || tasks.isEmpty())
+			return;
+		
+		TaskModel taskModel = getTaskModel();
+		IServer server = (IServer)taskModel.getObject(TaskModel.TASK_SERVER);
+		if (server == null)
+			return;
+		
+		boolean createdWC = false;
+		ServerWorkingCopy wc = null;
+		if (server instanceof ServerWorkingCopy)
+			wc = (ServerWorkingCopy)server;
+		else {
+			wc = (ServerWorkingCopy)server.createWorkingCopy();
+			createdWC = true;
+		}
+		
+		// compare lists
+		List<String> disabled = new ArrayList<String>();
+		List<String> enabled = new ArrayList<String>();
+		Iterator iterator = tasks.iterator();
+		while (iterator.hasNext()) {
+			TaskInfo task = (TaskInfo)iterator.next();
+			if (PublishOperation.REQUIRED == task.kind)
+				continue;
+			
+			String id = wc.getPublishOperationId(task.task2);
+			if (PublishOperation.PREFERRED == task.kind) {
+				if (!task.isSelected())
+					disabled.add(id);
+			} else if (PublishOperation.OPTIONAL == task.kind) {
+				if (task.isSelected())
+					enabled.add(id);
+			}
+		}
+		
+		List<String> curDisabled = wc.getDisabledPreferredPublishOperationIds();
+		List<String> curEnabled = wc.getEnabledOptionalPublishOperationIds();
+		
+		boolean different = false;
+		if (curEnabled.size() != enabled.size() || curDisabled.size() != disabled.size()) {
+			different = true;
+		} else {
+			for (String id : curDisabled) {
+				if (!disabled.contains(id))
+					different = true;
+			}
+			for (String id : curEnabled) {
+				if (!enabled.contains(id))
+					different = true;
+			}
+		}
+		
+		if (different) {
+			wc.resetPreferredPublishOperations();
+			wc.resetOptionalPublishOperations();
+			
+			Iterator<TaskInfo> iterator2 = tasks.iterator();
+			while (iterator2.hasNext()) {
+				TaskInfo task = iterator2.next();
+				if (PublishOperation.REQUIRED == task.kind)
+					continue;
+				
+				if (PublishOperation.PREFERRED == task.kind) {
+					if (!task.isSelected())
+						wc.disablePreferredPublishOperations(task.task2);
+				} else if (PublishOperation.OPTIONAL == task.kind) {
+					if (task.isSelected())
+						wc.enableOptionalPublishOperations(task.task2);
+				}
+			}
+		}
+		
+		if (createdWC && wc.isDirty())
+			wc.save(true, monitor);
+		monitor.done();
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/page/ModifyModulesComposite.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/page/ModifyModulesComposite.java
new file mode 100644
index 0000000..454ad8b
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/page/ModifyModulesComposite.java
@@ -0,0 +1,946 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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.server.ui.internal.wizard.page;
+
+import java.util.*;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.wst.server.core.*;
+import org.eclipse.wst.server.ui.ServerUICore;
+import org.eclipse.wst.server.ui.internal.*;
+import org.eclipse.wst.server.ui.internal.view.servers.ModuleServer;
+import org.eclipse.wst.server.ui.wizard.IWizardHandle;
+/**
+ * A wizard page used to add and remove modules.
+ */
+public class ModifyModulesComposite extends Composite {
+	public static final String TASK_REFRESH_MODULES = "refresh-modules";
+
+	private static final String ROOT = "root";
+	protected static Color color;
+	protected static Font font;
+
+	protected IWizardHandle wizard;
+
+	protected IServerAttributes server;
+	protected IRuntime runtime;
+	protected boolean runtimeDirty;
+	protected Object refreshModules;
+	protected boolean showPublishOption;
+	protected boolean publishImmediately;
+
+	protected Map<ChildModuleMapKey, IModule[]> childModuleMap = new HashMap<ChildModuleMapKey, IModule[]>();
+	protected Map<IModule, IModule[]> parentModuleMap = new HashMap<IModule, IModule[]>();
+
+	// original modules on the server
+	protected List<IModule> originalModules = new ArrayList<IModule>();
+
+	// modules available to be added to the server
+	protected List<IModule> modules = new ArrayList<IModule>();
+
+	// current modules on the server
+	protected List<IModule> deployed = new ArrayList<IModule>();
+
+	protected TreeViewer availableTreeViewer;
+	protected TreeViewer deployedTreeViewer;
+
+	protected Button add, addAll;
+	protected Button remove, removeAll;
+
+	protected TaskModel taskModel;
+
+	// a module that must be kept on the server
+	protected IModule requiredModule;
+	protected boolean isComplete = true;
+
+	// the parent modules of the above modules. at least one of these modules
+	// must be kept on the server
+	protected IModule[] requiredModules;
+
+	protected Map<IModule, IStatus> errorMap;
+
+	abstract class TreeContentProvider implements ITreeContentProvider {
+		public void dispose() {
+			// do nothing
+		}
+
+		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+			// do nothing
+		}
+		
+		public Object[] getChildren(Object parentElement) {
+			ModuleServer ms = (ModuleServer) parentElement;
+			IModule[] parent = ms.module;
+			IModule[] children = childModuleMap.get(new ChildModuleMapKey(parent));
+			
+			List<ModuleServer> list = new ArrayList<ModuleServer>();
+			if (children != null) {
+				int size = children.length;
+				for (int i = 0; i < size; i++) {
+					IModule child = children[i];
+					
+					parentModuleMap.put(child, parent);
+					
+					int size2 = parent.length;
+					IModule[] module2 = new IModule[size2 + 1];
+					System.arraycopy(parent, 0, module2, 0, size2);
+					module2[size2] = child;
+					list.add(new ModuleServer(null, module2));
+				}
+			}
+			return list.toArray();
+		}
+
+		public Object getParent(Object element) {
+			ModuleServer ms = (ModuleServer) element;
+			IModule[] child = ms.module;
+			IModule[] modules2 = parentModuleMap.get(child);
+			if (modules2 == null)
+				return null;
+			return new ModuleServer(null, modules2);
+		}
+
+		public boolean hasChildren(Object element) {
+			ModuleServer ms = (ModuleServer) element;
+			IModule[] parent = ms.module;
+			IModule[] children = childModuleMap.get(new ChildModuleMapKey(parent));
+			return (children != null && children.length > 0);
+		}
+	}
+
+	class AvailableContentProvider extends TreeContentProvider {
+		public Object[] getElements(Object inputElement) {
+			List<ModuleServer> list = new ArrayList<ModuleServer>();
+			Iterator iterator = modules.iterator();
+			while (iterator.hasNext()) {
+				IModule module = (IModule) iterator.next();
+				list.add(new ModuleServer(null, new IModule[] { module }));
+			}
+			return list.toArray();
+		}
+	}
+
+	class DeployedContentProvider extends TreeContentProvider {
+		public Object[] getElements(Object inputElement) {
+			List<ModuleServer> list = new ArrayList<ModuleServer>();
+			Iterator iterator = deployed.iterator();
+			while (iterator.hasNext()) {
+				IModule module = (IModule) iterator.next();
+				list.add(new ModuleServer(null, new IModule[] { module }));
+			}
+			return list.toArray();
+		}
+	}
+
+	/**
+	 * The key element for the child module map
+	 * ChildMapModuleKey
+	 */
+	protected class ChildModuleMapKey {
+		protected IModule[] moduleTree;
+		
+		protected ChildModuleMapKey(IModule curModule) {
+			if (curModule != null) {
+				moduleTree = new IModule[] { curModule };
+			}
+		}
+		
+		protected ChildModuleMapKey(IModule[] curModuleTree) {
+			moduleTree = curModuleTree;
+		}
+		
+		public boolean equals(Object obj) {
+			if (obj == this) // same object
+				return true;
+			
+			if (!(obj instanceof ChildModuleMapKey))
+				return false;
+				
+			IModule[] curCompareModule = ((ChildModuleMapKey) obj).moduleTree;
+			if (curCompareModule == moduleTree) {
+				// the module tree is the same
+				return true;
+			} else if (moduleTree == null || curCompareModule == null || moduleTree.length != curCompareModule.length){
+				return false;
+			} else {
+				// compare each module
+				for (int i = 0; i < curCompareModule.length; i++) {
+					if (!curCompareModule[i].equals(moduleTree[i]))
+						return false;
+				}
+				return true;
+			}
+		}
+
+		public int hashCode() {
+			// force the same hash code on all the instances to makes sure the
+			// equals(Object) method is being used for comparing the objects
+			return 12345;
+		}
+	}
+
+	/**
+	 * Create a new ModifyModulesComposite.
+	 * 
+	 * @param parent a parent composite
+	 * @param wizard a wizard
+	 * @param module the module that is being added
+	 */
+	public ModifyModulesComposite(Composite parent, IWizardHandle wizard, IModule module) {
+		super(parent, SWT.NONE);
+		this.wizard = wizard;
+		requiredModule = module;
+		
+		wizard.setTitle(Messages.wizModuleTitle);
+		wizard.setDescription(Messages.wizModuleDescription);
+		wizard.setImageDescriptor(ImageResource.getImageDescriptor(ImageResource.IMG_WIZBAN_SELECT_SERVER));
+		
+		createControl();
+	}
+
+	/**
+	 * Create a new ModifyModulesComposite.
+	 * 
+	 * @param parent a parent composite
+	 * @param wizard a wizard
+	 * @param module the module that is being added
+	 * @param showPublishOption show the publishing options
+	 */
+	public ModifyModulesComposite(Composite parent, IWizardHandle wizard, IModule module, boolean showPublishOption) {
+		super(parent, SWT.NONE);
+		this.wizard = wizard;
+		requiredModule = module;
+		this.showPublishOption = showPublishOption;
+		
+		wizard.setTitle(Messages.wizModuleTitle);
+		wizard.setDescription(Messages.wizModuleDescription);
+		wizard.setImageDescriptor(ImageResource.getImageDescriptor(ImageResource.IMG_WIZBAN_SELECT_SERVER));
+		
+		createControl();
+	}
+
+	public void setServer(IServerAttributes server) {
+		if (isVisible())
+			return;
+		
+		// see bug 185875, 205869
+		if (refreshModules == taskModel.getObject(TASK_REFRESH_MODULES) && server == this.server) {
+			if (server == null)
+				return;
+			if (runtime == server.getRuntime()) {
+				if (runtime == null)
+					return;
+				if (runtime instanceof IRuntimeWorkingCopy) {
+					IRuntimeWorkingCopy wc = (IRuntimeWorkingCopy) runtime;
+					if (wc.isDirty() == runtimeDirty)
+						return;
+				} else
+					return;
+			}
+		}
+		
+		refreshModules = taskModel.getObject(TASK_REFRESH_MODULES);
+		this.server = server;
+		if (server == null)
+			runtime = null;
+		else
+			runtime = server.getRuntime();
+		runtimeDirty = false;
+		if (runtime != null) {
+			if (runtime instanceof IRuntimeWorkingCopy) {
+				IRuntimeWorkingCopy wc = (IRuntimeWorkingCopy) runtime;
+				if (wc.isDirty())
+					runtimeDirty = true;
+			}
+		}
+		
+		originalModules = new ArrayList<IModule>();
+		deployed = new ArrayList<IModule>();
+		modules = new ArrayList<IModule>();
+		
+		childModuleMap = new HashMap<ChildModuleMapKey, IModule[]>();
+		
+		if (server == null)
+			return;
+		
+		System.setProperty("J2EEcache", "" + Math.random());
+		
+		// get currently deployed modules
+		IModule[] currentModules = server.getModules();
+		if (currentModules != null) {
+			int size = currentModules.length;
+			for (int i = 0; i < size; i++) {
+				originalModules.add(currentModules[i]);
+				deployed.add(currentModules[i]);
+			}
+		}
+		
+		// add new module
+		requiredModules = null;
+		errorMap = new HashMap<IModule, IStatus>();
+		if (requiredModule != null) {
+			try {
+				IModule[] parents = server.getRootModules(requiredModule, null);
+				if (parents != null && parents.length > 0)
+					requiredModules = parents;
+				else
+					requiredModules = new IModule[] { requiredModule };
+			} catch (CoreException ce) {
+				// ignore
+				//errorMap.put(newModule, ce.getStatus());
+				Trace.trace(Trace.INFO, "A possible server implementation error", ce);
+			} catch (Exception e) {
+				Trace.trace(Trace.WARNING, "Could not find root module", e);
+			}
+		}
+		if (requiredModules != null && !deployed.contains(requiredModules[0]))
+			deployed.add(requiredModules[0]);
+		
+		// get remaining modules
+		IModule[] modules2 = ServerUtil.getModules(server.getServerType().getRuntimeType().getModuleTypes());
+		if (modules2 != null) {
+			int size = modules2.length;
+			for (int i = 0; i < size; i++) {
+				IModule module = modules2[i];
+				if (!deployed.contains(module)) {
+					try {
+						IModule[] parents = server.getRootModules(module, null);
+						if (parents != null) {
+							int size2 = parents.length;
+							for (int j = 0; j < size2; j++) {
+								if (parents[j].equals(module)) {
+									IStatus status = server.canModifyModules(new IModule[] { module }, null, null);
+									if (status != null && !status.isOK())
+										errorMap.put(module, status);
+									modules.add(module);
+								}
+							}
+						}
+					} catch (CoreException ce) {
+						errorMap.put(module, ce.getStatus());
+						modules.add(module);
+					}
+				}
+			}
+		}
+		
+		// build child map
+		Iterator iterator = deployed.iterator();
+		while (iterator.hasNext()) {
+			IModule module = (IModule) iterator.next();
+			try {
+				IModule[] children = server.getChildModules(new IModule[] { module }, null);
+				if (children != null && children.length > 0)
+					childModuleMap.put(new ChildModuleMapKey(module), children);
+			} catch (Exception e) {
+				// ignore
+			}
+		}
+		
+		iterator = modules.iterator();
+		while (iterator.hasNext()) {
+			IModule module = (IModule) iterator.next();
+			try {
+				IModule[] children = server.getChildModules(new IModule[] { module }, null);
+				if (children != null && children.length > 0)
+					childModuleMap.put(new ChildModuleMapKey(module), children);
+			} catch (Exception e) {
+				// ignore
+			}
+		}
+		
+		// get children recursively one level
+		// put child elements into a different list to avoid concurrent modifications
+		Iterator<ChildModuleMapKey> iterator2 = childModuleMap.keySet().iterator();
+		List<ChildModuleMapKey> list = new ArrayList<ChildModuleMapKey>();
+		while (iterator2.hasNext()) {
+			list.add(iterator2.next());
+		}
+		
+		iterator = list.iterator();
+		while (iterator.hasNext()) {
+			ChildModuleMapKey key = (ChildModuleMapKey) iterator.next();
+			IModule[] children0 = childModuleMap.get(key);
+			if (children0 != null) {
+				int size = children0.length;
+				for (int i = 0; i < size; i++) {
+					int size2 = key.moduleTree.length;
+					IModule[] module2 = new IModule[size2 + 1];
+					System.arraycopy(key.moduleTree, 0, module2, 0, size2);
+					module2[size2] = children0[i];
+					
+					try {
+						IModule[] children = server.getChildModules(module2, null);
+						if (children != null && children.length > 0)
+							childModuleMap.put(new ChildModuleMapKey(module2), children);
+					} catch (Exception e) {
+						// ignore
+					}
+				}
+			}
+		}
+		
+		System.setProperty("J2EEcache", "");
+		
+		updateTaskModel();
+	}
+
+	public void setVisible(boolean b) {
+		if (b) {
+			Display.getDefault().syncExec(new Runnable() {
+				public void run() {
+					if (availableTreeViewer == null || availableTreeViewer.getControl().isDisposed())
+						return;
+					try { // update trees if we can
+						availableTreeViewer.refresh();
+						deployedTreeViewer.refresh();
+						setEnablement();
+					} catch (Exception e) {
+						// ignore
+					}
+				}
+			});
+		}
+		super.setVisible(b);
+	}
+
+	public void setTaskModel(TaskModel model) {
+		this.taskModel = model;
+	}
+
+	/**
+	 * Creates the UI of the page.
+	 */
+	protected void createControl() {
+		GridLayout layout = new GridLayout();
+		layout.horizontalSpacing = SWTUtil.convertHorizontalDLUsToPixels(this, 4);
+		layout.verticalSpacing = SWTUtil.convertVerticalDLUsToPixels(this, 4);
+		layout.numColumns = 3;
+		setLayout(layout);
+		setFont(getParent().getFont());
+		PlatformUI.getWorkbench().getHelpSystem().setHelp(this, ContextIds.MODIFY_MODULES_COMPOSITE);
+		
+		Display display = getDisplay();
+		color = display.getSystemColor(SWT.COLOR_DARK_GRAY);
+		FontData[] fd = getFont().getFontData();
+		int size2 = fd.length;
+		for (int i = 0; i < size2; i++)
+			fd[i].setStyle(SWT.ITALIC);
+		font = new Font(display, fd);
+		addDisposeListener(new DisposeListener() {
+			public void widgetDisposed(DisposeEvent e) {
+				font.dispose();
+			}
+		});
+		
+		Label label = new Label(this, SWT.NONE);
+		GridData data = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING);
+		data.horizontalSpan = 3;
+		label.setLayoutData(data);
+		label.setText(Messages.wizModuleMessage);
+		
+		label = new Label(this, SWT.NONE);
+		label.setText(Messages.wizModuleAvailableList);
+		
+		label = new Label(this, SWT.NONE);
+		label.setText("");
+		
+		label = new Label(this, SWT.NONE);
+		label.setText(Messages.wizModuleDeployedList);
+		
+		Tree availableTree = new Tree(this, SWT.BORDER | SWT.MULTI);
+		data = new GridData(GridData.FILL_BOTH);
+		data.heightHint = 200;
+		data.widthHint = 150;
+		availableTree.setLayoutData(data);
+		
+		availableTreeViewer = new TreeViewer(availableTree);
+		ILabelProvider labelProvider = ServerUICore.getLabelProvider();
+		labelProvider.addListener(new ILabelProviderListener() {
+			public void labelProviderChanged(LabelProviderChangedEvent event) {
+				Object[] obj = event.getElements();
+				if (obj == null)
+					availableTreeViewer.refresh(true);
+				else {
+					obj = ServerUIPlugin.adaptLabelChangeObjects(obj);
+					int size = obj.length;
+					for (int i = 0; i < size; i++)
+						availableTreeViewer.refresh(obj[i], true);
+				}
+			}
+		});
+		availableTreeViewer.setLabelProvider(labelProvider);
+		availableTreeViewer.setContentProvider(new AvailableContentProvider());
+		availableTreeViewer.setComparator(new ViewerComparator() {
+			public int compare(Viewer viewer, Object e1, Object e2) {
+				if (e1 instanceof ModuleServer && e2 instanceof ModuleServer) {
+					ModuleServer s1 = (ModuleServer) e1;
+					ModuleServer s2 = (ModuleServer) e2;
+					return (s1.module[s1.module.length - 1].getName().compareToIgnoreCase(s2.module[s2.module.length - 1].getName()));
+				}
+				
+				return super.compare(viewer, e1, e2);
+			}
+		});
+		availableTreeViewer.setInput(ROOT);
+		
+		availableTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				setEnablement();
+			}
+		});
+		availableTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
+			public void doubleClick(DoubleClickEvent event) {
+				setEnablement();
+				if (add.isEnabled())
+					add(false);
+			}
+		});
+		
+		// slosh buttons
+		Composite comp = new Composite(this, SWT.NONE);
+		data = new GridData(GridData.FILL_BOTH);
+		data.widthHint = 120;
+		comp.setLayoutData(data);
+		
+		layout = new GridLayout();
+		layout.marginWidth = 5;
+		layout.marginHeight = 25;
+		layout.verticalSpacing = 20;
+		comp.setLayout(layout);
+		
+		add = new Button(comp, SWT.PUSH);
+		add.setText(Messages.wizModuleAdd);
+		add.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+		add.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent event) {
+				add(false);
+			}
+		});
+		
+		remove = new Button(comp, SWT.PUSH);
+		remove.setText(Messages.wizModuleRemove);
+		remove.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+		remove.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent event) {
+				remove(false);
+			}
+		});
+		
+		label = new Label(comp, SWT.NONE);
+		label.setText("");
+		
+		addAll = new Button(comp, SWT.PUSH);
+		addAll.setText(Messages.wizModuleAddAll);
+		addAll.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+		addAll.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent event) {
+				add(true);
+			}
+		});
+		
+		removeAll = new Button(comp, SWT.PUSH);
+		removeAll.setText(Messages.wizModuleRemoveAll);
+		removeAll.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+		removeAll.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent event) {
+				remove(true);
+			}
+		});
+		
+		Tree deployedTree = new Tree(this, SWT.BORDER | SWT.MULTI);
+		data = new GridData(GridData.FILL_BOTH);
+		data.widthHint = 150;
+		deployedTree.setLayoutData(data);
+		
+		deployedTreeViewer = new TreeViewer(deployedTree) {
+			public void doUpdateItem(Widget widget, Object element, boolean fullMap) {
+				if (widget instanceof TreeItem && color != null) {
+					TreeItem item = (TreeItem) widget;
+					if (element instanceof ModuleServer) {
+						ModuleServer ms = (ModuleServer) element;
+						IModule m = ms.module[ms.module.length-1];
+						if (m.isExternal())
+							item.setForeground(color);
+						else
+							item.setForeground(null);
+						if (!(server instanceof IServer) || ((IServer)server).getModulePublishState(ms.module) != IServer.PUBLISH_STATE_NONE)
+							item.setFont(font);
+						else
+							item.setFont(null);
+					}
+				}
+				super.doUpdateItem(widget, element, fullMap);
+			}
+		};
+		labelProvider = ServerUICore.getLabelProvider();
+		labelProvider.addListener(new ILabelProviderListener() {
+			public void labelProviderChanged(LabelProviderChangedEvent event) {
+				Object[] obj = event.getElements();
+				if (obj == null)
+					deployedTreeViewer.refresh(true);
+				else {
+					int size = obj.length;
+					for (int i = 0; i < size; i++)
+						deployedTreeViewer.refresh(obj[i], true);
+				}
+			}
+		});
+		deployedTreeViewer.setLabelProvider(labelProvider);
+		deployedTreeViewer.setContentProvider(new DeployedContentProvider());
+		deployedTreeViewer.setComparator(new ViewerComparator() {
+			public int compare(Viewer viewer, Object e1, Object e2) {
+				if (e1 instanceof ModuleServer && e2 instanceof ModuleServer) {
+					ModuleServer s1 = (ModuleServer) e1;
+					ModuleServer s2 = (ModuleServer) e2;
+					return (s1.module[s1.module.length - 1].getName().compareToIgnoreCase(s2.module[s2.module.length - 1].getName()));
+				}
+				
+				return super.compare(viewer, e1, e2);
+			}
+		});
+		deployedTreeViewer.setInput(ROOT);
+		
+		deployedTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				setEnablement();
+			}
+		});
+		deployedTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
+			public void doubleClick(DoubleClickEvent event) {
+				setEnablement();
+				if (remove.isEnabled())
+					remove(false);
+			}
+		});
+		
+		if (showPublishOption) {
+			final Button publish = new Button(this, SWT.CHECK);
+			publish.setText(Messages.wizModulePublishImmediately);
+			data = new GridData(GridData.FILL_HORIZONTAL);
+			data.horizontalSpan = 3;
+			publish.setLayoutData(data);
+			
+			publishImmediately = ServerUIPlugin.getPreferences().getPublishOnAddRemoveModule();
+			publish.setSelection(publishImmediately);
+			publish.addSelectionListener(new SelectionAdapter() {
+				public void widgetSelected(SelectionEvent event) {
+					publishImmediately = publish.getSelection();
+				}
+			});
+		}
+		
+		setEnablement();
+		availableTree.setFocus();
+		
+		Dialog.applyDialogFont(this);
+	}
+
+	protected ModuleServer[] getAvailableSelection() {
+		IStructuredSelection sel = (IStructuredSelection) availableTreeViewer.getSelection();
+		if (sel.isEmpty())
+			return new ModuleServer[0];
+			
+		ModuleServer []  mss = new ModuleServer[sel.size()];
+		System.arraycopy(sel.toArray(), 0, mss, 0, sel.size());
+		return mss;
+	}
+
+	protected ModuleServer[] getDeployedSelection() {
+		IStructuredSelection sel = (IStructuredSelection) deployedTreeViewer.getSelection();
+		if (sel.isEmpty())
+			return new ModuleServer[0];
+		
+		ModuleServer []  mss = new ModuleServer[sel.size()];
+		System.arraycopy(sel.toArray(), 0, mss, 0, sel.size());
+		return mss;
+	}
+
+	protected static IModule getModule(ModuleServer ms) {
+		if (ms == null)
+			return null;
+		IModule[] modules2 = ms.module;
+		return modules2[modules2.length - 1];
+	}
+
+	protected static IModule[] getModules(ModuleServer[] ms) {
+		if (ms == null)
+			return null;
+		IModule[] modules2 = new IModule[ms.length];
+		for (int i = 0; i < ms.length; i++)
+			modules2[i] = getModule(ms[i]);
+		
+		return modules2;
+	}
+
+	protected void setEnablement() {
+		wizard.setMessage(null, IMessageProvider.NONE);
+		
+		int count = 0;
+		if (requiredModules != null) {
+			// count how many of requiredModules are deployed
+			int size = requiredModules.length;
+			for (int i = 0; i < size; i++) {
+				if (deployed.contains(requiredModules[i]))
+					count++;
+			}
+		}
+		
+		// give error if there are more than one possible required modules and none are
+		// added to the server
+		isComplete = true;
+		if (requiredModules != null && requiredModules.length > 1 && count == 0) {
+			String s = "";
+			int size = requiredModules.length;
+			for (int i = 0; i < size; i++) {
+				if (i > 0)
+					s += " | ";
+				s += requiredModules[i].getName();
+			}
+			wizard.setMessage(NLS.bind(Messages.wizModuleRequiredModules, s), IMessageProvider.ERROR);
+			isComplete = false;
+		}
+		
+		// selection specific messages
+		ModuleServer[] ms = getAvailableSelection();
+		if (ms == null ||  ms.length == 0) {
+			add.setEnabled(false);
+		} else {
+			boolean enabled = false;
+			// iterate through selection, if we find at least ONE module that can't be added, exit the loop
+			for (int i = 0; i < ms.length; i++) {
+				IModule module = getModule(ms[i]);
+				if (module != null) {
+					try {
+						IStatus status = errorMap.get(module);
+						if (modules.contains(module)) {
+							if (status == null )
+								enabled = true;
+							else if (status.getSeverity() == IStatus.ERROR) {
+								// this module can't be added because has errors, exit the loop
+								enabled = false;
+								wizard.setMessage(status.getMessage(), IMessageProvider.ERROR);
+								break;
+							} else if (status.getSeverity() == IStatus.WARNING){
+								enabled = true;
+								wizard.setMessage(status.getMessage(), IMessageProvider.WARNING);
+							}
+							else if (status.getSeverity() == IStatus.INFO){
+								enabled = true;
+								wizard.setMessage(status.getMessage(), IMessageProvider.INFORMATION);
+							}
+						}
+						else{
+							// at least ONE module from the selection can't be added, exit the loop   
+							enabled = false;
+							break;
+						}
+					} catch (Exception e) {
+						Trace.trace(Trace.INFO,"Unable to handle error map for module:" + module); 
+					}
+				}
+			}
+			add.setEnabled(enabled);
+		}
+		addAll.setEnabled(modules.size() > 0);
+		
+		ms = getDeployedSelection();
+		if (ms == null ||  ms.length == 0) {
+			remove.setEnabled(false);
+		} else {
+			boolean enabled = false;
+			// iterate through selection, if we find at least ONE module that can't be added, exit the loop
+			for (int i = 0; i < ms.length; i++) {
+				IModule module = getModule(ms[i]);
+				if (module != null && deployed.contains(module)) {
+					// provide error about removing required single module
+					// required modules can't be removed
+					if (requiredModules != null){
+						if (requiredModules.length == 1 && requiredModules[0].equals(module)) {
+							// this is a required module and can't be removed, exit the loop
+							wizard.setMessage(NLS.bind(Messages.wizModuleRequiredModule, module.getName()), IMessageProvider.ERROR);
+							enabled = false;
+							break;
+						}
+					}
+					else 
+						enabled = true;
+				}
+				else{
+					// this module is not found in the 'deployed' array, the module might be a child
+					// at least ONE module from the selection can't be removed, exit the loop
+					enabled = false;
+					break;
+				}
+			}
+			remove.setEnabled(enabled);
+		}
+	
+		if (requiredModules == null)
+			removeAll.setEnabled(deployed.size() > 0);
+		else
+			removeAll.setEnabled(deployed.size() > 1);
+	}
+
+	protected void add(boolean all) {
+		if (all) {
+			IModule[] modules2 = new IModule[modules.size()];
+			modules.toArray(modules2);
+			moveAll(modules2, true);
+		} else
+			moveAll(getModules(getAvailableSelection()), true);
+		updateTaskModel();
+	}
+
+	protected void remove(boolean all) {
+		if (all) {
+			// pick one of the required modules to keep
+			IModule keep = null;
+			if (requiredModules != null) {
+				int size2 = requiredModules.length;
+				for (int i = 0; i < size2; i++) {
+					if (keep == null && deployed.contains(requiredModules[i]))
+						keep = requiredModules[i];
+				}
+			}
+			
+			List<IModule> list = new ArrayList<IModule>();
+			list.addAll(deployed);
+			list.remove(keep);
+			
+			IModule[] modules2 = new IModule[list.size()];
+			list.toArray(modules2);
+			
+			moveAll(modules2, false);
+		} else
+			moveAll(getModules(getDeployedSelection()), false);
+		updateTaskModel();
+	}
+
+	protected void moveAll(IModule[] mods, boolean add2) {
+		int size = mods.length;
+		List<IModule> list = new ArrayList<IModule>();
+		for (int i = 0; i < size; i++) {
+			IStatus status = errorMap.get(mods[i]);
+			
+			if (status == null && !list.contains(mods[i]))
+				list.add(mods[i]);
+		}
+		
+		Iterator iterator = list.iterator();
+		while (iterator.hasNext()) {
+			IModule module = (IModule) iterator.next();
+			ModuleServer ms = new ModuleServer(null, new IModule[] { module });
+			if (add2) {
+				modules.remove(module);
+				deployed.add(module);
+				availableTreeViewer.remove(ms);
+				deployedTreeViewer.add(ROOT, ms);
+			} else {
+				modules.add(module);
+				deployed.remove(module);
+				availableTreeViewer.add(ROOT, ms);
+				deployedTreeViewer.remove(ms);
+			}
+		}
+
+		setEnablement();
+	}
+
+	protected void updateTaskModel() {
+		if (taskModel == null)
+			return;
+
+		taskModel.putObject(TaskModel.TASK_MODULES, getModuleMap());
+		wizard.update();
+	}
+
+	public List<IModule> getModulesToRemove() {
+		List<IModule> list = new ArrayList<IModule>();
+		Iterator iterator = originalModules.iterator();
+		while (iterator.hasNext()) {
+			IModule module = (IModule) iterator.next();
+			if (!deployed.contains(module))
+				list.add(module);
+		}
+		return list;
+	}
+
+	public List<IModule> getModulesToAdd() {
+		List<IModule> list = new ArrayList<IModule>();
+		Iterator iterator = deployed.iterator();
+		while (iterator.hasNext()) {
+			IModule module = (IModule) iterator.next();
+			if (!originalModules.contains(module))
+				list.add(module);
+		}
+		return list;
+	}
+	
+	private void addChildMap(List<IModule[]> map, IModule[] parents, IModule[] children) {
+		if (children == null)
+			return;
+		
+		int size = children.length;
+		for (int i = 0; i < size; i++) {
+			IModule module = children[i];
+			
+			int size2 = parents.length;
+			IModule[] modules2 = new IModule[size2 + 1];
+			System.arraycopy(parents, 0, modules2, 0, size2);
+			modules2[size2] = module;
+			map.add(modules2);
+			
+			IModule[] children2 = childModuleMap.get(new ChildModuleMapKey(module));
+			if (children2 != null)
+				addChildMap(map, modules2, children2);
+		}
+	}
+
+	public List getModuleMap() {
+		final List<IModule[]> map = new ArrayList<IModule[]>();
+	
+		Iterator iterator = deployed.iterator();
+		while (iterator.hasNext()) {
+			IModule module = (IModule) iterator.next();
+			IModule[] moduleTree = new IModule[] { module };
+			map.add(moduleTree);
+			IModule[] children = childModuleMap.get(new ChildModuleMapKey(module));
+			if (children != null)
+				addChildMap(map, moduleTree, children);
+		}
+		
+		return map;
+	}
+
+	public boolean isComplete() {
+		return isComplete;
+	}
+
+	public boolean shouldPublishImmediately() {
+		return publishImmediately;
+	}
+}
\ No newline at end of file