IBM
skip to main content
Shop Support Downloads
Home Products Consulting Industries News About IBM
Search
 

 
 

 
 
  OpenDoc  
  Download  
  Library  
   
 
IBM Worldwide
 

 
 
OpenDoc > Library >

Reprinted with permission from OS/2 Magazine (July 1996: 39-43), a Miller Freeman publication

Developing and Using OpenDoc Extensions
Objects, extensions, plug-ins, and more!
Emily Vander Veer

OpenDoc is a cross-platform, component-based architecture designed to let developers create software components that interact consistently through a set of standard APIs. In an OpenDoc document or application, this interaction provides the glue that binds component building blocks into complete software solutions. Think of a stereo system: as with stereo receivers, turntables, and speakers from different manufacturers, OpenDoc components produced by various developers can be interconnected to build functional systems because their interfaces are based on open standards.

One of OpenDoc's most compelling features is its extensibility. OpenDoc is essentially a framework for persistent, visible, interactive components; it doesn't mandate specific implementation details. It does, however, provide a technique for developers to define, implement, and enforce such details. Every OpenDoc object--specifically, every object that ultimately derives from ODObject--can be extended by a developer. These objects include components, or "parts," as well as OpenDoc service objects such as the clipboard, dispatcher, and session. By implementing extensions to OpenDoc service objects in conjunction with a run-time initialization entry point called a "shell plug-in," developers can actually extend the OpenDoc architecture itself. Extensions can be implemented privately, by developers producing a suite of interoperable parts; or publicly, to expose function for use by others. One of the benefits of implementing OpenDoc extensions is that now developers have a way to make their function easily accessible to other developers and system integrators.

Let's explore the concept of OpenDoc extensions and shell plug-ins: what they are, when to use them, and how to design them. Sample code is included to demonstrate the programming effort required for each. We'll begin by examining how to implement, query, and access an interface extension. Then we'll take a look at how shell plug-ins can be used to time the creation of extensions and how this affects the availability of extensible objects.

Some OpenDoc Definitions

Interface extensions

What we've been referring to as "extensions" are more accurately called "interface extensions." Their purpose is to extend an object's interface; that is, they allow objects to expose custom interfaces and communicate through them to other objects in a well-defined way. While interfaces can be exposed without using extensions, extensions allow them to be organized in a meaningful way and added to objects in groups. These sets of behaviors are implemented as objects themselves; that is, they can be queried, reused, inherited from, and so on. Therefore, once a set of behaviors is developed and tested, it can then be used to extend any other object.

An example of the type of function an extension can be used to implement is the ability to respond to speech input. Once the problem is solved for one part, the solution can be applied to any other. The resulting extension object actually becomes a mini-software developer's kit that makes it easy for other part developers to incorporate speech capability into their applications.

Other examples include the semantic interface extension, which supports scripting, and the menu extension, which allows parts to collaborate on part-spanning menus. These objects, along with the settings and view extensions and sample parts that exploit them, are part of the OpenDoc for OS/2 toolkit and are useful as guides for custom implementations. (For more detailed information, see the class descriptions of ODSemanticInterface, ODMenuExtension, ODSettingsExtension, and ODViewExtension in the OpenDoc for OS/2 Programmer's Reference.)

As noted above, implementing the semantic interface extension and responding to semantic events defines an object as "scriptable." This extension offers a ready-made means of implementing part interaction and is often recommended for this purpose. While the extension can play a very important role in the development of OpenDoc solutions, however, it does not provide a one-size-fits-all approach. The difference between modifying semantic interface extensions and "rolling your own" is one of performance and utility.

The primary benefit of subclassing and reusing the semantic interface is that it can be used to make a part scriptable; that is, to respond to events that have been specified, typically by a power user or system integrator, in a scripting language such as Object REXX. Because the design of this extension focuses on scriptability in favor of efficiency, its benefit comes at the price of nontrivial performance overhead. If high bandwidth, fast interaction, or specialized collaboration between objects are anticipated, creating and using an extension derived from ODExtension is better than subclassing the semantic interface extension.

There are three objects to be considered when implementing and using extensions: the extension itself, the base object (the object to be extended), and the calling object (the object that will be accessing the extension).

Extension. The extension must inherit from ODExtension, which is an abstract base class capable of maintaining associations to a base object and releasing resources, both its own and those associated to the base object. All other functionality must be implemented in the derived class. The methods ODExtension defines include BaseRemoved, CheckValid, GetBase, InitExtension, and IsValid.

Keeping in mind that one of the primary benefits of implementing extensions is the ability to reuse them, it's important to design them as groups of meaningfully associated behaviors. Ideally, each of the methods in an extension should relate to a single activity. It might help to make sure that a proposed extension can be described by the following sentence: "This extensions allows to ." For example, "This extension allows parts to be scriptable"; or "This extension allows parts to accept speech input." Listing 1 shows the IDL file for the implementation of the ODBaseSemanticInterface extension. In this example we see that semantic interfaces can be extensions of any class derived from ODPart or ODSession. (Remember that extensions in general can be attached to any object that inherits from ODObject.)

Base object. Whether the base object you choose derives from ODPart, ODDispatcher, or some other object, it is responsible for instantiating, managing, destroying, and providing access to any attached extensions. To that end, the object to be extended must create the extension and implement the HasExtension, AcquireExtension, and ReleaseExtension APIs defined in ODObject.

HasExtension, AcquireExtension, and ReleaseExtension all require an extension name as an argument. HasExtension returns a value of true if an extension with that name exists for the base object, and false if it does not. AcquireExtension returns the requested extension if it exists; if it does not exist, an exception is raised. ReleaseExtension does not return a value, but instead releases the specified extension if it exists or raises an error if it does not.

An extension is typically instantiated during the initialization routine (InitPart and InitPartFromStorage methods) of its base object. After both the base and the extension have been instantiated, the extension is available to other objects. The base object also takes care of destroying its extension and managing associated resources as appropriate.

Besides representing a logically cohesive set of behaviors, an extension also should be tied conceptually to its base object. In the case of semantic interfaces, if the goal of this extension is to make parts scriptable, it should be attached to a base descended from ODPart. In some cases, though, it will not be appropriate for a part to be an extension's base. For example, if a developer wants to log event activity for all parts, no one part should be extended. In this case, the dispatcher (an instantiation of ODDispatcher) should act as the base. As we will see shortly in our discussion of shell plug-ins, extensions to objects other than parts must be implemented in a slightly different manner.

Listing 2 shows an implementation of a base--a part called KeyPadPart--which supports the extension defined in Listing 1. KPInitSemanticInterface is called from KeyPad's initialization methods (InitPart and InitPartFromStorage). KeyPadPart derives indirectly from ODPart.

Calling object. One of the benefits of using extensions is that it provides other developers with a way to query an object to see if it offers a specific integrated set of behaviors, rather than forcing them to ask the object if it supports each individual method call necessary to perform a task. To query the existence of an extension, the calling object must invoke the extended object's HasExtension method, passing the name of the extension it intends to use. If the HasExtension call returns true, the calling object next invokes the extended object's AcquireExtension method to get a reference to the extension. The caller is then able to make calls directly to this newly acquired extension object, which in turn forwards the calls to its base object.

When it no longer needs access to the called object's extension, the calling object should invoke the extension object's ReleaseExtension method. The extension calls the ReleaseExtension of its base object, which deletes the extension and frees up resources as appropriate.

Shell plug-ins

A shell plug-in is a mechanism that allows developers to implement extensions (or other functions) early in the document instantiation process, before any parts are instantiated. To understand the significance of this statement, let's look at the OpenDoc run-time process.

OpenDoc's document-centered approach is substantially different from the classic application-centered model (some prefer to view the approach as application-centered, where the document represents a specialized form of an application). The classic model focuses on an application--such as a word-processing application--which provides the address space for its own execution and manages memory for every document it creates. An OpenDoc document, on the other hand, may be composed of multiple parts--specifically, part handlers--each of different types (like word processing, chart, spreadsheet, data feed, and so on).

OpenDoc part handlers can be compared to mini-applications in the classic model: applications because they define and implement behavior for a specific type of content, and "mini-" because the standard tasks common to all parts contained in a document are managed not by any of the individual part handlers, but by a shared OpenDoc library called the document shell. Because the document shell (or "docshell") handles file management, the event loop, printing, and interacting with system menus and dialog boxes, part handlers are free to concentrate on their value-add function. The centralization of these tasks by the OpenDoc architecture permits parts to be much more compact.

As owner of the process, the docshell instantiates and initializes itself before it instantiates any parts, even a root part. Shell plug-ins provide the ability to insert routines into this process. Extensions implemented as shell plug-ins thus have access to run-time objects that are unavailable by the time parts are instantiated. And because they are instantiated prior to any part, shell plug-ins can even be used to monitor and log activities related to the part creation process itself. A developer can choose to implement any function desired; implementing an extension is just one option.

You might think of shell plug-ins as allowing interface extensions to operate on a higher level than otherwise possible. Extensions implemented as shell plug-ins can extend the utility of the OpenDoc run time itself and operate on a document-wide basis. Because they are invoked prior to the instantiation of any part, they can also provide services that aren't logically associated with any particular part. Examples of the type of function a shell plug-in extension might provide are the creation of dispatch modules to intercept and log user events before dispatching them, or the creation of focus modules for new types of events, such as speech input, so parts can identify and accept these events.

A basic rule of thumb is that an extension should be implemented as a shell plug-in if it's not tied conceptually to any specific part, but instead offers part-neutral utility (like a spell checker); or if it must exist before any part is instantiated.

The implementation of a shell plug-in is very straightforward. As shown in Listing 3, all a developer must do is define and export an entry point called ODShellPlugInInstallProc that contains the custom functionality. This method will be invoked via DosLoadModule by the docshell after it has initialized itself and its session, but prior to the instantiation of any parts. To be available to the docshell for invocation, a DLL containing a shell plug-in must reside in the OpenDoc Shell Plug-In folder (WPS object ID OD_SHELL PLUGINS>) at run time. Although multiple shell plug-ins may exist, each must reside in its own DLL. Currently no method exists to specify ordering for the invocation of multiple shell plug-ins.

It's a wrap

OpenDoc extensions represent sets of behavior that can be added to an OpenDoc object and then modified, reused, queried, and accessed as necessary. Any object that inherits directly or indirectly from ODObject can be extended; this includes, but is not limited to, OpenDoc parts. Shell plug-ins provide a mechanism for hooking into the OpenDoc run time early in the document start-up process. Extensions implemented as shell plug-ins thus have the power to extend OpenDoc run-time objects; these extended objects then participate in the instantiation and management of OpenDoc parts, documents, and applications.

Understanding and using OpenDoc extensions are essential for the development of production-level, component-based OpenDoc for OS/2 applications. There is a tremendous amount of value in the ability to extend a part's interface in a reusable manner, or similarly to extend the functionality of the OpenDoc architecture itself. Developers, system integrators, and value-added resellers will be able to exploit such extensions in combination with other OpenDoc objects to create tightly integrated, cohesive applications with consistent user interfaces across parts.

References

OpenDoc for OS/2 is available on IBM Developer Connection 9 Special Edition CD or download from IBM Software's Application Development Download Zone.

OpenDoc for OS/2 Programming Guide (included in the OpenDoc developer toolkit).

OpenDoc for OS/2 Programming Reference (included in the OpenDoc developer toolkit).

OpenDoc: An Introduction to Part Development (Redbook, IBM order #SG24-4673-00, pp. 79-80).


Emily A. Vander Veer has participated in the design, implementation, and management of object-oriented software development projects for nearly five years.



Privacy Legal Contact