Towards Automated Component Compatibility Assessment ∗ Pˇremysl Brada Department of Computer Science and Engineering University of West Bohemia, Pilsen, Czech Republic
[email protected] (This position paper was presented at ECOOP 2001, Workshop on Component-oriented Programming, Budapest, Hungary.)
Abstract With the increasing use of software components, the methods for safe replacement of currently used components by newer versions will become of greater importance. While component specification and application composition issues have been researched, working approaches to assess compatibility of different components are hard to find. In this paper we define compatibility at two levels, of which contextual compatibility takes into account the actual usage of the component as bound in an assembled application. The presented method for determining compatibility of two components uses analysis of differences in their specifications structured into traits of related declarations.
1
Introduction
In the area of software components [17] a lot of effort has been put into the research of component specifications and their consistent composition [4] including the dynamic case [14, 10]. This lead to the adoption of industry architectures like the CORBA Component Model (CCM [11]), JavaBeans [8], as well as to research systems like SOFA [14]. However, the problem of determining compatibility of components has in our opinion received insufficient attention. Imagine you would like to use a “better version” of a component instead of the one you currently have. It would be best to just “plug it in” without writing any adaptation code, especially if you are a non-programmer [16]. Yet such substitution of the component should not affect the correct functioning (the consistency) of the application – the better version should be compatible with the current one. ∗ This work was supported by the Grant Agency of the Czech Republic, project number 201/99/0244, and by the Research Plan number MSM235200005 funded by the Ministry of Education of the Czech Republic.
1
There are several definitions of compatibility that can be found in related research. Perry in his work on the Inscape environment [12] defines three levels of compatibility of individual functions among which the strict compatibility is equivalent to subtyping and ensures plug-in substitutability. The DCE environment [13] and more recently the Java binary compatibility specification [7] define the rules for changes in interface description, resp. class implementation that are considered compatible with respect to already existing compiled code. Some production systems ensure the compatibility of software applications with respect to prospective upgrade – e.g. the DMI initiative [6] or various Linux packaging systems [5]. Their main disadvantage from our point of view is that they rely on dependency and compatibility descriptions which must be supplied manually and thus may not reflect correctly the real properties of the application implementation. Crnkovic and Larsson [9] define compatibility of black-box components with several levels of relaxation: behaviour compatibility preserves the interface as well as non-functional characteristics of the component, interface compatibility preserves only the functional interface, and input and output compatibility considers just the data which the component exchanges with its environment. However, the authors do not mention any means of determining whether a component satisfies a given level.
1.1
The Approach and Goal of the Paper
Checking compatibility of two components can be done dynamically or statically. Dynamic checking means running a suite of compliance tests that will ascertain correct interactions of the replacement component in the place of the current one. It allows a detailed assessment and may be the only option for frameworks with underspecified components (e.g. COM). However it may be hard to automate and relies on high quality test suites; this may pose a problem for production components with short time to market. Static checking uses only the component specification similarly to [18] and the verdict is based on determining whether the specifications of replacement component’s features are in the proper inclusion relation to those of the current one. It is fast, accurate, and does not interfere with the (possibly running) applications; however it is feasible only for components with rich specifications. Although neither approach is completely superior, the current trend towards the use of declarative languages in component development [2] makes static checking an attractive option. Also, similarly to Larsson we believe that it would be too constraining to reduce the compatibility issue to a subtyping view. This paper therefore presents the foundations of our approach to static component compatibility checking which takes into account the deployment environment. The next section describes the aspects involved in assessing component compatibility by static checking. Based on a structured view of component specifications, Section 3 defines two compatibility levels, a strict and a relaxed one. Section 4 briefly describes an experimental implementation of this method for automated component assessment in SOFA [14].
2
2
Component Features and Traits
When assessing a component for compatibility, we should consider all features it exposes for interaction. The most important aspect to check is their structure, i.e. the syntax of the operational interface used to invoke component functionality (function calls or events) and formats used by data features (which produce/consume data read/written by other subjects). An orthogonal property of the features is their “direction”. Primarily, each component provides features that its clients can use to interact with it, like COM or CORBA interfaces or event sinks in CCM. On the other hand, the component may require the connection to some features in its environment which its implementation depends on. For example, CORBA and SOFA components can require interfaces (in CCM called ‘receptacles’) through which they can access other components. In addition, the correct use of the features or of the whole component is sometimes defined by semantic constraints. These may e.g. mandate that interface methods be called in a particular order [15], or that operations over the component preserve an invariant property.
2.1
Traits in Component Specification
As components are black-box units, the only description of their features available to the users (including application developers) is commonly a specification of the component surface1 . In some systems, notably CORBA and SOFA, the surface specification can provide a fairly detailed information about the structure and semantics of component features. Such specifications can be used as a basis for analysing and comparing component properties. /* The component specification */ /* Suporting declarations. */ interface HTTP { frame HTTPClient { Response GET(URI request); provide: protocol: HTTP connection; GET* property boolean keepalive; }; property long timeout; require: module system { ::system::SimpleSockets sock; interface SimpleSockets { protocol: // the semantics int create(...); ( ?connection.GET { // omitted for brevity !sock.create?; !sock.write; protocol: !sock.read; !sock.destroy? ( create ; ... )* } )* }; }; } Figure 1: Surface specification of a component in SOFA CDL As components are coarse grained and fairly complex entities, these analyses should not provide results only for language constructs at a low level of abstraction – e.g. function signatures as in [12, 18]. It is better to approach the 1 We
do not use the term ‘interface’ to avoid mismatch with IDL interfaces.
3
comparison using declarations corresponding to the user’s view of component features, for example at the level of whole interfaces (in the IDL sense) and data format specifications (e.g. XML DTDs). To this end we use the structuring of the surface specification into declaration aggregates called specification traits [3]. A trait t is a named set of declarations di which describe a structure or semantics of a particular type of feature in the component surface: t = (name, {di }). For example, SOFA components have 4 traits: provided interfaces, required interfaces, component properties, and a behaviour description called frame protocol [15] (see Figures 1 and 2). Each trait is further classified into one of three trait categories according to the roles of its declarations: provisions P are traits describing provided features, requirements R describe required features (dependencies), and semantics S are traits specifying component or feature semantic properties. Trait property provides requires protocol
Catecory P P R S
Contents {(boolean, keepalive), (long, timeout)} {(HT T P, connection)} {(:: system :: SimpleSockets, sock)} {(?connection.GET {!sock.create?...})}
Figure 2: Trait contents of the HTTPClient component The next section shows how component compatibility can be defined and assessed using feature specification constructs grouped in traits.
3
Compatibility of Components
Following the informal definition in the Introduction, a replacement component is compatible with a current component if it satisfies three requirements: present the same operational interface to its environment, be able to read and write the same data (place, format) as the current one (the input-output compatibility in [9]), and conform to the semantics of the current component in all interactions in which it is engaged. It is important to note here that modifications of the provided and required features do not affect compatibility equally. The replacement component’s provided features should obviously be at least the same as those of the current one, otherwise its clients will not be able to successfully communicate with it. However, it need not be a problem if the replacement component has different requirements because the environment may be able to satisfy them, i.e. to provide features to which they will be bound. The situation is unlike most programming languages where the type of the “replacement” object must be a subtype of the current type (a typical example is the assignment of an subclass instance to a variable of a superclass type).
3.1
Compatibility Levels
The above observation tells us that the common notion of compatibility may be overly restrictive for the case of a component replacing a current one in
4
a particular context. We therefore differentiate two levels of component compatibility. Strict compatibility requires that the replacement component be a subtype of the current one and is warranted by “contravariant” differences in their specifications. This level should ensure substitution in any context. The “relaxed” level is termed contextual compatibility because it takes into account two important aspects of the environment in which the current component is deployed – the context of the component (see Figure 3): 1. Which of the current component’s provided features are actually used by other components in the given application configuration, i.e. bound to other components’ requirements. For example, a HTTPClient component which provides both HTTP and WebDAV interfaces can be assembled in an application that does not use the WebDAV protocol. 2. Whether the current environment provides all features that the replacement component declares as required, not necessarily considering the requirements of the current component. For example, a new version of the HTTPClient component may require an additional XMLParser interface that is being provided by a current parser component. 3.1.1
Trait-Based Definitions
The differences between the replacement component r and the current one c are represented in their specifications as changes in the corresponding declaration traits tri and tci . The changes fall into one of four types, determined by function change(tci , tri ): none means tri = tci , specialization means an extension of functionality, enriched data format or more stringent semantics, generalization means a reduction of functionality or more relaxed semantics, and mutation means the two traits have incomparable contents basically due to a mixture of specialization and generalization changes in the declared features. The function is parametrized by the comparison algorithm pertinent for the given trait, i.e. suitable for comparing the kind of declarations ti contains. Now we can define the two levels of compatibility in terms of these change types. The replacement component with specification C r = P r ∪ Rr ∪ S r = {tri } is strictly compatible with the current one C c = P c ∪ Rc ∪ S c = {tci } if and only if, for α ∈ {r, c}, α c r • ∀tα i ∈ P : change(ti , ti ) ∈ {none, specialization} and α c r • ∀tα j ∈ R : change(tj , tj ) ∈ {none, generalization} and α c r • ∀tα k ∈ S : change(tk , tk ) ∈ {none, generalization}.
The definition corresponds to the natural understanding: the replacement component provides at least the same, requires at most the same, and does not impose new semantic constraints. The contextual compatibility definition needs a description of the component’s context (see Figure 3). The subsets of declarations of provided features that are actually bound to (used by) other components constitute a set P 0c 0c c such that ∀t0c : t0c i ∈ P i ⊆ ti . The declarations of features that other components provide and that can satisfy the requirements of the component is denoted R¯c = {t¯cj }. The compatibility of semantics needs to take into account only the 5
declarations related to the bound provided features; thus we consider a reduced set S c /P 0c . The context is then a triple Cx = (P 0c , R¯c , S c /P 0c ).
replaced
Rc
HTTPClient
timeout
keepalive
HTTP Simple Sockets WebDAV XMLParser
keepalive
provide sys
BrowserEngine
HTTP Simple Sockets
sys
HTTPClient
require
Parser
current
Parser
BrowserEngine
P' c
semantics binding
Figure 3: Context and Component Substitution The replacement component is contextually compatible with the current one if and only if r • ∀tri ∈ P r : change(t0c i , ti ) ∈ {none, specialization} and
• ∀trj ∈ Rr : change(trj , t¯cj ) ∈ {none, specialization} and • ∀tck ∈ S c /P 0c , trk ∈ S r /P 0c : change(tck , trk ) ∈ {none, generalization}. In plain words, the replacement component provides at least the same features as are used of the current one in the context, requires at most what is available from other components, and its semantics is such that the clients will be able to use it in the same manner as the current one.
4
SOFA Implementation of the Approach
Our research in component compatibility is done as part of the SOFA project [14] where it is closely related to the work on component versioning [3]. Compatibility checking involves three steps. First, the context of the current component is determined. To achieve this we use the description of the application’s architecture (hierarchical aggregation of sub-components linked via interface bindings). This is read from a static CDL specification at the start and modified during the application lifetime as components are substituted. Traits representing the context are built from the specifications of components referenced in the architecture. In the second step, the change types for the pairs of corresponding traits in the replacement component C r and the context Cx are determined. Since the comparison traverses the declarations of types referenced in the specification, it also considers the changes at lower levels of granularity (e.g. adding a method to a provided interface propagates as specialization to the provides trait). Lastly, the computed differences in provided, required and semantics traits are matched against the above definitions. The result is a conclusion whether and how the “better” component is compatible with the current one.
6
architecture WebBrowser { // sub-components, each has its own architecture inst HTTPClient h; inst BrowserEngine b; inst Parser p; // interface bindings bind h.sock to sys.sockets; bind b.http to h.connection; }; Figure 4: CDL architecture specification of a web browser It can be seen that this method is susceptible to small changes like swapping method parameters or type renaming. The reason for this strictness is our belief that components should be substitutable with as little additional programming as possible (similarly to changing expansion cards in PCs). The concept of connectors [1] can be used for feature adaptations in case of such changes. In addition to the advantages of static checking noted in the Introduction, the presented approach makes it possible to compute the data for compatibility assessment in advance. This enables us to simplify the assessment in the common situation of upgrading a component to a subsequent revision. In this case the changes between the revisions are classified at the developer’s site and stored in component meta-data. They can be used to prevent consistencybreaking substitutions at the user’s site even if component specifications aren’t available.
5
Conclusions and Future Work
In this paper we have shown how using declarative specification of black-box components we can assess the chances of their successful substitution. The classification of changes between corresponding declaration traits in the specifications makes it possible to reason about the differences between components at a suitable abstraction level. Perhaps most importantly, it enables us to define and practically evaluate component compatibility with respect to a particular application context. Some of the presented ideas are still work in progress. The SOFA system includes a working tool for change type classification and compatibility assessment. Under development is the support for determining current application architecture and deriving the context. The overall goal is to provide tools that will guide the end-user in application composition and component updates, using the compatibility rules defined in this paper. There is also a lot of theoretical work to be done. One deficiency of our approach is that currently there is no way to determine whether the component actually needs all the required features – for example, if the WebDAV interface of the HTTPClient component is not used, no calls will be issued along the XMLParser interface. This should be accounted for in the definition of context and in contextual compatibility assessment. More work is also needed on the compatibility of semantic aspects. This might better be defined in relation to global rules for application consistency and semantics, and some forms of semantic specifications (for example timing
7
constraints) may be difficult to compare without additional information.
References [1] Balek, D. and Plasil, F.: Software Connectors: A Hierarchical Model. Technical report No. 2000/2, Department of Software Engineering, Charles University, Prague, 2000. [2] Boinot, P., et al.: A Declarative Approach for Designing and Developing Adaptive Components. In Proceedings of The 15th IEEE Conference on Automated Software Engineering. IEEE CS Press, September 2000. [3] Brada, P.: Component Revision Identification Based on IDL/ADL Component Specification. Accepted for poster presentation at the ESEC/FSE 2001 European Conference on Software Engineering, Vienna, Austria. [4] Christensen, H.B.: Experiences with Architectural Software Configuration Management in Ragnarok. Proceedings of SCM-8 Workshop, ECOOP 1998. Springer-Verlag 1998. [5] Debian Packaging Manual. Available at http://www.debian.org/doc/ [6] Desktop Management Task Force, Inc.: Desktop Management Interface Specification, version 2.0. DMTF 1998 [7] Gosling, J., Joy, B., Steele, G.: Java Language Specification, Chapter 13 (Java Binary Compatibility). Sun Microsystems, 1996 [8] JavaSoft: JavaBeans 1.0 Specification. Available at http://www.javasoft.com/beans/spec.html [9] Larrson, M., Crnkovic, I.: New Challenges for Configuration Management. Proceedings of the SCM-9 workshop, Toulouse, France, September 1999. LNCS 1675, Springer-Verlag 1999. [10] Mennie, D., Pagurek, B.: An Architecture to Support Dynamic Composition of Service Components. Workshop on Component Programming, ECOOP 2000. [11] Object Management Group: CORBA Components, Joint Revised Submission. OMG orbos/99-02-05 [12] Perry, D. E.: Version Control in the Inscape Environment. Proceedings of ICSE’87, Monterey, CA. [13] Peterson, M. T.: DCE: A Guide to Developing Portable Applications. McGraw-Hill, 1995. [14] Plasil, F., Balek, D., Janecek, R.: SOFA/DCUP: Architecture for Component Trading and Dynamic Updating. Proceedings of ICCDS 98, Annapolis, Maryland, USA. IEEE CS Press 1998. [15] Plasil, F., Visnovsky, S., Besta, M.: Behavior Protocols and Components. Submitted for publication to IEEE Transactions on Software Engineering 8
[16] Stav, E.: Component Based Environments for Non-Programmers: Two Case Studies. Workshop on Component Programming, ECOOP 2000. [17] Szyperski, C.: Component Software. ACM Press, Addison-Wesley 1998 [18] Zaremski, A.M. and Wing, J.M.: Specification Matching of Software Components. ACM Transactions on Software Engineering and Methodology, Vol. 6, No. 4, October 1997
9