Client architecture

Web components

Orion aims to provide independently useful web components with minimal coupling between them, so that application developers can choose to deploy only the subsets of interest to their applications. Each component is represented by a JavaScript library (typically a single JavaScript file) and any directly associated resources like style sheets or images. With Orion we consciously do not mandate the use of a special packaging format or component library requirement so you can for example use your favorite DOM manipulation library as is.

Dependencies between components in Orion are managed using the CommonJS AMD module format. However, as alluded to above, web components are not forced into using this model. For more loosely coupled functional interactions Orion provides a service registry similar to the OSGi service model, but with some of the declarative characteristics of Eclipse extension and extension points.

Services

References between client library objects are directed through a service registry. Library objects are provided with a service registry object on construction, allowing a developer using the library to override what service implementations are used by any given library object. For example, while the default implementation of the Preferences object uses the remote preference service over HTTP, a client could provide an implementation that uses HTML5 local storage, cookies, etc.

Here is a simplified example of the Explorer object using a service to prompt the user to confirm a file deletion:

 function Explorer(serviceRegistry, ...) {
     this.registry = serviceRegistry;
     ...
  }
  ...
  deleteFile: function(itemId) {
     var item = this.myTree.getItem(itemId);
     var service = <b>registry.getService</b>("orion.page.dialog");
     var message = "Are you sure you want to delete '" + item.Name + "'?";
     service.confirm(message, function(doit) { /* perform deletion */});
  }

The getService function takes the service name as argument, and returns either the service instance or null. For this example we're assuming the service is present however especially when using services dynamically contributed from a plugin you should check for null. One particularly important point to emphasize is that all service calls through the service registry are asynchronous and return a promise object (such as a Dojo Deferred). You cannot assume that the service function will have been executed by the time the service function returns and instead must chain any follow-on calls to the "promise". This allows the service implementation to employ a variety of long-running techniques to execute the service, such as calls to a remote web service, cross-domain postMessage, HTML5 web workers, etc.

Service definitions

A service definition includes both a declarative aspect, and optionally a concrete service object. Declarative service properties can be cached by the Orion service registry, avoiding loading of the plug-in providing that service until it is needed. The following is an example of a very simple service definition:

 var provider = new eclipse.PluginProvider();
 var serviceImpl = {
   run : function(text) {
     return text.toUpperCase();
   }
 };
 var serviceProps = {
   name : "UPPERCASE",
   img : "/images/gear.gif",
   key : [ "u", true ]
 };
 provider.registerServiceProvider("orion.edit.command", serviceImpl, serviceProps);
 provider.connect();

This service registers a new command in the editor. The service implementation has a single run method that converts the provided text to upper case. The service properties specify the name of the command, the icon, and key binding. This way the editor can display the command in the toolbar using the cached service properties, without actually loading the plugin.

Extensions

Orion implements the Eclipse concept of extensions and extension points using the Orion service registry. An extension point is simply a service interface that clients are expected to implement. Each instance of that service is an extension. You will often see the terms "extension" and "service" used interchangeably in the Orion documentation. There is no technical difference between them; rather, it is an expression of how the service is intended to be used. Services provided by clients are conceptually extensions, and services used by clients are simply called services.

Plugins

Services and extensions that reside on different web domains from the host Orion server are contributed to Orion by registering plugins. A plugin is declared via an HTML file, is opened in a "head-less" child IFrame and uses window.postMessage to advertise capabilities in the form of services and extensions back to Orion. Orion persists plugin information and at a later date when functionality is requested the plugin will be re-loaded either in an IFrame, in a Web Worker, or in-line as a web component based on security restrictions and functionality offered. Here is an example of a simple plug-in:

 <!DOCTYPE html>
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   &lt;!-- Dependencies --&gt;
   <script type="text/javascript" src="../orion/plugin.js"></script>
   <script>
     window.onload = function() {
       &lt;!-- Service declarations go here --&gt;
     };
   </script>
 </head>
 <body>
 </body>
 </html>

A simple plugin can consist of only the plugin's HTML file, and the required plugin.js script from Orion. More complex plugins will typically include several script files containing the concrete service implementation objects.

Plugins are installed or uninstalled from the Plugins page within the Orion user interface.