/*******************************************************************************
 * Copyright (c) 2013 GoPivotal, Inc.
 * 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:
 *     GoPivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springsource.ide.eclipse.commons.gettingstarted.launch;

import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.ui.DebugUITools;
import org.springsource.ide.eclipse.commons.gettingstarted.util.UiRunnable;

public class LaunchUtils {

	/**
	 * Terminate a given launch (if it is running) then relaunch it.
	 */
	public static void terminateAndRelaunch(final ILaunch launch) throws DebugException {
		if (!launch.isTerminated()) {
			launch.terminate();
		}
		whenTerminated(launch, new UiRunnable() {
			public void uiRun() {
				//must run in UI thread since it may popup dialogs in some cases.
				DebugUITools.launch(launch.getLaunchConfiguration(), launch.getLaunchMode());
			}
		});
	}

	/**
	 * Execute some code as soon as a given launch is terminated. If the launch is already terminated
	 * then the code is executed synchronously, otherwise it is executed asynchronously when 
	 * a termination event is received.
	 */
	public static void whenTerminated(ILaunch launch, Runnable runnable) {
		new WhenTerminated(launch, runnable);
	}
	private static class WhenTerminated implements IDebugEventSetListener {

		private ILaunch launch;
		private Runnable runnable;
		private DebugPlugin debugPlugin;

		public WhenTerminated(ILaunch launch, Runnable runnable) {
			this.launch = launch;
			this.runnable = runnable;
			this.debugPlugin = DebugPlugin.getDefault();
			debugPlugin.addDebugEventListener(this);
			
			//Careful... what if the launch has terminated since we last checked it...
			// in that case we might not get a termination event! So start off with
			// an initial check now.
			checkAndRun();
		}

		@Override
		public void handleDebugEvents(DebugEvent[] events) {
			for (DebugEvent e : events) {
				if (e.getKind()==DebugEvent.TERMINATE && e.getSource()==launch) {
					checkAndRun();
				}
			}
		}

		private void checkAndRun() {
			Runnable runit = check();
			debugPlugin.removeDebugEventListener(this);
			runit.run();
		}

		/**
		 * Checks whether condition for firing the runable is satisfied. If so, then the runable
		 * is returned. At the same time the runnable field is nulled to ensure it can not be
		 * executed more than once.
		 * <p>
		 * Executing the runable does not happen in this method because it is 'synchronized'
		 * and we don't want to hang on to the monitor any longer than necessary (especially
		 * not when firing the runnable!)
		 */
		private synchronized Runnable check() {
			if (runnable!=null) {
				if (launch.isTerminated()) {
					//bingo!
					Runnable it = runnable;
					runnable = null;
					return it;
				}
			}
			return null;
		}

	}


}

@@ -2608,24 +2608,47 @@
  = check();%0A
+%09%09%09if (runit!=null) %7B%0A%09
 %09%09%09debugPlug
@@ -2685,16 +2685,17 @@
 is);%0A%09%09%09
+%09
 runit.ru
@@ -2695,24 +2695,29 @@
 unit.run();%0A
+%09%09%09%7D%0A
 %09%09%7D%0A%0A%09%09/**%0A%09

Fix NPE in relaunch button that occurs when process termination takes a tad longer.