E4/Deeplinking

From Eclipsepedia

< E4
Jump to: navigation, search

Contents

Deeplinking - Making a client-side application URL-addressable

Web and networked applications have shown the power and versatility of hyperlinking. Rich graphical applications have, generally speaking, missed out on the benefits of hyperlinking: being able to easily connect anything to anything else, across technologies and platforms.

Why deeplinking?

Now that web browsers are increasingly being embedded into applications and hyperlinking is being implemented in desktop operating systems, we feel it would be beneficial if client-side applications could participate on an equal footing with web applications.

Why?

In order to understand the benefits that this could bring client-side applications, it is useful to first describe how URLs and hyperlinks are currently used in web applications:

URLs specify multiple entry points to applications

Web applications commonly use URLs to specify multiple "starting places" within an application. For example, a content management system may expose separate URLs to enter the application at the system blog, the wiki, or within the calendaring system.

In contrast, rich client applications have traditionally imposed a rigid navigational structure on the user, forcing him to start at the same entry screen and navigate everywhere else from there.

URLs specify the initial information to load into a page

URLs addressing pages within a web application may also include information about data to load. These "concatinated keys" of application + data identifier then may be emailed, sent via instant messaging clients, or embedded in applications and then used by other users to identify the same content on the same web site later.

Common URLs of this sort include:

  • Google search URLs identifying particular searches/results
  • URLs identifying SlideShare presentations or YouTube videos
  • A specific LinkedIn user's home or profile page
  • Etc...

Applied effectively, this capability provides constant-time navigation to common bits of data within an application.

In contrast, rich client applications have traditionally imposed a rigid navigational structure on the user, forcing him to start at the same entry screen and navigate everywhere else from there.

URLs help break applications into components

While users can reference parts of an application by encoding input parameters into the URL, application programmers can then use this same capability to break applications into modules identified by these same entry points.

In Web 1.0, or page-based applications, the entry points are specified by the set of pages and their URL parameters. This sort of application's API is then defined by the set of pages it has and the parameters these pages can accept.

In AJAX applications, these modules and entry points can be made even richer because the result of a request no longer is an HTML page, but rather is an XML or JSON document. This result document contains the data that is then rendered into the web client's page via JavaScript and CSS.

The API of this kind of application then becomes the set of XML or JSON results that can be retrieved by sending requests with parameters encoded in the query string. This kind of API then basically becomes a generic remote procedure call with HTTP as the transport, the set of procedures defined by the kinds of URLs that may be written, and XML or JSON as the result type.

In contrast, rich client applications tend to have application code that is tightly and rigidly coupled to the navigational structure of the underlying data.

Allowing cross-platform, open access to information

Whenever the Google spider indexes a web site, the web application on that site is being used by a heterogeneous tool set.

Less grandly, web sites can be scraped and controlled by Microsoft Office applications, by scripting languages, and by programming environments different from the ones originally used to create them.

In each case, the end result is that making data URL-addressable enables applications that the originators never conceived or imagined.

Deeplinking: a technology-neutral, URL-based integration platform

We have observed many benefits that URL addressing brings to web applications

  • Having multiple "start pages" and relaxing the navigation structure within your application
  • Identifying bits of information, such as a video, slide show, or search result using a form that is easy to store and share
  • Enabling applications to be broken into components. These components may be defined by user interface pages (ie: a YouTube video) or may perform an action and retrieve a result (ie: a RESTful AJAX invocation).
  • Allowing and encouraging open, cross-platform use of data, enabling uses that the original application authors never imagined.

We believe that it is time to make these benefits available to rich graphical client applications as well. Deeplinking implements a solid first step toward enabling this goal.

How does deeplinking work?

Deeplinking provides a specification and a Java/Eclipse RCP-based reference implementation for how any rich graphical application can be made deeply URL-addressable, and thus begin to participate and inter-operate in a networked, hyperlinked world. Deeplinks work regardless of if the application is started; if it is not already started, the deeplink URL handler will automatically launch it.

The best way to understand deeplinking is by describing how it works. Deeplink URLs are of the following form:

deeplink://appName/handlerType/handlerInstanceId/action?param1=value1&param2=value2... 

where the action and parameters are optional. The appName is the name of the subdirectory where the application lives. The remaining two segments are the handler type and the instance ID. The handler type is the "type" of the "object" that the link addresses. The handlerInstanceId is the string ID the application needs in order to look up an object of the specified type.

For example, an Eclipse RCP application could have a following deeplink:

deeplink://rcp-app/perspective/org.eclipse.e4.rcp-app.client.perspective.id?customerID=1234567

The elements of this deeplink have the following meanings:

  • rcp-app : The name of the application, which by convention must have the same name as the subdirectory that the application lives in
  • perspective : This deeplink opens or displays a perspective
  • org.eclipse.e4.rcp-app.client.perspective.id : The Eclipse RCP perspective ID
  •  ?customerID=1234567 : defines, in the same way as a web application, parameters to pass to the Perspective Factory and thus into the application. Specifically, the IPerspectiveFactory referenced by the perspective ID must extend the AbstractDeepLinkInstanceHandler interface. In our example, the perspective might then instruct views or editors to load a particular customer by his/her ID or set the workbench selection to that customer.

Deeplinking is designed so that any application that can listen to a network socket using the HTTP protocol can participate. And the URL format is designed so that handler types can be created for any kind of application: Eclipse RCP, Swing, .NET, etc. The one current restriction is that deeplinks are a 100% local protocol. I.e: a deeplink:// URL always refers to an application running on "localhost" and the deeplink URL handler resolves the port number assignment based on the application name.

Using Deeplinking

For working examples, it is recommended to read the source code of the examples shipped with deeplinking as these will always contain the latest recommended usage patterns. These can be found in the org.eclipse.ui.deeplink.example bundles.

As described before, a generic deeplink URL is in the following form:

deeplink://appName/handlerType/handlerInstanceId/action?param1=value1&param2=value2... 

In order to use deeplinking, one may perform one of two tasks:

  • Define deeplinks in one's application
  • Extend deeplinking to support additional handlerTypes

This document will cover the former.

Out of the box, deeplinking supports two handlerTypes:

  • perspective : Open a perspective and optionally execute a callback that may return data to the caller.
  • extensionpt : Run a callback that is defined by an Eclipse extension point. This callback also may optionally return data to the caller.

The good news is that out of the box, all Eclipse perspectives are automatically turned into deeplinks of the form:

deeplink://appName/perspective/com.your-company.your-app.perspective.id 

You do not need to do any work beyond enabling deeplinking to use these.

However, if you want your perspectives to process URL parameters or to return results, or if you want to define a "headless" callback that can perform any arbitrary (UI or non-UI) processing, you will need to do a small amount of additional work:

(In addition, deeplinking provides an extension point that you can use to define any handlerType you could want. For example, if you were careful, you might want to create a handlerType that turns all Eclipse Commands or menu actions into deeplinks...)

Enabling Deeplinking

In order to enable Deeplinking in your RCP application, you will first need to add the following bundle dependencies to your main Eclipse RCP bundle (the one containing Application.java):

  • org.eclipse.e4.core.deeplink

To initialize Deeplinking inside your RCP application, you will need to add a snippet of code similar to the following to Application.java, before it calls #createAndRunWorkbench:

final int ONE_SECOND = 1000;
final String COMMANDLINE_KEY = "application.args";

//....
final DeepLinkManager deepLinkManager = new DeepLinkManager(true, Activator.getDefault().getLog());

String installation = deepLinkManager.getInstallationIDFromPath(Platform.getInstallLocation().getURL());
int serverPort = deepLinkManager.getPortNumberForInstallation(installation);
System.getProperties().setProperty("org.osgi.service.http.port", Integer.toString(serverPort));

BundleContext bundleContext = Activator.getDefault().getBundle().getBundleContext();
new DeepLinkBundleList(bundleContext).startupBundlesForHttpServlets();

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);

Defining a post-perspective-switch callback

In order to define a callback on a perspective, you only need to include the proper dependency and make your perspective factory class extend the callback's abstract class and implement the callback method.

The dependency is:

  • org.eclipse.e4.core.deeplink.handler

Your perspective factory will then follow the form:

public class Perspective extends AbstractDeepLinkInstanceHandler implements IPerspectiveFactory { 
   public void createInitialLayout(IPageLayout layout) { 
      // your perspective initialization... 
   }

   @Override
   public Map<String, String> activate(String handlerInstanceID, 
         String action, Map<String, String[]> params) 
   { 
      // Do whatever you want here. We'll return a simple Date value as an example... 
      HashMap<String, String> result = new HashMap<String, String>(); 
      result.put("date", new Date().toString()); 
      return result; 
   } 
} 

That's it. You don't need to register your perspective callback because it's already registered by its perspective ID. Deeplinking will automatically look it up and call the callback if it exists.

Defining a "headless" extension point callback

An extension point callback is defined nearly the same way as a perspective callback.

The dependency is:

  • org.eclipse.e4.core.deeplink.typehandler.extensionpt

The code looks like the following:

public class SayHelloHandler extends AbstractDeepLinkInstanceHandler {
  @Override
  public Map<String, String> activate(String handlerInstanceID, String action, 
        Map<String, String[]> params) 
  {
     Map<String, String> results = new HashMap<String, String>();
     String helloMessage = "Hello, " + action;
     System.out.println(helloMessage);
     results.put("Hello", helloMessage);
     return results;
  }
}

Once you have the code, you have to register the callback using an extension point:

  • Go to the MANIFEST.MF editor for your bundle and switch to the "extensions" tab.
  • Click "Add..." and add a new org.eclipse.e4.core.deeplink.typehandler.extensionpt.deepLinkExtensionPointInstanceHandler extension point.
  • Click on the child in the tree
  • Change the "id" field to the URL fragment that will identify the deeplink. For example, our "Hello, world" extension point uses "sayhello" as its id.
  • Click "Browse..." and choose the class you created previously as your callback.

Once this is done, your callback may be accessed using a URL of the form:

deeplink://appName/extensionpt/id/optionalAction?param1=value1... 

Or to use the deep link defined by the code example above:

deeplink://appName/extensionpt/sayhello/George

Configuring a computer to use deeplinking

Deeplinking has several levels of support, and the amount of configuration a computer requires in order to use deeplinking varies depending on what level of support you need. These levels are:

  1. You always know what port each application's deeplinking HTTP server will run on. The application will always be running and/or does not need to be automatically started if it is not running. If the application is not already running, failure to invoke a deeplink is not a problem. For example, an AJAX application embedded inside a web browser inside an RCP application would fit these requirements.
  2. You have multiple applications that need to communicate via deeplinks and you need to use deeplink:// URLs to keep them separate. If a particular application is not running, it needs to be started automatically so that it always responds to deeplink requests.

Let's look at each of these options in turn and understand what is required to deploy them on a client PC.

Direct deeplink access using HTTP and known ports

If you always know the port number where a deeplink application will run and the application will always be running, then simply installing the application will automatically make its deeplinks available as HTTP URLs. Anything that can invoke an HTTP URL can utilize these. As mentioned previously, one use for this could be to enable an AJAX-based web application running in an embedded web browser to communicate with its own host application.

To provide a margin of safety, incoming deeplink HTTP connections are checked to ensure that they only originate from localhost. However, you should always be careful about what functionality you expose through deeplinks.

In this case, no additional configuration is required in order to use deeplinking. Deeplink URLs will be of the following form:

http://localhost:port/deeplink/handlerType/handlerInstanceId/action?param1=value1&param2=value2...

In other word, if you know the physical port on a machine where a particular deeplink handler lives you can replace deeplink://appName with http://localhost:port/deeplink and invoke the deeplink with anything that knows how to talk the HTTP protocol.

Named access across multiple applications

Deeplinking currently supplies examples for integrating with the Windows shell. The deeplinking/E4 team would appreciate contributions of instructions, examples, and/or code to make Deeplinking integrate with the Unixes and OSX. With that said, Deeplinking itself has been tested and is known to function on both Win32 and Linux.

If you have multiple applications and you want to access their deeplinks symbolically by name through the deeplink://appName protocol, then you need to register a platform-specific protocol handler for the deeplink:// protocol and follow a few conventions.

A Windows protocol handler is simply a command-line application (with no GUI) that receives a URL as the 0th command-line parameter and does the "right thing" with it for its type of protocol. In the case of a deeplink URL, the "right thing" would be to:

  1. Detect if the application is running and if not, to start it.
  2. Translate the deeplink URL into a HTTP URL addressed to the port where the application's deeplink handler is running.
  3. Proxy the request through to the HTTP handler running inside the application.

The command-line application that performs all of the above functions is called the Deeplink Launch Proxy

In order to use this launch proxy, you need to understand three things:

  1. How to register your launch proxy's install location with Windows.
  2. How to specify the launch proxy's log file location.
  3. The naming conventions you need to follow so that the launch proxy can find your applications.

Registering your launch proxy with Windows

This launch proxy is registered with Windows using the deeplink.reg file that is included in the distribution. You can edit this file using any text editor that can edit files containing multibyte character sets. For example, the open source notepad++ application works well.

Inside this file, simply follow the directions in the comments and replace the supplied paths with the correct ones for where you will install the launch proxy. When you are done, save and double-click the file to load it into Windows.

Registering your launch proxy on Linux/Gnome

Follow the instructions at http://ubuntuforums.org/showthread.php?t=125584, but for the deeplink:// protocol to register the launch proxy on Linux/Gnome.

How to change the launch proxy's default log file and location

The launch proxy is actually a command-line (headless) Eclipse RCP application. By default, headless RCP applications put their log files in a file named configuration/<currentTimeMillis>.log.

There are two problems with this approach.

  1. Since this folder is inside the launch proxy installation folder, it might not be writable.
  2. Creating a separate log file each time the launch proxy runs will cause the number of files inside the configuration folder to grow very large over time. This clearly will not scale.

Fortunately, there is a simple solution.

OSGI runtimes, including Eclipse's, support passing an environment variable to specify the name and location of the log file. One reasonable choice is to use some form of the following line, adapted to your preferred method of detecting the user's home directory:

-vmargs -Dosgi.logfile="${system_property:user.home}"/.launchproxy.log 

This line can be included in the RCP .ini file associated with your application's executable rather than on the command line if you prefer.

Launch proxy naming and directory conventions

So far, we have registered and configured our launch proxy. The last step is to install our deeplink-aware applications so that the launch proxy can find them if someone tries to use a deeplink:// URL when the application isn't already loaded. In order to accomplish this goal, the launch proxy defines two conventions:

  • A configuration file called deeplink4.properties in a well-defined location mapping application names to port numbers.
  • A subdirectory structure relative to that configuration file specifying the location of the application executables.

The configuration file is defined on Win32 to live in the c:\Program Files\Deeplink folder. (If you need to change this location, you must recompile all binaries that use deeplinking.) The configuration file must exist before deeplinking is used but if the applications have write access to this file and the application's binary launcher is named either "eclipse.exe" or "launcher.exe" (or the same thing but without ".exe" on Linux or Mac), it may be empty and the applications will automatically populate it.

The deeplink-aware applications themselves must live in subdirectories of the folder containing the deeplink configuration file. The name of the subdirectory is the name that deeplinking will use to identify the application.

(In order to support Linux or Mac, currently one must manually configure a proper platform path when compiling for those platforms. Investigations on how to more cleanly support multiple platforms are welcomed.)

The format of deeplink4.properties

deeplink4.properties is a standard Java Properties file minimally containing one key in the following form for each deeplink-aware application installed on the system:

instance.<appName>.port=9000

where <appName> is the name of the folder in which the application lives and the number is the port number on which the application will open an HTTP server to listen for deeplink requests.

deeplink4.properties supports the following additional settings:

  • base.port.number -- The initial port number where deeplinking will begin assigning ports to applications.
  • instance.<appName>.command=executable.exe -- The name of the executable used to launch the specified instance. If the executable name is eclipse.exe, eclipse, launcher.exe, or launcher, this need not be specified by hand.

Where to get Deeplinking

Deeplinking is available today from the E4 update site. The source code is in the E4 Git repository. Addresses for fetching your very own clone are at the bottom of the following pages:

Both EGit and Tortoise Git have been tested and are known to work with the Eclipse Git repository.