an architectural overview of the ACE frameworkA Case Study of Successful Cross-Platform Systems Software Reuse
by Douglas C. Schmidt <schmidt@cs.wustl.edu>
Douglas C.Schmidt is an associate professor in the computer science and
radiology departments at Washington University (St. Louis). He focuses
on design patterns, implementation, and experimental analysis of
object-oriented techniques.
This article describes the software architecture of ACE [1], which is a freely available, open source C++ framework targeted for developers of high-performance and realtime communication services and applications. The ACE framework provides an integrated set of components that help developers navigate between the "Scylla and Charybdis" limitations of (1) low-level native OS APIs, which are inflexible and nonportable and (2) higher-level distributed object computing middleware, which is often inefficient and unreliable. This article describes the structure and functionality of ACE, outlines several complex communication middleware applications that have been developed with ACE, and summarizes the key lessons learned developing and deploying the reusable object-oriented (OO) communication software components and frameworks in ACE. Overview of ACE ACE is an OO framework that implements core concurrency and distribution patterns [2] for communication software. ACE provides a rich set of reusable C++ wrappers and framework components that are targeted for developers of high-performance, realtime services and applications across a wide range of OS platforms, including Win32, most versions of UNIX, and many realtime operating systems. The components in ACE provide reusable implementations of the following common communication software tasks:
The Structure and Functionality of ACE The ACE framework contains ~150,000 lines of C++ code divided into ~450 classes. To separate concerns and to reduce the complexity of the framework, ACE is designed using a layered architecture. Figure 1 illustrates the relationships between the key components in ACE.
The lower layers of ACE contain an OS adapter and C++ wrappers that portably encapsulate core OS communication and concurrency services. The higher layers of ACE extend the C++ wrappers to provide reusable frameworks, self-contained distributed service components, and higher-level distributed computing middleware components. Together, these layers and components simplify the creation, composition, and configuration of communication systems, without incurring significant performance overhead. The role of each layer is outlined below. The ACE OS Adaptation Layer The OS adaptation layer constitutes approximately 13% of ACE, i.e., ~20,000 lines of code. This layer resides directly atop the native OS APIs written in C. The OS adaptation layer shields the other layers and components in ACE from platform-specific dependencies associated with the following OS APIs:
The portability of ACE's OS adaptation layer enables it to run on a wide range of operating systems. The OS platforms supported by ACE include Win32 (WinNT 3.5.x and 4.x, Win95, and WinCE using MSVC++ and Borland C++), most versions of UNIX (SunOS 4.x and 5.x; SGI IRIX 5.x and 6.x; HP-UX 9.x, 10.x, and 11.x; DEC UNIX 3.x and 4.x; AIX 3.x and 4.x; DG/UX; Linux; SCO; UnixWare; NetBSD; and FreeBSD), realtime operating systems (VxWorks, Chorus, LynxOS, and pSoS), and MVS OpenEdition. Because of the abstraction provided by ACE's OS adaptation layer, a single source tree is used for all these platforms. This design greatly simplifies the portability and maintainability of ACE. The ACE C++ Wrapper Layer It is possible to program highly portable C++ applications directly atop ACE's OS adaptation layer. However, most ACE developers use the C++ wrappers layer shown in Figure 1. The ACE C++ wrappers simplify application development by providing typesafe C++ interfaces that encapsulate and enhance the native OS concurrency, communication, memory management, event demultiplexing, dynamic linking, and filesystem APIs. The C++ wrappers provided by ACE are quite comprehensive, constituting ~50% of its source code. Applications can combine and compose these wrappers by selectively inheriting, aggregating, and/or instantiating the following components:
The C++ wrappers provide many of the same features as the OS adaptation layer in ACE. However, these features are structured in terms of C++ classes and objects, rather than standalone C functions. This OO packaging helps to reduce the effort required to learn and use ACE correctly [15]. For instance, the use of C++ improves application robustness because the C++ wrappers are strongly typed. Therefore, compilers can detect type system violations at compile time rather than at runtime. In contrast, it is not possible to detect type system violations for C-level OS APIs, such as sockets or filesystem I/O, until runtime. ACE employs a number of techniques to minimize or eliminate the performance overhead. For instance, ACE uses C++ inlining extensively to eliminate method call overhead that would otherwise be incurred from the additional type safety and levels of abstraction provided by its OS adaptation layer and the C++ wrappers. In addition, ACE avoids the use of virtual methods for performance-critical wrappers, such as send()/recv() methods for socket and file I/O. The ACE Framework Components The remaining ~40% of ACE consists of communication software framework components that integrate and enhance the C++ wrappers. These framework components support the flexible configuration of concurrent communication applications and services [8]. The framework layer in ACE contains the following components:
The ACE framework components facilitate the development of communication software that can be updated and extended without the need to modify, recompile, relink, or often restart running applications [8]. This flexibility is achieved in ACE by combining (1) C++ language features, such as templates, inheritance, and dynamic binding, (2) design patterns, such as Abstract Factory, Strategy, and Service Configurator [9, 17], and (3) OS mechanisms, such as dynamic linking and multithreading. Self-contained Distributed Service Components In addition to its C++ wrappers and framework components, ACE provides a standard library of distributed services that are packaged as self-contained components. Although these service components are not strictly part of the ACE framework library, they play two important roles:
Higher-level Distributed Computing Middleware Components Developing robust, extensible, and efficient communication applications is challenging, even when using a communication framework like ACE. In particular, developers must still master a number of complex OS and communication concepts such as:
It is possible to alleviate some of the complexity of developing communication applications by employing higher-level distributed computing middleware, such as CORBA [18], DCOM [19], or Java RMI [20]. Higher-level distributed computing middleware resides between clients and servers and automates many tedious and error-prone aspects of distributed application development, including:
To provide developers of communication software with these features, several higher-level middleware applications are bundled with the ACE release. The ACE ORB (TAO) [21] is a realtime implementation of CORBA built using the framework components and patterns provided by ACE. TAO contains the network interface, OS, communication protocol, and CORBA middleware components and features shown in Figure 2. TAO is based on the standard OMG CORBA reference model [18], with the enhancements designed to overcome the shortcomings of conventional ORBs [22] for high-performance and realtime applications. TAO, like ACE, is freely available at <www.cs.wustl.edu/~schmidt/TAO.html>.
JAWS [23] is a high-performance, adaptive Web server built using the framework components and patterns provided by ACE. Figure 3 illustrates the major structural components and design patterns in JAWS. JAWS is structured as a framework of frameworks. The overall JAWS framework contains the following components and frameworks: an Event Dispatcher, Concurrency Strategy, I/O Strategy, Protocol Pipeline, Protocol Handlers, and Cached Virtual Filesystem. Each framework is structured as a set of collaborating objects implemented by combining and extending components in ACE. JAWS is also freely available at <www.cs.wustl.edu/~jxh/research/>.
Lessons Learned Developing and Deploying ACE This section summarizes the lessons I've learned during the past seven years developing the reusable OO communication software components in the ACE framework and deploying ACE in a wide range of commercial applications in the avionics, telecommunications, and medical domains. Software Reuse Fails Largely for Nontechnical ReasonsIn theory, organizations recognize the importance of reuse as a means to reduce cycletime and improve software quality. In practice, many factors conspire to make it hard to achieve systematic software reuse. Most of the impediments are largely political, economical, organizational, and psychological, rather than technical. For instance, teams that develop reusable middleware platforms are often viewed with suspicion by application development teams, whose members resent the fact that they are no longer empowered to make key architectural decisions. Successful Reuse-in-the-large Requires PrerequisitesIn my experience, large-scale reuse of software works best when the following conditions apply:
These prerequisites often do not exist in contemporary organizations. In such cases, I've observed that organizations often fall victim to the "not-invented-here" syndrome and redevelop most of their software components from scratch. Unfortunately, increasing deregulation and global competition make it hard to succeed with this type of development process. Iteration and Incremental Growth Are EssentialIt is crucial for organizations to explicitly recognize that good components, frameworks, and software architectures require time to craft, hone, and apply. In general, developing, using, and reusing software requires a mature organization that can distinguish key sources of variability and commonality in its application domain. Identifying and separating these concerns require multiple iterations. For reuse to succeed in-the-large, management must have the vision and resolve to support the incremental evolution of reusable software. Fred Brooks's observation that "Plan to throw the first one away, you will anyway," [25] applies as much today as it did 20 years ago. Moreover, in my experience, "the best is often the enemy of the good" when it comes to deploying reusable software frameworks and components. Often, an 80% solution that can be deployed and evolved incrementally is preferable to waiting for a 100% solution that never ships. There's No Substitute for Hands-on ExperienceDeveloping high-quality communication software is hard; developing high-quality reusable communication software is even harder. The principles, methods, and skills required to develop reusable software simply cannot be learned by generalities. Instead, developers must learn through hands-on experience how to design, implement, optimize, validate, maintain, and enhance reusable software components and frameworks. Only by actively engaging in these activities will developers truly internalize good development practices and patterns. Integrate Infrastructure Developers with Application DevelopersMost useful components and frameworks originate from solving real problems in a particular application domain, such as telecommunications, medical imaging, avionics, and Web programming. A time-honored way of producing effective reusable components, therefore, is to generalize them from working systems and applications. This was how ACE evolved. I've found that creating "component teams," which build reusable frameworks in isolation from application teams, is often counterproductive. When intimate feedback from application developers is lacking, the software artifacts produced by component teams rarely solve real problems and are unlikely to be reused systematically. Design to an Architecture Rather Than Program to a Particular Middleware Technology "Standard"It is very risky to expect that emerging industry middleware standards, like CORBA, DCOM, or Java RMI, will automatically eliminate the complexity of developing communication software. No single solution is a panacea, nor are "standards" necessarily ubiquitous or implemented consistently. Therefore, for complex communication software systems, it is essential to design and use architectures that can transcend any specific middleware technology standard. I've found it is much more effective to devise a common software architecture that can be instantiated on multiple middleware platforms, rather than programming directly to a particular middleware API, which can rapidly become obsolete. OS API "Wars" Are Largely IrrelevantACE's OS adaptation layer makes the selection of the native OS API, e.g., POSIX vs. Win32 vs. realtime operating systems, largely an implementation detail. Using ACE, it is straightforward to develop highly portable communication software that runs efficiently on a wide range of operating systems and C++ compilers. Moreover, ACE provides this portability without incurring the performance penalties associated with interpreted virtual machines. (However, a Java version [26] of many ACE components is also available at <www.cs.wustl.edu/~schmidt/JACE.html>.) Thus, the portability provided by ACE allows developers to select an OS platform based on features, price, performance, development tools, and ease of integration with other applications. Beware of Simple(minded) Solutions to Complex Software Problems
Trying to apply overly simple solutions to complex problems is an
exercise in frustration and a recipe for failure. For instance,
attempting to translate the software implementations entirely from
high-level SDL specifications or "analysis rules" rarely Respect and Reward Quality Developers and ArchitectsUltimately, reusable components and frameworks are only as good as the people who build and use them. Developing robust, efficient, and reusable middleware requires teams with a wide range of skills. We need expert analysts and designers who have mastered design patterns, software architectures, and communication protocols to alleviate the inherent and accidental complexities of communication software. Moreover, we need expert programmers who can implement these patterns, architectures, and protocols in reusable frameworks and components. In my experience, it is exceptionally hard to find high-quality software developers. Ironically, many companies treat their developers as interchangeable, "unskilled labor" who can be replaced easily. Over time, companies that respect and reward their high-quality software developers are increasingly outperforming those that do not. Concluding Remarks Computing power and network bandwidth have increased dramatically over the past decade. However, the design and implementation of communication software remain expensive and error-prone. Much of the cost and effort stems from the continual rediscovery and reinvention of fundamental patterns and framework components across the software industry. However, the growing heterogeneity of hardware architectures, the diversity of OS and network platforms, and global competition make it increasingly costly to build correct, portable, and efficient applications from scratch. Object-oriented application frameworks and patterns help to reduce the cost and improve the quality of software by leveraging proven software designs and implementations to produce reusable components that can be customized to meet new application requirements. The ACE framework described in this article illustrates how the development of communication software like ORBs and Web servers can be significantly simplified and unified. The widespread adoption of ACE is a testament to the power of an open source software process and to the benefits of systematic software reuse in complex communication systems. One key to the success of ACE has been its ability to capture common communication software design patterns and consolidate these patterns into flexible framework components. The framework components efficiently encapsulate and enhance low-level OS mechanisms for interprocess communication, event demultiplexing, dynamic configuration, concurrency, synchronization, and filesystem access. The ACE C++ wrappers, framework components, distributed services, and higher-level distributed computing middleware components described in this article are freely available at <www.cs.wustl.edu/~schmidt/ACE.html>. This URL contains complete source code, documentation, and example applications, including JAWS and TAO. ACE has been used in research and development projects at many universities and companies. For instance, ACE has been used to build realtime avionics systems at Boeing [27]; telecommunication systems at Bellcore [4], Ericsson [28], Motorola [2], and Lucent; medical imaging systems at Siemens [9] and Kodak [16]; and distributed simulation systems at SAIC/DARPA. It is also widely used for research projects and classroom instruction. A description of many of the projects using the ACE, TAO, and JAWS frameworks is available at <www.cs.wustl.edu/~schmidt/ACE-users.html>. In addition, <comp.soft-sys.ace> is a USENET newsgroup devoted to ACE-related topics. Acknowledgments Much of the success of ACE is due to the dedication of the core development team at Washington University, as well as the hundreds of developers throughout the Internet who contribute their time and effort to improve ACE. I greatly appreciate their help, as well as the support of USENIX, which has sponsored some of the research on TAO's realtime scheduling service. Notes [1] D. C. Schmidt, "ACE: An Object-Oriented Framework for Developing Distributed Applications," in Proceedings of the 6th USENIX C++ Technical Conference, (Cambridge, MA), USENIX Association, April 1994. [2] D. C. Schmidt, "A Family of Design Patterns for Application-level Gateways," in The Theory and Practice of Object Systems (special issue on patterns and pattern languages), vol. 2, no. 1, 1996. [3] D. C. Schmidt, "Acceptor and Connector: Design Patterns for Initializing Communication Services," in Pattern Languages of Program Design (R. Martin, F. Buschmann, and D. Riehle, eds.). Reading, MA: Addison-Wesley, 1997. [4] D. C. Schmidt, "Reactor: An Object Behavioral Pattern for Concurrent Event Demultiplexing and Event Handler Dispatching," in Pattern Languages of Program Design (J. O. Coplien and D. C. Schmidt, eds.). Reading, MA: Addison-Wesley, 1995. [5] D. C. Schmidt and C. D. Cranor, "Half-sync/Half-async: An Architectural Pattern for Efficient and Well-structured Concurrent I/O," in Pattern Languages of Program Design (J. O. Coplien, J. Vlissides, and N. Kerth, eds.). Reading, MA: Addison-Wesley, 1996. [6] T. Harrison, I. Pyarali, D. C. Schmidt, and T. Jordan, "Proactor -- An Object Behavioral Pattern for Dispatching Asynchronous Event Handlers," in The 4th Pattern Languages of Programming Conference (Washington University technical report #WUCS-97-34), September 1997. [7] D. C. Schmidt, T. H. Harrison, and E. Al-Shaer, "Object-oriented Components for High-speed Network Programming," in Proceedings of the 1st Conference on Object-Oriented Technologies and Systems (Monterey, CA), USENIX, June 1995. [8] D. C. Schmidt and T. Suda, "An Object-oriented Framework for Dynamically Configuring Extensible Distributed Communication Systems," in IEE/BCS Distributed Systems Engineering Journal (special issue on configurable distributed systems), vol. 2, pp. 280--293, December 1994. [9] P. Jain and D. C. Schmidt, "Service Configurator: A Pattern for Dynamic Configuration of Services," in Proceedings of the 3rd Conference on Object-Oriented Technologies and Systems, USENIX, June 1997. [10] R. G. Lavender and D. C. Schmidt, "Active Object: An Object Behavioral Pattern for Concurrent Programming," in Pattern Languages of Program Design (J. O. Coplien, J. Vlissides, and N. Kerth, eds.). Reading, M A: Addison-Wesley, 1996. [11] D. C. Schmidt, A. Gokhale, T. Harrison, and G. Parulkar, "A High-performance Endsystem Architecture for realtime CORBA," in IEEE Communications Magazine, vol. 14, February 1997. [12] J. Hu, I. Pyarali, and D. C. Schmidt, "Measuring the Impact of Event Dispatching and Concurrency Models on Web Server Performance Over High-speed Networks," in Proceedings of the 2nd Global Internet Conference, IEEE, November 1997. [13] P. Jain, S. Widoff, and D. C. Schmidt, "The Design and Performance of MedJava -- A Distributed Electronic Medical Imaging System Developed with Java Applets and Web Tools," in Proceedings of the 4rd Conference on Object-Oriented Technologies and Systems, USENIX, April 1998. [14] R. H. Halstead, Jr., "Multilisp: A Language for Concurrent Symbolic Computation," in ACM Trans. Programming Languages and Systems, vol. 7, pp. 501--538, October 1985. [15] D. C. Schmidt, "IPC\_SAP: An Object-oriented Interface to Interprocess Communication Services," in C++ Report, vol. 4, November/December 1992. [16] I. Pyarali, T. H. Harrison, and D. C. Schmidt, "Design and Performance of an Object-oriented Framework for High-performance Electronic Medical Imaging," in Computing Systems, vol. 9. USENIX, November/December 1996. [17] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995. [18] Object Management Group, The Common Object Request Broker: Architecture and Specification, 2.2 ed., February. 1998. [19] D. Box, Essential COM. Reading, MA: Addison-Wesley, 1997. [20] A. Wollrath, R. Riggs, and J. Waldo, "A Distributed Object Model for the Java System," Computing Systems, vol. 9. USENIX, November/December 1996. [21] D. C. Schmidt, D. L. Levine, and S. Mungee, "The Design and Performance of realtime Object Request Brokers," in Computer Communications, vol. 21, pp. 294--324, April 1998. [22] D. C. Schmidt, S. Mungee, S. Flores-Gaitan, and A. Gokhale, "Alleviating Priority Inversion and Non-determinism in Real-time CORBA ORB Core Architectures," in Proceedings of the Fourth IEEE Real-Time Technology and Applications Symposium (Denver, CO), IEEE, June 1998. [23] D. C. Schmidt and J. Hu, "Developing Flexible and High-performance Web Servers with Frameworks and Patterns," in ACM Computing Surveys, vol. 30, 1998. [24] C. D. Gill, D. L. Levine, and D. C. Schmidt, "Evaluating Strategies for Real-Time CORBA Dynamic Scheduling," submitted to the International Journal of Time-Critical Computing Systems, special issue on realtime middleware. [25] F. P. Brooks, The Mythical Man-Month. Reading, MA: Addison-Wesley, 1975. [26] P. Jain and D. Schmidt, "Experiences Converting a C++ Communication Software Framework to Java," in C++ Report, vol. 9, January 1997. [27] T. H. Harrison, D. L. Levine, and D. C. Schmidt, "The Design and Performance of a realtime CORBA Event Service," in Proceedings of OOPSLA '97 (Atlanta, GA), ACM, October 1997. [28] D. C. Schmidt and P. Stephenson, "Experiences Using Design Patterns to Evolve System Software Across Diverse OS Platforms," in Proceedings of the 9th European Conference on Object-Oriented Programming (Aarhus, Denmark), ACM, August 1995.
|
|
First posted: 16th February 1999 jr Last changed: 16th February 1999 jr |
|