Added API to allow platform directory locations to be injected into the library without worrying about the specific DI framework used to do so.
diff --git a/bundles/org.eclipse.e4.core.deeplink/src/org/eclipse/e4/core/deeplink/DeepLinkManager.java b/bundles/org.eclipse.e4.core.deeplink/src/org/eclipse/e4/core/deeplink/DeepLinkManager.java
index b2f2796..802e582 100644
--- a/bundles/org.eclipse.e4.core.deeplink/src/org/eclipse/e4/core/deeplink/DeepLinkManager.java
+++ b/bundles/org.eclipse.e4.core.deeplink/src/org/eclipse/e4/core/deeplink/DeepLinkManager.java
@@ -8,198 +8,231 @@
  * Contributors:
  *    David Orme - initial API and implementation
  ******************************************************************************/
-package org.eclipse.e4.core.deeplink;

-

-import java.io.File;

-import java.io.FileInputStream;

-import java.io.FileNotFoundException;

-import java.io.FileOutputStream;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-import java.net.MalformedURLException;

-import java.net.URL;

-

-import org.eclipse.core.runtime.ILog;

-import org.eclipse.core.runtime.IProgressMonitor;

-import org.eclipse.core.runtime.IStatus;

-import org.eclipse.core.runtime.Platform;

-import org.eclipse.core.runtime.Status;

-import org.eclipse.core.runtime.jobs.Job;

+package org.eclipse.e4.core.deeplink;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.e4.core.deeplink.internal.DeepLinkProperties;
 import org.eclipse.e4.core.deeplink.internal.DeepLinkProxy;
 import org.eclipse.e4.core.deeplink.internal.DeeplinkPortAssigner;
 import org.eclipse.e4.core.deeplink.internal.InstallationLauncher;
 import org.eclipse.e4.core.deeplink.internal.ParsedDeepLinkURL;
-import org.eclipse.equinox.app.IApplicationContext;

-import org.osgi.framework.BundleContext;

-import org.osgi.framework.BundleException;

-

-/**

- * The DeepLinkManager is the entry-point for the DeepLinking API. This class

- * can be used as a singleton, but you do not have to. Using it as a singleton

- * has some minor performance benefits.

- */

-public class DeepLinkManager {

-	

-	private static final String PROP_FILE_HEADER = "Deep Link 4.0 Configuration";

-	private static DeepLinkManager deepLinkManager = null;

-

-	// FIXME: Need to make this a configuration option!!!!

-	private static final String SERVER_ROOT_PATH = "/tmp/deeplink";
-//	private static final String SERVER_ROOT_PATH = "C:\\Program Files\\DeepLink";
-	private static final String APPLICATION_PROPS = SERVER_ROOT_PATH + "/deeplink4.properties";

-

-	private ILog logger;

-	private DeepLinkProperties properties;

-	private DeeplinkPortAssigner portAssigner;

-	private DeepLinkProxy deepLinkProxy;

-	

-	

-	public static DeepLinkManager getDefault() {

-		return deepLinkManager;

-	}

-	

-	public DeepLinkManager(boolean regeneratePortNumbersOnClash, ILog logger) {

-		deepLinkManager = this;

-		this.logger = logger;

-		properties = getDeepLinkProperties();

-		this.portAssigner = new DeeplinkPortAssigner(properties, regeneratePortNumbersOnClash);

-		InstallationLauncher installationLauncher = new InstallationLauncher(SERVER_ROOT_PATH, properties);

-		this.deepLinkProxy = new DeepLinkProxy(installationLauncher);

-	}

-	

-	// TODO: Test drive this?

-	public String getInstallationIDFromPath(URL url) {

-		String fileURL = url.toString();

-		String path = fileURL.replace("file:/", "");

-		if (path.lastIndexOf('/') == path.length()-1) {

-			path = path.substring(0, path.length()-1);

-		}

-		int endOfPrefix = path.lastIndexOf('/');

-		if (endOfPrefix > 0) {

-			path = path.substring(endOfPrefix+1);

-		}

-		return path;

-	}

-	

-	public int getPortNumberForInstallation(String installation) {

-		int result = portAssigner.getPortNumberForInstallation(installation);

-		try {

-			properties.store(propsOutputStream(), PROP_FILE_HEADER);

-		} catch (IOException e) {

-			throw new RuntimeException(e.getMessage(), e);

-		}

-		return result;

-	}

-	

-	/**

-	 * Takes a deeplink URL as a String and composes the appropriate URL object, looking

-	 * up the correct port number for the application, and returns the result.

-	 */

-	public DeepLinkResult processDeepLink(String deepLinkURL) {

-		try {

-			ParsedDeepLinkURL deepLink = new ParsedDeepLinkURL(deepLinkURL);

-			int portNumber = portAssigner.getPortNumberForInstallation(deepLink.installation);

-			URL httpLink = new URL("http", "localhost", portNumber, "/deeplink" + deepLink.restOfURL);

-			DeepLinkResult result = deepLinkProxy.execute(deepLink.installation, httpLink, deepLinkURL);

-			logInfo("INVOKED: " + deepLink.installation + deepLink.restOfURL + 

-					"\" loaded=\"" + result.loaded + "\" callbackRan=\"" + result.callbackRan + "\" exception=\"" + result.exception + "\"");

-			return result;

-		} catch (DeepLinkURLException e) {

-			logFailure("Unable to open URL: " + deepLinkURL, e);

-			throw e;

-		} catch (MalformedURLException e) {

-			logFailure("Malformed deep link URL (" + e.getMessage() + "): " + deepLinkURL, e);

-			throw new RuntimeException(e.getMessage(), e);

-		} catch (IOException e) {

-			logFailure("Unable to contact deep link handler for: " + deepLinkURL, e);

-			throw new RuntimeException(e.getMessage(), e);

-		} catch (DeepLinkResultException e) {

-			logFailure("Unable to parse result XML: " + e.getMessage(), e);

-			throw e;

-		}

-	}

-

-	private DeepLinkProperties getDeepLinkProperties() {

-		DeepLinkProperties result = new DeepLinkProperties();

-		try {

-			result.load(propsInputStream());

-		} catch (IOException e) {

-			throw new RuntimeException(e.getMessage(), e);

-		}

-		return result;

-	}

-

-	private OutputStream propsOutputStream() {

-		try {

-			return new FileOutputStream(new File(APPLICATION_PROPS));

-		} catch (FileNotFoundException e) {

-			throw new RuntimeException(e.getMessage(), e);

-		}

-	}

-

-	private InputStream propsInputStream() {

-		try {

-			return new FileInputStream(new File(APPLICATION_PROPS));

-		} catch (FileNotFoundException e) {

-			throw new RuntimeException(e.getMessage(), e);

-		}

-	}

-

-	private void logInfo(String message) {

-		IStatus status = new Status(Status.INFO, Activator.PLUGIN_ID, message);

-		logger.log(status);

-	}

-	

-	private void logFailure(String message, Throwable t) {

-		IStatus status = new Status(Status.ERROR, Activator.PLUGIN_ID, message, t);

-		logger.log(status);

-	}

-

-	/**

-	 * Start the deep linking HTTP server if it isn't already started.  If it 

-	 * is started, restart it using the port specified by the deep linking

-	 * configuration.

-	 * 

-	 * @throws BundleException if a bundle cannot be resolved or loaded.

-	 */

-	public void startServer() throws BundleException {

-		String installation = getInstallationIDFromPath(Platform.getInstallLocation().getURL());

-		int serverPort = getPortNumberForInstallation(installation);

-		

-		System.getProperties().setProperty("org.osgi.service.http.port", Integer.toString(serverPort));

-		BundleContext bundleContext = Activator.getDefault().getBundle().getBundleContext();

-		new DeepLinkBundleList(bundleContext).startupBundlesForHttpServlets();

-	}

-

-	private static final int ONE_SECOND = 1000;

-	private final String COMMANDLINE_KEY = "application.args";

-	

-	/**

-	 * Any command line arguments that begin with "deeplink://" will be interpreted

-	 * as a deeplink URL, and those deep links will be scheduled to be invoked.

-	 * <p>

-	 * A Job is used to actually run the deep link because this method is 

-	 * typically called before the Workbench has been loaded, so we wait for 

-	 * the Workbench before running deep links that might reference 

-	 * workbench resources like perspectives, commands, etc.

-	 * 

-	 * @param context The IApplicationContext containing the command line arguments to examine.

-	 */

-	public void processCommandLineArguments(IApplicationContext context) {

-		final String[] args = (String[]) context.getArguments().get(COMMANDLINE_KEY);

-		Job deepLinkJob = new Job("Deep Link Launcher") {

-			@Override

-			protected IStatus run(IProgressMonitor monitor) {

-				for (String arg : args) {

-					if (arg.startsWith("deeplink://")) {

-						deepLinkManager.processDeepLink(arg);

-					}

-				}

-				return Status.OK_STATUS;

-			}};

-		deepLinkJob.schedule(ONE_SECOND);

-	}

-

-}

+import org.eclipse.equinox.app.IApplicationContext;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+/**
+ * The DeepLinkManager is the entry-point for the DeepLinking API. This class
+ * can be used as a singleton, but you do not have to. Using it as a singleton
+ * has some minor performance benefits.
+ */
+public class DeepLinkManager {
+	
+	private static final String DEEPLINK_PROPERTIES_FILE_NAME = "/deeplink4.properties";
+	private static final String PROP_FILE_HEADER = "Deep Link 4.0 Configuration";
+	private static DeepLinkManager deepLinkManager = null;
+
+	// We'll provide a reasonable default for win32.  Clients should normally inject their own
+	// path explicitly.
+	private static final String DEFAULT_SERVER_PATH="C:\\Program Files\\Deeplink";
+	
+	private final String SERVER_CONFIG_PATH;
+	private final String SERVER_ROOT_PATH;
+	private final String APPLICATION_PROPS;
+
+	private ILog logger;
+	private DeepLinkProperties properties;
+	private DeeplinkPortAssigner portAssigner;
+	private DeepLinkProxy deepLinkProxy;
+	
+	
+	public static DeepLinkManager getDefault() {
+		return deepLinkManager;
+	}
+
+	/**
+	 * Initialize DeepLinking.
+	 * 
+	 * @param regeneratePortNumbersOnClash true if port numbers should be automatically generated.  false otherwise.
+	 * @param logger an ILog implementation to use for logging.
+	 * @param rootPath Directories containing applications that use deeplinking live under this folder/path.
+	 * @param configPath The directory containing the deeplink4.properties file
+	 */
+	public DeepLinkManager(boolean regeneratePortNumbersOnClash, ILog logger, String rootPath, String configPath) {
+		SERVER_ROOT_PATH = rootPath;
+		SERVER_CONFIG_PATH = configPath;
+		APPLICATION_PROPS = SERVER_CONFIG_PATH + DEEPLINK_PROPERTIES_FILE_NAME;
+		init(regeneratePortNumbersOnClash, logger);
+	}
+	
+	/**
+	 * Initialize DeepLinking.  Initialize with Win32-specific paths, assuming that "c:\Program Files\DeepLink" is
+	 * writable by the user who will be running the application.
+	 * 
+	 * @param regeneratePortNumbersOnClash true if port numbers should be automatically generated.  false otherwise.
+	 * @param logger an ILog implementation to use for logging.
+	 */
+	public DeepLinkManager(boolean regeneratePortNumbersOnClash, ILog logger) {
+		SERVER_CONFIG_PATH = DEFAULT_SERVER_PATH;
+		SERVER_ROOT_PATH = DEFAULT_SERVER_PATH;
+		APPLICATION_PROPS = SERVER_CONFIG_PATH + DEEPLINK_PROPERTIES_FILE_NAME;
+		init(regeneratePortNumbersOnClash, logger);
+	}
+
+	private void init(boolean regeneratePortNumbersOnClash, ILog logger) {
+		deepLinkManager = this;
+		this.logger = logger;
+		properties = getDeepLinkProperties();
+		this.portAssigner = new DeeplinkPortAssigner(properties, regeneratePortNumbersOnClash);
+		InstallationLauncher installationLauncher = new InstallationLauncher(SERVER_ROOT_PATH, properties);
+		this.deepLinkProxy = new DeepLinkProxy(installationLauncher);
+	}
+	
+	// TODO: Test drive this?
+	public String getInstallationIDFromPath(URL url) {
+		String fileURL = url.toString();
+		String path = fileURL.replace("file:/", "");
+		if (path.lastIndexOf('/') == path.length()-1) {
+			path = path.substring(0, path.length()-1);
+		}
+		int endOfPrefix = path.lastIndexOf('/');
+		if (endOfPrefix > 0) {
+			path = path.substring(endOfPrefix+1);
+		}
+		return path;
+	}
+	
+	public int getPortNumberForInstallation(String installation) {
+		int result = portAssigner.getPortNumberForInstallation(installation);
+		try {
+			properties.store(propsOutputStream(), PROP_FILE_HEADER);
+		} catch (IOException e) {
+			throw new RuntimeException(e.getMessage(), e);
+		}
+		return result;
+	}
+	
+	/**
+	 * Takes a deeplink URL as a String and composes the appropriate URL object, looking
+	 * up the correct port number for the application, and returns the result.
+	 */
+	public DeepLinkResult processDeepLink(String deepLinkURL) {
+		try {
+			ParsedDeepLinkURL deepLink = new ParsedDeepLinkURL(deepLinkURL);
+			int portNumber = portAssigner.getPortNumberForInstallation(deepLink.installation);
+			URL httpLink = new URL("http", "localhost", portNumber, "/deeplink" + deepLink.restOfURL);
+			DeepLinkResult result = deepLinkProxy.execute(deepLink.installation, httpLink, deepLinkURL);
+			logInfo("INVOKED: " + deepLink.installation + deepLink.restOfURL + 
+					"\" loaded=\"" + result.loaded + "\" callbackRan=\"" + result.callbackRan + "\" exception=\"" + result.exception + "\"");
+			return result;
+		} catch (DeepLinkURLException e) {
+			logFailure("Unable to open URL: " + deepLinkURL, e);
+			throw e;
+		} catch (MalformedURLException e) {
+			logFailure("Malformed deep link URL (" + e.getMessage() + "): " + deepLinkURL, e);
+			throw new RuntimeException(e.getMessage(), e);
+		} catch (IOException e) {
+			logFailure("Unable to contact deep link handler for: " + deepLinkURL, e);
+			throw new RuntimeException(e.getMessage(), e);
+		} catch (DeepLinkResultException e) {
+			logFailure("Unable to parse result XML: " + e.getMessage(), e);
+			throw e;
+		}
+	}
+
+	private DeepLinkProperties getDeepLinkProperties() {
+		DeepLinkProperties result = new DeepLinkProperties();
+		try {
+			result.load(propsInputStream());
+		} catch (IOException e) {
+			throw new RuntimeException(e.getMessage(), e);
+		}
+		return result;
+	}
+
+	private OutputStream propsOutputStream() {
+		try {
+			return new FileOutputStream(new File(APPLICATION_PROPS));
+		} catch (FileNotFoundException e) {
+			throw new RuntimeException(e.getMessage(), e);
+		}
+	}
+
+	private InputStream propsInputStream() {
+		try {
+			return new FileInputStream(new File(APPLICATION_PROPS));
+		} catch (FileNotFoundException e) {
+			throw new RuntimeException(e.getMessage(), e);
+		}
+	}
+
+	private void logInfo(String message) {
+		IStatus status = new Status(Status.INFO, Activator.PLUGIN_ID, message);
+		logger.log(status);
+	}
+	
+	private void logFailure(String message, Throwable t) {
+		IStatus status = new Status(Status.ERROR, Activator.PLUGIN_ID, message, t);
+		logger.log(status);
+	}
+
+	/**
+	 * Start the deep linking HTTP server if it isn't already started.  If it 
+	 * is started, restart it using the port specified by the deep linking
+	 * configuration.
+	 * 
+	 * @throws BundleException if a bundle cannot be resolved or loaded.
+	 */
+	public void startServer() throws BundleException {
+		String installation = getInstallationIDFromPath(Platform.getInstallLocation().getURL());
+		int serverPort = getPortNumberForInstallation(installation);
+		
+		System.getProperties().setProperty("org.osgi.service.http.port", Integer.toString(serverPort));
+		BundleContext bundleContext = Activator.getDefault().getBundle().getBundleContext();
+		new DeepLinkBundleList(bundleContext).startupBundlesForHttpServlets();
+	}
+
+	private static final int ONE_SECOND = 1000;
+	private final String COMMANDLINE_KEY = "application.args";
+	
+	/**
+	 * Any command line arguments that begin with "deeplink://" will be interpreted
+	 * as a deeplink URL, and those deep links will be scheduled to be invoked.
+	 * <p>
+	 * A Job is used to actually run the deep link because this method is 
+	 * typically called before the Workbench has been loaded, so we wait for 
+	 * the Workbench before running deep links that might reference 
+	 * workbench resources like perspectives, commands, etc.
+	 * 
+	 * @param context The IApplicationContext containing the command line arguments to examine.
+	 */
+	public void processCommandLineArguments(IApplicationContext context) {
+		final String[] args = (String[]) context.getArguments().get(COMMANDLINE_KEY);
+		Job deepLinkJob = new Job("Deep Link Launcher") {
+			@Override
+			protected IStatus run(IProgressMonitor monitor) {
+				for (String arg : args) {
+					if (arg.startsWith("deeplink://")) {
+						deepLinkManager.processDeepLink(arg);
+					}
+				}
+				return Status.OK_STATUS;
+			}};
+		deepLinkJob.schedule(ONE_SECOND);
+	}
+
+}