[323750] Hostname entry is very slow in New Server Wizard
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Timer.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Timer.java
new file mode 100644
index 0000000..ae6d51a
--- /dev/null
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Timer.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Manages a single timer that will run and notify an ActionListener when the
+ * timer expires. The dispose() method should be called before the timer object
+ * is taken down in order to safely bring down the executor and any associated threads.
+ */
+public class Timer {
+
+	private ExecutorService executor = Executors.newSingleThreadExecutor();
+	private TimerRunnable timerRunnable = new TimerRunnable();
+	protected ActionListener listener = null;
+	private long delay;
+
+	public Timer(long delay, ActionListener curListener) {
+		super();
+		this.delay = delay;
+		listener = curListener;
+	}
+
+	/**
+	 * Runs the timer if it is stopped or updates the stop time directly
+	 * to effectively restart the timer.
+	 */
+	public void runTimer(){
+		timerRunnable.setStopTime(System.currentTimeMillis() + delay);
+
+		if(!timerRunnable.isRunning()){
+			timerRunnable.setIsScheduled(true);
+			executor.execute(timerRunnable);
+		}
+	}
+
+	/**
+	 * Cancels the timer and then kills the executor which brings down the thread.
+	 */
+	public void dispose(){
+		if(timerRunnable.isRunning()) {
+			timerRunnable.setIsCancelled(true);
+		}
+		killExecutor();
+	}
+
+	public void killExecutor(){
+		executor.shutdown();
+	}
+
+	public boolean isRunning(){
+		return timerRunnable.isRunning();
+	}
+
+	public boolean isScheduled(){
+		return timerRunnable.isScheduled();
+	}
+
+	/**
+	 * This is the Runnable that is called by the executor each time we want to
+	 * run the timer.
+	 */
+	class TimerRunnable implements Runnable {
+
+		private boolean isScheduled = false;
+		private boolean isRunning = false;
+		private boolean isCancelled = false;
+		private long stopTime = 0;
+		// default is 50 ms
+		private long waitTime = 50;
+
+		public void run() {
+			isRunning = true;
+			isScheduled = false;
+			try {
+				while (stopTime - System.currentTimeMillis() > 0 && isRunning && !isCancelled) {
+					try {
+						synchronized (this) {
+							wait(waitTime);
+						}
+						if (Thread.interrupted())
+							throw new InterruptedException();
+					} catch (InterruptedException e) {
+						// Do nothing
+					}
+				}
+				if (!isCancelled) {
+					if (listener != null) {
+						listener.actionPerformed(new ActionEvent(Timer.this, 0, "", System.currentTimeMillis(), 0));
+					}
+				} else {
+					setIsCancelled(false);
+				}
+			} finally {
+				isRunning = false;
+			}
+		}
+
+		public synchronized boolean isRunning() {
+			return isRunning;
+		}
+
+		public synchronized void setRunning(boolean setting) {
+			isRunning = setting;
+		}
+
+		public synchronized long getStopTime() {
+			return stopTime;
+		}
+
+		public synchronized void setStopTime(long time) {
+			stopTime = time;
+		}
+
+		public synchronized long getWaitTime() {
+			return waitTime;
+		}
+
+		public synchronized void setIsCancelled(boolean cancelled) {
+			isCancelled = cancelled;
+		}
+
+		public boolean isScheduled() {
+			return isScheduled;
+		}
+
+		public void setIsScheduled(boolean isScheduled) {
+			this.isScheduled = isScheduled;
+		}
+	}
+}
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/NewServerWizardFragment.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/NewServerWizardFragment.java
index b7797f2..eb93cae 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/NewServerWizardFragment.java
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/fragment/NewServerWizardFragment.java
@@ -17,6 +17,7 @@
 
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.wst.server.core.*;
 import org.eclipse.wst.server.core.internal.ServerWorkingCopy;
@@ -159,8 +160,11 @@
 		if(comp != null){
 			Composite composite = comp.getNewManualServerComposite();
 			if(composite != null && composite instanceof NewManualServerComposite){
-				NewManualServerComposite manualComp = (NewManualServerComposite)composite;
-
+				NewManualServerComposite manualComp = (NewManualServerComposite) composite;
+				if (manualComp.isTimerRunning() || manualComp.isTimerScheduled()) {
+					return false;
+				}
+				
 				if(!supportsRemote && !SocketUtil.isLocalhost(manualComp.getCurrentHostname())){
 					isComplete = false;
 				}if (!manualComp.canSupportModule() ){
@@ -180,4 +184,18 @@
 			return null;
 		}
 	}
+	
+	public void performCancel(IProgressMonitor monitor) throws CoreException {
+		if(comp != null) {
+			comp.getNewManualServerComposite().dispose();
+		}
+		super.performCancel(monitor);
+	}
+	
+	public void performFinish(IProgressMonitor monitor) throws CoreException {
+		if(comp != null) {
+			comp.getNewManualServerComposite().dispose();
+		}
+		super.performFinish(monitor);
+	}
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/page/NewManualServerComposite.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/page/NewManualServerComposite.java
index 4d575af..05765ee 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/page/NewManualServerComposite.java
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/wizard/page/NewManualServerComposite.java
@@ -10,6 +10,8 @@
  *******************************************************************************/
 package org.eclipse.wst.server.ui.internal.wizard.page;
 
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.beans.PropertyChangeEvent;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
@@ -104,6 +106,9 @@
 	
 	private IServerType oldServerType;
 	
+	HostnameChangedAction hostnameChangeAction;
+	Timer timer = null;
+	
 	private boolean canSupportModule=true;
 
 	/**
@@ -192,7 +197,7 @@
 		
 		hostname.addModifyListener(new ModifyListener() {
 			public void modifyText(ModifyEvent e) {
-				hostnameChanged(hostname.getText());
+				setHostnameChangeTimer(hostname.getText());
 			}
 		});
 		
@@ -775,14 +780,82 @@
 			}
 		}
 	}
-	
-	public String getCurrentHostname(){
-		if(hostname != null)
+
+	public String getCurrentHostname() {
+		if (hostname != null)
 			return hostname.getText();
 		return null;
 	}
 
 	public boolean canSupportModule() {
 		return canSupportModule;
-	}	
+	}
+
+	void setHostnameChangeTimer(String hostName) {
+		if (hostnameChangeAction == null) {
+			hostnameChangeAction = new HostnameChangedAction(hostName);
+		} else {
+			hostnameChangeAction.setHostName(hostName);
+		}
+
+		if (timer == null) {
+			timer = new Timer(300, hostnameChangeAction);
+		}
+		/*
+		 * Kick off the timer and then call setMessage if the Timer wasn't
+		 * previously running because we want to trigger the isComplete on the page
+		 * so that it stops the user from proceeding to the next page while the
+		 * timer is running.
+		 */
+		if (!timer.isRunning()) {
+			timer.runTimer();
+			wizard.setMessage(null, IMessageProvider.NONE);
+		} else {
+			timer.runTimer();
+		}
+	}
+
+	public boolean isTimerRunning() {
+		if (timer == null) {
+			return false;
+		}
+		return timer.isRunning();
+	}
+
+	public boolean isTimerScheduled() {
+		if (timer == null) {
+			return false;
+		}
+		return timer.isScheduled();
+	}
+
+	/**
+	 * Disposes the timer when the wizard is disposed.
+	 */
+	public void dispose() {
+		if (timer != null) {
+			timer.dispose();
+		}
+	}
+
+	private class HostnameChangedAction implements ActionListener {
+
+		String hostName;
+
+		public HostnameChangedAction(String name) {
+			hostName = name;
+		}
+
+		public void actionPerformed(ActionEvent a) {
+			Display.getDefault().asyncExec(new Runnable() {
+				public void run() {
+						hostnameChanged(hostName);
+				}
+			});
+		}
+
+		void setHostName(String host) {
+			hostName = host;
+		}
+	}
 }
\ No newline at end of file