The CoSi Component Model: Reviving the Black-Box ... - CiteSeerX

10 downloads 0 Views 221KB Size Report
Black-Box Nature of Components*. Premek Brada. Department of Computer Science and Engineering. University of West Bohemia. Pilsen, Czech Republic.
The CoSi Component Model: Reviving the Black-Box Nature of Components? Premek Brada Department of Computer Science and Engineering University of West Bohemia Pilsen, Czech Republic

Abstract. Many component models and frameworks have been created since the advent of component-based software engineering. While they all claim to follow fundamental component principles, the black-box nature in particular, a deeper look reveals surprising problems mainly in the component models built into the mainstream frameworks. In this paper we elaborate on these fundamental principles, analyse a selection of industrial and research component models in light of them, and propose a component model named CoSi. Its aim is to address the problems uncovered by the analysis while keeping the good aspects of current state state of the art models. It supports OSGi-style lightweight components with a rich set of features, and puts a strong emphasis on facilitating component comprehension and application consistency.

1

Introduction

Since the advent of component-based software engineering (CBSE), many frameworks have followed the component paradigm to support application development. Enterprise JavaBeans [1, 2], CORBA Component Model [3], Spring [4], and OSGi [5, 6] have been successful in practical applications and serve as good examples of the industrial applicability of component principles. The universally accepted foundational works [7, 8] list several constituting characteristics of components, of which the key one is the need to treat components as opaque black boxes with explicit interface declaration. These characteristics are enforced and the structure of the component’s surface1 is defined by a component model (more precisely, its meta-model part), which defines the (functional) features it provides for client components or declares as dependencies, as well as its behavioural specifications and extra-functional properties. The other ?

1

This research was supported by the grant “Methods and models for consistency verification of advanced component-based applications” number 201/08/0266 from the Grant Agency of the Czech Republic. We use this term instead of the commonly used “interface” to avoid mistaking it for an interface type as in Java or IDL; a SOFA [9] synonym is frame.

key roles [10] of component models are to describe the allowed ways of intercomponent bindings, i.e. architectural constraints, and aspects like component lifecycle management or capabilities of an underlying runtime framework. The success of CBSE lies in pursuing the black-box idea thoroughly, much further than the preceding software engineering concepts (modules, objects). For example, the design of component-based applications involves activities like highlevel architectural modeling with reused components, integration of (sometimes ill-documented) 3rd-party components, analysis of effects caused by component dependencies or updates, etc. In all these we need to reason about the complete component in the black-box fashion, without being distracted by implementation details, to overcome the conceptual complexity of the application. 1.1

The Goal of the Paper

In this paper we want to discuss in more detail the fundamental problems found in current component models and frameworks, and propose a component model called CoSi. Its aim is to address the problems while keeping the good aspects of current state state of the art models. The discussion of these models is covered by section 2 which first elaborates on the foundational characteristics (“what constitutes a good component model”) and then analyses several industrial and research component frameworks from these perspectives. It is motivated by the finding that adherence to component principles are generally acknowledged, at least superficially, by the component framework designers. Nevertheless, deeper look reveals surprising problems in the component models built into the mainstream frameworks. The approach taken in the study is a purist’s one, showing what problems are caused by the component model deficiencies. The CoSi component model is described in sections 3 and 4. Building on the results of the analysis and on an architectural rationale, the section describes the meta-model used, the capabilities of the underlying runtime framework, and several technical details. The strong and weak aspects of the proposed model are discussed in section 5, by means of comparing them with related work and component frameworks.

2

Current Component Models: Strengths and Weaknesses

There exist dozens of component models with different purposes, features and levels of popularity. In this section we first discuss the fundamental and practical properties a component model should posess, and present an digest of a broader analysis of several ones with respect to these properties; for a more detailed evaluation see [11]. The models we look at here are a sampling of the more popular ones, each representing a distinct approach – OSGi Release 4 [12], CORBA Component Model version 4 [3], and the iPOJO [13] research model.

2.1

Motivation and Evaluated Properties

The properties we were interested in are based on the characteristics generally perceived as constituting the “what and why” of software components. They have been treated in detail many times, e.g. in [7, 10, 8]. Though full agreement does not exist in the research community, the general understanding [14] is that a component is: – a black-box functional unit with explicit (contractually specified) provided features and dependencies; – a unit of independent deployment and substitution (ideally, these tasks require no human intervention); and – subject of 3rd party composition, in contexts unforeseen at design time. The first characteristics is possibly the most important one from both theoretical and practical standpoints. It takes the notions of modularity and information hiding [15] a step further conceptually as well as on the granularity scale. It also serves as a prerequisite for the latter two characteristics, and facilitates analysis and modeling of existing components (since surface representation can be reconstructed from the specification) needed during component-based development. Two more characteristics can be added that we believe are important for CBSE success. A reasonably rich set of feature types is necessary, mainly to achieve sufficient expressive power relevant to the target domain of the component model – interfaces are the accepted base but more should be available, e.g. attributes, events. This enables clean component-based design and facilitates reuse. Recent experiences of the industrial CBSE have additionally shown the importance of the ease of development with components.2 Practical aspects therefore need to be considered alongside the fundamental ones when evaluating a component model. We therefore evaluate the component models from the following perspectives: 1. Are the components truly black-box ? Does the component model prevent situations where features can be used by the clients (cross the component surface boundary) yet not described in component specification? 2. Can we easily obtain complete representation of component’s surface, e.g. for modeling purposes? Is appropriate information accessible in machine readable form (declarative meta-data, introspection), without instantiating the component? 3. Does the model help development efficiency? Is the feature set of the component model sufficiently rich and at a suitable level of abstraction, does programming a component require the creation of only a few artifacts backed by good tool support? In the following subsections we briefly describe each given model (plus associated runtime framework) and provide substantiation for its evaluation. 2

Discontent with the overly complex development using EJB components was behind the success of lightweight solutions in POJO style, like the Spring framework [4] or OSGi.

2.2

Open Services Gateway Initiative (OSGi)

The Open Services Gateway Initiative (OSGi) platform [5, 12] is an open Javabased framework for efficient component-based service deployment and management. The base component model of OSGi is relatively simple in terms of feature types and component implementation, but some of the standardized services in effect enhance it with additional features. Black box In theory, bundles have a good chance to be considered clean black boxes – all features can be specified in manifest headers, and system services can provide additional meta-data. In practice, several OSGi core aspects violate the black box principle. In particular, provided services can be registered with the framework and required services looked up and bound, without being declared in the manifest. Representation Again, the bundle manifest theoretically provides a good starting point for discovering features, and introspection together with possible additional meta-data from system services could be used to recover the details. However, all feature-related manifest headers are defined as optional and since OSGi Revision 4 the key headers Export-Service and Import-Service have been deprecated3 . This can in the extreme case turn discovering component features into complete guesswork. Development The component model itself provides only two kinds of applicationrelevant abstractions (packages and services), which is not very helpful for modeling. However, their granularity fits well with current application architectures and the conceptual simplicity is appealing also from the practical standpoint. OSGi is a code-based model with low structural overhead and good tool support. 2.3

CORBA Component Model

The CORBA Component Model [3] is the core of an industrial-strength component framework. It defines one feature-rich type of components and uses an Interface Definition Language (CIDL) plus a type repository for component specification. Black box CORBA components are clean black boxes – only the information contained in CIDL specification is available to potential clients. Since implementation skeleton is generated from the specification, mutual correspondence is ensured. Representation The declarations contained in the CIDL are stored in a type repository and therefore the representation of a given type can be obtained easily. The only disadvantage is that stand-alone component analysis is difficult if repository with its declaration is not accessible. Development CORBA component model provides one of the richest sets of features, and has the advantage of multiple implementation language support. On the other hand, developing CORBA components is rather tedious by today’s standards due to its IDL-first approach. 3

In fact, they are not much used in practice – a sample of 112 bundles we analyzed contained only 5 bundles with service specification headers.

2.4

iPOJO

The iPOJO framework [13, 16] aims at building an extensible service-oriented model on top of the OSGi infrastructure. Component features are defined by so called handlers which “wrap” the implemention POJO component class. Black box Since handlers are the only legal way to interact with iPOJO component, the component model follows the black-box principle. However, we find the component meta-model conceptually unclean – the single concept of handlers is used to implement several rather different aspects, from functional features to component management. Representation The component declaration (usually in XML format) contains all features it supports, and is clearly related to the respective Java implementation types. Component surface type reconstruction is thus straightforward. The possibility to implement new feature handlers however means there is no common meta-model which can make it extremely hard to study and understand an iPOJO component in isolation. Development The core component model is rather poor on component features, only service interfaces (provided and required) can be specified. The framework has however been designed with extensibility in mind. New handlers can be easily implemented and linked to the framework, enabling components to declare and support new types of features. 2.5

Summary

To summarize the analysis from [11] and the above text, we can say that the component models differ (sometimes significantly) in the level to which they achieve the fundamental properties of CBSE. Table 1 presents the results in a concise form including some component models not discussed above; simplified classification values were used to express the level for individual perspectives. In one sentence and exaggerating slightly, we could say that any given component model is either not too black-box, or not too practical; sometimes both. Characteristic Black-box Representation Development

OSGi poor moderate good

EJB poor moderate good

CCM good moderate moderate

iPOJO good good moderate

SOFA good moderate moderate

Table 1. Component model comparison overview

3

The CoSi Component Model and Framework

The findings about the fundamental properties of component models are not too encouraging, especially for the industrial ones. Since the development simplicity

is something we consider a value for real-world success of components, we have designed an experimental component model that aims to take the best of both worlds – strictly adhering to the fundamental concepts yet providing sufficient practicality. The model is called CoSi, an acronym from Components Simplified. 3.1

Rationale

The ideas that were driving the design of the CoSi component model are as follows, roughly in decreasing order of importance: – Strong pure black-box. That is, nothing is acccessible from outside a component except what is explicitly declared as such. – Complete yet minimal feature specification. The component specification has to contain all basic information about features (existence, name, type), introspection can be used to augment the necessary details. – Maximum simplicity in the underlying infrastructure4 . The emphasis is on the component model properties, not on the framework capabilities. In practice this means no distribution, remoting, security, or dynamic updates, simple runtime framework, preference of text over XML formats. OSGi was a strong inspiration in this respect. – Support weakly typed languages. The component model is designed so that it enables research in suitability of scripting languages for component implementation especially in the context of component substitutability. The Groovy scripting language was chosen for component implementation, for its close ties to Java. – Reasonable feature set. We want to include practically useful features like events and streams, and use named features; CORBA Component Model was inspirative in this respect. In some architectural and practical design decisions the model follows the ideas of the OSGi core, which we think in principle strikes a good balance between (potential) rigour and simplicity, despite the shortcomings described above. In fact, CoSi could be seen as an attempt to build an OSGi-like component model which is formally strong from the black-box and surface representation perspectives. This strength is achieved in particular by the emphasis on complete specification and a rich feature set. The CoSi component model design aims to make it easier and practical to use high-level abstractions for inter-component communication (thanks to the rich feature set) and to model/visualize existing components (being able to reconstruct details of all surface elements starting from component specification). 3.2

The Component Model of CoSi

The CoSi meta-model allows flat (not hierarchical) components with four feature types, a lifecycle management interface and component surface specification in the form of meta-data contained in a descriptor file; see Figure 1. 4

That’s where the “simplified” part of the model’s name comes from.

Fig. 1. The CoSi component meta-model.

The component has a provider, name and version which together provide its unique identification. Four types of features can be provided and/or required by a component. Each feature has a name (where applicable), type and optional attributes like version identification. Several features of the same type can be specified, distinguished by their names. – Service is an implementation of functionality, specified by a Java interface. Provided services are registered with the runtime container which then mediates the bindings to the requiring components. The binding is realized by service reference objects. – Type referes to a language class or interface. Provided types are exported by the component’s packages, and are accessible by the requiring components via the exporter’s classloader. – Events enable messaging among components, mediated by a system message service. Events are named and typed, which enables event consumers to set filters on the kinds of events they subscribe to. Both asynchronous and synchronous event delivery is supported. – Attributes define typed values which can be set or read by the component. Attributes can be read-only and read-write, and are implemented as (key:String, value:Object) pairs accessible via a system-wide attribute registry. The component model has no abstraction of the application architecture (like OSGi, unlike e.g. SOFA [17]), due to its flat nature. Bindings between the components, that is between pairs or tuples of provided and required features, are therefore expressed intrinsically by the matching provided-required feature pairs and managed by the CoSi container (see next). 3.3

Component Runtime – the CoSi Container

The deployment, component lifecycle management, static and dynamic feature binding (type resolution, service management), and user interactions are the task of the container . The core of the container is quite small. It keeps the various

lists, most importantly component and service registries, and exports several standard packages and interfaces for use by the installed components. An integral part of the container is however a set of standard system services, which realize core framework functionality. In the current CoSi version there are two such services: the system service which implements container startup and shutdown sequence, component lifecycle management and meta-data querying, and message service which implements the message queue and the delivery mechanisms for Event features. These services are implemented by system components, with internal structure and run-time representation of standard CoSi components. However, they are compiled into the framework implementation library and access some of its core functionalities directly.

Fig. 2. The CoSi container and runtime interfaces.

The CoSi distribution provides also some additional standard services: simple input/output, logging and shell. All of these are implemented as standard components (using Groovy and not bundled in the framework library) which get loaded at framework startup according to its configuration. The framework run-time uses a classloader architecture to isolate the individual component type spaces (similarly to OSGi, cf. [12, Section 3.4]) and allow the interaction between Java and Groovy implementation classes. There is one instance of so called module classloader per component, which loads resources located on the bundle’s own classpath. It is capable to load both Java classes (from .class files) and Groovy scripts which it translates on-the-fly to class code through the Groovy parser. In addition, this classloader loads classes of required features, by contacting the classloader of the exporting component through a delegation manager. Finally, it references a system classloader which loads the types that must be

shared – system classes (java.*, com.sun.*, groovy.* etc. packages) and CoSi framework classes. The component can access key container services and registries via an object typed to the BundleContext interface. This is injected to the component upon its activation. The context object provides functionalities that allow the component to register and obtain services, read and write attributes, obtain its own metadata, and access standard input and output streams provided by the container. 3.4

Component Lifecycle and Management

Each installed component (the package) has a unique identifier assigned by the container and is represented by a Bundle interface object. This has methods to query component state and meta-data and manage state transitions. Further, a compulsory control class or activator of the component provides operations for its initialization and lifecycle management. The component lifecycle is essentially the same as in OSGi, cf. [12, Section 4.3]. It includes the following key states: INSTALLED (component package has been successfully read, verified for formal correctness and completeness, and assigned an identifier), RESOLVED (static feature type dependencies have been resolved, component has not yet been started or has been stopped), STARTED (a resolved component is running, after calling the activator object’s start() method). Components can be installed, updated, and uninstalled. The update mechanism is simpler than in e.g. SOFA or OSGi: the component is stopped if necessary, uninstalled, the new package installed and resolved. The component’s identifier is preserved during the process. 3.5

Implementation Classes and Distribution Format

The author of a CoSi component needs to create at least the control class of the component (known as Activator in OSGi world) which implements the lifecycle callback methods. Then, an interface plus an implementation class are usually written for each provided service or event sink, and for all provided types. Last but not least, the manifest file containing specification of the component’s surface features and other meta-data has to be created. The compulsory MANIFEST.MF file, written in the standard manifest text format, contains manifest headers which declare component’s properties. The Cosi-Version meta-header enables versioning of the whole component metamodel. The OSGi rules [12, Section 3.2] for header format and parameters are used. The distribution format of the CoSi component is a JAR file with structure resembling OSGi bundle archive. The bin/, lib/ and imports/ directories are required, holding respectively the component implementation classes (in Groovy or .class format), bundled libraries (if any), and class types of component’s

required features as a “snapshot” of those types used when the component was developed. Additional directories and files can be included as needed, e.g. for documentation and component’s resources like icon or localization bundles. At present, the CoSi framework does not use any kind of elaborated component repository. The container uses a filesystem directory at a configurable location from which components are automatically installed upon container startup, and it imports the distribution packages of installed components into its internal cache. Beyond that there are no structures or services through which the component distribution packages could be obtained; that is left to future extensions. 3.6

Application Consistency

The CoSi framework enforces the consistency of component application through several integrity measures. First, the container raises an exception if an attempt is made to register a provided or bind a required feature that is not declared in the component’s meta-data. This enforces the components’ black-box nature by preventing bindings to undeclared component internals. Next, a component cannot be installed, or updated, if its dependencies cannot be satisfied by the components currently installed. For example, if the component to be installed declares an attribute dependency, the container checks whether an exporter of the attribute with the same type already exists in the framework. While these controls cannot guarantee that the depended-on feature will be available at the time the component needs it, they protect the application against run-time errors caused by clearly unsatisfiable dependencies. Quite importantly, the container ensures consistency of component bindings during the lifecycle state transitions: a STARTED (resp. INSTALLED) component cannot be stopped (resp. uninstalled) if it provides a feature bound to (resp. required by) an existing client component(s). Again, this ensures application consistency is maintained throughout architectural changes.

4

Example

To illustrate the concepts described above, let us now show an example of a simple CoSi application. It consists of a weather station component connected to sensors (e.g. thermometers); the connection is obtained via a sensor registry service, which the individual sensors need to contact in order to become available. Figure 3 provides a model of the application. The measuring station consists in this simple implementation of only two files – the control class and the manifest. The manifest file looks as follows; notice how it reflects the component’s connections, corresponding to the architecture shown in Figure 3. Bundle-Name: MeasuringStation Bundle-Version: 1.0.0 Control-Class: cz.zcu.kiv.measuringstation.impl.Activator Require-Interfaces: cz.zcu.kiv.simpleshell.SimpleShell,

Fig. 3. Example application architecture.

cz.zcu.kiv.sensorregistry.SensorRegistry Require-Types: cz.zcu.kiv.simpleshell.Command, cz.zcu.kiv.sensorregistry.Sensor Provide-Attributes: sensor.numValues; type=java.lang.Integer

The following fragment shows parts of the control class. It shows how a service reference is obtained from application context and then used, as well as the export of a provided attribute. Its value is later obtained by the Sensor objects using the context’s getAttributeValue() method. import cz.zcu.kiv.sensorregistry.SensorRegistry; // ... further imports omitted for brevity public class Activator implements BundleControl, Command { private SensorRegistry sRegistryService; private List sensors; /* Component initialization code */ public void start(BundleContext context) throws Exception { sRegistryService = (SensorRegistry) context.getService( "cz.zcu.kiv.sensorregistry.SensorRegistry"); // service call to obtain sensor references sensors = sRegistryService.getAllAvalilableSensors(); // export the provided attribute Integer numValues = ... ; // obtain from a config source context.setAttributeValue("sensor.numValues", numValues); } // ... further methods omitted for brevity }

The service registry creates and exports the corresponding service reference through the BundleContext.registerService() method. The component im-

plementation consists of the manifest (which declares this provided service) and four classes: Sensor and SensorRegistry in a cz.zcu.kiv.sensorregistry package are the public types used in communication with other components (cf. the measuring station required interfaces), the Activator and SensorRegistryImpl in package cz.zcu.kiv.sensorregistry.impl are “hidden” internals not accessible by other components. As a last example, we show a part of a user session with the CoSi container and this application. We have augmented the main component’s implementation with an attempt to set another attribute, to present the black-box checks enforced by the container (compare with the output of attributes 6 command). After stopping the wind speed sensor component, the measures which the station outputs contain only the values from the temperature sensor. D:\work\research\CoSi\>.\start Starting CoSi framework... Bundle measuringstation.jar cannot set attribute sensor.windDirection because this attribute isn’t in Provide-Attribute header of manifest.mf. >attributes 6 Provided attributes of bundle measuringstation.jar (id 6) -------------------------------------------------------sensor.numValues (java.lang.Integer) >stop 4 >measure Sensor type: Temperature 19,9 degree Celsius 13,1 degree Celsius >

5