It has been a long time in coming, but the
industrial revolution of software is finally upon us.
~ Bill Gates, February 1997
Until recently, software architectures were inherently monolithic--applications were monstrous executables containing all the required application code. This situation began to change with the advent of dynamic link libraries (DLLs). DLLs allow common functions, variables, and resources, to be factored out into independent executable files. However, there is no standard way to query a DLL about the items it contains, i.e. a client must know at least a subset of what the DLL contains to be able to use its functionality. Another problem with plain DLLs is that they don’t explicitly support object-orientation.
This is where Microsoft COM comes in. It can be viewed as an object extension of DLLs. COM is a binary standard for object integration. (It has an advantage over other similar systems in its low overhead when components are in the same process.) COM itself only provides a framework for higher-level object standards. One such standard is Microsoft OLE. OLE-compliant applications can exchange both data and their visual representations with other OLE applications, as well as provide rendered representations for non-OLE applications (much like the cut-and-paste data sharing from the previous era). OLE also provides standards for in-place activation, which allows multiple objects to share a common frame window. To the user this seems like there is just one document to which he brings objects from different servers. However, this is where OLE documents stop. No further component integration is readily feasible.
Another low-level component system, by the name of CORBA, is being promoted by the Object Management Group (OMG). It is very similar to COM, or rather DCOM, but suffers from many deficiencies. For example, it does not allow objects to export multiple interfaces.
ObjectAge, the subject of this thesis, is a component-based system somewhat similar to OLE. It is intended for compound document applications. ObjectAge carries over the strengths of OLE, such as providing smooth compound document functionality, and attempts to improve on its weaknesses, such as its legacy of old-fashioned, non-object-based software architectures.
The ObjectAge project includes this paper and a Win32 implementation. The implementation was meant as a means to verify the ObjectAge architecture and design, not as a project goal. Hence it is incomplete. ObjectAge has been designed and implemented from the ground up, basing only on COM and Win32. The implementation is in C++ (Visual C++ has been used). Some components use MFC internally, but it is in no way tied to the system. Additionally, two sample applications have been designed and developed to the extent that it was necessary to verify the system design: a simple system for managing random notes and an advanced C++ integrated development environment. These two applications have been described in this paper as well. All the C++ code for this project contains about 63,000 lines (1.7MB).
This paper describes the high-level design of ObjectAge. It begins with a background section and a general system overview. Each subsequent chapter begins with a brief introduction to an ObjectAge concept, followed by a more detailed explanation of its implementation. At the end of this paper, there are chapters on possible applications, future development directions, and conclusions.
What are components? In the broadest interpretation, components are physical, reusable parts. Components can be DLLs, ActiveX components, Java applets, static object libraries, tables, and even raw documents. However, this is not to say that object-orientation is dead: far from it. Rather, creating a good component-based system requires a sound underlying object-oriented architecture. Components provide two things: a mechanism for physically packaging the nearly independent parts of an object-based system and an infrastructure enabling the collaboration and communication among these parts. In many ways, this means that components will make traditional programming languages almost irrelevant: even today, it is possible to craft components in almost any language. While the move toward component-based systems will change the way in which software is produced and deployed, the wicked problems of software development will remain. Specifically, developing a component-based system of scale involves all of the same problems encountered in developing any complex object-oriented system: the difficulty of crafting a resilient architecture, the challenges of managing an incremental and iterative process, and the problems of preserving a clear separation of concerns are all issues that apply to component-based development. The good news is that the experience of object-orientation suggests ways to attack these issues; the bad news is that components encourage the development of yet more complex classes of systems [8].
According to a paper by David Chappell [3]:
Component-based development--creating applications from reusable parts--is the next great wave in software engineering. Building with components isn’t new, as the approach has been in use for several years. But today is a major turning point in the life of this technology. For the first time, the pieces are in place for component-based development to enter the mainstream of software engineering in commercial information services organizations.
Component software brings about significant changes in software engineering practices: it shifts the focus from implementation to design. With components, more than ever before, good design can mean the difference between success and failure. Componentware standards must reinforce good design through rigid protocols. If well designed, they help to solve many problems mentioned earlier.
According to [3], component-based development has become popular because the benefits of this approach
are so compelling. Among them are:Component software requires high-level standards. ObjectAge is one such standard. It builds upon COM and improves on OLE to provide a significant level of application distribution (in terms of code modules) and component cooperation. ObjectAge supports hierarchical documents composed of many document objects. It is intended for traditional file-based applications such as word processors, spreadsheets, software development environments, and even databases.
Other componentware systems do exist. However, they are aimed at other domains. Examples include:
The primary reason for creating ObjectAge was increasing the level of code reuse. Not only are components free to share their content and representations, but also much of what constitutes an application is provided in separate and universally-compatible components. Moreover, this architecture is completely open. If a need for some previously absent functionality arises, objects can provide new interfaces, and new components that use them can be added to the system. This provides for robust evolution of functionality over time, i.e. client components enumerate servers’ interfaces from the most powerful to the least, and try to implement functionality using the best available one. Old components will be compatible, but the functionality will not be the most sophisticated. On the other hand, newer components will provide more powerful interfaces, and the functionality will be proportional. This can be exemplified by a clipboard-copy command handler: when it comes to rendering it has two obvious options: render as a bitmap or as a vector metafile. Obviously, in most cases, metafiles are preferable, since they can be scaled to any size without loss of quality. However, if an object can only render itself as a bitmap, vector export is not possible, and the clipboard-copy component will have to settle for only one format.
Most of today’s common application functionality is provided for objects out of the box through included standard components and interfaces. They include:
Each of these features will be discussed in some detail in the following sections. Object designers need to implement functionality specific only to their objects, such as internal data representation and various related interfaces, persistence code, views, etc.
Here is an example screenshot of the working system:
Distributed Multi-User Support
Since the storage manager is a separate component, and DCOM, which is a distributed extension to COM, exists, it is possible for docobjects to be placed in separate server processes, possibly running on dedicated servers. With the addition of a locking mechanism, it would become possible for many users to be working on different portions of the same document at the same time.
Internet Object Storage and Distribution
Stretching this idea further, it should be straightforward to share objects over an external network, such as the Internet. This would introduce completely new uses for the system.
These new uses would undoubtedly include large databases. This begs for fault tolerance and other high-end database issues. Of course, to implement this, the storage manager could simply use Microsoft SQL Server as its storage interface, thereby delegating all the sophisticated functionality.
Alternatively, objects can be coded so that their data is distributed, and use internal notification mechanisms to communicate with other objects tied to the same data. This is possible today, with the architecture already in place. However, it is contrary to the spirit of ObjectAge, as it requires cooperative objects and is not automated.
Another very important area of future development is OLE interoperability. Since OLE-compliant applications expose objects that are similar to ObjectAge’s docobjects, it is possible to create stubs for 2-way communication.
On one hand, the OLE docobject class could be created. It would simply store the OLE document. Its view would use OLE to render and preview the object, and, as OLE evolves, add more functionality. This would enable ObjectAge to host the bounty of OLE applications that are available on the market.
On the other hand, an OLE-compliant mini-shell can be introduced. OLE clients will think that ObjectAge docobjects are actually OLE servers. To achieve this behavior, ObjectAge would have to register OLE entries for each ObjectAge document type. This could be automated by providing standard setup routines, or automatically perform this registration whenever the ObjectAge shell is started.
One major benefit would be the ease of creating OLE servers. Currently, lesser developers are not capable of working with OLE. They should not have such problems with ObjectAge because of its simplicity (particularly if some ObjectAge developer tools were created).
ObjectAge, together with two applications, has been designed and partially implemented. As mentioned in the introduction, the implementation was never supposed to be complete. Some more or less cosmetic work remains. However, it is pure coding and is not the main subject of this thesis.
Many glitches came up during development, sometimes requiring significant changes to the design. For example, initially all docobjects were requested to keep their data in on-disk data structures. This would have been an incredible boon to other components, such as undo/redo command handlers. They could simply look at a SS storage, and extract logged changes. A full-featured undo/redo would be given for free to all docobjects without any work on their part. However, even with advanced caching, data structure reads and writes were about 110 times slower. When automatic transaction logging was added (creating undo/redo deltas), the performance drop was of several orders of magnitude. It was clearly unacceptable, and so ObjectAge had to revert to more traditional means.
Through several months of development, most ObjectAge interfaces and components took their final shape. Generally, the system came out as expected, providing most of the required features (actually, all of them, except just mentioned automatic undo/redo).
System performance is satisfactory. ObjectAge does not impose a lot of overhead. However, since applications must follow rigid rules, it obviously eliminates some potential for optimizations.
Using COM did not turn out to be a major benefit, since all components are required to live in the same address space. Most profits to be gained by using COM are only apparent in more complicated settings: out-of-process objects, and network objects (DCOM). Therefore ObjectAge could have been implemented on any platform that provides DLLs with only a little extra work. However, OLE integration would not be possible anywhere but on Windows. This being one of the most compelling potential features of ObjectAge, the decision to use Windows was the right one.