Bringing AOP into Component Object Model Xi Wang and Xiaoge Wang Department of Computer Science Tsinghua University, Beijing, China [email protected]

Abstract. In this paper, we propose a novel lightweight component model called AspectCOM building on a subset of Microsoft component object model (COM) to support aspect-oriented programming paradigm, simplifying the construction aspect-oriented software. In AspectCOM, we provide a special kind of components named adapter for weaving. Aspects as components and existing COM components can be easily adapted to adapters. To support it, we add some new constructs as extensions to the interface definition language, as well as a XML Schema for automatic assembly. We also address the problem of a conflict between aggregation and interface negotiation in our component model.

1

Introduction

Aspect-oriented programming (AOP) [1] is an increasingly popular approach to separation of cross-cutting concerns, improving the extensibility and integrability of softwares. An aspect serves as a cross-cutting part, to provide additional interfaces to existing objects when entering a new context, and/or to intercept method invocations for logging, error handling, security checking or some other purposes. As the aim of AOP, isolating aspects from business logic greatly aids developers to construct modular systems. Current aspect-oriented platforms heavily focus on the Java programming language [2]. However, a large number of applications are still developed in C/C++ of a certain component model such as Microsoft component object model (COM) [3], as well as plenty of well-developed components deserve reuse so that to adopt existing components becomes inevitable. Meanwhile, compared to current Java-based AOP systems, a lightweight component platform can be implemented on a wide range of mobile devices with limited memory size and low computational power, which is extremely suitable for pervasive computing. This project has its original in the design of Elastos 3, a component-based embedded operating system1 , which must be highly configurable and lightweight. We propose a novel AOP component model AspectCOM, which is based on a subset of COM and it is language independent that components can be written in both C/C++ and some other languages through certain bridges. AspectCOM 1

Elastos is designed for 3G cellphones, based on a COM-compatible component model called CAR. See http://www.koretide.com.cn/ for more details.

2

is COM-compatible, with only a few new constructs as extensions to the Microsoft interface definition language (IDL). An extended IDL compiler and a XML assembly tool are also provided to aid the development. The rest of this paper is organized as follows. In section 2 some basic ideas of COM are reviewed and in section 3 we will describe the rationale of AspectCOM in details. Following in section 4 formal examinations on he rules are made while in section 5 tools and issues in our prototype implementation will be discussed. Related work and conclusions are followed in section 6 and 7, respectively.

2

COM Revisited

COM is an interface-based binary standard for objects, to separate interfaces from implementations. Each component or interface takes a 128-bit number as its unique identifier, called CLSID or IID. A COM component exposes its functionalities by interfaces, while interfaces are composed by a set of logically related methods. All other interfaces are derived from the IUnknown interface, which is defined as follows. interface IUnknown { HRESULT QueryInterface([in] REFIID riid, [out] void **ppvObject); ULONG AddRef(); ULONG Release(); } The methods AddRef and Release specify the reference-counting memory model of COM, while QueryInterface is the key to navigate in a component. That is, once you get a pointer to an interface, QueryInterface can be used to ask for extra interfaces. Every component has a factory as its meta-class for creating instances. Whether a component is created independently or aggregately depends on the parameters passed to the factory. COM provides an API CoCreateInstance to invoke factories of components. 2.1

Components

Various approaches have been proposed to formalize COM. including calculiCOM based methods such as COMEL [4, 5] and λ [6]. A formalized framework in Z notation [7] is proposed by Sullivan et al. [8, 9], and it is refined [10] using Alloy [11] and related tools [12] to assist analysis. In this paper, we will follow the formalized COM component model [13] by Sullivan etc. in Z notation. An interface in COM is of some interface type with an unique IID, as we just mentioned. This is modeled by a relationship IIDsofInterface : Interface ↔ IID between the set of all interfaces Interface and the set of all IIDs IID. For a given

3

IID on an interface, QueryInterface returns a corresponding result pointer. This is modeled by a partial function QI : Interface × IID  7 Interface that QI (ptr , iid ) refers to a call to ptr->QueryInterface(iid, &ret) where ret stores the result. And (ptr , iid ) ∈ dom QI (domain of QI ) means a successful call to QueryInterface returning a non-null pointer for the given iid on ptr . A component c is an organic set of interfaces, namely c.interfaces. And the set of IIDs of c, c.iids = IIDsOfInterface(| c.interfaces |), is the IID set of all its interfacess. According to the COM specification, each component has a unique interface of type IUnknown as its identifier, namely c.identity (see the Identity rule below). When being created, the first interface returned by the component to the creator is c.firstInterface. Sometimes c.firstInterface equals to c.identity, but sometimes not, especially when it is created aggregately. Formally, Component interfaces : F Interface iids : F IID first, identity : Interface first ∈ interfaces ∀ i : interfaces; d : IID | (i , d ) ∈ dom QI • QI (i , d ) ∈ interfaces iids = IIDsOfInterface(| interfaces |)

2.2

[O1] [O2] [O3]

Interface Querying Rules

For implementing QueryInterface there are several important rules [14], described as follows: Legality. A successful call to QueryInterface on an interface must return an interface of a type having the IID that was supplied as a parameter. LegalInterface : P Interface ∀ i : LegalInterface; d : IID | (i , d ) ∈ dom QI • d ∈ IIDsOfInterface(| {QI (i , d )} |)

[Q1]

Identity. For any given component, a call to QueryInterface for IUnknown must always return the same physical pointer value. So it can be used to identify whether two interfaces belong to the same component. ∀ X : Component; i : Interface | i ∈ X .interfaces • QI (i , IID IUnknown) = X .identity Stability. The set of interfaces accessible on a component via QueryInterface must be static. Integrity. QueryInterface or the partial function QI must be reflexive, symmetric, and transitive with respect to the set of interfaces that are accessible.

4

– A query on an interface for the IID of itself must succeed; – If a query on an interface for a given IID returns a result pointer successfully, a query on the result pointer for the IID of the original interface must succeed; – If a query on an interface for a given IID returns a result pointer successfully and a sequential query on the result pointer for another IID succeeds, a query on the original interface for the latter IID must succeed. ∀ a : LegalInterface; iiA : IID | (a 7→ iidA) ∈ IIDsOfInterface • (a, iidA) ∈ dom QI [Q2] ∀ a, b : LegalInterface; iidA, iidB : IID | (a, iidB ) ∈ dom QI • (a 7→ iidA) ∈ IIDsOfInterface ∧ QI (a, iidB ) = b ⇒ (b, iidA) ∈ dom QI [Q3] ∀ a, b, c : LegalInterface; iidA, iidB , iidC : IID | {(a, iidB ), (b, iidC )} ⊆ dom QI • (a 7→ iidA) ∈ IIDsOfInterface ∧ QI (A, iidB ) = b ∧ QI (b, iidC ) = c ⇒ (a, iidC ) ∈ dom QI [Q4] A component is legal when [Q1]–[Q4] are met. LegalComponent : P Component ∀ C : LegalComponent • Reflexivity ∧ Symmetry ∧ Transitivity ∧ C .interfaces ⊆ LegalInterface A weaker rule of reachability for interfaces is examined in [10, 13]. An interface is reachable if we can query for all interfaces of a component from it successfully. Later we will use this rule in our theorems. ∀ c : Component; i : Interface; d : c.iids | i ∈ c.interfaces • (i , d ) ∈ dom QI 2.3

[Q5]

Component Reuse

The most common and straight mechanism for component reuse in COM is containment, which is a typical concept in most object-oriented languages and systems. The outer component “contains” the reused inner component, and when the outer component implements certain interfaces, it usually delegates every call to one of the interfaces to the same interface in the inner component. Aggregation is another reuse mechanism of more complexity. To avoid extra implementation overhead, the outer component exposes interfaces from the inner component directly as if these interfaces were implemented on the outer component itself. To consist with the rules of identity and integrity, the outer component and the inner component must cooperate to meet several constraints [15]. 1. The outer component must pass its IUnknown (“controlling unknown”) to the inner component when creating the inner component.

5

2. Being aggregated, the inner component must save the “controlling unknown” for later use, and return its non-delegating IUnknown to the outer component. 3. If the inner component itself aggregates other components it must pass the same “controlling unknown” it receives down to them. 4. The outer component calls QueryInterface in the non-delegating IUnknown to obtain a result when the outer component is queried for an interface exposed from the inner component. 5. The inner component must delegate any IUnknown call occurring in any of its interfaces other than the non-delegating IUnknown to the saved “controlling unknown”. Or formally (except rule 3 on multilevel aggregation): Aggregates : Component ↔ Component ∀ inner , outer : Component | (outer 7→ inner ) ∈ Aggregates • inner .interfaces ∩ outer .interfaces 6= ∅ ∧ (∃ o : outer .interfaces • ∀ i : inner .interfaces\{inner .first} • (λ d : IID • QI (i , d ) = λ d : IID • QI (o, d )))

3

[A1] [A2]

Rationale of AspectCOM

So far we have revisited the basic concepts and rules of COM. Now we would like to bring in the methodology of aspect-oriented programming to COM. 3.1

Terminology

To build up an aspect-oriented system on COM, core concepts of AOP [16] are mapped into AspectCOM as follows: Join point. A join point is a well-defined place in the execution flow of a program. In AspectCOM, join points are basically method calls due to the interface-based structures. Pointcut A pointcut is a program construct that selects join points and collects context at those points, that is, a set of join points. Advice. Advice is the behavior to execute at a join point that has been selected by a pointcut. Aspects. An aspect is a modular unit to implement a concern. In AspectCOM, an aspect is a component composed of a set of join points, pointcuts and advice, to provide extra interfaces and/or to intercept existing interfaces of some other component. We extend the IDL to support the concepts above.

6

3.2

Design Issues

Now let’s take an example to inspect further issues. Suppose there is an interface IFile defined as follows: interface IFile : ... { ... HRESULT WriteByte([in] BYTE val); HRESULT WriteBytes([in,size_is(ulSize)] LPBYTE *pVal, [in] ULONG ulSize); ... } A component File implements the IFile interface. Now we would like to add a security aspect FileSecurityAspect to check permissions before any call to Write*. The methods such as WriteByte and WriteBytes are join points, and the selected Write* is a pointcut. We can define the aspect using our extended IDL as follows2 : coaspect FileSecurityAspect: IFile { pointcut WritePoints(): (HRESULT Write*(..)); before WritePoints; } Compile it with our aspect IDL compiler to generate codes as follows. class FileSecurityAspectFactory: public IClassFactory { ... }; class FileSecurityAspect: public IFile { // the original intercepted interface IFile* m_pFile; public: ... HRESULT Write_before() { // add your advice code here } HRESULT WriteByte(BYTE val) { HRESULT hr = Write_before(); 2

Attributes in IDL such as uuid are omitted here.

7

if (SUCCEEDED(hr)) hr = m_pFile->WriteByte(val); return hr; } HRESULT WriteBytes(LPBYTE *pVal, ULONG ulSize) { HRESULT hr = Write_before(); if (SUCCEEDED(hr)) hr = m_pFile->WriteBytes(pVal, ulSize); return hr; } ... }; If the IFile interface implemented by the aspect component FileSecurityAspect (notated as I 0 ) saves and takes the place of that implemented by the component File (notated as I ), everything will go well. That is, the original interface is contained by the interface of the aspect component. So the main problems are: 1. How I 0 obtains I . 2. How I 0 substitutes I . Being unknown to the designer of a component, aspects must be composed dynamically, and aggregation will be the essential composition means rather than containment. Aspects aggregating a component is a straightforward idea but it does not work. Consider if the aspect component FileSecurityAspect aggregates the component File, it seems to “work” that the outer component FileSecurityAspect can intercept interface queries to the inner component File and provide a substituted interface for IFile. But if another aspect is added later, it must be the outer component for both FileSecurityAspect and File according to the COM rules, which forces them to change their outer components and the rule of identity is broken. So how about a component aggregating aspects? The component File must hold a list of non-delegating IUnknown interfaces of aspects internally, and make some change to the query rules. This requires all COM components like File to be rewritten and recompiled according to the new rules, which is certainly unacceptable. 3.3

Architecture

We propose an approach to overcome the limitations above. Reliance First of all, components like File remain traditional aggregatable COM components without any changes, which can be created either independently or aggregately. However, aspect components like FileSecurityAspect

8

are also aggregatable COM components, and more strictly, they are created aggregately only. An aspect component relies on certain interfaces (reliance) and requires an outer component always. When creating an aspect component, it differs from a trivial aggregatable component on the following facts: – An outer component is always required, so the “controlling unknown” must be non-null; – The “controlling unknown” will be used to query a set of reliance (the interfaces it relies on); Adapter Secondly, let’s introduce a particular adapter component that implements the IAdapt interface, which is defined as follows: interface IAdapt : IUnknown { HRESULT Attach([in] IUnknown* pUnk); HRESULT Detach([in] IUnknown* pUnk); } An adapter manages a list of non-delegating IUnknown interfaces of aspect components internally, where: – Attach adds an interface to the front of the list; – Detach removes a certain interface from the list; – When querying for an interface other than IUnknown on QueryInterface, the adapter queries on the managed interfaces (non-delegating IUnknowns of adapted components) one after another until succeeds to obtain an interface instance of the request IID or fails with E NOINTERFACE; Weaving Thirdly, the process of weaving requires other components be adapted to the adapter. Adapting a component takes two steps: a component is created aggregately taking the adapter as the outer component, then it is attached via the IAdapt interface of the adapter. The entire process is shown as follows: 1. Create an instance of the adapter component; 2. Create a trivial aggregatable component or an aspect component that takes the adapter as the outer component, and obtains its non-delegating IUnknown; 3. During the creation, an component may query on the IUnknown of the adapter (“controlling unknown”) for reliance and save the resulting interfaces for further use; 4. Attach the non-delegating IUnknown to the adapter; 5. Repeat step 2 to 4 to adapt other components;

9

A Case Study Let’s take File and FileSecurityAspect again for an example, as shown in Figure 1. Firstly the adapter is created. Following the core component File is created aggregately and attached to the adapter. At this time, the adapter acts like the component File except exposing one more interface IAdapt. If we query from any exposing interfaces of the adapter for IID IFile at this time, the adapter first queries on the non-delegating IUnknown of File at the head of the internal list and returns the implementation in File. Later the aspect FileSecurityAspect is created aggregately. During the creation, it queries for IFile on the “controlling unknown” (The IUnknown of the adapter) and obtains a pointer to the interface implemented by File. Then it is attached to the adapter, and its non-delegating IUnknown takes the first position in the internal list of the adapter. Now if we query for IFile from any exposing interface, the adapter queries the on the non-delegating IUnknown of FileSecurityAspect first. Now the IFile implementation of FileSecurityAspect hides that of File and is returned.

IUnknown

IUnknown

IUnknown

IUnknown

IUnknown

Fig. 1. Adapter and Attached Components.

The pseudo code of weaving is as follows: // create the adapter IAdapt* pAdapt; CoCreateInstance(CLSID_Adapter, NULL, CLSCTX_ALL, IID_IAdapt, &pAdapt); // create and adapt the core component IUnknown* pUnkCore; CoCreateInstance(CLSID_File, pAdapt, CLSCTX_ALL, IID_IUnknown, &pUnkCore); pAdapt->Attach(pUnkCore); // create and adapt the aspect component

10

IUnknown* pUnkAspect; CoCreateInstance(CLSID_FileSecurityAspect, pAdapt, CLSCTX_ALL, IID_IUnknown, &pUnkAspect); pAdapt->Attach(pUnkAspect); // ... We can also add other aspects such as logging if required. This is our solution to the two problems in section 3.2: I 0 obtains I in its CreateInstance by querying on the adapter’s IUnknown (“controlling unknown”), and substitutes the latter by being attached to the adapter. Or more detailedly, – For component developers, a component is following the same develop steps as a trivial COM component, merely it must be aggregatable. – For aspect developers, they can write a specification in our extended IDL syntax, get it compiled by our IDL compiler to generate some code, and fill in custom code (although all these can be done by hand without our IDL compiler). An aspect is also distributed as an aggregating-only COM component. – For both component and aspect developers, trivial components and aspects can be either distributed independently to leave them weaved by application developers, or weaved beforehand as black-boxes. – For application developers, they can either create components and aspects, and wave them together by hand, or use weaved components provided by component or aspect developers transparently.

4

A Formal Review of Rules

An aspect is a more specific COM component, and the process of adaptation is based on aggregation. However, there still remains several doubts which need clarifying that our extensions to COM lead to good behavior, that is, the legality of components in adaptations.

4.1

Adaptation of Components

First we define an operation } on two interfaces i1 and i2 . The result of i1 } i2 is i1 if i1 is non-null, otherwise it is i2 . ( i2 if i1 is null, i1 } i2 = i1 otherwise. Adaptation ◦ is defined formally as follows:

11

◦ : Component × Component  7 Component ∀ c, c1 , c2 ∈ Component | c = c1 ◦ c2 • QI (c.first, IID IUnknown) = c.identity ∧ (∀ d : IID\{IID IUnknown} • QI (c.first, d ) = QI (c1 .first, d ) } QI (c2 .first, d )) ∧ (∀ i1 : c1 .interfaces\{c1 .first}; i2 : c2 .interfaces\{c2 .first} • (λ d : IID • QI (c.identity, d ) = λ d : IID • QI (i1 , d ) = λ d : IID • QI (i2 , d )))

[D1] [D2]

[D3]

In an adaptation c = c1 ◦c2 , both c1 and c2 take c as their outer components, and c will pass queries except IUnknown to the non-delegating IUnknowns of c1 and c2 sequentially. Any interface of adapted component c1 and c2 other than their non-delegating IUnknowns must delegate all QueryInterface calls to the “controlling unknown”3 . We define an operation ⊕ on two interface sets j1 and j2 (notate j1 .iids for the result of IIDsOfInterface(| j1 |)).

⊕: F Interface × F Interface → F Interface ∀ j1 , j2 : F Interface • j2 ⊕ j1 = {i : Interface | i ∈ j1 ∨ (i ∈ j2 ∧ ({i }.iids ∩ j1 .iids\{IID IUnknown} = ∅))}

⊕ merges the two interface sets excluding interfaces in j2 as which there are interfaces in j1 with the same IIDs. ⊕ degenerates to ∪ if and only if j1 .iids ∪ j2 .iids\{IID IUnknown} = ∅, that is, j1 .iids ∩ j2 .iids = {IID IUnknown}. An alternative description of [D1] and [D2] is, for c = c1 ◦ c2 , c.interfaces = c2 .interfaces ⊕ c1 .interfaces ⊕ {c.first}. We get the following lemmas. Lemma 1. Both } and ⊕ associative. Lemma 2. Let c be a component adapting components c1 and c2 . An interface of c is not the non-delegating IUnknown of c if and only if it is: a) an interface of c1 except c1 ’s non-delegating IUnknown , or b) an interface of c2 except those as which there is an interface in c1 with the same IID. Formally, ∀ c, c1 , c2 : Component; i : Interface | c = c1 ◦ c2 • i ∈ c.interfaces\{c.first} ⇔ (i ∈ c1 .interfaces\{c1 .first}) ∨ (i ∈ (c2 .interfaces ⊕ c1 .interfaces)\c1 .interfaces)) From Lemma 1 we get: Theorem 1. (Component, ◦) is a semigroup. This theorem means that the order of evaluation is immaterial if adaptation appears more than once. In other words, no parentheses are required for adaptations. 3

In [13] multilevel aggregation is not modeled, so there is a little difference between [A2] of aggregation and [D3] of adaptation. We will ignore the difference.

12

4.2

Adaptation and Aggregation

Now we define a map ciids that ciids : Component 7→ F IID ∀ c : Component • ciids(c) = c.iids Obviously, (F IID, ∪) is also a semigroup, and its identity element is {IID IUnknown}. So it is a monoid. The kernel of ciids is defined as follows. ker ciids = {c : Component | ciids(c) = {IID IUnknown}} It is a subsemigroup of (Component, ◦) for it is closed. Any member of ker ciids is a component with only one interface IUnknown. By the definition of adaptation, the relationship between the two semigroups comes clear. Theorem 2. The map ciids is a homomorphism from (Component, ◦) to (F IID, ∪). Formally, ∀ c1 , c2 : Component • ciids(c1 ◦ c2 ) = ciids(c1 ) ∪ ciids(c2 ) Corollary 1. Let c1 and c2 be two components, and c adapts them. Then both the sets of interface types exposed by c1 and c2 are included by the set of types exposed by c. Formally, ∀ c, c1 , c2 : Component | c = c1 ◦ c2 • c1 .iids ⊆ c.iids ∧ c2 .iids ⊆ c.iids By the definition of ciids and Theorem 2, (c1 ◦ c2 ).iids = ciids(c1 ◦ c2 ) = ciids(c1 ) ∪ ciids(c2 ) = c1 .iids ∪ c2 .iids. Another corollary concerns the relationship between adaptation and aggregation. Corollary 2. Let c be a component adapting components c1 and c2 . If any interface of c1 and c2 is exposed, then c aggregates c1 and c2 . Formally, ∀ c, c1 , c2 : Component | c = c1 ◦ c2 6 ∅ • (c1 .iids\{IID IUnknown} = ⇒ (c 7→ c1 ) ∈ Aggregates) ∧ (c2 .iids\c1 .iids 6= ∅ ⇒ (c 7→ c2 ) ∈ Aggregates) Combining conditions with Lemma 2 and Corollary 1, we may get cn .interfaces ∩ c.interfaces 6= ∅(n = 1, 2). Moreover, by the definitions of aggregation and adaptation, since they follow the same delegating rule, we come to the conclusion that adaptation is a special form of aggregation if any interface of adapted components is exposed.

13

4.3

Legality of Adaptation

A question rises whether components constructed by adaptations may be legal. We provide two theorems to ensure this. First here is a sufficient and necessary condition for an “outermost” component, i.e. a component without an outer component. Theorem 3. Let c be a component adapting components c1 and c2 . c is not adapted by any other component. c is a legal component if and only if the nondelegating IUnknowns of both c1 and c2 are legal [Q1] and reachable [Q5]. Formally, ∀ c, c1 , c2 : Component | c = c1 ◦ c2 ∧ c.identity = c.first • c ∈ LegalComponent ⇔ ∀ d : cn .iids(n = 1, 2) • (cn .first, d ) ∈ dom QI ∧ d ∈ IIDsOfInterface(| {QI (cn .first, d )} |) How about the legality of inner components in adaptations? A necessary condition [13] is: the set of interface types exposed by the outer component must include the set of types exposed by the inner component. Formally, ∀ inner , outer : Component | (outer 7→ inner ) ∈ Aggregates • inner ∈ LegalComponent ⇒ inner .iids ⊆ outer .iids By Corollary 1 we see the necessary condition holds for adaptation c = c1 ◦c2 that c1 .iids ⊆ c.iids. That is, c exposes all interfaces of c1 except its non-delegating IUnknown4 . Here we have a sufficient condition. Theorem 4. Let c be a component adapting components c1 and c2 . If c is a legal component and the non-delegating IUnknown of c1 is reachable [Q5], then c1 is a legal component. Formally, ∀ c, c1 , c2 : Component, d : c1 .iids | c = c1 ◦ c2 • c ∈ LegalComponent ∧ (c1 .first, d ) ∈ dom QI ⇒ c1 ∈ LegalComponent See proofs of the two theorems in Appendix A. 4.4

Adaptation Trees

Given a component k with only one interface IUnknown (k ∈ ker ciids) and a component c0 that its non-delegating IUnknown is legal and reachable, for c = c0 ◦ k and c is not adapted by any other component, both c and c0 are legal components according to Theorem 3 and 4. So that for the example in Section 4

we do not discuss the legality of c2 here for some of its interfaces of may be hidden by c1 .

14

Fig. 2. Adaptation Trees.

3.3, we can adapt a traditional COM component File using an adapter, and it acts legally, shown in Figure 1(a) and 2(a). We describe the structure of adaptations using trees. For example, more aspects can be adapted as shown in Figure 2(b) and 2(c). Clients adapt components together and the components can be either traditional aggregatable components or adapters that adapt other components, as in Figure 2(c). When a final component is made out, its internal structure, i.e. the adaptation tree, might be extremely complex. According to basic querying rules of adapters, a pre-order traversal will the performed on the adaptation tree when querying for certain interface, which might lead to poor performance. By Theorem 1, the order of evaluation of adaptations is immaterial for (Component, ◦) is a semigroup. So to improve the performance, we can flat complex binary trees to multi-branch trees as shown in Figure 2(d). In real world, we can either apply some cache policy to QueryInterface implementations of adapters, or make adapters as multi-branch trees rather than binary trees, or both. 4.5

The Interface Negotiation Problem

Aggregation-based pattern is lightweight and extremely fit for developing embedded client applications. However, when applying it to compose GUI components, there is an interesting conflict in COM [13], as shown in Figure 3. Component Bitmap and File are purchased in market and composed with a mediator to make out a new component PersistentBitmap. “A persistent bitmap is a bitmap that can be loaded from and saved to a file and displayed in several styles on the screen. Redrawing bitmaps must be as fast as possible, so Bitmap is aggregated and its IDraw interface is exposed to clients of the aggregate.

15

Clients use the IPersistFile interface of File to request loading or saving the bitmap. Upon a request or when the file changes on disk, File notifies the mediator, which sends or receives the bitmap data to or from File through its IData interface, perhaps using a compression algorithm. The mediator uses its own interface of type IStyle to render the raw bitmap from the file. It uses Bitmap’s IStyle interface to set its drawing style accordingly.” IUnknown

IUnknown

IUnknown

IStyle Bitmap IDraw

IRawBits Mediator

IUnknown

INotify

INotifyManager File IData

IPersistFile

IStyle

Fig. 3. The PersistentBitmap Example.

The problem is, after the three components are aggregated, they cannot obtain required interfaces. For example, The component Mediator needs IRawBits and IStyle of the Bitmap component, but it only holds the outer IUnknown. If querying on the outer IUnknown for the two interfaces, it cannot get IRawBits for this interface is not exposed in aggregation, and more, it will get a wrong IStyle interface. Several approaches have been discussed [17]. One possible solution is to force the outer component pass Mediator the interfaces it requires. However, this tightly couples the outer component to the mediator. An updated Mediator might require different interfaces and it will require a modification on the outer component as well, which is a serious downside. In fact, the outer component should not know the relationship between the mediator and the other components. Another solution is that the mediator provides an extra interface to obtain the non-delegating IUnknowns of interfaces it requires, which are passed in by the outer component. In fact, this is a general solution to this problem. In AspectCOM we may consider this as an adaptation of Mediator, File, and Bitmap, where Mediator relies on the other two. As we presented in Section

16

3.3, first an adapter is created. Then File and Bitmap are created aggregately and attached to the adapter. When creating Mediator, it has not been attached to the adapter yet. So it may query on the IUnknown passed in (“controlling unknown”) and obtain the correct IRawBits and IStyle of Bitmap successfully. After saving the required interfaces, Mediator is attached to the adapter and the component PersistentBitmap is made out with a good behavior. There are also some limitations. For example, as in Figure 4, several GUI widgets are to be composed to a new form, and there are two IPos interfaces on Text and Button respectively. In AspectCOM the mediator cannot distinguish them, so it works well for Frame but is helpless dealing with the other two. In such circumstances, the general approach we have just discussed can be combined that the mediator provides an extra interface such as ILayoutManager to manage the non-delegating IUnknowns of required interfaces, and the outer component will pass in the non-delegating IUnknowns of Text and Button. IUnknown

IUnknown

Layout Manager (mediator) ISizeNotify

ILayoutManager

ISizeNotifyManger

IUnknown

IUnknown

Frame

IPos ISize IFrameStyle

IPosISize IUnknown

IPos

Text

Button

IText

IButton

Fig. 4. The InputForm Example.

5

Prototype Implementation

Based on the ideas above, we develop several tools to aid aspect-oriented development. We extend the IDL syntax of COM, and provide a compiler to generated codes for aspect component developers. And we provide an automatic XML assembly tool to adapt components dynamically for applications. Some other issues in real world will also be examined.

17

5.1

Aspect IDL

Aspect component developers may define an aspect in IDL, compile it with our IDL compiler to generated codes, and fill in functions. In current implementation, an aspect may be defined as follows. [ coaspect-attribute-list ] coaspect aspectname: reliance-list { [ interface-attributes ] interface interfacename { ... } pointcut pointcutname(args): pointcut-definition; advice-type pointcutname; advice-type(args): pointcut-definition; } The coaspect-attribute-list is similar to the attribute list for coclass and interface, to provide information such as uuid, version, etc. And reliance-list is the interfaces the aspect relies on, such as IFile for FileSecurityAspect in Section 3.2. Interface declarations and definitions in an aspect are also similar to those in coclass. A pointcut is a method pattern that matches a set of methods. Pointcuts may compose through the operations union (“||”), intersection (“&&”) and difference (“\\”). The advice-type can be either before or after. A formal description of the syntax is in Appendix B. 5.2

XML Assembly

Clients may write codes to assemble components, as in Section 3.3. However, if we would like to compose several components and adapt them into a new component, writing such codes turns to a boring job, especially for components that will change in applications. For example, in Figure 4 the form layout might change and a new layout manager is needed when it is updated or configured by users. Another example is that sometime we would like to add security and logging aspects to a File component, and some days later we might add or remove certain aspects. In other word, we may compose several aspect components into one for convenience in certain contexts as in Figure 2(c). In the circumstances above, rewriting and recompiling new components frequently is not practical. Therefore we provide a tool to aid. Clients may write a simple XML file to describe how to compose existing components into a new component. See the XML Schema in Appendix C.

18

For example, the following XML describes how to make out the PersistentBitmap component shown in Figure 3. Techniques used here are similar to those in Windows Script Components. A DLL file aspect.dll is provided, and a XML file is distributed as a component. When registering, the COM utility regsvr32 can be used as follows. regsvr32 aspect.dll /n /i:http://myserver/PersistentBitmap.xml And aspect.dll registers the component as well as writes the content of the XML file into Windows registry. When creating instances with its CLSID as an ordinary component, aspect.dll reads in the XML contents and analyses dependencies between components to determine a proper adapting order. Then an adapter is created, and the components are created aggregately and adapted to the adapter in order. 5.3

Interface Hiding

An assumption here is that all interfaces except the non-delegating IUnknowns of inner components will be exposed in adaptations. However when making a new component in real world, we might want to hide some interfaces, such as IData and INotifyManager of File in Figure 3, leaving interfaces necessary only to clients5 . According to COM rules, E NOINTERFACE should be returned when querying for a non-existent interface, and the adapter will query on the next adapted component. In our implementation, we allow an adapted component to return a value other than S OK or E NOINTERFACE, and the adapter will stop querying. So the mediator implementation in Figure 3 may return some other value when querying for IData, INotifyManager, etc., and the interfaces are hidden from clients. 5

In AspectCOM an adapted component obtains its reliance in creation, so it will not be affected.

19

5.4

Optimizations

In AspectCOM, an interface in an aspect component may contain another hidden interface and delegate calls to it. This is our basic pattern to support AOP, such as the example in Section 3.3: a call to WriteBytes on IFile of FileSecurityAspect will be delegated to that of File. HRESULT FileSecurityAspect::WriteBytes( LPBYTE *pVal, ULONG ulSize) { HRESULT hr = Write_before(); if (SUCCEEDED(hr)) hr = m_pFile->WriteBytes(pVal, ulSize); return hr; } Does this pattern bring on significant overhead, especially when several aspects are adapted together? The overhead of a method invocation involves packing and extracting of parameters (mostly push and pop instructions on stacks as well as several register instructions). In the case above, clients pack parameters pVal and ulSize first, and when entering FileSecurityAspect::WriteBytes they are extracted. Later in the call m pFile->WriteBytes the parameters will be packed again and then extracted. However, the parameters are not used by the aspect at all. So when calling m pFile->WriteBytes, it should “jump” to the method directly rather than extracting and packing parameters. The overhead rises that the parameters will be packed and extracted twice rather than once. And if more aspects are adapted, the overhead will increase accordingly and become significant. We have carefully examined several modern C++ compilers, and found out that the binary instructions generated by Microsoft Visual C++ 7.1 on Windows XP and GNU C++ 3.4 on Linux (Fedora 3) for the code snippet above are highly optimized. In release/optimized model, the compilers do remove packing/extracting instructions when invoking m pFile->WriteBytes, and use a “jump” instruction instead of a “call” instruction. So we can ensure that interfaces in aspects “contain” those in other components will not bring extra overheads, as shown in Figure 5. Another performance issue concerns interface querying. We have discussed in Section 4.4 that complex adaptation trees may lead to poor performance when querying for interfaces. So when adapting components, developers may make a more “flat” adaptation tree to avoid unnecessary adapters. An interface pool is also provided in the adapter’s implementation, which is trivial in COM. If certain interface is queried frequently, it will be put into the pool. If being queried later, the cached interface will be taken out from the pool efficiently rather than traversing on the adaptation tree. Interface invocation and querying are two key actions in AspectCOM. So the performance and optimized solutions are evaluated carefully. Our discussions

20

8 7 6 Debug Release Debug (no check) Release (no check)

5 4 3 2 1 0 0

50

100

150

200

Fig. 5. Performance. X is the number of aspects added, and Y is the time (×10−5 s), tested on a Pentium4 2.5G Hz with 512M memory. One component with aspects to check parameters and another with doing-nothing aspects, are compiled with Microsoft Visual C++ 7.1 in both debug and release mode.

mostly focus on C/C++ implementations, for most COM components are still written in C/C++ rather than Java or other programming languages.

6

Related Work

Many well developed tools provide support for aspect-oriented software development, such as AspectJ, AspectWerkz, JBoss AOP, SpringAOP, etc., most of which are Java-based. Aspects may be described in Java sources, Java 5 annotations or extra XML files. Aspect weaving may occur at building time by compilers for extended syntax, as well as load time and run time by modifying byte-codes in virtual machines or generating proxies to intercept methods. Some other tools such as aspect# support AOP in similar ways. AspectC++ [18] extends the C++ syntax to provide AOP support. Its frontend weaves classes at build time, to transform AspectC++ sources to standard C++ sources and invoke an existing C++ compiler. Eclipse core runtime [19] provides an interface-based mechanism for adaptable objects. Adaptable objects can be dynamically extended to provide different interfaces. Adapters are registered dynamically to the platform, and the platform manages these adapters. When querying for interfaces, adaptable objects may ask the platform for retrieving results. It does not support intercepting existing interfaces. Mixin [20] is a popular technique in many script languages such as Python to intercept methods. In such languages, a method is looked up first in the derived class then in base classes in a certain order. Deriving and adjusting orders of base classes make intercepting possible.

21

AspectCOM may fit in the category of the weaving at load time, and it also enables to weave at run time without proxies. It makes use of COM aggregation mechanism heavily to change the behavior of original components. Currently AspectCOM is applied mostly in developing embedded client applications for its lightweight model and compatibility with COM, compared to popular Java AOP solutions. We mainly use AspectCOM to assemble components and aspects similar to the case in Figure 3, and to add aspects such as logging and securitycheck to existing components. It is capable to compose components and add cross-cutting aspects with only few changes to existing COM-compatible environments. However, due to the restricted component model, it is less expressible for it does not have so rich sets of join points as Java AOP solutions do.

7

Conclusions and Future Work

AspectCOM is a component-based approach. It supports AOP at load time and run time. No modifications are required for existing COM components, as long as they are aggregatable. Our approach can also be applied to other similar interface-based component models, such as OpenCOM [21], XPCOM [22], UNO [23], etc. AspectCOM is based on COM aggregation, and it is proved that an adapted component leads to good behavior. We also address the interface negotiation problem in COM, where AspectCOM may contribute to composing modules in the mediator pattern. We extend the IDL to support aspect-related constructs and implement the IDL compiler. To assist development and deployment, we also provide an assembly tool to assemble and make out new components automatically from XML description files. In the future we may develop more tools for AspectCOM, especially on visualizations for developing, deploying and analyzing “adapted” components, and try to apply it in developing server applications. Integrating with remote components is also one of our interests.

References 1. Kiczales, G., Lamping, J., Mendhekar, A., Maeda, C., Lopes, C., Loingtier, J.M., Irwin, J.: Aspect-oriented programming. In: Proceedings of the 11th European Conference on Object-Oriented Programming. (1997) 2. Kiczales, G., Hilsdale, E., Hugunin, J., Kersten, M., Palm, J., Griswold, W.: An overview of AspectJ. In: Proceedings of the 15th European Conference on ObjectOriented Programming. (2001) 3. Rogerson, D.: Inside COM. Microsoft Press (1997) 4. Ibrahim, R., Szyperski, C.: The COMEL language. Technical report, University of Queensland (1997) 5. Ibrahim, R., Szyperski, C.: Formalization of component object model (COM) the COMEL language. In: Proceedings of the 12th European Conference on ObjectOriented Programming Workshops. (1998)

22 6. Pucella, R.: Towards a formalization for COM part I: The primitive calculus. In: Proceedings of the 17th ACM SIGPLAN Conference on Object-Oriented Programming Systems, Languages and Applications. (2002) 7. Spivey, M.: The Z Notation: A Reference Manual. Prentice Hall International Series in Computer Science. Prentice-Hall (1992) 8. Sullivan, K., Socha, J., Marchukov, M.: Using formal methods to reason about architectural standards. In: Proceedings of the 19th International Conference on Software Engineering. (1997) 9. Sullivan, K., Marchukov, M.: Interface negotiation and efficient reuse: A relaxed theory of the component object model. Technical Report 97–11, Department of Computer Science, University of Virginia (1997) 10. Jackson, D., Sullivan, K.: COM revisited : Tool-assisted modelling of an architectural framework. In: Proceedings of the 8th ACM SIGSOFT International Symposium on Foundations of Software Engineering. (2000) 11. Jackson, D.: Alloy: A lightweight object modelling notation. Technical Report 797, Laboratory for Computer Science, Massachusetts Institute of Technology (2000) 12. Jackson, D., Schechter, I., Shlyakhter, I.: Alcoa: the Alloy constraint analyzer. In: Proceedings of the International Conference on Software Engineering. (2000) 13. Sullivan, K., Marchukov, M., Socha, J.: Analysis of a conflict between aggregation and interface negotiation in Microsoft’s component object model. IEEE Transactions on Software Engineering 25(4) (1999) 584–599 14. Kindel, C.: The rules of the component object model. Technical report, Microsoft Corporation (1995) 15. Goswell, C.: The COM programmer’s cookbook. Technical report, Microsoft Office Product Unit (1996) 16. Filman, R., Elrad, T., Clarke, S., Aksit, M.: Aspect-Oriented Software Development. Addison Wesley (2004) 17. Sullivan, K., Marchukov, M.: Mediator integration of aggregated COM components (1998) 18. Spinczyk, O., Gal, A., Schr¨ oder-Preikschat, W.: AspectC++: An aspect-oriented extension to C++. In: Proceedings of the 40th International Conference on Technology of Object-Oriented Languages and Systems. (2002) 19. Gamma, E., Beck, K.: Contributing to Eclipse: Principles, Patterns, and Plug-Ins. Addison Wesley (2003) 20. Bracha, G., Cook, W.: Mixin-based inheritance. In: Proceedings of the Conference on Object-Oriented Programming: Systems, Languages, and Applications / Proceedings of the European Conference on Object-Oriented Programming. (1990) 21. Coulson, G., Blair, G., Grace, P., Joolia, A., Lee, K., Ueyama, J.: OpenCOM v2: A component model for building systems software. In: Proceedings of IASTED Software Engineering and Applications. (2004) 22. Turner, D., Oeschger, I.: Creating XPCOM Components. (2003) 23. : (Universal network objects. http://udk.openoffice.org/.)

A

Proofs

Proof of Theorem 3 The necessary condition is obvious, so we only prove the sufficient condition. 1) c.identity = c.first.

23

2) ∀ d : c.iids • (cn .first, d ) ∈ dom QI ∧ d ∈ IIDsOfInterface(| {QI (cn .first, d )} |)(n = 1, 2). 3) By line 1) and [D1] we have QI (c.first, IID IUnknown) = c.identity = c.first. 4) By line 1) and [D1] we have ∀ d : IID\{IID IUnknown} • QI (c.first, d ) = QI (c1 .first, d ) } QI (c2 .first, d ). 5) Combining Lemma 2 with line 1) and [D2], we have ∀ i : c.interfaces\{c.first}, d : IID • QI (i , d ) = QI (c.identity, d ) = QI (c.first, d ). 6) In line 5) if d = IID IUnknown, QI (i , d ) = QI (c.first, IID IUnknown) = c.first. 7) In line 5) if d 6= IID IUnknown, QI (i , d ) = QI (c.first, d ) = QI (c1 .first, d ) } QI (c2 .first, d ). 8) Combining line 3) and 6) we have ∀ i : c.interfaces • QI (i , IID IUnknown) = c.first. 9) Combining line 4) and 7) we have ∀ i : c.interfaces; d : IID\{IID IUnknown} • QI (i , d ) = QI (c1 .first, d ) } QI (c2 .first, d ). 10) In line 9) if d ∈ c.iids, by Lemma 2 QI (c1 .first, d ) ∈ dom QI ∨ QI (C2 , first, d ) ∈ dom QI . 11) Combining line 8), 9) and 10) we have ∀ i : c.interfaces; d : c.iids • (i , d ) ∈ dom QI . 12) Combining line 2), 8) and 9) we have legality [Q1] for all interfaces of c. 13) ∀ x : c.interfaces; iidX : IID | (x 7→ iidX ) ∈ IIDsOfInterface, so iidX ∈ c.iids. By line 11) (x , iidX ) ∈ dom QI . We have reflexivity [Q2] for all interfaces of c. 14) ∀ x , y : c.interfaces; iidX , iidY : IID, and (x , iidY ) ∈ dom QI ∧ (x 7→ iidX ) ∈ IIDsOfInterface ∧ QI (x , iidY ) = y. So iidX ∈ c.iids, and by line 11) (y, iidX ) ∈ dom QI . We have symmetry [Q3] for all interfaces of c. 15) ∀ x , y, z : c.interfaces; iidX , iidY , iidZ : IID, {(x , iidY ), (y, iidZ ))} ⊆ dom QI ∧ (x 7→ iidX ) ∈ IIDsOfInterface ∧ QI (x , iidY ) = y ∧ QI (y, iidZ ) = z . Since z = (y, iidZ ), by line 12) (z 7→ iidZ ) ∈ IIDsOfInterface, so iidZ ∈ c.iids. we have transitivity [Q4] for all interfaces of c. 16) Combining line 12), 13), 14) and 15) we have c ∈ LegalComponent.  Proof of Theorem 4

1) 2) 3) 4) 5)

c ∈ LegalComponent. ∀ d : c1 .iids • (c1 .first, d ) ∈ dom QI . By line 1) we have c1 .first ∈ LegalInterface. By [D3] ∀ i : ci .interfaces\{ci .first}; d : IID • QI (i , d ) = QI (c.identity, d ). Combining line 1), 2), 3) and 4) we have legality, reflexivity, symmetry and transitivity for all interfaces of c1 . So c1 ∈ LegalComponent 

24

B

Aspect IDL Syntax

AspectDef

::= AttributeList coaspect identifier [: InterfaceList] AspectBlock InterfaceList ::= identifier { , identifier } AspectBlock ::= { {AspectStatement} } AspectStatement ::= InterfaceDef | InterfaceDecl | PointcutDecl | AdviceDef PointcutDecl ::= pointcut identifier ( [args] ) : PointcutDef ; PointcutDef ::= ( MethodPattern ) | PointcutDef || PointcutDef | PointcutDef && PointcutDef | PointcutDef \\ PointcutDef AdviceDef ::= AdviceType identifier ; | AdviceType : PointcutDef ; AdviceType ::= before | after

C

XML Assembly Schema

25



Bringing AOP into Component Object Model

AspectC++ [18] extends the C++ syntax to provide AOP support. Its front- end weaves classes at build time, to transform AspectC++ sources to standard.

274KB Sizes 2 Downloads 191 Views

Recommend Documents

Component-based game object system - GitHub
3.7.2 Can we reuse game object types, or their behaviors, in new games? . 7. 3.7.3 Is it easy to ...... gameprogrammingpatterns.com/component.html. [16] Pie21.

Archetype Object Model - openEHR
Mar 20, 2007 - the openEHR Free Commercial Use Licence can be found at ...... tion/TRUNK/publishing/architecture/am/aom.pdf. ... Archetypes are constraint-based models of domain entities, or what some might call ... revised, also to version 2.0, to i

LNCS 7575 - Multi-component Models for Object ... - Springer Link
visual clusters from the data that are tight in appearance and configura- tion spaces .... Finally, a non-maximum suppression is applied to generate final detection ...

Bringing fireflies into the backyard - UF/IFAS Leon County Extension
Her column is submitted as a service of the University of Florida. IFAS Extension in Leon County, http://leon.ifas.ufl.edu. Thursday, April 9, 2009. Tallahassee ...

A Middleware-Independent Model and Language for Component ...
A component implements a component type τ, same as a class implements an interface. A component (τ, D, C) is characterized by its type τ, by the distribution D of Boolean type which indicates whether the implementation is distributed, and by its c

object oriented database model pdf
There was a problem previewing this document. Retrying... Download. Connect more apps... Try one of the apps below to open or edit this item. object oriented ...

The Object-Relation-Kin Model
Database Management System Supporting ORK Model ... unavoidable logics mapping the cache (in-memory objects) between the data store (disk storages).

Component Testing
Jul 8, 2002 - silicon atom. ... you really have to understand the nature of the atom. ..... often that you see a desktop computer burst into flames and burn down ...

Component Testing
Jul 8, 2002 - use a meter to test suspect components and troubleshoot electronic circuits. ..... The valence electron is held loosely to the atom and moves.

The DCI Paradigm: Taking Object Orientation Into the ... - fullOO.info
1. The DCI Paradigm: Taking Object Orientation. Into the Architecture World. James O. ... To the world of computer science, there can be no such thing as Delight .... The engineering and architectural metaphors arose only a few years apart. It should

The DCI Paradigm: Taking Object Orientation Into the ... - fullOO.info
that often distances architecture efforts from the code and breeds scepti- cism among coders. .... write clean code [36] or, taking the architectural metaphor more literally, habitable code ...... Booch, Grady. Software engineering with Ada, 1987.

Model generation for robust object tracking based on ...
scription of the databases of the PASCAL object recogni- tion challenge). We try to overcome these drawbacks by proposing a novel, completely unsupervised ...

Fan Shape Model for Object Detection
This flexibility allows FSM to tolerate large ... Scale variance is big challenge for object detection. ... To summarize, there are five main advantages of the fan ... age data. Previous fundamental research of the part-based object model can be foun

Explicitly distributed AOP using AWED
Mar 20, 2006 - extending JAsCo, a system providing dynamic aspects for. Java. Categories ... Distributed applications are inherently more complex to develop than ... have been developed that offer features for aspect-oriented development ...

Explicitly distributed AOP using AWED
JBoss application server [2], which is built on J2EE, the Java-based middleware platform. Concretely, we ..... syncex Object around(Facade f): distribution(f) {.

Component symbols.pdf
Cell Supplies electrical energy to a circuit, the longer line ... Loudspeaker Converts electrical energy into sound energy. Page 1 of 1. Component symbols.pdf.

Capacity component -
Dec 1, 2016 - Draft JAO/DAO on biodiversity tagging formulated. • Draft DAO on recommended modes of. PPP for biodiversity conservation. • DAO on establishment of PPP unit in. DENR signed and implemented. • “Establishment of a Carbon. Sequestr