OpenDoc case study

OpenDoc and OLE are user interface frameworks built on top of CORBA and COM, respectively. They introduce a new abstraction called a compound document which makes it significantly easier to build applications. Compound documents have enough built-in smarts and flexibility for even end-users to build custom applications.

Compound document examples

One of the earliest examples of compound documents is the EZ application in the Andrew Toolkit. While ostensibly a text editor, it allows you to embed data from any other application built with the Toolkit. For example, Raster and Chart are stand-alone applications for drawing figures and making spreadsheets, respectively. But they can also be embedded in an EZ document, laid out with the text, and used in-place.

Another example is Visual Basic. The external and internal representations of an application are laid out visually, as objects in (Microsoft's version of) Basic. The components are edited in-place, not just by setting parameters but also by attaching scripts, e.g. the response to a button press.

Finally, we have the Web page. The components of a page can be arbitrary data objects; the browser just has to have external or internal utilities for viewing them. The user can set preferences for which data types get which viewers. The markup language and the browser take care of the layout. With new standards like LiveConnect and JavaBeans, components can also talk to each other and form a complete application. Of course, OpenDoc and OLE also want to get in on the act. Web pages will be a crucial battlefield for component software, especially with the advent of Network Computers (NCs).

Old vs. new

Both the old and new frameworks use the Document/View paradigm, as described in the first lecture. The framework provides lots of built-in Views which can be combined hierarchically to make control panels, menus, and dialog boxes. This enforces a certain look-and-feel standard for all applications.

However, previous frameworks stopped there. When it came to implementing your Document class, you were on your own. Many applications, such as word processors, are naturally composed of different kinds of components like text, rulers, and figures. Such applications had to come up with their own data structure for representing the Document and the components, their own data transfer mechanism between components, their own linking and constraint mechanism, and their own scripting protocol. Third-party components were rare, because they had to conform to the application's proprietary component interface.

The new frameworks have all of this functionality built-in. Now you can build up your internal Document representation hierarchically, just like you can for the visual representation. Compound documents provide a standard interface for components, a data structure for a dynamic collection of components, a data transfer mechanism, a linking mechanism, and a scripting protocol. Because of this open standard, third-party support is automatic. And thanks to the distributed object foundation, components can come from and link to anywhere on the network.

Automatic support for compound documents essentially reduces application programming to component design. Since the component interface is standard, components don't have to come from one manufacturer. Rather than applications, companies can provide component suites which users can assemble however they wish. This significantly lowers the entry cost into the software market.

Embedding

A compound document is a dynamic container of components which has both a visual and persistent representation. In other words, it has the properties of a component itself. In OpenDoc, a document is of type ODPart and it contains objects of type ODPart, which may contain more ODParts. As a visual container, a compound document has a View object which acts like a geometry manager, negotiating the layout of the Views of the embedded components, dispatching events, and controlling the input focus. As a persistent container, it has a DataService object which acts like a "filesystem within a file", controlling the layout of the serialized components on disk.

There must be some way for the compound document to choose the appropriate View or DataService for each component. Here are three possibilities, all of which can be tried in succession:

  1. A global registry for preferred component/View pairs.
  2. Each component provides a list of its favorite Views, e.g. the View which was used to create it.
  3. Views in a Chain of Responsibility can decide whether they support the component.
Netscape, for example, uses the first approach.

Linking

OpenDoc handles linking via a sophisticated implementation of the Observer pattern. Since network communication should be reduced as much as possible, OpenDoc uses an update, rather than invalidate, protocol. However, OpenDoc also supports a "pull" mechanism where subscribers can request an update on their perogative. To handle both of these cases efficiently, intermediate Link objects cache the most recent update and dole it out to components who pull.

OpenDoc also has an intermediate LinkSource object which holds the list of subscribers. If some of the subscribers are remote, the distributed shared memory would normally create Proxies for them. However, this is wasteful, so the LinkSource records subscribers by their global names and uses the ORB to look them up. This is a common pattern for avoiding Proxies.

Transfer

Combining independent components together to form an application creates a problem: how do they communicate? For example, if I drag data from one component and drop it on another, how is it interpreted? If the destination is a text field, maybe it should be interpreted as a string. If the destination is a scale, maybe it should be interpreted as a number. If the destination is a drawing program, maybe it should be interpreted as an image.

The solution taken in OpenDoc is for the sender and recipient to negotiate. What is sent is not simply a bare number or string, but rather data held in a "carrier" object called a StorageUnit. The carrier describes which formats are available, e.g. as an icon, as a floating-point number, in French, or in monochrome. The receiver examines the formats and picks one it understands. The sender may have provided copies of the data in these other formats, the carrier may automatically convert the data into another format, or the carrier may request the sender for the specific format.

The receiver can also request a link back to the carrier. This is the "Paste With Link" option. Thus drag-and-drop can be used to establish dependencies.

The carrier system works, but uniform data transfer still requires elaborate and standardized systems for describing and converting data, not just at a representational level but at a semantic level. It will be interesting to watch further developments in this area.

Scripting

Most Macintosh applications are scriptable via Apple events. AppleScript compiles into Apple event calls, which can then be directed at any application. Some applications allow users to customize the event handlers, e.g. attaching a script which sends out more events, or recording incoming events in order to generate macros.

OpenDoc provides a standard infrastructure for handling events. At the most basic level, events can dynamically inspect and modify component properties. This is useful for application builders and debuggers. Events can also contain complex queries involving named parts, ranges, and conditions. For example, "delete seconds 5 through 30 of the first movie of paragraph 3 of my document." By handling such events, OpenDoc essentially provides a higher-level naming scheme beyond what the shared memory provides.

Incidentally, OLE also uses such complex names, called monikers, pervasively. For example, a link can have a moniker, rather than a specific object, as its source. This is the analogue of a symbolic link in Unix. A spreadsheet can link to a range of cells in another spreedsheet, no matter what values they contain.

Apple's Open Scripting Architecture (OSA) is essentially a standard set of events that components should respond to. Thus any scripting language which generates OSA events can control OpenDoc applications. OpenDoc provides a class which encapsulates a batch of OSA events; you can think of it as "OSA bytecode". Components can store and execute this bytecode, e.g. as the binding of an event handler.

Scripting isn't the only way to make application-dependent requests. OpenDoc and OLE both allow additional application-dependent interfaces to be attached to components. These interfaces will presumably be standardized for popular domains.


OpenDoc also provides a sophisticated persistence mechanism for compound documents called Bento. It is more of an object management mechanism than a component software mechanism. Bento will probably be integrated with CORBA in the near future, giving CORBA more of the facilities of an object-oriented database.

For more information on OpenDoc and ActiveX, chase the links on the main page.


Thomas Minka
Last modified: Fri Feb 14 17:57:25 EST 1997