CQ-4081: Initial import
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/.classpath b/bundles/org.eclipse.e4.core.deeplink.handler/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/.project b/bundles/org.eclipse.e4.core.deeplink.handler/.project
new file mode 100644
index 0000000..e48608f
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.e4.core.deeplink.handler</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.e4.core.deeplink.handler/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..e60562e
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Mon Dec 21 14:06:00 CST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.core.deeplink.handler/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..eace3fb
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Handler Plug-in
+Bundle-SymbolicName: org.eclipse.e4.core.deeplink.handler;singleton:=true
+Bundle-Version: 4.6.0.qualifier
+Bundle-Activator: org.eclipse.e4.core.deeplink.api.Activator
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.osgi.services;bundle-version="3.1.200",
+ javax.servlet;bundle-version="2.4.0",
+ javax.servlet.jsp;bundle-version="2.0.0",
+ org.eclipse.equinox.http.registry;bundle-version="1.0.100",
+ org.eclipse.equinox.http.servlet;bundle-version="1.0.100",
+ org.eclipse.equinox.http.jetty;bundle-version="1.1.0",
+ org.mortbay.jetty.server,
+ org.eclipse.core.resources,
+ org.eclipse.e4.core.functionalprog;bundle-version="1.0.0",
+ org.apache.commons.lang;bundle-version="2.3.0"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Export-Package: org.eclipse.e4.core.deeplink.api;uses:="org.eclipse.e4.core.deeplink.handler,javax.servlet.http"
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/about.html b/bundles/org.eclipse.e4.core.deeplink.handler/about.html
new file mode 100644
index 0000000..f77f378
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/about.html
@@ -0,0 +1,22 @@
+<h1>About This Content</h1>
+
+23 June, 2010
+
+<h2>License</h2>
+
+<p>The Eclipse Foundation makes available all content in this plug-in
+("Content"). Unless otherwise indicated below, the Content is provided
+to you under the terms and conditions of the Eclipse Public License
+Version 1.0 ("EPL"). A copy of the EPL is available at
+http://www.eclipse.org/legal/epl-v10.html. For purposes of the EPL,
+"Program" will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse
+Foundation, the Content is being redistributed by another party
+("Redistributor") and different terms and conditions may apply to your
+use of any object code in the Content. Check the Redistributor’s
+license that was provided with the Content. If no such license exists,
+contact the Redistributor. Unless otherwise indicated below, the terms
+and conditions of the EPL still apply to any source code in the
+Content and such source code may be obtained at
+http://www.eclipse.org.</p>
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/build.properties b/bundles/org.eclipse.e4.core.deeplink.handler/build.properties
new file mode 100644
index 0000000..6f20375
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/plugin.xml b/bundles/org.eclipse.e4.core.deeplink.handler/plugin.xml
new file mode 100644
index 0000000..9a663a1
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/plugin.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension-point id="deepLinkTypeHandler" name="Deep Link Type Handler" schema="schema/deepLinkTypeHander.exsd"/>
+
+</plugin>
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/schema/deepLinkTypeHander.exsd b/bundles/org.eclipse.e4.core.deeplink.handler/schema/deepLinkTypeHander.exsd
new file mode 100644
index 0000000..e2eddfb
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/schema/deepLinkTypeHander.exsd
@@ -0,0 +1,112 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.e4.enterprise.deeplink.deeplink.handler" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.eclipse.e4.enterprise.deeplink.deeplink.handler" id="deepLinkTypeHandler" name="Deep Link Type Handler"/>
+ </appinfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ <documentation>
+ A deep link typeHandler allows the developer to extend the set of deep link URLs.
+ </documentation>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="1" maxOccurs="unbounded">
+ <element ref="typeHandler"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="typeHandler">
+ <complexType>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.e4.core.deeplink.api.AbstractDeepLinkTypeHandler:"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiinfo"/>
+ </appinfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/AbstractDeepLinkInstanceHandler.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/AbstractDeepLinkInstanceHandler.java
new file mode 100644
index 0000000..e6894f6
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/AbstractDeepLinkInstanceHandler.java
@@ -0,0 +1,88 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.api;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * This class defines the abstract behavior for all deeplink-addressable objects
+ * that define method callbacks.
+ */
+public abstract class AbstractDeepLinkInstanceHandler {
+
+ /**
+ * Activate a deeplink callback on this object.
+ * <p>
+ *
+ * @param handlerInstanceID
+ * The application instance (or RCP container) that this deeplink
+ * currently is living in.
+ * @param action
+ * the "action" or verb part of the URL or an empty string if
+ * none.
+ * @param params
+ * Any deeplink parameters specified as a part of the URL.
+ * @return A Map<String, String> containing any arbitrary results.
+ */
+ public abstract Map<String, String> activate(String handlerInstanceID, String action, Map<String, String[]> params);
+
+ /**
+ * Execute the application-specified handler for the URL
+ *
+ * @param results
+ * @throws LinkFailureException
+ */
+ public void handle(final ParameterProcessResults[] results, final String handlerInstanceId, final String action, final Map<String, String[]> params) {
+ debug("Command received: " + handlerInstanceId);
+ debug("Running action for app: " + handlerInstanceId);
+ try {
+ results[0].outputData = activate(handlerInstanceId, action , params);
+ results[0].activatedParameterCallback = true;
+ } catch (Throwable t) {
+ error(handlerInstanceId);
+ rethrowException(t);
+ }
+ }
+
+ protected IStatus status(int severity, String message) {
+ return new Status(severity, Activator.PLUGIN_ID, message);
+ }
+
+ protected void error(String message) {
+ getLog().log(status(Status.ERROR, message));
+ }
+
+ protected void info(String message) {
+ getLog().log(status(Status.INFO, message));
+ }
+
+ protected void debug(String message) {
+ getLog().log(status(Status.WARNING, message));
+ }
+
+ private ILog getLog() {
+ return Activator.getDefault().getLog();
+ }
+
+ private void rethrowException(Throwable terminatingException) {
+ if (terminatingException != null) {
+ if (!(terminatingException instanceof RuntimeException)) {
+ throw new RuntimeException(terminatingException);
+ }
+ throw (RuntimeException) terminatingException;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/AbstractDeepLinkTypeHandler.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/AbstractDeepLinkTypeHandler.java
new file mode 100644
index 0000000..b228243
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/AbstractDeepLinkTypeHandler.java
@@ -0,0 +1,171 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.api;
+
+import static org.eclipse.e4.core.functionalprog.optionmonad.None.none;
+import static org.eclipse.e4.core.functionalprog.optionmonad.Some.some;
+
+import static org.apache.commons.lang.StringEscapeUtils.escapeXml;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Map;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.e4.core.functionalprog.optionmonad.Option;
+
+/**
+ * Given the following archetypal deeplink:
+ * <ul>
+ * <li>deeplink://appInstance/handlerType/handlerInstanceId/action?param1=value1¶m2=value2...
+ * </ul>
+ * A DeepLinkTypeHandler is a strategy pattern object that defines how deeplink
+ * requests are handled for a particular deeplink handlerType.
+ * <p>
+ * Each DeepLinkTypeHandler must be registered with the system via the
+ * deepLinkTypeHandler extension point.
+ * <p>
+ * This is (more or less) following a Servlet Delegate pattern.
+ * This knows about HTTP stuff (req/resp etc) and should not leak this info to
+ * subclasses or collaborators.
+ */
+public abstract class AbstractDeepLinkTypeHandler {
+
+ public static final String CLASS = "class";
+ private HttpServletRequest request;
+ private HttpServletResponse response;
+
+ private URLPathInfoParser parsedPathParts;
+
+ public IStatusFactory statusFactory = new StatusFactory();
+
+ public void init(HttpServletRequest request, HttpServletResponse response, URLPathInfoParser pathParts) {
+ try {
+ request.setCharacterEncoding("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ response.setContentType("text/xml");
+ this.request = request;
+ this.response = response;
+ this.parsedPathParts = pathParts;
+ }
+
+ public abstract void processDeepLink() throws IOException;
+
+ public void doGet() throws IOException{
+ processDeepLink();
+ }
+
+ public void doPost() throws IOException{
+ processDeepLink();
+ }
+
+
+ private Option<IConfigurationElement> find(IConfigurationElement[] elementsToLookIn, String idToFind) {
+ for (IConfigurationElement configurationElement : elementsToLookIn) {
+ String id = configurationElement.getAttribute(idToFind);
+ if (getHandlerId().equals(id)) {
+ return some(configurationElement);
+ }
+ }
+ return none();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Map<String, String[]> getParameterMap() {
+ return request.getParameterMap();
+ }
+
+ protected String getHandlerType() {
+ return parsedPathParts.handlerType;
+ }
+
+ protected String getHandlerId() {
+ return parsedPathParts.handlerId;
+ }
+
+ protected String getAction() {
+ return parsedPathParts.action.getOrSubstitute("");
+ }
+
+ /*
+ * FIXME DJO/JADN 2010-02-20. This method is monolithic, thus does not lend
+ * itself to being modified either here or by subclasses eg if the "value"
+ * [from results.outputData.get(key)] needs anything other than .toString()
+ * then you are in trouble.
+ */
+ protected void outputResponse(final ParameterProcessResults results) throws IOException {
+ ServletOutputStream out = response.getOutputStream();
+ out.println("<?xml version=\"1.0\"?>");
+ out.println("<deeplink>");
+ out.println(" <result id=\"" + parsedPathParts.handlerId + "\" loaded=\"" + results.loaded + "\" callbackRan=\"" + results.activatedParameterCallback + "\"/>");
+ if (results.activatedParameterCallback) {
+ out.println(" <outputData id=\"" + parsedPathParts.handlerId + "\">");
+ if (results.outputData != null) {
+ for (String key : results.outputData.keySet()) {
+ String value = escapeXml(results.outputData.get(key));
+ out.print(" <element key=\"" + key + "\">");
+ out.print(value);
+ out.println("</element>");
+ }
+ }
+ out.println(" </outputData>");
+ }
+ out.println("</deeplink>");
+ }
+
+ protected IConfigurationElement[] getCongfigElementsFromRegistry(String pluginId, String extPointID) {
+ IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
+ return extensionRegistry.getConfigurationElementsFor(pluginId, extPointID);
+ }
+
+ protected void findInstanceHandlerAndExecuteCallback(ParameterProcessResults[] results, IConfigurationElement[] congfigElementsFromRegistry, Map<String, String[]> parameters, String extensionPointID) {
+ Option<IConfigurationElement> found = find(congfigElementsFromRegistry, extensionPointID);
+ if (found.hasValue()) {
+ IConfigurationElement configurationElement = found.get();
+ Option<AbstractDeepLinkInstanceHandler> instanceHandlerOption = buildInstanceHandlerFromClassAttribute(configurationElement);
+ if (instanceHandlerOption.hasValue()) {
+ AbstractDeepLinkInstanceHandler instanceHandler = instanceHandlerOption.get();
+ instanceHandler.handle(results, getHandlerId(), getAction(), parameters);
+ }
+ }
+ }
+
+ private Option<AbstractDeepLinkInstanceHandler> buildInstanceHandlerFromClassAttribute(IConfigurationElement configurationElement) {
+ try {
+ Object interimObject = configurationElement.createExecutableExtension(CLASS);
+ if (interimObject instanceof AbstractDeepLinkInstanceHandler) {
+ AbstractDeepLinkInstanceHandler handler = (AbstractDeepLinkInstanceHandler) interimObject;
+ return some(handler);
+ } else {
+ Activator.getDefault().getLog().log(new Status(Status.INFO, Activator.PLUGIN_ID, ("Found extension point but not DeepLinkInstanceHandler: " + configurationElement.getName())));
+ return none();
+ }
+ } catch (CoreException e) {
+ logExtensionError(configurationElement.getName(), e);
+ return none();
+ }
+ }
+
+ private void logExtensionError(String name, CoreException e) {
+ Activator.getDefault().getLog().log(new Status(Status.ERROR, Activator.PLUGIN_ID, ("Incorrect extension for: " + name), e));
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/Activator.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/Activator.java
new file mode 100644
index 0000000..bd1f533
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/Activator.java
@@ -0,0 +1,88 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.api;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.e4.core.deeplink.handler.HttpServiceTracker;
+import org.eclipse.equinox.http.registry.HttpContextExtensionService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends Plugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.eclipse.e4.core.deeplink.handler";
+ public static final String DEEP_LINK_EXT_PT_ID = "deepLinkTypeHandler";
+
+ // The shared instance
+ private static Activator plugin;
+
+ protected HttpServiceTracker httpServiceTracker;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ plugin=this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+
+ // RAP [fappel]: ensure that the rap http context was loaded before
+ // the mapping of servlets from branding takes place
+ String serviceName = HttpContextExtensionService.class.getName();
+ ServiceTracker httpContextExtensionServiceTracker
+ = new ServiceTracker( context, serviceName, null )
+ {
+ public Object addingService( final ServiceReference reference ) {
+ Object result = super.addingService( reference );
+ httpServiceTracker = new HttpServiceTracker(context);
+ httpServiceTracker.open();
+ return result;
+ }
+ };
+ httpContextExtensionServiceTracker.open();
+// /*
+// * DO NOT RUN ANY OTHER CODE AFTER THIS LINE. If you do, then you are
+// * likely to cause a deadlock in class loader code. Please see Bug 86450
+// * for more information.
+// */
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/IStatusFactory.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/IStatusFactory.java
new file mode 100644
index 0000000..adee0d8
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/IStatusFactory.java
@@ -0,0 +1,31 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.api;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * A abstraction for injecting specific IStatus objects into an implementation
+ * for testability purposes. It smells, but there doesn't seem to be an easier
+ * way to make this code testable since Eclipse's Status doesn't implement
+ * equals/hashCode (as-of 3.4).
+ */
+public interface IStatusFactory {
+
+ public IStatus error(String message);
+
+ public IStatus error(String message, Throwable t);
+
+ public IStatus warning(String message);
+
+ public IStatus info(String message);
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/ParameterProcessResults.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/ParameterProcessResults.java
new file mode 100644
index 0000000..858de5b
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/ParameterProcessResults.java
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+/**
+ *
+ */
+package org.eclipse.e4.core.deeplink.api;
+
+import java.util.Map;
+
+/**
+ * A data transfer object (since Java can't return multiple values from a
+ * function) encapsulating the result of executing a deeplink.
+ */
+public class ParameterProcessResults {
+ public boolean loaded = false;
+ public boolean activatedParameterCallback = false;
+ public Map<String, String> outputData = null;
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/StatusFactory.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/StatusFactory.java
new file mode 100644
index 0000000..1348ebd
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/StatusFactory.java
@@ -0,0 +1,41 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.api;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+
+/**
+ * A abstraction for injecting specific IStatus objects into an implementation
+ * for testability purposes. It smells, but there doesn't seem to be an easier
+ * way to make this code testable since Eclipse's Status doesn't implement
+ * equals/hashCode (as-of 3.4).
+ */
+public class StatusFactory implements IStatusFactory {
+
+ public IStatus error(String message) {
+ return new Status(Status.ERROR, Activator.PLUGIN_ID, message);
+ }
+
+ public IStatus error(String message, Throwable t) {
+ return new Status(Status.ERROR, Activator.PLUGIN_ID, message, t);
+ }
+
+ public IStatus warning(String message) {
+ return new Status(Status.WARNING, Activator.PLUGIN_ID, message);
+ }
+
+ public IStatus info(String message) {
+ return new Status(Status.INFO, Activator.PLUGIN_ID, message);
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/URLPathInfoParser.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/URLPathInfoParser.java
new file mode 100644
index 0000000..23bc155
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/api/URLPathInfoParser.java
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.api;
+
+import org.eclipse.e4.core.functionalprog.optionmonad.Option;
+
+import static org.eclipse.e4.core.functionalprog.optionmonad.None.none;
+import static org.eclipse.e4.core.functionalprog.optionmonad.Some.some;
+
+/**
+ * Archetypal deeplink:
+ * deeplink://appInstance/handlerType/handlerInstanceId/action?param1=value1¶m2=value2...
+ *
+ * Example:
+ * deeplink://myApp/perspective/org.eclipse.myApp.myPerspectiveID/do-stuff?animal=monkey&colour=blue&mood=angry
+ *
+ */
+public class URLPathInfoParser {
+ public final String handlerType;
+ public final String handlerId;
+ public final Option<String> action;
+
+ public URLPathInfoParser(String urlPathInfo) {
+ if (urlPathInfo == null || "/".equals(urlPathInfo)) {
+ throw new IllegalArgumentException("Empty URL path: nothing to do");
+ }
+
+ String[] urlParts = urlPathInfo.split("/+");
+ if (urlParts.length < 3) {
+ throw new IllegalArgumentException("Need both of: '/handlerType/handlerInstanceId' in URL but got: " + urlPathInfo);
+ }
+ handlerType = urlParts[1];
+ handlerId = urlParts[2];
+ if (urlParts.length > 3) {
+ action = some(urlParts[3]);
+ } else {
+ action = none();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return handlerType + "/" + handlerId + (action.hasValue() ? "/" + action.get() : "");
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/DeepLinkTypeHandlerFactory.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/DeepLinkTypeHandlerFactory.java
new file mode 100644
index 0000000..2fbfc73
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/DeepLinkTypeHandlerFactory.java
@@ -0,0 +1,59 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.handler;
+
+import static org.eclipse.e4.core.functionalprog.optionmonad.None.none;
+import static org.eclipse.e4.core.functionalprog.optionmonad.Some.some;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.e4.core.deeplink.api.AbstractDeepLinkTypeHandler;
+import org.eclipse.e4.core.deeplink.api.URLPathInfoParser;
+import org.eclipse.e4.core.functionalprog.optionmonad.Option;
+
+/**
+ * (non-API) class DeepLinkTypeHandlerFactory.
+ * <p>
+ * Given the following archetypal deeplink:
+ * <ul>
+ * <li>deeplink://appInstance/handlerType/handlerInstanceId/action?param1=value1¶m2=value2...
+ * </ul>
+ * A DeepLinkTypeHandler is a strategy pattern object that defines how deeplink
+ * requests are handled for a particular deeplink handlerType.
+ * <p>
+ * This class is a factory for retrieving a type handler for a particular handlerType.
+ */
+public class DeepLinkTypeHandlerFactory {
+
+ private Map<String, AbstractDeepLinkTypeHandler> handlers = null;
+
+ public DeepLinkTypeHandlerFactory(Map<String, AbstractDeepLinkTypeHandler> typeHandlerMap) {
+ handlers = typeHandlerMap;
+ }
+
+ public Option<AbstractDeepLinkTypeHandler> createTypeHandler(HttpServletRequest request,
+ HttpServletResponse response) {
+
+ URLPathInfoParser pathParts = new URLPathInfoParser(request.getPathInfo());
+
+ AbstractDeepLinkTypeHandler typeHandler = handlers.get(pathParts.handlerType);
+ if (typeHandler != null ) {
+ typeHandler.init(request, response, pathParts);
+ return some(typeHandler);
+ } else {
+ return none();
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/DeepLinkTypeHandlerMapper.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/DeepLinkTypeHandlerMapper.java
new file mode 100644
index 0000000..9c6f167
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/DeepLinkTypeHandlerMapper.java
@@ -0,0 +1,58 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.handler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.e4.core.deeplink.api.AbstractDeepLinkTypeHandler;
+import org.eclipse.e4.core.deeplink.api.Activator;
+
+
+/**
+ * A function object that computes a Map linking the handlerType identifiers
+ * to instances of {@link AbstractDeepLinkTypeHandler} using the extension
+ * point registry.
+ */
+public class DeepLinkTypeHandlerMapper {
+
+ Map<String, AbstractDeepLinkTypeHandler> handlerMap = new HashMap<String, AbstractDeepLinkTypeHandler>();
+
+ public DeepLinkTypeHandlerMapper(IExtensionRegistry extensionRegistry) {
+ if(extensionRegistry == null) {
+ throw new IllegalArgumentException("IExtensionResgistry passed to DeepLinkTypeHandlerMapper was null");
+ }
+
+ IConfigurationElement[] elements = extensionRegistry.getConfigurationElementsFor(Activator.PLUGIN_ID,
+ Activator.DEEP_LINK_EXT_PT_ID);
+
+ for (IConfigurationElement element : elements) {
+ String name = element.getAttribute("name");
+ try {
+ AbstractDeepLinkTypeHandler handler = (AbstractDeepLinkTypeHandler) element.createExecutableExtension("class");
+ handlerMap.put(name, handler);
+ } catch (CoreException e) {
+ Activator.getDefault().getLog().log(
+ new Status(Status.ERROR, Activator.PLUGIN_ID, "Incorrect deep link extension for: " + name, e));
+ continue;
+ }
+ }
+ }
+
+ public Map<String, AbstractDeepLinkTypeHandler> getMap() {
+ return handlerMap;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/HttpServiceTracker.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/HttpServiceTracker.java
new file mode 100644
index 0000000..9640f41
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/HttpServiceTracker.java
@@ -0,0 +1,94 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.handler;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.e4.core.deeplink.api.Activator;
+import org.eclipse.equinox.http.registry.HttpContextExtensionService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+/**
+ * An OSGI service tracker that registers the /deeplink servlet context with
+ * the deeplink handler servlet.
+ * <p>
+ * FIXME: Future: Add public API to add (/context, Servlet) pairs? Extension point?
+ */
+public class HttpServiceTracker extends ServiceTracker {
+ public static final String DEFAULT_SERVLET = "rap";
+ public static final String ID_HTTP_CONTEXT = "org.eclipse.rap.httpcontext";
+
+ private HttpService httpService;
+ private final List<String> servletAliases = new ArrayList<String>();
+
+ public HttpServiceTracker(final BundleContext context) {
+ super(context, HttpService.class.getName(), null);
+ // FIXME: Hard code this for now....
+ servletAliases.add("deeplink");
+ }
+
+ public Object addingService(final ServiceReference reference) {
+ httpService = (HttpService) context.getService(reference);
+ HttpContext obContext = getE4HttpContext(reference);
+
+ if (servletAliases.size() == 0) {
+ // register default servlet
+ servletAliases.add(DEFAULT_SERVLET);
+ }
+ for (Iterator<String> it = servletAliases.iterator(); it.hasNext();) {
+ String name = (String) it.next();
+ try {
+ RequestHandler handler = new RequestHandler();
+ httpService.registerServlet(
+ "/" + name, handler, null, obContext); //$NON-NLS-1$
+ } catch (Exception e) {
+ String text = "Could not register servlet mapping ''{0}''.";
+ Object[] param = new Object[] { name };
+ String msg = MessageFormat.format(text, param);
+ Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ IStatus.OK, msg, e);
+ Activator.getDefault().getLog().log(status);
+ }
+ }
+ return httpService;
+ }
+
+ private HttpContext getE4HttpContext(final ServiceReference reference) {
+ String name = HttpContextExtensionService.class.getName();
+ ServiceReference serviceRef = context.getServiceReference(name);
+ HttpContextExtensionService service = (HttpContextExtensionService) context
+ .getService(serviceRef);
+ return service.getHttpContext(reference, ID_HTTP_CONTEXT);
+ }
+
+ public void removedService(final ServiceReference reference, final Object service) {
+ for (Iterator<String> iterator = servletAliases.iterator(); iterator
+ .hasNext();) {
+ String name = (String) iterator.next();
+ httpService.unregister("/" + name); //$NON-NLS-1$
+ }
+ super.removedService(reference, service);
+ }
+
+ public void addServletAlias(final String name) {
+ servletAliases.add(name);
+ }
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/RequestHandler.java b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/RequestHandler.java
new file mode 100644
index 0000000..04ee61d
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.deeplink.handler/src/org/eclipse/e4/core/deeplink/handler/RequestHandler.java
@@ -0,0 +1,144 @@
+/******************************************************************************
+ * Copyright (c) David Orme 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:
+ * David Orme - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.core.deeplink.handler;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.e4.core.deeplink.api.AbstractDeepLinkTypeHandler;
+import org.eclipse.e4.core.deeplink.api.Activator;
+import org.eclipse.e4.core.functionalprog.optionmonad.Option;
+
+
+/**
+ * The deeplink handler servlet.
+ */
+public class RequestHandler extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private static DeepLinkTypeHandlerFactory typeHandlerFactory;
+
+ public RequestHandler() {
+ DeepLinkTypeHandlerMapper deepLinkTypeHandlerMapper = new DeepLinkTypeHandlerMapper(Platform.getExtensionRegistry());
+ Map<String, AbstractDeepLinkTypeHandler> handlerMap = deepLinkTypeHandlerMapper.getMap();
+ typeHandlerFactory = new DeepLinkTypeHandlerFactory(handlerMap);
+ }
+
+ // Used to inject mapping dependency for testing
+ RequestHandler(Map<String, AbstractDeepLinkTypeHandler> handlerMap) {
+ typeHandlerFactory = new DeepLinkTypeHandlerFactory(handlerMap);
+ }
+
+ public void init(final ServletConfig config) throws ServletException {
+ super.init(config);
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ if( !isRequestFromLocalhost(req) ) {
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ try {
+ Option<AbstractDeepLinkTypeHandler> typeHandlerOption = typeHandlerFactory.createTypeHandler(req, resp);
+ if (typeHandlerOption.hasValue()) {
+ AbstractDeepLinkTypeHandler typeHandler = typeHandlerOption.get();
+ typeHandler.doGet();
+ } else {
+ String message = "No deep link handler for URL path: " + req.getPathInfo();
+ logInfo(message);
+ returnErrorMessageToClient(message, resp);
+ }
+ } catch (Throwable t) {
+ logUnhandledExceptionAndReturnResult(t, resp);
+ }
+ }
+
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ if( !isRequestFromLocalhost(req) ) {
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ try {
+ Option<AbstractDeepLinkTypeHandler> typeHandlerOption = typeHandlerFactory.createTypeHandler(req, resp);
+ if (typeHandlerOption.hasValue()) {
+ AbstractDeepLinkTypeHandler typeHandler = typeHandlerOption.get();
+ typeHandler.doPost();
+ } else {
+ String message = "No deep link handler for URL path: " + req.getPathInfo();
+ logInfo(message);
+ returnErrorMessageToClient(message, resp);
+ }
+ } catch (Throwable t) {
+ logUnhandledExceptionAndReturnResult(t, resp);
+ }
+ }
+
+ private boolean isRequestFromLocalhost(HttpServletRequest req) {
+ String remoteHost = req.getRemoteHost();
+ return "localhost".equalsIgnoreCase(remoteHost) || "127.0.0.1".equalsIgnoreCase(remoteHost);
+ }
+
+ private void logUnhandledExceptionAndReturnResult(Throwable t, HttpServletResponse response) {
+ returnErrorMessageToClient(t.getMessage(), response);
+ logException("Unhandled exception processing HTTP request: "
+ + t.getMessage(), t);
+ }
+
+ /**
+ * FIXME: All result XML generation needs to be centralized in a single
+ * factory.
+ */
+ private void returnErrorMessageToClient(String message, HttpServletResponse response) {
+ response.setContentType("text/xml");
+ try {
+ ServletOutputStream out = response.getOutputStream();
+ out.println("<?xml version=\"1.0\"?>");
+ out.println("<deeplink>");
+ out.println(" <result exception=\"" + message + "\"/>");
+ out.println("</deeplink>");
+ } catch (IOException e) {
+ logException("Unable to return error result to client: "
+ + e.getMessage(), e);
+ }
+ }
+
+ private void logException(String message, Throwable t) {
+ Activator.getDefault().getLog().log(
+ new Status(Status.ERROR, Activator.PLUGIN_ID,
+ message, t));
+ }
+
+ private void logInfo(String message) {
+ Activator.getDefault().getLog().log(
+ new Status(Status.ERROR, Activator.PLUGIN_ID,
+ message));
+ }
+
+}
\ No newline at end of file