Virtual Component A Design Pattern For Memory-Constrained Embedded Applications

Download as pdf or txt
Download as pdf or txt
You are on page 1of 13

Virtual Component A Design Pattern for Memory-Constrained Embedded Applications

Angelo Corsaro, Douglas C. Schmidt, Raymond Klefstad, Carlos ORyan fcorsaro,schmidt,klefstad,[email protected] Electrical and Computer Engineering Department University of California, Irvine, CA 92697, USA

Abstract
The proliferation of embedded systems and handheld devices with limited memory is forcing middleware and application developers to deal with memory consumption as a design constraint [1]. This paper uses the POSA format [2] to present the Virtual Component compound pattern [] that helps to reduce the memory footprint of middleware, particularly standards-based middleware such as CORBA or J2EE, by transparently migrating component functionality into an application on-demand. This compound pattern applies the Factory Method [3], the Proxy [3], and Component Congurator [4] design patterns to achieve its goals. We describe the Virtual Component pattern as a separate named abstraction since each of these constituent pattern does not independently resolve the set of forces addressed by the Virtual Component pattern. Keywords: Design Patterns, Embedded Systems, Memory Footprint, CORBA, Abstract Factory, Component Congurator.

Example

There is a trend to develop memory-constrained applications, such as embedded systems, using feature-rich middleware, such as Java RMI [5], COM+ [6], or CORBA [7]. Memory-contrained applications have historically been developed manually and hard-coded to use low-level languages and software tools, which is tedious and errorprone. The growing use of middleware provides a more powerful distributed computing model that enables clients to invoke operations on reusable components without hard-coding dependencies on their location, programming language, OS platform, communication protocols and interconnects, and hardware [8]. However, middleware (particularly standards-based middleware) often has many features that are not be needed by all applications that use it. For example, in the context of CORBA: A server application may not actually use all three versions (i.e. version 1.0, 1.1 and 1.2) of the standard CORBA Internet inter-Orb protocol (IIOP). A client application may not need to use all the collocation optimizations, interceptors, or smart proxy mechanisms specied in the CORBA standard. Pure client CORBA applications that invoke requests, but do not service requests from other clients, do not require a portable object adapter (POA) (a POA maps client requests to the appropriate servant in a CORBA server).

1 Intent

The Virtual Component design pattern provides an application-transparent way of loading and unloading components that implement middleware software functionality. This pattern ensures that the middleware provides a rich and congurable set of functionality, yet occupies main memory only for components that are actu- Providing all these features in one monolithic impleally used. mentation increases middleware footprint, which may 1

make it unsuitable for use in memory-constrained applications. Two types of footprint are important to memoryconstrained applications: Static footprint, which is the amount of storage needed to hold an image of an application. This storage can reside in the ROM where the program is stored, on secondary disk storage, etc. The static footprint of a particular version of a particular application is time-invariant since it does not change as the application runs. Dynamic footprint, which represents the amount of memory used by a running instance of the application. The dynamic footprint is essentially the sum of the code, the heap, and stack size. Based on this definition, it is clear that the dynamic footprint is timedependent. For memory-constrainted applications, it is essential to have a hard upper bound on the dynamic footprint size.

Problem

Middleware provides developers with a powerful and reusable set of abstractions for building applications. Implementing memory-constrained applications via reusable middleware remains hard, however, since the following forces must still be resolved: 1. The specications for features and options for middleware (particularly standards-based middleware) are large and continually growing 2. Middleware implementations can incur a large static and dynamic memory footprint unless they are designed in advance to avoid this and 3. Memory-constrained applications cannot afford to waste storage on unnecessary or rarely used functionality.

In addressing the previous forces, care must be taken to One way to reduce middleware footprint is to provide provide a solution that: 1. Presents a clean architecture that is amenable to compile-time options that produce subsets of middleware reuse and can be retargeted easily. tailored for the needs of each specic application. As the number of capabilities in the middleware grows, however, 2. Does not introduce accidental complexity in the exthis solution does not scale well since it either posed API, i.e. is transparent. Forces application developers to know in advance Supporting all the features and options specied by what features they will require or middleware can therefore create a large memory footIncreases the development cycle by forcing applicaprint, making the middleware unsuitable for memorytion developers to recompile the middleware whenconstrained applications. ever they change the set of features they require. Few applications use all the functionality provided by In addition, compile-time subsetting approaches may middleware. However, implementers of middleware cannot reduce middleware dynamic memory footprint sufnot wantonly eliminate capabilities from their products ciently since the resources associated with infrequently based on the needs of any particular application. Midused components will be allocated during executioneven dleware must therefore be designed to support easy cusif they are not used during a particular run of an applitomization to meet application functionality requirements cation. It is therefore necessary to devise a more effecand memory constraints. tive technique to reduce static and dynamic footprint of , ! Implementations of standard CORBA must be premiddleware by removing unneeded functionality and propared to provide all the services in the specication, even viding ne-grained control over the different middleware though applications rarely use all its features. For examcomponents used by an application at run-time. ple, business applications use only a few transport protocols or QoS policies. Likewise, embedded applications may not use the CORBA Any data type, or may choose 3 Context to use Anys in a limited way. Moreover, real-time apComponentized middleware, where congurability and plications rarely use middleware meta-programming feacustomizability is needed to meet memory constraints im- tures [9], such as the CORBA dynamic invocation interposed by application requirements and the run-time plat- face (DII) that discovers and invokes operations on obform. jects at run-time. 2

5 Solution
Identify components whose interfaces represent the building blocks of the middleware being developed and implement these capabilities using concrete components. Use factories to create the concrete components needed by an application via a set of loading/unloading strategies that provide different ways to instantiate and manage the concrete components occupying memory at run-time. This pattern virtualizes each component since the middleware does not know whether a component is present or when it will be instantiated until its functionality is actually used. In detail: each component identied during the middleware decomposition should be implemented in a manner that is as decoupled from other components as possible. A concrete component should be associated with a loading strategy to support different application usecases, e.g., some concrete components should be loaded eagerly, whereas others should be loaded lazily. Component unloading can also be done eagerly (e.g., as soon as the instance reference count goes to zero) or lazily (e.g., when when memory gets low, unload components with zero reference count based on a least-recently-used algorithm, etc.). The Virtual Component pattern provides component-level control over the subset of functionality that is needed for a given application, thereby minimizing the overall static and dynamic footprint by controlling when, how, and what components will be instantiated as the application runs.

Class
Component

Collaborator

Responsibility
Define an interface for a well defined and encapsulated service provided by the middleware

crete components into applications that do not require their capabilities.


,

! For example, the implementation of a POA in a particular ORB is a concrete component.


Class
ConcreteComponent

Collaborator

Responsibility
Provide an implementation for the service defined by the Component

Component factory, denes an interface and a factory method that creates components.
,

! For example, the component factory in CORBA is the CORBA::ORB interface, whose resolve_ initial_references() operation is a factory method that returns an object reference to a component based on a string parameter passed to the operation. Other factory methods in CORBA include CORBA::Object::_narrow() and CORBA:: ORB::string_to_object().
Class
ComponentFactory

6 Structure
The participants in the Virtual Component pattern include the following: Component, which denes the interface to the capabilities provided by a component and represents the contract between a component and its clients.
, ! For example, if an ORBs Portable Object Adapter (POA) is designated as a component, its component interface is the PortableServer API specied by the OMG CORBA specication [7]. Concrete component, which provides the actual implementation of a component. The Virtual Component pattern avoids loading and/or deploying con-

Collaborator

Responsibility
Provide an interface for the creation of Components

Concrete component factory, a concrete implementation of the component factory that produces concrete components.

! For example, the implementations of the CORBA::ORB or CORBA::Object interfaces provided by a specic ORB are concrete component factories. Loading strategy, which denes how and when a concrete component is loaded and instantiated.
,

Class
ConcreteComponentFactory

Collaborator
Loading Strategy

interface

Component ComponentFactory
+CreateComponent()

Responsibility
Create ConcreteComponents, using the appropriate loading strategy

UnloadingStrategy

ConcreteComponentFactory
+CreateComponent()

<<create>>

ConcreteComponent

LoadingStrategy ! For example, an embedded application that needs to minimize its dynamic footprint can use a loading Figure 1: Virtual Component Static Structure. strategy that loaded and instantiated a POA concrete component lazily, i.e., on-demand at run-time. Conversely, a real-time application that cannot tolerate 7 Dynamics the jitter introduced by this lazy initialization can use a loading strategy that pre-loaded and initialized the To illustrate the collaborations performed by participants POA eagerly, i.e., at ORB initialization time. in the Virtual Component pattern, we examine three different loading scenarios. In this paper, we distinguish beClass Collaborator tween the loading and the instantiation of a component:
,

LoadingStrategy

Responsibility
Provide a way of loading and instantiating components

Unloading strategy, which denes how and when a concrete component and its associated resources are unloaded.

, ! For example, components could be reference counted, and once the instance count reaches zero the associated resources are unloaded, i.e., the component instance can be released and its associated DLL could be unloaded. Unloading may be lazy or eager. Lazy unloading means a component is unloaded only when available memory becomes low, at which Scenario 1. This scenario shows an eager static loading point classes with a zero instance count may be un- strategy, where the application loads the entire concrete loaded. Eager unloading means a component class is component during program startup. unloaded immediately whenever the instance count At application startup time, all needed components goes to zero. are loaded and initialized. The application code creates a concrete component Class Collaborator via a component factory, which can either provide UnloadingStrategy a pre-initialized component or create a new one. Responsibility No subsequent dynamic loading is necessary in this Provide a way of unloading case. components The application code then uses the component. In this scenario the unloading policy does nothing, The structure of participants in the Virtual Component i.e., it is a no-op. Unloading a component that is not pattern is shown in the class diagram in Figure 1. used at a particular time or during a particular path

Loading refers to the activities performed to make the implementation of a component available for instantiation. For example, in a C++ application where components are packaged in DLLs, loading a component corresponds to loading the DLL associated with a component. Conversely, in a Java application the loading phase corresponds to loading the classes that implement the component within the JVM. Instantiation corresponds to actually creating and initializing an instance of a particular component. Clearly, to be instantiated a component must rst be loaded.

through a program defeats the purpose of eager static loading since additional latency will be required to reload the component if its needed later in the program. The main goal of eager static loading is to ensure that whatever is used by the system will already be in place when it is needed. This property is desirable for applications that cannot tolerate jitter introduced by lazy loading and instantiation.

aClient

aComponentFactory CreateComponent

aLoadingStrategy

aComponent

loadComponent

<<create>>

aMethod

Scenario 3. This scenario shows a lazy dynamic loading The following gure illustrates the eager static loading strategy. This strategy defers loading the concrete component until it is actually accessed by a client, rather than strategy: loading it when it is requested by the application. aComponentFactory aComponent aClient
CreateComponent <<create>>

aMethod

Scenario 2. This scenario shows an eager dynamic loading strategy. In this strategy, a concrete component factory loads the entire concrete component when the application resolves the component at run-time, rather than loading it eagerly during program startup.

The application code creates a concrete component via a component factory. The concrete component factory creates and returns a reference to a proxy for the concrete component. This proxy knows how to load the components functionality and data when it is accessed. The application code then uses the component via its proxy, which triggers the proxy to load the components functionality and data. Depending on how the middleware was congured, the proxy can load different types of concrete components. Component use is reference counted, and when a component reference count drops to zero the unloading strategy associated with the component is invoked.

The application code creates a concrete component The following gure illustrates the lazy dynamic loadvia a component factory. ing strategy: The eager dynamic loading strategy associated with the concrete component factory loads the concrete component. Depending on how the middleware was congured, the factory can create different types of concrete components. The application code then uses the component. Component use is reference counted, and when a component reference count drops to zero the unloading strategy associated with the component is invoked.
aClient aComponentFactory aLoadingStrategy aComponentProxy aComponent createComponent loadComponent <<create>>

aMethod

<<create>>

Implementation

The following gure illustrates the eager dynamic loadThis section describes the activities associated with iming strategy: plementing the Virtual Component pattern. 5

1. Partition the middleware into a set of decoupled components with well-dened interfaces. Required components are those required by any application that might use the middleware since they are fundamental to its internal operational behavior. In contrast, optional components are those that may be used by specic applications. Identify the optional components that exist in the middleware and make them into virtual components. Each virtual component will be represented by a common abstract interface, a concrete implementation, and a loading strategy. , ! The ACE ORB (TAO) [10], which is an open-source implementation of CORBA, decomposes its C++ implementation of CORBA [7] into many core ORB components, such as the Portable Object Adaptor (POA), Realtime CORBA, CORBA Messaging, transport protocols, and interface repository, that are optional for memoryconstrained applications.

client, server, or both client and server. However, all capabilities must be available in case they are needed. Component implementation alternatives. CORBA capabilities, such as buffer allocation, object adaptor, network protocol, or dynamic thread scheduling, can be implemented using a variety of different strategies, yet only one (or in some cases, none) is actually needed at any given time. These optional implementations of TAOs components described above are implemented as strategies that reside on secondary storage until they are needed or wanted, at which point they are linked into the applications address space. 3. Dene the component component loading strategies. Dene a mean to congure which loading strategies should be used for each concrete component in the middleware. There are several sub-activities involved here: 3.1. Determine how to associate component identity with concrete component implementations. Concrete components can be named using integer values or strings. Strings can be read more easily by programmers and simple management tools, but integer values require less space and may be compared more efciently. , ! For example, CORBAs resolve_initial_ references() operation is passed a string containing the component to be resolved.

2. Dene implementation strategies for concrete components. Some components identied in implementation activity 1 may have more than one implementation alternative. Where alternative implementations exist for a component, or where a set of different subcomponents work together to implement a component, apply the Strategy pattern [3] to dene each concrete component implementation for the virtual component interface. For example, there may be three different types of POAs: a standard POA, a real-time POA, and a multicast POA []. When the POA is needed, only the concrete class of the To prevent name clashes, interface identiers can be desired type of POA is loaded and instantiated. , ! Common examples of optional middleware compo- generated algorithmically. , ! For instance, Microsoft COM identies components nents in TAO include: using 128 bit globally unique identiers (GUIDs) based Excess components. Certain applications may or on the address of the network interface, the date, and the may not need a particular CORBA feature, such time. as Any data types or portable interceptors. The The component factory includes a method that returns middlewareand in some cases even application developersmay not be able to determine before component references to clients. The type of information returned from this method depends largely on the prorun-time if a feature is needed. Role-dependent components. The CORBA stan- gramming language. , ! For example, CORBA and Java clients will receive dard denes a large set of related capabilities, such an object reference, whereas pointers may be an approprias CORBA IIOP message marshalers/demarshalers, portable object adapters (POAs), or interoperable ob- ate choice for C++. ject reference (IOR) format parsers. Only a subset of 3.2. Determine the loading strategy for concrete these capabilities are actually used at run-time, de- components. Some applications are more concerned pending on the role that an application play, e.g., about footprint, while others may be more concerned 6

about predictable real-time performance. We therefore differentiate the following binding times: Eager static, which loads the entire concrete component immediately during program initialization. This strategy is intended for those applications concerned with real-time performance. In this case, the loading strategy may pre-load user-selected concrete components at initialization time to eliminate jitter that would result if a lazy component faulting strategy were used. The implementation of this strategy relies on implementation language mechanisms to perform static initialization. For example Java provides static blocks, while in C++ there are idioms that can be used to simulate a Java static block. The code that is executed at application startup time then ensures that all components are loaded and instantiated. Eager dynamic, where the concrete component factory loads the entire concrete component when it is instructed to resolve the component at run-time. This strategy is suited for applications that are concerned about minimal footprint, but can tolerate initial delays from dynamic loading. The implementation of this strategy could use the Component Congurator pattern [4] to implement the different components. When a component needs to be created, the component factory will use the services provided by the component congurator to load and initialize it. Lazy dynamic, which defers the loading of a concrete component until it is actually accessed by the client. This strategy is suited for footprint-sensitive applications that can tolerate the latency of loading a concrete component into memory on-demand. The implementation of this strategy would require the component factory to create component proxies that will lazily create their associated concrete components only when a component is actually accessed.
, ! TAO uses the eager dynamic scheme in which the ACE Service Congurator [11] framework (which implements the Component Congurator pattern [4]) is used to load and initialize components, which are created via factory methods. For example, a POA provides a series of services needed to dispatch upcalls to CORBA servants in a portable, efcient, and type-safe manner. This optional component need not be loaded and initialized in the ORB until an application tries to re-

solve it via the CORBA::ORB::resolve_initial_ reference() operation. At this point, the appropriate POA class is loaded and an instance is created. A similar approach is used for the other TAO components mentioned above. Likewise, ZEN [12] is a Java implementation of CORBA that uses Javas dynamic class loading (or JVM support for on-demand class loading) to load concrete components at the appropriate time. ZEN uses naming conventions to construct class names from the values, such as version number, message type, or IOR format name, that lead to the use of the class, so that the ORB can know what classes to load. These uniform naming conventions enable new classes to be added easily later. In both these CORBA implementations, optional middleware components can either be Eager static, e.g., when a developer knows a particular set of core ORB services are required or Eager dynamic, i.e. loaded on-demand only when needed and optionally cached in primary memory for fast reuse. 4. Dene the concrete component unloading strategies. Depending on application characteristics, different unloading strategies can be used. At one end of the spectrum, we could have an unloading strategy that does nothing, i.e., it could be a Null Object [13]. Conversely, we could have an unloading strategy that unloads all the resources associated with a component, including the DLL associated with the component. Naturally, unloading DLLs must be performed in a way that is consistent with how DLLs are opened and loaded. For instance, one approach might rely on the reference counting provided by the OS for opened DLLs. Another approach would implement this reference counting in the middleware to avoid unnecessary system calls and provide greater control over resource management.

Example Resolved

Applications written using CORBA rely on a stub to make operations invoked on CORBA objects appear local, even if they are remote. A stub is an instance of the Proxy pattern [3, 2] that marshals and demarshals parameters and forwards client requests to a target object. Internally, an 7

ORB may need to use different stub implementations for proxies are loaded lazily. the same CORBA object or for different instances of the aClient anAccount anAccount_Proxy_Broker anAccount_Proxy_Impl same CORBA object. For example, different stub imget_balance select_proxy plementations can be used to implement collocation op<<create>> timizations [14], interceptors [15], or smart proxies [16]. The Virtual Component pattern has been applied to get_balance TAO. TAO uses this pattern to control how it loads and initializes stubs associated with CORBA objects. To see how TAO uses the Virtual Component pattern, consider the following code that denes the IDL interface for a Bank Account application. Figure 3: Interaction Diagram of the Virtual Compointerface Account { nent Pattern Applied in TAO
float get_balance (); float withdraw (in float amount); void deposit (in float amount); };

The C++ code fragment below shows the base class for the stub implementation associated with the Account interface. This stub code is generated by the TAO IDL compiler. The TAO_Account_Proxy_Impl provides The class diagram depicted in Figure 2 represents the exactly the same methods dened in the Account instructure of the classes created by the TAOs IDL comterface, but each method has an additional argument of piler for the Account interface. This set of collaborating type CORBA::Object, which represents the target object on which the method is invoked. This argument is needed to make the TAO_Account_Proxy_Impl concrete implementation stateless; thus, all the TAO_ Account_Proxy_Impl instances are yweights [3].
Account
delegates +get_balance() +widraw() +deposit()

Account_Proxy_Impl

Account_Proxy_Broker

+select_proxy()

Direct_Account_Proxy_Impl

Strategized_Account_Proxy_Broker
<<creates>>

ThruPOA_Account_Proxy_Impl

<<creates>>

Remote_Account_Proxy_Broker

Remote_Account_Proxy_Impl

class TAO_Account_Proxy_Impl : public virtual TAO_Object_Proxy_Impl { public: virtual TAO_Account_Proxy_Impl (void) {} virtual CORBA::Float get_balance (CORBA::Object *obj) = 0;

Figure 2: Static Structure of the Virtual Component Pattern Applied in TAO

classes implements the Virtual Component pattern. Figvirtual CORBA::Float withdraw ure 2 and Figure 1 shows how the Account_Proxy_ (CORBA::Object *obj, Impl class plays the component role, while the impleCORBA::Float amount) = 0; mentation of this class plays the concrete component virtual void deposit role. The Account_Proxy_Broker plays the role of (CORBA::Object *obj, the component factory, while the Remote_Account_ CORBA::Float amount) = 0; Proxy_Broker and the Strategized_Account_ Proxy_Broker play the role of the concrete component factory. In this incarnation of the Virtual Component pat- protected: TAO_Account_Proxy_Impl (void); tern, the loading strategy is associated with the concrete }; factory rather than the concrete component. Concrete implementations of TAO_Account_ Figure 3 shows the interaction diagram for this instance of the Virtual Component pattern. In this use case, the Proxy_Impl provide different ways of performing a 8

call on a CORBA object. Decorators [3] could also be Now lets consider what happens when the following used to add behavior to existing concrete implementa- code is executed: tions. // ... The TAO_Account_Proxy_Broker class shown CORBA::Float balance = below is the base class for the various concrete stub iman_account->get_balance (); plementation factories. // ...
class TAO_Account_Proxy_Broker This fragment of code results in the invocation of the fol{ lowing method: public: virtual TAO_Account_Proxy_Broker (void); CORBA::Float Account::get_balance () { TAO_Account_Proxy_Impl &proxy = virtual TAO_Account_Proxy_Impl & this->the_TAO_Account_Proxy_Broker_-> select_proxy (Account *obj) = 0; select_proxy (this); protected: TAO_Account_Proxy_Broker (void); }; return proxy.get_balance (this); }

The role of this class is to create the appropriate stub to perform a call on a given CORBA object, based on properties of the running application. In TAO, this factory represents the portion of code that encapsulates the loading strategy for stub implementations. In fact, depending on how the application is compiled, different instances of the component factory will allow either eager or lazy loading. The actual instance of the TAO_Account_Proxy_ Broker subclass created for an Account object depends on both static conguration and runtime properties. TAO then dynamically uses the CORBA::Object:: _narrow() operation as a factory method to create the appropriate subclass of TAO_Account_Proxy_ Broker. The appropriate TAO_Account_Proxy_ Broker factory will be created when the following client code runs:
// Get the object reference somehow. CORBA::Object obj = ...

At this point, depending on the instance of the TAO_Account_Proxy_Broker subclass that was associated with the Account object, the most appropriate TAO_Account_Proxy_Impl will be returned. The code executed by the concrete instance of the TAO_Account_Proxy_Impl, (e.g., if we consider the TAO_Account_Strategized_ Proxy_Broker) would behave as described below:
TAO_Account_Proxy_Impl & TAO_Account_Strategized_Proxy_Broker:: select_proxy (Account *object) { int strategy = TAO_ORB_Core::collocation_strategy (object); if (this->proxy_cache_[strategy] != 0) return *this->proxy_cache_[strategy];

// This call loads and creates the // appropriate instance of the proxy // Narrow the object down to the right type. // depending on the strategy. Account_var an_account = this->create_proxy (strategy); Account::_narrow (obj); return *this->proxy_cache_[strategy]; // ... }

After this operation is performed, the appropriate TAO_ Account_Proxy_Broker implementation will have In this code fragment, the TAO_Account_ been set for the Account object. Strategized_Proxy_Broker uses a strategy 9

to determine the most appropriate proxy for performing the requested operation. It then lazily obtains an instance of the needed proxy implementation. Depending on how the create_proxy() method is implemented, a combination of both lazy creation and lazy loading are possible. In the current implementation, no unloading take place. As shown in this example, the Virtual Component pattern can be used to control the way in which different concrete components in a software system are loaded and initialized. In the context of TAO, this pattern helps to reduce the dynamic footprint for CORBA applications, especially for applications that have many different CORBA object instances. Moreover, the Virtual Component patterns frees application developers from having to know which features it will need, while providing a mechanism to ensure that what is needed will be in the right place at the right time.

so JVMs always use lazy loading. For applications that cannot tolerate the jitter introduced by lazy class loading, the Virtual Component pattern could be used to provide eager class loading in Java middleware and applications. Product demos are an example of the Virtual Component pattern. To reduce theft, many stores have resorted to displaying either non-functional mock-ups of some products, such as electronic devices, or only the boxes that contain the products, such as with software. The actual products reside somewhere back in the warehouse. From the outside, the box appears like it has the real product inside, but it is just a virtual product. When a customer wants to purchase such a product, they place the box in their cart and carry it to the checkout. This product fault results in the checkout clerk fetching the real product to place it in the box. This entire process happens (almost) transparently to the customer. When the customer gets home, they have their real product in hand.

10 Known Uses

11 Consequences

The ACE ORB (TAO) [10] is a C++ implementation of The Virtual Component design pattern has the following CORBA that uses the Virtual Component pattern to im- benets: The static and dynamic footprint of the middleware plement its portable object adapter (POA), client-side supcan be adapted to suit the needs of the application. port for its Interface Repository, pluggable protocols, hanFor example, any particular component that is known dling of multiple IOR formats, and to support the CORBA to be unused can be eliminated from the static footdynamic invocation interface (DII) and dynamic skeleton print. In addition, only those components in active interface (DSI). use are included in the dynamic footprint. The ZEN ORB [12] is a Java implementation of It allows middleware developers to offer alternative CORBA that uses the Virtual Component pattern for a implementations for components of their system, wide range of optional capabilities, including pluggable which improves middleware exibility by supportobject adapters, object resolvers, IOR parsers, GIOP mesing different application requirements. For example, sage handlers, message buffer allocation, CDR stream alternative algorithms for buffer allocation may be reader/writers, protocol transports, and Any data types. offered, via the Strategy pattern [3], yet only one alSince ZENs design was heavily inuenced by the lessons ternative at a time must be plugged in. learned on the TAO project, it is more aggressive in its uses of the Virtual Component pattern throughout the It provides ne-grained control over the timing of ORB. component loading and unloading. The strategy Java Virtual Machines (JVMs) use a variation of this for loading and unloading a component can be cuspattern in which a component is essentially represented tomized to suit the needs of each application. For by a class. In order to avoid loading classes that may not example, components that cause jitter when loaded be used, JVMs defer the loading of a particular class up lazily can be congured to be loaded eagerly. Likeuntil its rst active use. Moreover, JVMs unload classes wise, those not contributing to jitter may be loaded for which there are no instances. Unfortunately, there is and instantiated lazily to minimize the dynamic footno standard way of specifying eager loading of classes, print. 10

However, this pattern also incurs the following liabilities: The use of a decoupling layer between the components in the middleware introduces some overhead. Whether this overhead is acceptable or excessive depends on the application, implementation language, compiler technology, and the role that the component plays in the critical path of the application. Advanced software techniques, such as global compiler optimizations or aspect-oriented programming, can be used to reduce or eliminate much of this overhead. Certain loading and unloading strategiesi.e., eager dynamic and lazy dynamiccan cause processing delay and jitter that may be unacceptable for realtime applications. Eager static loading may be used to eliminate these delays, however, if adequate memory is available for the resulting dynamic footprint expansion.

12 See Also

The Virtual Component pattern can be viewed as a compound pattern [] that combines elements of the Factory Method [3], Proxy [3], and Component Congurator [4] patterns. The Factory Method pattern denes an interface for creating an object, but allows subclasses to decide the particular type. Essentially, a factory method defers the instantiation of an object to subclasses. The Proxy pattern provides a surrogate or placeholder for another object to control access to it. The Component Congurator pattern is a dynamic component conguration mechanism that uses a scripting mechanism to dene what components are created, brought into the system, and removed from the system. The Virtual Component compound pattern combines these patterns to create a dynamic component conguration mechanism that relies on a implicit faulting mechanism to load in a required component. It is worth noting that the patterns constituting the Virtual Component pattern used in isolation do not address all the forces addressed by the Virtual Component pattern. It is the synergy provided by this compound pattern that make it possible to address all the forces outlined in 13 Concluding Remarks Section 4. One variation of the Proxy pattern [2, 3] has a simple As embedded applications are increasingly developed implementation of an object that can be substituted with with standards-based, reusable middleware, there is a 11

the full implementation of that object upon demand. The Proxy pattern can be used to implement component proxy in the lazy dynamic variant of the Virtual Component pattern. In particular, a factory would instantiate a component proxy instead of a concrete component. The proxy would then create the concrete component at its rst active use i.e., when the rst method call is invoked on the object. The Strategy design pattern [3] denes a common interface with alternative implementations so different objects may be plugged-in to allow variations in desired behavior. Some virtual components may be dened by a strategy applied to optional components identied by application developers. The Lazy Acquisition [17] design pattern provides a way of deferring resource acquisition. This pattern could be used un synergy with the Virtual Component pattern for the lazy loading strategies. Early real-time operating systems provided programmer-controlled memory segment overlays. For example, DEC RT-11 allowed programmers to dene segments of code and/or data to load at different times to overlay the same area of memory. These memory segments were hard to implement, however, because programmers had to decide before run-time which functions to group into segments. They also had to decide at run time how to explicitly switch from segment to segment. This approach and its corresponding Segmentation pattern is described by [1]. Systems with abundant primary and secondary storage and virtual memory can rely on operating system virtual memory mechanisms to subset the footprint of middleware and application software. In turn, OS virtual memory mechanisms are based on patterns such as Copy-onWrite and Paging described in [1]. Virtual memory may not be predictable enough for many types of real-time embedded systems, however. In addition, many embedded systems have primitive operating systems and hardware that make conventional virtual memory solution infeasible.

growing mismatch between what is provided by the mid- [2] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, and M. Stal, Pattern-Oriented Software Architecture A dleware and what is needed by any particular application. System of Patterns. New York: Wiley and Sons, 1996. This mismatch can yield wasted memory resources for situations where the middleware does not provide an effec- [3] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns: Elements of Reusable Object-Oriented tive level of congurability. For example, if a CORBA Software. Reading, Massachusetts: Addison-Wesley, ORB is used to support an embedded application it is cru1995. cial to avoid paying memory footprint costs for function[4] D. C. Schmidt, M. Stal, H. Rohnert, and F. Buschmann, ality that is not needed by the application. Pattern-Oriented Software Architecture: Patterns for Developers of middleware must therefore make hard Concurrent and Networked Objects, Volume 2. New York: Wiley & Sons, 2000. choices about what functionality to include and what functionality to omit. If too much functionality is in- [5] A. Wollrath, R. Riggs, and J. Waldo, A Distributed Object Model for the Java System, USENIX Computing cluded, middleware footprint will be unsuitable for emSystems, vol. 9, November/December 1996. bedded applications that have stringent constraints on the size of their EPROM and RAM memory. Conversely, if [6] D. Box, Essential COM. Addison-Wesley, Reading, Massachusetts, 1997. too little functionality is included, the middleware may not support application needs adequately, which pushes [7] Object Management Group, The Common Object Request Broker: Architecture and Specication, 2.6 ed., Dec. more development effort and cost onto application devel2001. opers. [8] M. Henning and S. Vinoski, Advanced CORBA The Virtual Component pattern described in this paper Programming With C++. Reading, Massachusetts: allows developers of standards-based middleware to offer Addison-Wesley, 1999. a large set of functionality to their users while keeping the [9] N. Wang, D. C. Schmidt, O. Othman, and static and dynamic memory footprints proportional to the K. Parameswaran, Evaluating Meta-Programming features they actually use. This pattern virtualizes each Mechanisms for ORB Middleware, IEEE Communication Magazine, special issue on Evolving component since the middleware does not know whether Communications Software: Techniques and Technologies, a component is present or when it will be instantiated unvol. 39, Oct. 2001. til its functionality is actually used. The Virtual Com[10] D. C. Schmidt, D. L. Levine, and S. Mungee, The ponent pattern has been applied successfully in a variety Design and Performance of Real-Time Object Request of standards-based middleware, including TAO, ZEN, and Brokers, Computer Communications, vol. 21, Java virtual machines. pp. 294324, Apr. 1998.
[11] D. C. Schmidt and T. Suda, The Service Congurator Framework: An Extensible Architecture for Dynamically Conguring Concurrent, Multi-Service Network Daemons, in Proceedings of the Second International Workshop on Congurable Distributed Systems, (Pittsburgh, PA), pp. 190201, IEEE, Mar. 1994.

14 Acknowledgments

Thanks to the DOC Group at Washington University, St. Louis and the University of California, Irvine for apply- [12] R. Klefstad, D. C. Schmidt, and C. ORyan, The Design of a Real-time CORBA ORB using Real-time Java, in ing the Virtual Component pattern in the TAO and ZEN Proceedings of the International Symposium on ORB middleware projects. Thanks also to Don Hinton Object-Oriented Real-time Distributed Computing, IEEE, for helpful comments on the paper.
Apr. 2002. [13] B. Woolf, The Null Object Pattern, in Pattern Languages of Program Design (R. Martin, F. Buschmann, and D. Riehle, eds.), Reading, Massachusetts: Addison-Wesley, 1997. [14] N. Wang, D. C. Schmidt, and S. Vinoski, Collocation Optimizations for CORBA, C++ Report, vol. 11, pp. 4752, November/December 1999.

References
[1] J. Noble and C. Weir, Small Memory Software: Patterns for Systems with Limited Memory. Boston: Addison-Wesley, 2001.

12

[15] P. Narasimhan, L. E. Moser, and P. M. Melliar-Smith, Using Interceptors to Enhance CORBA, IEEE Computer, vol. 32, pp. 6468, July 1999. [16] N. Wang, D. C. Schmidt, M. Kircher, and K. Parameswaran, Towards a Reective Middleware Framework for QoS-enabled CORBA Component Model Applications, IEEE Distributed Systems Online, vol. 2, July 2001. [17] M. Kircher, Lazy acquisition.

13

You might also like